Commit 882ea279 authored by Victor Costan's avatar Victor Costan Committed by Commit Bot

Network Service: CookieManager observer API refactoring.

This CL aligns CookieManager's interface for listening to cookie changes
(observer pattern) with the proposed addition to
RestrictedCookieManager, which will be used by the Async Cookies API.

Bug: 729800
Cq-Include-Trybots: master.tryserver.chromium.linux:linux_mojo
Change-Id: I0464b0d1d0388279e549d19c5dbeea2d0342cc60
Reviewed-on: https://chromium-review.googlesource.com/913151Reviewed-by: default avatarRandy Smith <rdsmith@chromium.org>
Reviewed-by: default avatarTom Sepez <tsepez@chromium.org>
Reviewed-by: default avatarReilly Grant <reillyg@chromium.org>
Commit-Queue: Victor Costan <pwnall@chromium.org>
Cr-Commit-Position: refs/heads/master@{#536944}
parent 895d8f4a
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
#include "net/cookies/canonical_cookie.h" #include "net/cookies/canonical_cookie.h"
ExtensionCookieNotifier::ExtensionCookieNotifier(Profile* profile) ExtensionCookieNotifier::ExtensionCookieNotifier(Profile* profile)
: profile_(profile) { : profile_(profile), binding_(this) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI); DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
DCHECK(profile); DCHECK(profile);
...@@ -28,14 +28,12 @@ ExtensionCookieNotifier::ExtensionCookieNotifier(Profile* profile) ...@@ -28,14 +28,12 @@ ExtensionCookieNotifier::ExtensionCookieNotifier(Profile* profile)
->GetNetworkContext() ->GetNetworkContext()
->GetCookieManager(mojo::MakeRequest(&manager_ptr)); ->GetCookieManager(mojo::MakeRequest(&manager_ptr));
network::mojom::CookieChangeNotificationPtr notification_ptr; network::mojom::CookieChangeListenerPtr listener_ptr;
binding_ = binding_.Bind(mojo::MakeRequest(&listener_ptr));
std::make_unique<mojo::Binding<network::mojom::CookieChangeNotification>>( manager_ptr->AddGlobalChangeListener(std::move(listener_ptr));
this, mojo::MakeRequest(&notification_ptr));
manager_ptr->RequestGlobalNotifications(std::move(notification_ptr));
} }
void ExtensionCookieNotifier::OnCookieChanged( void ExtensionCookieNotifier::OnCookieChange(
const net::CanonicalCookie& cookie, const net::CanonicalCookie& cookie,
network::mojom::CookieChangeCause cause) { network::mojom::CookieChangeCause cause) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI); DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
......
...@@ -21,20 +21,18 @@ class CanonicalCookie; ...@@ -21,20 +21,18 @@ class CanonicalCookie;
// Sends cookie-change notifications on the UI thread via // Sends cookie-change notifications on the UI thread via
// chrome::NOTIFICATION_COOKIE_CHANGED_FOR_EXTENSIONS for all cookie // chrome::NOTIFICATION_COOKIE_CHANGED_FOR_EXTENSIONS for all cookie
// changes associated with the given profile. // changes associated with the given profile.
class ExtensionCookieNotifier class ExtensionCookieNotifier : public network::mojom::CookieChangeListener {
: public network::mojom::CookieChangeNotification {
public: public:
explicit ExtensionCookieNotifier(Profile* profile); explicit ExtensionCookieNotifier(Profile* profile);
~ExtensionCookieNotifier() override; ~ExtensionCookieNotifier() override;
private: private:
// network::mojom::CookieChangeNotification implementation. // network::mojom::CookieChangeListener implementation.
void OnCookieChanged(const net::CanonicalCookie& cookie, void OnCookieChange(const net::CanonicalCookie& cookie,
network::mojom::CookieChangeCause cause) override; network::mojom::CookieChangeCause cause) override;
Profile* profile_; Profile* profile_;
std::unique_ptr<mojo::Binding<network::mojom::CookieChangeNotification>> mojo::Binding<network::mojom::CookieChangeListener> binding_;
binding_;
DISALLOW_COPY_AND_ASSIGN(ExtensionCookieNotifier); DISALLOW_COPY_AND_ASSIGN(ExtensionCookieNotifier);
}; };
......
...@@ -144,9 +144,15 @@ network::mojom::CookieChangeCause ChangeCauseTranslation( ...@@ -144,9 +144,15 @@ network::mojom::CookieChangeCause ChangeCauseTranslation(
} // namespace } // namespace
CookieManager::NotificationRegistration::NotificationRegistration() {} CookieManager::ListenerRegistration::ListenerRegistration() {}
CookieManager::NotificationRegistration::~NotificationRegistration() {} CookieManager::ListenerRegistration::~ListenerRegistration() {}
void CookieManager::ListenerRegistration::DispatchCookieStoreChange(
const net::CanonicalCookie& cookie,
net::CookieStore::ChangeCause cause) {
listener->OnCookieChange(cookie, ChangeCauseTranslation(cause));
}
CookieManager::CookieManager(net::CookieStore* cookie_store) CookieManager::CookieManager(net::CookieStore* cookie_store)
: cookie_store_(cookie_store) {} : cookie_store_(cookie_store) {}
...@@ -197,98 +203,74 @@ void CookieManager::DeleteCookies( ...@@ -197,98 +203,74 @@ void CookieManager::DeleteCookies(
std::move(callback)); std::move(callback));
} }
void CookieManager::RequestNotification( void CookieManager::AddCookieChangeListener(
const GURL& url, const GURL& url,
const std::string& name, const std::string& name,
network::mojom::CookieChangeNotificationPtr notification_pointer) { network::mojom::CookieChangeListenerPtr listener) {
std::unique_ptr<NotificationRegistration> notification_registration( auto listener_registration = std::make_unique<ListenerRegistration>();
std::make_unique<NotificationRegistration>()); listener_registration->listener = std::move(listener);
notification_registration->notification_pointer =
std::move(notification_pointer);
notification_registration->subscription = cookie_store_->AddCallbackForCookie( listener_registration->subscription = cookie_store_->AddCallbackForCookie(
url, name, url, name,
base::BindRepeating( base::BindRepeating(
&CookieManager::CookieChanged, &CookieManager::ListenerRegistration::DispatchCookieStoreChange,
// base::Unretained is safe as destruction of the
// CookieManager will also destroy the
// notifications_registered list (which this object will be
// inserted into, below), which will destroy the
// CookieChangedSubscription, unregistering the callback.
base::Unretained(this),
// base::Unretained is safe as destruction of the // base::Unretained is safe as destruction of the
// NotificationRegistration will also destroy the // ListenerRegistration will also destroy the
// CookieChangedSubscription, unregistering the callback. // CookieChangedSubscription, unregistering the callback.
base::Unretained(notification_registration.get()))); base::Unretained(listener_registration.get())));
notification_registration->notification_pointer.set_connection_error_handler( listener_registration->listener.set_connection_error_handler(
base::BindOnce(&CookieManager::NotificationPipeBroken, base::BindOnce(&CookieManager::RemoveChangeListener,
// base::Unretained is safe as destruction of the // base::Unretained is safe as destruction of the
// CookieManager will also destroy the // CookieManager will also destroy the
// notifications_registered list (which this object will be // notifications_registered list (which this object will be
// inserted into, below), which will destroy the // inserted into, below), which will destroy the
// notification_pointer, rendering this callback moot. // listener, rendering this callback moot.
base::Unretained(this), base::Unretained(this),
// base::Unretained is safe as destruction of the // base::Unretained is safe as destruction of the
// NotificationRegistration will also destroy the // ListenerRegistration will also destroy the
// CookieChangedSubscription, unregistering the callback. // CookieChangedSubscription, unregistering the callback.
base::Unretained(notification_registration.get()))); base::Unretained(listener_registration.get())));
notifications_registered_.push_back(std::move(notification_registration)); listener_registrations_.push_back(std::move(listener_registration));
} }
void CookieManager::RequestGlobalNotifications( void CookieManager::AddGlobalChangeListener(
network::mojom::CookieChangeNotificationPtr notification_pointer) { network::mojom::CookieChangeListenerPtr listener) {
std::unique_ptr<NotificationRegistration> notification_registration( auto listener_registration = std::make_unique<ListenerRegistration>();
std::make_unique<NotificationRegistration>()); listener_registration->listener = std::move(listener);
notification_registration->notification_pointer =
std::move(notification_pointer);
notification_registration->subscription = listener_registration->subscription =
cookie_store_->AddCallbackForAllChanges(base::BindRepeating( cookie_store_->AddCallbackForAllChanges(base::BindRepeating(
&CookieManager::CookieChanged, &CookieManager::ListenerRegistration::DispatchCookieStoreChange,
// base::Unretained is safe as destruction of the // base::Unretained is safe as destruction of the
// CookieManager will also destroy the // ListenerRegistration will also destroy the
// notifications_registered list (which this object will be
// inserted into, below), which will destroy the
// CookieChangedSubscription, unregistering the callback.
base::Unretained(this),
// base::Unretained is safe as destruction of the
// NotificationRegistration will also destroy the
// CookieChangedSubscription, unregistering the callback. // CookieChangedSubscription, unregistering the callback.
base::Unretained(notification_registration.get()))); base::Unretained(listener_registration.get())));
notification_registration->notification_pointer.set_connection_error_handler( listener_registration->listener.set_connection_error_handler(
base::BindOnce(&CookieManager::NotificationPipeBroken, base::BindOnce(&CookieManager::RemoveChangeListener,
// base::Unretained is safe as destruction of the // base::Unretained is safe as destruction of the
// CookieManager will also destroy the // CookieManager will also destroy the
// notifications_registered list (which this object will be // notifications_registered list (which this object will be
// inserted into, below), which will destroy the // inserted into, below), which will destroy the
// notification_pointer, rendering this callback moot. // listener, rendering this callback moot.
base::Unretained(this), base::Unretained(this),
// base::Unretained is safe as destruction of the // base::Unretained is safe as destruction of the
// NotificationRegistration will also destroy the // ListenerRegistration will also destroy the
// CookieChangedSubscription, unregistering the callback. // CookieChangedSubscription, unregistering the callback.
base::Unretained(notification_registration.get()))); base::Unretained(listener_registration.get())));
notifications_registered_.push_back(std::move(notification_registration));
}
void CookieManager::CookieChanged(NotificationRegistration* registration, listener_registrations_.push_back(std::move(listener_registration));
const net::CanonicalCookie& cookie,
net::CookieStore::ChangeCause cause) {
registration->notification_pointer->OnCookieChanged(
cookie, ChangeCauseTranslation(cause));
} }
void CookieManager::NotificationPipeBroken( void CookieManager::RemoveChangeListener(ListenerRegistration* registration) {
NotificationRegistration* registration) { for (auto it = listener_registrations_.begin();
for (auto it = notifications_registered_.begin(); it != listener_registrations_.end(); ++it) {
it != notifications_registered_.end(); ++it) {
if (it->get() == registration) { if (it->get() == registration) {
// It isn't expected this will be a common enough operation for // It isn't expected this will be a common enough operation for
// the performance of std::vector::erase() to matter. // the performance of std::vector::erase() to matter.
notifications_registered_.erase(it); listener_registrations_.erase(it);
return; return;
} }
} }
......
...@@ -51,46 +51,45 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) CookieManager ...@@ -51,46 +51,45 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) CookieManager
SetCanonicalCookieCallback callback) override; SetCanonicalCookieCallback callback) override;
void DeleteCookies(network::mojom::CookieDeletionFilterPtr filter, void DeleteCookies(network::mojom::CookieDeletionFilterPtr filter,
DeleteCookiesCallback callback) override; DeleteCookiesCallback callback) override;
void RequestNotification(const GURL& url, void AddCookieChangeListener(
const GURL& url,
const std::string& name, const std::string& name,
network::mojom::CookieChangeNotificationPtr network::mojom::CookieChangeListenerPtr listener) override;
notification_pointer) override; void AddGlobalChangeListener(
void RequestGlobalNotifications(network::mojom::CookieChangeNotificationPtr network::mojom::CookieChangeListenerPtr listener) override;
notification_pointer) override;
void CloneInterface( void CloneInterface(
network::mojom::CookieManagerRequest new_interface) override; network::mojom::CookieManagerRequest new_interface) override;
uint32_t GetClientsBoundForTesting() const { return bindings_.size(); } size_t GetClientsBoundForTesting() const { return bindings_.size(); }
uint32_t GetNotificationsBoundForTesting() const { size_t GetListenersRegisteredForTesting() const {
return notifications_registered_.size(); return listener_registrations_.size();
} }
private: private:
struct NotificationRegistration { // State associated with a CookieChangeListener.
NotificationRegistration(); struct ListenerRegistration {
~NotificationRegistration(); ListenerRegistration();
~ListenerRegistration();
// Translates a CookieStore change callback to a CookieChangeListener call.
void DispatchCookieStoreChange(const net::CanonicalCookie& cookie,
net::CookieStore::ChangeCause cause);
// Owns the callback registration in the store. // Owns the callback registration in the store.
std::unique_ptr<net::CookieStore::CookieChangedSubscription> subscription; std::unique_ptr<net::CookieStore::CookieChangedSubscription> subscription;
// Pointer on which to send notifications. // The observer receiving change notifications.
network::mojom::CookieChangeNotificationPtr notification_pointer; network::mojom::CookieChangeListenerPtr listener;
DISALLOW_COPY_AND_ASSIGN(NotificationRegistration); DISALLOW_COPY_AND_ASSIGN(ListenerRegistration);
}; };
// Used to hook callbacks // Handles connection errors on change listener pipes.
void CookieChanged(NotificationRegistration* registration, void RemoveChangeListener(ListenerRegistration* registration);
const net::CanonicalCookie& cookie,
net::CookieStore::ChangeCause cause);
// Handles connection errors on notification pipes.
void NotificationPipeBroken(NotificationRegistration* registration);
net::CookieStore* const cookie_store_; net::CookieStore* const cookie_store_;
mojo::BindingSet<network::mojom::CookieManager> bindings_; mojo::BindingSet<network::mojom::CookieManager> bindings_;
std::vector<std::unique_ptr<NotificationRegistration>> std::vector<std::unique_ptr<ListenerRegistration>> listener_registrations_;
notifications_registered_;
DISALLOW_COPY_AND_ASSIGN(CookieManager); DISALLOW_COPY_AND_ASSIGN(CookieManager);
}; };
......
...@@ -22,9 +22,8 @@ ...@@ -22,9 +22,8 @@
// * SynchronousMojoCookieWrapper: Takes a network::mojom::CookieManager at // * SynchronousMojoCookieWrapper: Takes a network::mojom::CookieManager at
// construction; exposes synchronous interfaces that wrap the // construction; exposes synchronous interfaces that wrap the
// network::mojom::CookieManager async interfaces to make testing easier. // network::mojom::CookieManager async interfaces to make testing easier.
// * CookieChangeNotification: Test class implementing // * CookieChangeListener: Test class implementing the CookieChangeListener
// the CookieChangeNotification interface and recording // interface and recording incoming messages on it.
// incoming messages on it.
// * CookieManagerTest: Test base class. Automatically sets up // * CookieManagerTest: Test base class. Automatically sets up
// a cookie store, a cookie service wrapping it, a mojo pipe // a cookie store, a cookie service wrapping it, a mojo pipe
// connected to the server, and the cookie service implemented // connected to the server, and the cookie service implemented
...@@ -97,8 +96,8 @@ class SynchronousCookieManager { ...@@ -97,8 +96,8 @@ class SynchronousCookieManager {
return num_deleted; return num_deleted;
} }
// No need to wrap RequestNotification and CloneInterface, since their use // No need to wrap Add*Listener and CloneInterface, since their use
// is pure async. // is purely async.
private: private:
static void GetCookiesCallback( static void GetCookiesCallback(
base::RunLoop* run_loop, base::RunLoop* run_loop,
...@@ -122,14 +121,6 @@ class SynchronousCookieManager { ...@@ -122,14 +121,6 @@ class SynchronousCookieManager {
run_loop->Quit(); run_loop->Quit();
} }
static void RequestNotificationCallback(
base::RunLoop* run_loop,
network::mojom::CookieChangeNotificationRequest* request_out,
network::mojom::CookieChangeNotificationRequest request) {
*request_out = std::move(request);
run_loop->Quit();
}
network::mojom::CookieManager* cookie_service_; network::mojom::CookieManager* cookie_service_;
DISALLOW_COPY_AND_ASSIGN(SynchronousCookieManager); DISALLOW_COPY_AND_ASSIGN(SynchronousCookieManager);
...@@ -1460,26 +1451,24 @@ TEST_F(CookieManagerTest, DeleteByAll) { ...@@ -1460,26 +1451,24 @@ TEST_F(CookieManagerTest, DeleteByAll) {
namespace { namespace {
// Receives and records notifications from the network::mojom::CookieManager. // Receives and records notifications from the network::mojom::CookieManager.
class CookieChangeNotification class CookieChangeListener : public network::mojom::CookieChangeListener {
: public network::mojom::CookieChangeNotification {
public: public:
struct Notification { // Records a cookie change received from CookieManager.
Notification(const net::CanonicalCookie& cookie_in, struct Change {
network::mojom::CookieChangeCause cause_in) { Change(const net::CanonicalCookie& cookie,
cookie = cookie_in; network::mojom::CookieChangeCause cause)
cause = cause_in; : cookie(cookie), cause(cause) {}
}
net::CanonicalCookie cookie; net::CanonicalCookie cookie;
network::mojom::CookieChangeCause cause; network::mojom::CookieChangeCause cause;
}; };
CookieChangeNotification( CookieChangeListener(network::mojom::CookieChangeListenerRequest request)
network::mojom::CookieChangeNotificationRequest request)
: run_loop_(nullptr), binding_(this, std::move(request)) {} : run_loop_(nullptr), binding_(this, std::move(request)) {}
void WaitForSomeNotification() { // Blocks until the listener observes a cookie change.
if (!notifications_.empty()) void WaitForChange() {
if (!observed_changes_.empty())
return; return;
base::RunLoop loop; base::RunLoop loop;
run_loop_ = &loop; run_loop_ = &loop;
...@@ -1487,143 +1476,134 @@ class CookieChangeNotification ...@@ -1487,143 +1476,134 @@ class CookieChangeNotification
run_loop_ = nullptr; run_loop_ = nullptr;
} }
// Adds existing notifications to passed in vector. void ClearObservedChanges() { observed_changes_.clear(); }
void GetCurrentNotifications(std::vector<Notification>* notifications) {
notifications->insert(notifications->end(), notifications_.begin(), const std::vector<Change>& observed_changes() const {
notifications_.end()); return observed_changes_;
notifications_.clear();
} }
// network::mojom::CookieChangesNotification // network::mojom::CookieChangeListener
void OnCookieChanged(const net::CanonicalCookie& cookie, void OnCookieChange(const net::CanonicalCookie& cookie,
network::mojom::CookieChangeCause cause) override { network::mojom::CookieChangeCause cause) override {
notifications_.push_back(Notification(cookie, cause)); observed_changes_.push_back(Change(cookie, cause));
if (run_loop_) if (run_loop_)
run_loop_->Quit(); run_loop_->Quit();
} }
private: private:
std::vector<Notification> notifications_; std::vector<Change> observed_changes_;
// Loop to signal on receiving a notification if not null. // Loop to signal on receiving a notification if not null.
base::RunLoop* run_loop_; base::RunLoop* run_loop_;
mojo::Binding<network::mojom::CookieChangeNotification> binding_; mojo::Binding<network::mojom::CookieChangeListener> binding_;
}; };
} // anonymous namespace } // anonymous namespace
TEST_F(CookieManagerTest, Notification) { TEST_F(CookieManagerTest, AddCookieChangeListener) {
GURL notification_url("http://www.testing.com/pathele"); const GURL listener_url("http://www.testing.com/pathele");
std::string notification_domain("testing.com"); const std::string listener_url_host("www.testing.com");
std::string notification_name("Cookie_Name"); const std::string listener_url_domain("testing.com");
network::mojom::CookieChangeNotificationPtr ptr; const std::string listener_cookie_name("Cookie_Name");
network::mojom::CookieChangeNotificationRequest request( ASSERT_EQ(listener_url.host(), listener_url_host);
mojo::MakeRequest(&ptr));
network::mojom::CookieChangeListenerPtr listener_ptr;
network::mojom::CookieChangeListenerRequest request(
mojo::MakeRequest(&listener_ptr));
CookieChangeNotification notification(std::move(request)); CookieChangeListener listener(std::move(request));
cookie_service_client()->RequestNotification( cookie_service_client()->AddCookieChangeListener(
notification_url, notification_name, std::move(ptr)); listener_url, listener_cookie_name, std::move(listener_ptr));
std::vector<CookieChangeNotification::Notification> notifications; EXPECT_EQ(0u, listener.observed_changes().size());
notification.GetCurrentNotifications(&notifications);
EXPECT_EQ(0u, notifications.size());
notifications.clear();
// Set a cookie that doesn't match the above notification request in name // Set a cookie that doesn't match the above notification request in name
// and confirm it doesn't produce a notification. // and confirm it doesn't produce a notification.
service_wrapper()->SetCanonicalCookie( service_wrapper()->SetCanonicalCookie(
net::CanonicalCookie( net::CanonicalCookie(
"DifferentName", "val", notification_url.host(), "/", base::Time(), "DifferentName", "val", listener_url_host, "/", base::Time(),
base::Time(), base::Time(), /*secure=*/false, base::Time(), base::Time(), /*secure=*/false,
/*httponly=*/false, net::CookieSameSite::NO_RESTRICTION, /*httponly=*/false, net::CookieSameSite::NO_RESTRICTION,
net::COOKIE_PRIORITY_MEDIUM), net::COOKIE_PRIORITY_MEDIUM),
true, true); true, true);
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
notification.GetCurrentNotifications(&notifications); EXPECT_EQ(0u, listener.observed_changes().size());
EXPECT_EQ(0u, notifications.size());
notifications.clear();
// Set a cookie that doesn't match the above notification request in url // Set a cookie that doesn't match the above notification request in url
// and confirm it doesn't produce a notification. // and confirm it doesn't produce a notification.
service_wrapper()->SetCanonicalCookie( service_wrapper()->SetCanonicalCookie(
net::CanonicalCookie( net::CanonicalCookie(
notification_name, "val", "www.other.host", "/", base::Time(), listener_cookie_name, "val", "www.other.host", "/", base::Time(),
base::Time(), base::Time(), /*secure=*/false, base::Time(), base::Time(), /*secure=*/false,
/*httponly=*/false, net::CookieSameSite::NO_RESTRICTION, /*httponly=*/false, net::CookieSameSite::NO_RESTRICTION,
net::COOKIE_PRIORITY_MEDIUM), net::COOKIE_PRIORITY_MEDIUM),
true, true); true, true);
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
notification.GetCurrentNotifications(&notifications); EXPECT_EQ(0u, listener.observed_changes().size());
EXPECT_EQ(0u, notifications.size());
notifications.clear();
// Insert a cookie that does match. // Insert a cookie that does match.
service_wrapper()->SetCanonicalCookie( service_wrapper()->SetCanonicalCookie(
net::CanonicalCookie( net::CanonicalCookie(
notification_name, "val", notification_url.host(), "/", base::Time(), listener_cookie_name, "val", listener_url_host, "/", base::Time(),
base::Time(), base::Time(), /*secure=*/false, base::Time(), base::Time(), /*secure=*/false,
/*httponly=*/false, net::CookieSameSite::NO_RESTRICTION, /*httponly=*/false, net::CookieSameSite::NO_RESTRICTION,
net::COOKIE_PRIORITY_MEDIUM), net::COOKIE_PRIORITY_MEDIUM),
true, true); true, true);
// Expect asynchrony // Expect asynchrony
notification.GetCurrentNotifications(&notifications); EXPECT_EQ(0u, listener.observed_changes().size());
EXPECT_EQ(0u, notifications.size());
notifications.clear(); // Expect to observe a cookie change.
listener.WaitForChange();
// Expect notification std::vector<CookieChangeListener::Change> observed_changes =
notification.WaitForSomeNotification(); listener.observed_changes();
notification.GetCurrentNotifications(&notifications); ASSERT_EQ(1u, observed_changes.size());
EXPECT_EQ(1u, notifications.size()); EXPECT_EQ(listener_cookie_name, observed_changes[0].cookie.Name());
EXPECT_EQ(notification_name, notifications[0].cookie.Name()); EXPECT_EQ(listener_url_host, observed_changes[0].cookie.Domain());
EXPECT_EQ(notification_url.host(), notifications[0].cookie.Domain());
EXPECT_EQ(network::mojom::CookieChangeCause::INSERTED, EXPECT_EQ(network::mojom::CookieChangeCause::INSERTED,
notifications[0].cause); observed_changes[0].cause);
notifications.clear(); listener.ClearObservedChanges();
// Delete all cookies matching the domain. This includes one for which // Delete all cookies matching the domain. This includes one for which
// a notification will be generated, and one for which a notification // a notification will be generated, and one for which a notification
// will not be generated. // will not be generated.
network::mojom::CookieDeletionFilter filter; network::mojom::CookieDeletionFilter filter;
filter.including_domains = std::vector<std::string>(); filter.including_domains = std::vector<std::string>();
filter.including_domains->push_back(notification_domain); filter.including_domains->push_back(listener_url_domain);
// If this test fails, it may indicate a problem which will lead to // If this test fails, it may indicate a problem which will lead to
// no notifications being generated and the test hanging, so assert. // no notifications being generated and the test hanging, so assert.
ASSERT_EQ(2u, service_wrapper()->DeleteCookies(filter)); ASSERT_EQ(2u, service_wrapper()->DeleteCookies(filter));
// The notification may already have arrived, or it may arrive in the future. // The notification may already have arrived, or it may arrive in the future.
notification.WaitForSomeNotification(); listener.WaitForChange();
notification.GetCurrentNotifications(&notifications); observed_changes = listener.observed_changes();
ASSERT_EQ(1u, notifications.size()); ASSERT_EQ(1u, observed_changes.size());
EXPECT_EQ(notification_name, notifications[0].cookie.Name()); EXPECT_EQ(listener_cookie_name, observed_changes[0].cookie.Name());
EXPECT_EQ(notification_url.host(), notifications[0].cookie.Domain()); EXPECT_EQ(listener_url_host, observed_changes[0].cookie.Domain());
EXPECT_EQ(network::mojom::CookieChangeCause::EXPLICIT, EXPECT_EQ(network::mojom::CookieChangeCause::EXPLICIT,
notifications[0].cause); observed_changes[0].cause);
} }
TEST_F(CookieManagerTest, GlobalNotifications) { TEST_F(CookieManagerTest, AddGlobalChangeListener) {
const std::string kExampleHost = "www.example.com"; const std::string kExampleHost = "www.example.com";
const std::string kThisHost = "www.this.com"; const std::string kThisHost = "www.this.com";
const std::string kThisETLDP1 = "this.com"; const std::string kThisETLDP1 = "this.com";
const std::string kThatHost = "www.that.com"; const std::string kThatHost = "www.that.com";
network::mojom::CookieChangeNotificationPtr ptr; network::mojom::CookieChangeListenerPtr listener_ptr;
network::mojom::CookieChangeNotificationRequest request( network::mojom::CookieChangeListenerRequest request(
mojo::MakeRequest(&ptr)); mojo::MakeRequest(&listener_ptr));
CookieChangeNotification notification(std::move(request)); CookieChangeListener listener(std::move(request));
cookie_service_client()->RequestGlobalNotifications(std::move(ptr)); cookie_service_client()->AddGlobalChangeListener(std::move(listener_ptr));
std::vector<CookieChangeNotification::Notification> notifications; EXPECT_EQ(0u, listener.observed_changes().size());
notification.GetCurrentNotifications(&notifications);
EXPECT_EQ(0u, notifications.size());
notifications.clear();
// Confirm the right notification is seen from setting a cookie. // Confirm the right change is observed after setting a cookie.
service_wrapper()->SetCanonicalCookie( service_wrapper()->SetCanonicalCookie(
net::CanonicalCookie("Thing1", "val", kExampleHost, "/", base::Time(), net::CanonicalCookie("Thing1", "val", kExampleHost, "/", base::Time(),
base::Time(), base::Time(), /*secure=*/false, base::Time(), base::Time(), /*secure=*/false,
...@@ -1633,20 +1613,19 @@ TEST_F(CookieManagerTest, GlobalNotifications) { ...@@ -1633,20 +1613,19 @@ TEST_F(CookieManagerTest, GlobalNotifications) {
true, true); true, true);
// Expect asynchrony // Expect asynchrony
notification.GetCurrentNotifications(&notifications); EXPECT_EQ(0u, listener.observed_changes().size());
EXPECT_EQ(0u, notifications.size());
notifications.clear();
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
notification.GetCurrentNotifications(&notifications); std::vector<CookieChangeListener::Change> observed_changes =
EXPECT_EQ(1u, notifications.size()); listener.observed_changes();
EXPECT_EQ("Thing1", notifications[0].cookie.Name()); ASSERT_EQ(1u, observed_changes.size());
EXPECT_EQ("val", notifications[0].cookie.Value()); EXPECT_EQ("Thing1", observed_changes[0].cookie.Name());
EXPECT_EQ(kExampleHost, notifications[0].cookie.Domain()); EXPECT_EQ("val", observed_changes[0].cookie.Value());
EXPECT_EQ("/", notifications[0].cookie.Path()); EXPECT_EQ(kExampleHost, observed_changes[0].cookie.Domain());
EXPECT_EQ("/", observed_changes[0].cookie.Path());
EXPECT_EQ(network::mojom::CookieChangeCause::INSERTED, EXPECT_EQ(network::mojom::CookieChangeCause::INSERTED,
notifications[0].cause); observed_changes[0].cause);
notifications.clear(); listener.ClearObservedChanges();
// Set two cookies in a row on different domains and confirm they are both // Set two cookies in a row on different domains and confirm they are both
// signalled. // signalled.
...@@ -1666,15 +1645,15 @@ TEST_F(CookieManagerTest, GlobalNotifications) { ...@@ -1666,15 +1645,15 @@ TEST_F(CookieManagerTest, GlobalNotifications) {
true, true); true, true);
base::RunLoop().RunUntilIdle(); base::RunLoop().RunUntilIdle();
notification.GetCurrentNotifications(&notifications); observed_changes = listener.observed_changes();
EXPECT_EQ(2u, notifications.size()); ASSERT_EQ(2u, observed_changes.size());
EXPECT_EQ("Thing1", notifications[0].cookie.Name()); EXPECT_EQ("Thing1", observed_changes[0].cookie.Name());
EXPECT_EQ(network::mojom::CookieChangeCause::INSERTED, EXPECT_EQ(network::mojom::CookieChangeCause::INSERTED,
notifications[0].cause); observed_changes[0].cause);
EXPECT_EQ("Thing2", notifications[1].cookie.Name()); EXPECT_EQ("Thing2", observed_changes[1].cookie.Name());
EXPECT_EQ(network::mojom::CookieChangeCause::INSERTED, EXPECT_EQ(network::mojom::CookieChangeCause::INSERTED,
notifications[1].cause); observed_changes[1].cause);
notifications.clear(); listener.ClearObservedChanges();
// Delete cookies matching one domain; should produce one notification. // Delete cookies matching one domain; should produce one notification.
network::mojom::CookieDeletionFilter filter; network::mojom::CookieDeletionFilter filter;
...@@ -1685,79 +1664,69 @@ TEST_F(CookieManagerTest, GlobalNotifications) { ...@@ -1685,79 +1664,69 @@ TEST_F(CookieManagerTest, GlobalNotifications) {
ASSERT_EQ(1u, service_wrapper()->DeleteCookies(filter)); ASSERT_EQ(1u, service_wrapper()->DeleteCookies(filter));
// The notification may already have arrived, or it may arrive in the future. // The notification may already have arrived, or it may arrive in the future.
notification.WaitForSomeNotification(); listener.WaitForChange();
notification.GetCurrentNotifications(&notifications); observed_changes = listener.observed_changes();
ASSERT_EQ(1u, notifications.size()); ASSERT_EQ(1u, observed_changes.size());
EXPECT_EQ("Thing1", notifications[0].cookie.Name()); EXPECT_EQ("Thing1", observed_changes[0].cookie.Name());
EXPECT_EQ(kThisHost, notifications[0].cookie.Domain()); EXPECT_EQ(kThisHost, observed_changes[0].cookie.Domain());
EXPECT_EQ(network::mojom::CookieChangeCause::EXPLICIT, EXPECT_EQ(network::mojom::CookieChangeCause::EXPLICIT,
notifications[0].cause); observed_changes[0].cause);
} }
// Confirm the service operates properly if a returned notification interface // Confirm the service operates properly if a returned notification interface
// is destroyed. // is destroyed.
TEST_F(CookieManagerTest, NotificationRequestDestroyed) { TEST_F(CookieManagerTest, ListenerDestroyed) {
// Create two identical notification interfaces. // Create two identical listeners.
GURL notification_url("http://www.testing.com/pathele"); const GURL listener_url("http://www.testing.com/pathele");
std::string notification_name("Cookie_Name"); const std::string listener_url_host("www.testing.com");
ASSERT_EQ(listener_url.host(), listener_url_host);
network::mojom::CookieChangeNotificationPtr ptr1; const std::string listener_cookie_name("Cookie_Name");
network::mojom::CookieChangeNotificationRequest request1(
mojo::MakeRequest(&ptr1)); network::mojom::CookieChangeListenerPtr listener1_ptr;
std::unique_ptr<CookieChangeNotification> notification1( network::mojom::CookieChangeListenerRequest request1(
std::make_unique<CookieChangeNotification>(std::move(request1))); mojo::MakeRequest(&listener1_ptr));
cookie_service_client()->RequestNotification( auto listener1 = std::make_unique<CookieChangeListener>(std::move(request1));
notification_url, notification_name, std::move(ptr1)); cookie_service_client()->AddCookieChangeListener(
listener_url, listener_cookie_name, std::move(listener1_ptr));
network::mojom::CookieChangeNotificationPtr ptr2;
network::mojom::CookieChangeNotificationRequest request2( network::mojom::CookieChangeListenerPtr listener2_ptr;
mojo::MakeRequest(&ptr2)); network::mojom::CookieChangeListenerRequest request2(
std::unique_ptr<CookieChangeNotification> notification2( mojo::MakeRequest(&listener2_ptr));
std::make_unique<CookieChangeNotification>(std::move(request2))); auto listener2 = std::make_unique<CookieChangeListener>(std::move(request2));
cookie_service_client()->RequestNotification( cookie_service_client()->AddCookieChangeListener(
notification_url, notification_name, std::move(ptr2)); listener_url, listener_cookie_name, std::move(listener2_ptr));
// Add a cookie and receive a notification on both interfaces. // Add a cookie and receive a notification on both interfaces.
service_wrapper()->SetCanonicalCookie( service_wrapper()->SetCanonicalCookie(
net::CanonicalCookie( net::CanonicalCookie(
notification_name, "val", notification_url.host(), "/", base::Time(), listener_cookie_name, "val", listener_url_host, "/", base::Time(),
base::Time(), base::Time(), /*secure=*/false, base::Time(), base::Time(), /*secure=*/false,
/*httponly=*/false, net::CookieSameSite::NO_RESTRICTION, /*httponly=*/false, net::CookieSameSite::NO_RESTRICTION,
net::COOKIE_PRIORITY_MEDIUM), net::COOKIE_PRIORITY_MEDIUM),
true, true); true, true);
std::vector<CookieChangeNotification::Notification> notifications; EXPECT_EQ(0u, listener1->observed_changes().size());
notification1->GetCurrentNotifications(&notifications); EXPECT_EQ(0u, listener2->observed_changes().size());
EXPECT_EQ(0u, notifications.size());
notifications.clear();
notification2->GetCurrentNotifications(&notifications);
EXPECT_EQ(0u, notifications.size());
notifications.clear();
notification1->WaitForSomeNotification(); listener1->WaitForChange();
notification1->GetCurrentNotifications(&notifications); EXPECT_EQ(1u, listener1->observed_changes().size());
EXPECT_EQ(1u, notifications.size()); listener1->ClearObservedChanges();
notifications.clear(); listener2->WaitForChange();
EXPECT_EQ(1u, listener2->observed_changes().size());
listener2->ClearObservedChanges();
EXPECT_EQ(2u, service()->GetListenersRegisteredForTesting());
notification2->WaitForSomeNotification(); // Destroy the first listener.
notification2->GetCurrentNotifications(&notifications); listener1.reset();
EXPECT_EQ(1u, notifications.size());
notifications.clear();
EXPECT_EQ(2u, service()->GetNotificationsBoundForTesting());
// Destroy the first interface
notification1.reset();
// Confirm the second interface can still receive notifications. // Confirm the second interface can still receive notifications.
network::mojom::CookieDeletionFilter filter; network::mojom::CookieDeletionFilter filter;
EXPECT_EQ(1u, service_wrapper()->DeleteCookies(filter)); EXPECT_EQ(1u, service_wrapper()->DeleteCookies(filter));
notification2->WaitForSomeNotification(); listener2->WaitForChange();
notification2->GetCurrentNotifications(&notifications); EXPECT_EQ(1u, listener2->observed_changes().size());
EXPECT_EQ(1u, notifications.size());
notifications.clear(); EXPECT_EQ(1u, service()->GetListenersRegisteredForTesting());
EXPECT_EQ(1u, service()->GetNotificationsBoundForTesting());
} }
// Confirm we get a connection error notification if the service dies. // Confirm we get a connection error notification if the service dies.
......
...@@ -137,9 +137,9 @@ struct CookieDeletionFilter { ...@@ -137,9 +137,9 @@ struct CookieDeletionFilter {
CookieDeletionSessionControl session_control = IGNORE_CONTROL; CookieDeletionSessionControl session_control = IGNORE_CONTROL;
}; };
interface CookieChangeNotification { interface CookieChangeListener {
// TODO(rdsmith): Should this be made a batch interface? // TODO(rdsmith): Should this be made a batch interface?
OnCookieChanged(CanonicalCookie cookie, CookieChangeCause cause); OnCookieChange(CanonicalCookie cookie, CookieChangeCause cause);
}; };
interface CookieManager { interface CookieManager {
...@@ -173,31 +173,31 @@ interface CookieManager { ...@@ -173,31 +173,31 @@ interface CookieManager {
// Returns the number of cookies deleted. // Returns the number of cookies deleted.
DeleteCookies(CookieDeletionFilter filter) => (uint32 num_deleted); DeleteCookies(CookieDeletionFilter filter) => (uint32 num_deleted);
// Send a CookieChangeNotification over which notification // Subscribes the given listener to changes to a cookie.
// for cookie changes will be sent. When the specified cookie //
// associated with the domain/path specified in the URL changes, a // The subscription is canceled by closing the CookieChangeListener's pipe.
// notification will be posted to the passed pointer.
// //
// Note that if the caller may be racing with other uses of the cookie store, // Note that if the caller may be racing with other uses of the cookie store,
// it should follow the notification request with a probe of the relevant // it should follow the subscription request with a probe of the relevant
// information about the tracked cookie, to make sure that a change to the // information about the tracked cookie, to make sure that a change to the
// cookie did not happen while the notification was being installed. // cookie did not happen right before the listener was registered.
// //
// TODO(rdsmith): Should this have a filter to register for a lot of // TODO(rdsmith): Should this have a filter to register for a lot of
// notifications at once? Maybe combine with the deletion filter? // notifications at once? Maybe combine with the deletion filter?
RequestNotification( // TODO(rdsmith): Describe the performance implications of using this meethod.
// The comments in CookieMonster::AddCallbackForCookie look pretty scary.
AddCookieChangeListener(
url.mojom.Url url, url.mojom.Url url,
string name, string name,
CookieChangeNotification notification_pointer); CookieChangeListener listener);
// Send a CookieChangeNotification over which notification // Subscribes the given listener to changes to this CookieManager's cookies.
// for all cookie changes will be sent. When a cookie associated with //
// the CookieManager changes, a notification will be posted to the // The subscription is canceled by closing the CookieChangeListener's pipe.
// passed pointer.
// //
// TODO(rdsmith): Should this have a filter to register for a lot of // TODO(rdsmith): Should this have a filter to register for a lot of
// notifications at once? Maybe combine with the deletion filter? // notifications at once? Maybe combine with the deletion filter?
RequestGlobalNotifications(CookieChangeNotification notification_pointer); AddGlobalChangeListener(CookieChangeListener notification_pointer);
// Clone the interface for use somewhere else. After this call, // Clone the interface for use somewhere else. After this call,
// requests to the same implementation may be posted to the other side // requests to the same implementation may be posted to the other side
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment