diff --git a/atom/browser/api/atom_api_cookies.cc b/atom/browser/api/atom_api_cookies.cc index 86cc59847b24..763052e1b128 100644 --- a/atom/browser/api/atom_api_cookies.cc +++ b/atom/browser/api/atom_api_cookies.cc @@ -20,6 +20,7 @@ #include "net/url_request/url_request_context.h" #include "net/url_request/url_request_context_getter.h" +using atom::AtomCookieDelegate; using content::BrowserThread; namespace mate { @@ -54,6 +55,27 @@ struct Converter { } }; +template<> +struct Converter { + static v8::Local ToV8(v8::Isolate* isolate, + const AtomCookieDelegate::ChangeCause& val) { + switch (val) { + case AtomCookieDelegate::ChangeCause::CHANGE_COOKIE_EXPLICIT: + return mate::StringToV8(isolate, "explicit"); + case AtomCookieDelegate::ChangeCause::CHANGE_COOKIE_OVERWRITE: + return mate::StringToV8(isolate, "overwrite"); + case AtomCookieDelegate::ChangeCause::CHANGE_COOKIE_EXPIRED: + return mate::StringToV8(isolate, "expired"); + case AtomCookieDelegate::ChangeCause::CHANGE_COOKIE_EVICTED: + return mate::StringToV8(isolate, "evicted"); + case AtomCookieDelegate::ChangeCause::CHANGE_COOKIE_EXPIRED_OVERWRITE: + return mate::StringToV8(isolate, "expired-overwrite"); + default: + return mate::StringToV8(isolate, "unknown"); + } + } +}; + } // namespace mate namespace atom { @@ -206,11 +228,14 @@ void SetCookieOnIO(scoped_refptr getter, Cookies::Cookies(v8::Isolate* isolate, AtomBrowserContext* browser_context) - : request_context_getter_(browser_context->url_request_context_getter()) { + : request_context_getter_(browser_context->url_request_context_getter()), + cookie_delegate_(browser_context->cookie_delegate()) { Init(isolate); + cookie_delegate_->AddObserver(this); } Cookies::~Cookies() { + cookie_delegate_->RemoveObserver(this); } void Cookies::Get(const base::DictionaryValue& filter, @@ -239,6 +264,13 @@ void Cookies::Set(const base::DictionaryValue& details, base::Bind(SetCookieOnIO, getter, Passed(&copied), callback)); } +void Cookies::OnCookieChanged(const net::CanonicalCookie& cookie, + bool removed, + AtomCookieDelegate::ChangeCause cause) { + Emit("changed", cookie, cause, removed); +} + + // static mate::Handle Cookies::Create( v8::Isolate* isolate, diff --git a/atom/browser/api/atom_api_cookies.h b/atom/browser/api/atom_api_cookies.h index 5f60cb7ab49d..a27a7780e5af 100644 --- a/atom/browser/api/atom_api_cookies.h +++ b/atom/browser/api/atom_api_cookies.h @@ -8,6 +8,7 @@ #include #include "atom/browser/api/trackable_object.h" +#include "atom/browser/net/atom_cookie_delegate.h" #include "base/callback.h" #include "native_mate/handle.h" #include "net/cookies/canonical_cookie.h" @@ -26,7 +27,8 @@ class AtomBrowserContext; namespace api { -class Cookies : public mate::TrackableObject { +class Cookies : public mate::TrackableObject, + public AtomCookieDelegate::Observer { public: enum Error { SUCCESS, @@ -52,8 +54,14 @@ class Cookies : public mate::TrackableObject { const base::Closure& callback); void Set(const base::DictionaryValue& details, const SetCallback& callback); + // AtomCookieDelegate::Observer: + void OnCookieChanged(const net::CanonicalCookie& cookie, + bool removed, + AtomCookieDelegate::ChangeCause cause) override; + private: net::URLRequestContextGetter* request_context_getter_; + scoped_refptr cookie_delegate_; DISALLOW_COPY_AND_ASSIGN(Cookies); }; diff --git a/atom/browser/api/atom_api_session.cc b/atom/browser/api/atom_api_session.cc index 45405099ddef..96fcca561d9d 100644 --- a/atom/browser/api/atom_api_session.cc +++ b/atom/browser/api/atom_api_session.cc @@ -49,6 +49,7 @@ #include "net/url_request/url_request_context_getter.h" #include "ui/base/l10n/l10n_util.h" +using atom::api::Cookies; using content::BrowserThread; using content::StoragePartition; @@ -335,7 +336,7 @@ void OnClearStorageDataDone(const base::Closure& callback) { Session::Session(v8::Isolate* isolate, AtomBrowserContext* browser_context) : devtools_network_emulation_client_id_(base::GenerateGUID()), browser_context_(browser_context) { - // Observe DownloadManger to get download notifications. + // Observe DownloadManager to get download notifications. content::BrowserContext::GetDownloadManager(browser_context)-> AddObserver(this); @@ -521,7 +522,7 @@ void Session::GetBlobData( v8::Local Session::Cookies(v8::Isolate* isolate) { if (cookies_.IsEmpty()) { - auto handle = atom::api::Cookies::Create(isolate, browser_context()); + auto handle = Cookies::Create(isolate, browser_context()); cookies_.Reset(isolate, handle.ToV8()); } return v8::Local::New(isolate, cookies_); @@ -631,6 +632,7 @@ void Initialize(v8::Local exports, v8::Local unused, v8::Isolate* isolate = context->GetIsolate(); mate::Dictionary dict(isolate, exports); dict.Set("Session", Session::GetConstructor(isolate)->GetFunction()); + dict.Set("Cookies", Cookies::GetConstructor(isolate)->GetFunction()); dict.SetMethod("fromPartition", &FromPartition); } diff --git a/atom/browser/atom_browser_context.cc b/atom/browser/atom_browser_context.cc index 0b8146995d08..63c8c81fb4df 100644 --- a/atom/browser/atom_browser_context.cc +++ b/atom/browser/atom_browser_context.cc @@ -71,7 +71,8 @@ AtomBrowserContext::AtomBrowserContext( const std::string& partition, bool in_memory, const base::DictionaryValue& options) : brightray::BrowserContext(partition, in_memory), - network_delegate_(new AtomNetworkDelegate) { + network_delegate_(new AtomNetworkDelegate), + cookie_delegate_(new AtomCookieDelegate) { // Construct user agent string. Browser* browser = Browser::Get(); std::string name = RemoveWhitespace(browser->GetName()); @@ -107,6 +108,10 @@ net::NetworkDelegate* AtomBrowserContext::CreateNetworkDelegate() { return network_delegate_; } +net::CookieMonsterDelegate* AtomBrowserContext::CreateCookieDelegate() { + return cookie_delegate(); +} + std::string AtomBrowserContext::GetUserAgent() { return user_agent_; } diff --git a/atom/browser/atom_browser_context.h b/atom/browser/atom_browser_context.h index 23d8c64b62c9..0ff1cc6321ad 100644 --- a/atom/browser/atom_browser_context.h +++ b/atom/browser/atom_browser_context.h @@ -8,7 +8,9 @@ #include #include +#include "atom/browser/net/atom_cookie_delegate.h" #include "brightray/browser/browser_context.h" +#include "net/cookies/cookie_monster.h" namespace atom { @@ -31,6 +33,7 @@ class AtomBrowserContext : public brightray::BrowserContext { // brightray::URLRequestContextGetter::Delegate: net::NetworkDelegate* CreateNetworkDelegate() override; + net::CookieMonsterDelegate* CreateCookieDelegate() override; std::string GetUserAgent() override; std::unique_ptr CreateURLRequestJobFactory( content::ProtocolHandlerMap* protocol_handlers) override; @@ -50,6 +53,9 @@ class AtomBrowserContext : public brightray::BrowserContext { AtomBlobReader* GetBlobReader(); AtomNetworkDelegate* network_delegate() const { return network_delegate_; } + AtomCookieDelegate* cookie_delegate() const { + return cookie_delegate_.get(); + } protected: AtomBrowserContext(const std::string& partition, bool in_memory, @@ -66,6 +72,7 @@ class AtomBrowserContext : public brightray::BrowserContext { // Managed by brightray::BrowserContext. AtomNetworkDelegate* network_delegate_; + scoped_refptr cookie_delegate_; DISALLOW_COPY_AND_ASSIGN(AtomBrowserContext); }; diff --git a/atom/browser/net/atom_cookie_delegate.cc b/atom/browser/net/atom_cookie_delegate.cc new file mode 100644 index 000000000000..5024a34ada3e --- /dev/null +++ b/atom/browser/net/atom_cookie_delegate.cc @@ -0,0 +1,41 @@ +// Copyright (c) 2016 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "atom/browser/net/atom_cookie_delegate.h" + +#include "content/public/browser/browser_thread.h" + +namespace atom { + +AtomCookieDelegate::AtomCookieDelegate() { +} + +AtomCookieDelegate::~AtomCookieDelegate() { +} + +void AtomCookieDelegate::AddObserver(Observer* observer) { + observers_.AddObserver(observer); +} + +void AtomCookieDelegate::RemoveObserver(Observer* observer) { + observers_.RemoveObserver(observer); +} + +void AtomCookieDelegate::NotifyObservers( + const net::CanonicalCookie& cookie, bool removed, ChangeCause cause) { + FOR_EACH_OBSERVER(Observer, + observers_, + OnCookieChanged(cookie, removed, cause)); +} + +void AtomCookieDelegate::OnCookieChanged( + const net::CanonicalCookie& cookie, bool removed, ChangeCause cause) { + content::BrowserThread::PostTask( + content::BrowserThread::UI, + FROM_HERE, + base::Bind(&AtomCookieDelegate::NotifyObservers, + this, cookie, removed, cause)); +} + +} // namespace atom diff --git a/atom/browser/net/atom_cookie_delegate.h b/atom/browser/net/atom_cookie_delegate.h new file mode 100644 index 000000000000..20acbd3b7ca9 --- /dev/null +++ b/atom/browser/net/atom_cookie_delegate.h @@ -0,0 +1,48 @@ +// Copyright (c) 2016 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef ATOM_BROWSER_NET_ATOM_COOKIE_DELEGATE_H_ +#define ATOM_BROWSER_NET_ATOM_COOKIE_DELEGATE_H_ + +#include "base/observer_list.h" +#include "net/cookies/cookie_monster.h" + +namespace atom { + +class AtomCookieDelegate : public net::CookieMonsterDelegate { + public: + AtomCookieDelegate(); + ~AtomCookieDelegate() override; + + class Observer { + public: + virtual void OnCookieChanged(const net::CanonicalCookie& cookie, + bool removed, + ChangeCause cause) {} + protected: + virtual ~Observer() {} + }; + + void AddObserver(Observer* observer); + void RemoveObserver(Observer* observer); + + // net::CookieMonsterDelegate: + void OnCookieChanged(const net::CanonicalCookie& cookie, + bool removed, + ChangeCause cause) override; + + + private: + base::ObserverList observers_; + + void NotifyObservers(const net::CanonicalCookie& cookie, + bool removed, + ChangeCause cause); + + DISALLOW_COPY_AND_ASSIGN(AtomCookieDelegate); +}; + +} // namespace atom + +#endif // ATOM_BROWSER_NET_ATOM_COOKIE_DELEGATE_H_ diff --git a/docs/api/session.md b/docs/api/session.md index 4870400724ed..4883f915ff58 100644 --- a/docs/api/session.md +++ b/docs/api/session.md @@ -395,6 +395,27 @@ session.defaultSession.cookies.set(cookie, (error) => { }) ``` +### Instance Events + +The following events are available on instances of `Cookies`: + +#### Event: 'changed' + +* `event` Event +* `cookie` Object - The cookie that was changed +* `cause` String - The cause of the change with one of the following values: + * `explicit` - The cookie was changed directly by a consumer's action. + * `overwrite` - The cookie was automatically removed due to an insert + operation that overwrote it. + * `expired` - The cookie was automatically removed as it expired. + * `evicted` - The cookie was automatically evicted during garbage collection. + * `expired-overwrite` - The cookie was overwritten with an already-expired + expiration date. +* `removed` Boolean - `true` if the cookie was removed, `false` otherwise. + +Emitted when a cookie is changed because it was added, edited, removed, or +expired. + ### Instance Methods The following methods are available on instances of `Cookies`: diff --git a/filenames.gypi b/filenames.gypi index d8b3ede3c59a..9c4b42f622d5 100644 --- a/filenames.gypi +++ b/filenames.gypi @@ -227,6 +227,8 @@ 'atom/browser/net/asar/url_request_asar_job.h', 'atom/browser/net/atom_cert_verifier.cc', 'atom/browser/net/atom_cert_verifier.h', + 'atom/browser/net/atom_cookie_delegate.cc', + 'atom/browser/net/atom_cookie_delegate.h', 'atom/browser/net/atom_network_delegate.cc', 'atom/browser/net/atom_network_delegate.h', 'atom/browser/net/atom_ssl_config_service.cc', diff --git a/lib/browser/api/session.js b/lib/browser/api/session.js index af553e442e1a..ac47244db326 100644 --- a/lib/browser/api/session.js +++ b/lib/browser/api/session.js @@ -1,6 +1,6 @@ const {EventEmitter} = require('events') const {app} = require('electron') -const {fromPartition, Session} = process.atomBinding('session') +const {fromPartition, Session, Cookies} = process.atomBinding('session') // Public API. Object.defineProperties(exports, { @@ -15,6 +15,7 @@ Object.defineProperties(exports, { }) Object.setPrototypeOf(Session.prototype, EventEmitter.prototype) +Object.setPrototypeOf(Cookies.prototype, EventEmitter.prototype) Session.prototype._init = function () { app.emit('session-created', this) diff --git a/spec/api-session-spec.js b/spec/api-session-spec.js index 2d8d1ca82a2f..c01abcc6edd3 100644 --- a/spec/api-session-spec.js +++ b/spec/api-session-spec.js @@ -179,6 +179,37 @@ describe('session module', function () { }) }) }) + + it('emits a changed event when a cookie is added or removed', function (done) { + const {cookies} = session.fromPartition('cookies-changed') + + cookies.once('changed', function (event, cookie, cause, removed) { + assert.equal(cookie.name, 'foo') + assert.equal(cookie.value, 'bar') + assert.equal(cause, 'explicit') + assert.equal(removed, false) + + cookies.once('changed', function (event, cookie, cause, removed) { + assert.equal(cookie.name, 'foo') + assert.equal(cookie.value, 'bar') + assert.equal(cause, 'explicit') + assert.equal(removed, true) + done() + }) + + cookies.remove(url, 'foo', function (error) { + if (error) return done(error) + }) + }) + + cookies.set({ + url: url, + name: 'foo', + value: 'bar' + }, function (error) { + if (error) return done(error) + }) + }) }) describe('ses.clearStorageData(options)', function () { diff --git a/vendor/brightray b/vendor/brightray index bac6039049b1..e4626bac7e9c 160000 --- a/vendor/brightray +++ b/vendor/brightray @@ -1 +1 @@ -Subproject commit bac6039049b1463e0ce2ecc7f7ced90e96bedc25 +Subproject commit e4626bac7e9c9cca78231107d07bb017d2a30db6