From dbbc2f19f4c0bd71d2c1c81754a6fe449c770604 Mon Sep 17 00:00:00 2001 From: Haojian Wu Date: Sun, 14 Jun 2015 16:19:53 +0800 Subject: [PATCH] Implement cookies.get API. --- atom/browser/api/atom_api_cookies.cc | 209 +++++++++++++++++++++++++++ atom/browser/api/atom_api_cookies.h | 56 +++++++ atom/browser/api/lib/cookies.coffee | 3 + atom/common/node_bindings.cc | 1 + filenames.gypi | 3 + 5 files changed, 272 insertions(+) create mode 100644 atom/browser/api/atom_api_cookies.cc create mode 100644 atom/browser/api/atom_api_cookies.h create mode 100644 atom/browser/api/lib/cookies.coffee diff --git a/atom/browser/api/atom_api_cookies.cc b/atom/browser/api/atom_api_cookies.cc new file mode 100644 index 000000000000..bc784321da81 --- /dev/null +++ b/atom/browser/api/atom_api_cookies.cc @@ -0,0 +1,209 @@ +// Copyright (c) 2015 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "atom/browser/api/atom_api_cookies.h" + +#include "atom/browser/atom_browser_context.h" +#include "atom/common/native_mate_converters/value_converter.h" +#include "base/bind.h" +#include "content/public/browser/browser_thread.h" +#include "native_mate/callback.h" +#include "native_mate/dictionary.h" +#include "native_mate/object_template_builder.h" +#include "net/cookies/cookie_monster.h" +#include "net/cookies/cookie_store.h" +#include "net/cookies/cookie_util.h" +#include "net/url_request/url_request_context.h" + +#include "atom/common/node_includes.h" + +using content::BrowserThread; + +namespace { + +bool GetCookieListFromStore(net::CookieStore* cookie_store, + const std::string& url, + const net::CookieMonster::GetCookieListCallback& callback) { + DCHECK(cookie_store); + GURL gurl(url); + net::CookieMonster* monster = cookie_store->GetCookieMonster(); + // Empty url will match all url cookies. + if (url.empty()) { + monster->GetAllCookiesAsync(callback); + return true; + } + + if (!gurl.is_valid()) + return false; + + monster->GetAllCookiesForURLAsync(gurl, callback); + return true; +} + +void RunGetCookiesCallbackOnUIThread(const base::DictionaryValue* filter, + const std::string& error_message, const net::CookieList& cookie_list, + const atom::api::Cookies::GetCookiesCallback& callback) { + // Should release filter here. + delete filter; + + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + v8::Locker locker(isolate); + v8::HandleScope handle_scope(isolate); + + if (!error_message.empty()) { + v8::Local error = v8::String::NewFromUtf8(isolate, + error_message.c_str()); + callback.Run(error, v8::Null(isolate)); + return; + } + callback.Run(v8::Null(isolate), + mate::Converter::ToV8(isolate, cookie_list)); +} + +bool MatchesDomain(const base::DictionaryValue* filter, + const std::string& cookie_domain) { + std::string filter_domain; + if (!filter->GetString("domain", &filter_domain)) + return true; + + // Add a leading '.' character to the filter domain if it doesn't exist. + if (net::cookie_util::DomainIsHostOnly(filter_domain)) + filter_domain.insert(0, "."); + + std::string sub_domain(cookie_domain); + // Strip any leading '.' character from the input cookie domain. + if (!net::cookie_util::DomainIsHostOnly(sub_domain)) + sub_domain = sub_domain.substr(1); + + // Now check whether the domain argument is a subdomain of the filter domain. + for (sub_domain.insert(0, "."); + sub_domain.length() >= filter_domain.length();) { + if (sub_domain == filter_domain) { + return true; + } + const size_t next_dot = sub_domain.find('.', 1); // Skip over leading dot. + sub_domain.erase(0, next_dot); + } + return false; +} + +bool MatchesCookie(const base::DictionaryValue* filter, + const net::CanonicalCookie& cookie) { + std::string name, domain, path; + bool is_secure, session; + if (filter->GetString("name", &name) && name != cookie.Name()) + return false; + if (filter->GetString("path", &path) && path != cookie.Path()) + return false; + if (!MatchesDomain(filter, cookie.Domain())) + return false; + if (filter->GetBoolean("secure", &is_secure) && + is_secure != cookie.IsSecure()) + return false; + if (filter->GetBoolean("session", &session) && + session != cookie.IsPersistent()) + return false; + return true; +} + +} // namespace + +namespace mate { + +template<> +struct Converter { + static v8::Local ToV8(v8::Isolate* isolate, + const net::CanonicalCookie& val) { + mate::Dictionary dict(isolate, v8::Object::New(isolate)); + dict.Set("name", val.Name()); + dict.Set("value", val.Value()); + dict.Set("source", val.Source()); + dict.Set("domain", val.Domain()); + dict.Set("path", val.Path()); + dict.Set("secure", val.IsSecure()); + dict.Set("http_only", val.IsHttpOnly()); + dict.Set("session", val.IsPersistent()); + if (!val.IsPersistent()) + dict.Set("expirationDate", val.ExpiryDate().ToDoubleT()); + return dict.GetHandle(); + } +}; + +} + +namespace atom { + +namespace api { + +Cookies::Cookies() { +} + +Cookies::~Cookies() { +} + +void Cookies::Get(const base::DictionaryValue& options, + const GetCookiesCallback& callback) { + // The filter will be deleted manually after callback function finishes. + base::DictionaryValue* filter = options.DeepCopyWithoutEmptyChildren(); + + content::BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, + base::Bind(&Cookies::GetCookiesOnIOThread, base::Unretained(this), + filter, callback)); +} + +void Cookies::GetCookiesOnIOThread(const base::DictionaryValue* filter, + const GetCookiesCallback& callback) { + net::CookieStore* cookie_store = + AtomBrowserContext::Get()->url_request_context_getter() + ->GetURLRequestContext()->cookie_store(); + std::string url; + filter->GetString("url", &url); + if (!GetCookieListFromStore(cookie_store, url, + base::Bind(&Cookies::OnGetCookies, base::Unretained(this), filter, + callback))) { + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + base::Bind(&RunGetCookiesCallbackOnUIThread, filter, "url is not valid", + net::CookieList(), callback)); + } +} + +void Cookies::OnGetCookies(const base::DictionaryValue* filter, + const GetCookiesCallback& callback, + const net::CookieList& cookie_list) { + net::CookieList result; + for (const auto& cookie : cookie_list) { + if (MatchesCookie(filter, cookie)) + result.push_back(cookie); + } + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind( + &RunGetCookiesCallbackOnUIThread, filter, "", result, callback)); +} + +mate::ObjectTemplateBuilder Cookies::GetObjectTemplateBuilder( + v8::Isolate* isolate) { + return mate::ObjectTemplateBuilder(isolate) + .SetMethod("get", &Cookies::Get); +} + +// static +mate::Handle Cookies::Create(v8::Isolate* isolate) { + return CreateHandle(isolate, new Cookies); +} + +} // namespace api + +} // namespace atom + +namespace { + +void Initialize(v8::Local exports, v8::Local unused, + v8::Local context, void* priv) { + v8::Isolate* isolate = context->GetIsolate(); + mate::Dictionary dict(isolate, exports); + dict.Set("cookies", atom::api::Cookies::Create(isolate)); +} + +} // namespace + +NODE_MODULE_CONTEXT_AWARE_BUILTIN(atom_browser_cookies, Initialize); diff --git a/atom/browser/api/atom_api_cookies.h b/atom/browser/api/atom_api_cookies.h new file mode 100644 index 000000000000..ad0988106df1 --- /dev/null +++ b/atom/browser/api/atom_api_cookies.h @@ -0,0 +1,56 @@ +// Copyright (c) 2015 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef ATOM_BROWSER_API_ATOM_API_COOKIES_H_ +#define ATOM_BROWSER_API_ATOM_API_COOKIES_H_ + +#include + +#include "base/callback.h" +#include "base/values.h" +#include "native_mate/wrappable.h" +#include "native_mate/handle.h" +#include "native_mate/dictionary.h" + +#include "net/cookies/canonical_cookie.h" + +namespace atom { + +namespace api { + +class Cookies : public mate::Wrappable { + public: + // node.js style callback function(error, result) + typedef base::Callback, v8::Local)> + GetCookiesCallback; + + static mate::Handle Create(v8::Isolate* isolate); + + protected: + Cookies(); + ~Cookies(); + + void Get(const base::DictionaryValue& options, + const GetCookiesCallback& callback); + + void GetCookiesOnIOThread(const base::DictionaryValue* filter, + const GetCookiesCallback& callback); + + void OnGetCookies(const base::DictionaryValue* filter, + const GetCookiesCallback& callback, + const net::CookieList& cookie_list); + + // mate::Wrappable implementations: + mate::ObjectTemplateBuilder GetObjectTemplateBuilder( + v8::Isolate* isolate) override; + + private: + DISALLOW_COPY_AND_ASSIGN(Cookies); +}; + +} // namespace api + +} // namespace atom + +#endif // ATOM_BROWSER_API_ATOM_API_COOKIES_H_ diff --git a/atom/browser/api/lib/cookies.coffee b/atom/browser/api/lib/cookies.coffee new file mode 100644 index 000000000000..1d9114fd2c19 --- /dev/null +++ b/atom/browser/api/lib/cookies.coffee @@ -0,0 +1,3 @@ +bindings = process.atomBinding 'cookies' + +module.exports = bindings.cookies diff --git a/atom/common/node_bindings.cc b/atom/common/node_bindings.cc index eb627d06b757..329683c60602 100644 --- a/atom/common/node_bindings.cc +++ b/atom/common/node_bindings.cc @@ -32,6 +32,7 @@ using content::BrowserThread; REFERENCE_MODULE(atom_browser_app); REFERENCE_MODULE(atom_browser_auto_updater); REFERENCE_MODULE(atom_browser_content_tracing); +REFERENCE_MODULE(atom_browser_cookies); REFERENCE_MODULE(atom_browser_dialog); REFERENCE_MODULE(atom_browser_menu); REFERENCE_MODULE(atom_browser_power_monitor); diff --git a/filenames.gypi b/filenames.gypi index 99d37b5d3093..e4da4c89398d 100644 --- a/filenames.gypi +++ b/filenames.gypi @@ -13,6 +13,7 @@ 'atom/browser/api/lib/auto-updater.coffee', 'atom/browser/api/lib/browser-window.coffee', 'atom/browser/api/lib/content-tracing.coffee', + 'atom/browser/api/lib/cookies.coffee', 'atom/browser/api/lib/dialog.coffee', 'atom/browser/api/lib/global-shortcut.coffee', 'atom/browser/api/lib/ipc.coffee', @@ -69,6 +70,8 @@ 'atom/browser/api/atom_api_auto_updater.cc', 'atom/browser/api/atom_api_auto_updater.h', 'atom/browser/api/atom_api_content_tracing.cc', + 'atom/browser/api/atom_api_cookies.cc', + 'atom/browser/api/atom_api_cookies.h', 'atom/browser/api/atom_api_dialog.cc', 'atom/browser/api/atom_api_global_shortcut.cc', 'atom/browser/api/atom_api_global_shortcut.h',