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