Commit 0f741288 authored by Mihai Sardarescu's avatar Mihai Sardarescu Committed by Commit Bot

Update the CHROME_CONNECTED cookie using the cookie manager API.

This CL removes the hack to write the CHROME_CONNECTED cookie using
a WKWebView and instead uses the cookie manager API. This works well
as the Chrome iOS cookie manager is now backed by the WKHTTPCookieStore.

Bug: 1102794

Change-Id: Ida2d74372616ce8331eadbde507fb131e4afa0ac
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2323901
Commit-Queue: Mihai Sardarescu <msarda@chromium.org>
Reviewed-by: default avatarNohemi Fernandez <fernandex@chromium.org>
Cr-Commit-Position: refs/heads/master@{#794243}
parent e769f1ea
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include "components/signin/ios/browser/active_state_manager.h" #include "components/signin/ios/browser/active_state_manager.h"
#import "components/signin/ios/browser/manage_accounts_delegate.h" #import "components/signin/ios/browser/manage_accounts_delegate.h"
#import "components/signin/public/identity_manager/identity_manager.h" #import "components/signin/public/identity_manager/identity_manager.h"
#include "net/cookies/cookie_access_result.h"
namespace content_settings { namespace content_settings {
class CookieSettings; class CookieSettings;
...@@ -33,18 +34,18 @@ class WebStatePolicyDecider; ...@@ -33,18 +34,18 @@ class WebStatePolicyDecider;
class AccountReconcilor; class AccountReconcilor;
class PrefService; class PrefService;
@class AccountConsistencyNavigationDelegate;
@class WKWebView;
// Handles actions necessary for Account Consistency to work on iOS. This // Handles actions necessary for Account Consistency to work on iOS. This
// includes setting the Account Consistency cookie (informing Gaia that the // includes setting the Account Consistency cookie (informing Gaia that the
// Account Consistency is on). // Account Consistency is on).
//
// This is currently only used when WKWebView is enabled.
class AccountConsistencyService : public KeyedService, class AccountConsistencyService : public KeyedService,
public signin::IdentityManager::Observer, public signin::IdentityManager::Observer,
public ActiveStateManager::Observer { public ActiveStateManager::Observer {
public: public:
// Name of the cookie that is managed by AccountConsistencyService and is used
// to inform Google web properties that the browser is connected and that
// Google authentication cookies are managed by |AccountReconcilor|).
static const char kChromeConnectedCookieName[];
// Name of the preference property that persists the domains that have a // Name of the preference property that persists the domains that have a
// CHROME_CONNECTED cookie set by this service. // CHROME_CONNECTED cookie set by this service.
static const char kDomainsWithCookiePref[]; static const char kDomainsWithCookiePref[];
...@@ -125,17 +126,11 @@ class AccountConsistencyService : public KeyedService, ...@@ -125,17 +126,11 @@ class AccountConsistencyService : public KeyedService,
// Applies the pending CHROME_CONNECTED cookie requests one by one. // Applies the pending CHROME_CONNECTED cookie requests one by one.
void ApplyCookieRequests(); void ApplyCookieRequests();
void FinishedSetCookie(net::CookieAccessResult cookie_access_result);
// Called when the current CHROME_CONNECTED cookie request is done. // Called when the current CHROME_CONNECTED cookie request is done.
void FinishedApplyingCookieRequest(bool success); void FinishedApplyingCookieRequest(bool success);
// Returns the cached WKWebView if it exists, or creates one if necessary.
// Can return nil if the browser state is not active.
WKWebView* GetWKWebView();
// Actually creates a WKWebView. Virtual for testing.
virtual WKWebView* BuildWKWebView();
// Stops any page loading in the WKWebView currently in use and releases it.
void ResetWKWebView();
// Returns whether the CHROME_CONNECTED cookie should be added to |domain|. // Returns whether the CHROME_CONNECTED cookie should be added to |domain|.
// If the cookie is already on |domain|, this function will return false // If the cookie is already on |domain|, this function will return false
// unless |force_update_if_too_old| is true. In this case, it will return true // unless |force_update_if_too_old| is true. In this case, it will return true
...@@ -156,9 +151,8 @@ class AccountConsistencyService : public KeyedService, ...@@ -156,9 +151,8 @@ class AccountConsistencyService : public KeyedService,
// ActiveStateManager::Observer implementation. // ActiveStateManager::Observer implementation.
void OnActive() override; void OnActive() override;
void OnInactive() override;
// Browser state associated with the service, used to create WKWebViews. // Browser state associated with the service.
web::BrowserState* browser_state_; web::BrowserState* browser_state_;
// Used to update kDomainsWithCookiePref. // Used to update kDomainsWithCookiePref.
PrefService* prefs_; PrefService* prefs_;
...@@ -180,12 +174,6 @@ class AccountConsistencyService : public KeyedService, ...@@ -180,12 +174,6 @@ class AccountConsistencyService : public KeyedService,
// the time when the cookie was last updated. // the time when the cookie was last updated.
std::map<std::string, base::Time> last_cookie_update_map_; std::map<std::string, base::Time> last_cookie_update_map_;
// Web view used to apply the CHROME_CONNECTED cookie requests.
__strong WKWebView* web_view_;
// Navigation delegate of |web_view_| that informs the service when a cookie
// request has been applied.
AccountConsistencyNavigationDelegate* navigation_delegate_;
// Handlers reacting on GAIA responses with the X-Chrome-Manage-Accounts // Handlers reacting on GAIA responses with the X-Chrome-Manage-Accounts
// header set. // header set.
std::map<web::WebState*, std::unique_ptr<web::WebStatePolicyDecider>> std::map<web::WebState*, std::unique_ptr<web::WebStatePolicyDecider>>
......
...@@ -37,17 +37,6 @@ namespace { ...@@ -37,17 +37,6 @@ namespace {
// should be added again on a domain it was previously set. // should be added again on a domain it was previously set.
const int kHoursThresholdToReAddCookie = 24; const int kHoursThresholdToReAddCookie = 24;
// JavaScript template used to set (or delete) the CHROME_CONNECTED cookie.
// It takes 3 arguments: the domain of the cookie, its value and its expiration
// date. It also clears the legacy X-CHROME-CONNECTED cookie.
NSString* const kChromeConnectedCookieTemplate =
@"<html><script>domain=\"%@\";"
"document.cookie=\"X-CHROME-CONNECTED=; path=/; domain=\" + domain + \";"
" expires=Thu, 01-Jan-1970 00:00:01 GMT\";"
"document.cookie=\"CHROME_CONNECTED=%@; path=/; domain=\" + domain + \";"
" expires=\" + new Date(%f).toGMTString() + \"; secure;"
" samesite=lax;\"</script></html>";
// WebStatePolicyDecider that monitors the HTTP headers on Gaia responses, // WebStatePolicyDecider that monitors the HTTP headers on Gaia responses,
// reacting on the X-Chrome-Manage-Accounts header and notifying its delegate. // reacting on the X-Chrome-Manage-Accounts header and notifying its delegate.
// It also notifies the AccountConsistencyService of domains it should add the // It also notifies the AccountConsistencyService of domains it should add the
...@@ -158,45 +147,8 @@ void AccountConsistencyHandler::WebStateDestroyed() { ...@@ -158,45 +147,8 @@ void AccountConsistencyHandler::WebStateDestroyed() {
account_consistency_service_->RemoveWebStateHandler(web_state()); account_consistency_service_->RemoveWebStateHandler(web_state());
} }
// WKWebView navigation delegate that calls its callback every time a navigation const char AccountConsistencyService::kChromeConnectedCookieName[] =
// has finished. "CHROME_CONNECTED";
@interface AccountConsistencyNavigationDelegate : NSObject<WKNavigationDelegate>
// Designated initializer. |callback| will be called every time a navigation has
// finished. |callback| must not be empty.
- (instancetype)initWithCallback:(const base::RepeatingClosure&)callback
NS_DESIGNATED_INITIALIZER;
- (instancetype)init NS_UNAVAILABLE;
@end
@implementation AccountConsistencyNavigationDelegate {
// Callback that will be called every time a navigation has finished.
base::RepeatingClosure _callback;
}
- (instancetype)initWithCallback:(const base::RepeatingClosure&)callback {
self = [super init];
if (self) {
DCHECK(!callback.is_null());
_callback = callback;
}
return self;
}
- (instancetype)init {
NOTREACHED();
return nil;
}
#pragma mark - WKNavigationDelegate
- (void)webView:(WKWebView*)webView
didFinishNavigation:(WKNavigation*)navigation {
_callback.Run();
}
@end
const char AccountConsistencyService::kDomainsWithCookiePref[] = const char AccountConsistencyService::kDomainsWithCookiePref[] =
"signin.domains_with_cookie"; "signin.domains_with_cookie";
...@@ -251,8 +203,6 @@ AccountConsistencyService::AccountConsistencyService( ...@@ -251,8 +203,6 @@ AccountConsistencyService::AccountConsistencyService(
} }
AccountConsistencyService::~AccountConsistencyService() { AccountConsistencyService::~AccountConsistencyService() {
DCHECK(!web_view_);
DCHECK(!navigation_delegate_);
} }
// static // static
...@@ -344,7 +294,6 @@ void AccountConsistencyService::LoadFromPrefs() { ...@@ -344,7 +294,6 @@ void AccountConsistencyService::LoadFromPrefs() {
void AccountConsistencyService::Shutdown() { void AccountConsistencyService::Shutdown() {
identity_manager_->RemoveObserver(this); identity_manager_->RemoveObserver(this);
ActiveStateManager::FromBrowserState(browser_state_)->RemoveObserver(this); ActiveStateManager::FromBrowserState(browser_state_)->RemoveObserver(this);
ResetWKWebView();
web_state_handlers_.clear(); web_state_handlers_.clear();
} }
...@@ -365,10 +314,8 @@ void AccountConsistencyService::ApplyCookieRequests() { ...@@ -365,10 +314,8 @@ void AccountConsistencyService::ApplyCookieRequests() {
applying_cookie_requests_ = true; applying_cookie_requests_ = true;
const GURL url("https://" + cookie_requests_.front().domain); const GURL url("https://" + cookie_requests_.front().domain);
std::string cookie_value = ""; std::string cookie_value;
// Expiration date of the cookie in the JavaScript convention of time, a base::Time expiration_date;
// number of milliseconds since the epoch.
double expiration_date = 0;
switch (cookie_requests_.front().request_type) { switch (cookie_requests_.front().request_type) {
case ADD_CHROME_CONNECTED_COOKIE: case ADD_CHROME_CONNECTED_COOKIE:
cookie_value = signin::BuildMirrorRequestCookieIfPossible( cookie_value = signin::BuildMirrorRequestCookieIfPossible(
...@@ -382,22 +329,43 @@ void AccountConsistencyService::ApplyCookieRequests() { ...@@ -382,22 +329,43 @@ void AccountConsistencyService::ApplyCookieRequests() {
return; return;
} }
// Create expiration date of Now+2y to roughly follow the SAPISID cookie. // Create expiration date of Now+2y to roughly follow the SAPISID cookie.
expiration_date = expiration_date = base::Time::Now() + base::TimeDelta::FromDays(730);
(base::Time::Now() + base::TimeDelta::FromDays(730)).ToJsTime();
break; break;
case REMOVE_CHROME_CONNECTED_COOKIE: case REMOVE_CHROME_CONNECTED_COOKIE:
// Nothing to do. Default values correspond to removing the cookie (no // Default values correspond to removing the cookie (no value, expiration
// value, expiration date in the past). // date in the past).
expiration_date = base::Time::UnixEpoch();
break; break;
} }
NSString* html = [NSString
stringWithFormat:kChromeConnectedCookieTemplate, std::unique_ptr<net::CanonicalCookie> cookie =
base::SysUTF8ToNSString(url.host()), net::CanonicalCookie::CreateSanitizedCookie(
base::SysUTF8ToNSString(cookie_value), expiration_date]; url,
// Load an HTML string with embedded JavaScript that will set or remove the /*name=*/kChromeConnectedCookieName, cookie_value,
// cookie. By setting the base URL to |url|, this effectively allows to modify /*domain=*/url.host(),
// cookies on the correct domain without having to do a network request. /*path=*/std::string(),
[GetWKWebView() loadHTMLString:html baseURL:net::NSURLWithGURL(url)]; /*creation_time=*/base::Time::Now(), expiration_date,
/*last_access_time=*/base::Time(),
/*secure=*/true,
/*httponly=*/false, net::CookieSameSite::LAX_MODE,
net::COOKIE_PRIORITY_DEFAULT);
net::CookieOptions options;
options.set_include_httponly();
options.set_same_site_cookie_context(
net::CookieOptions::SameSiteCookieContext::MakeInclusive());
network::mojom::CookieManager* cookie_manager =
browser_state_->GetCookieManager();
cookie_manager->SetCanonicalCookie(
*cookie, url, options,
base::BindOnce(&AccountConsistencyService::FinishedSetCookie,
base::Unretained(this)));
}
void AccountConsistencyService::FinishedSetCookie(
net::CookieAccessResult cookie_access_result) {
DCHECK(cookie_access_result.status.IsInclude());
FinishedApplyingCookieRequest(true);
} }
void AccountConsistencyService::FinishedApplyingCookieRequest(bool success) { void AccountConsistencyService::FinishedApplyingCookieRequest(bool success) {
...@@ -427,35 +395,6 @@ void AccountConsistencyService::FinishedApplyingCookieRequest(bool success) { ...@@ -427,35 +395,6 @@ void AccountConsistencyService::FinishedApplyingCookieRequest(bool success) {
} }
} }
WKWebView* AccountConsistencyService::GetWKWebView() {
if (!ActiveStateManager::FromBrowserState(browser_state_)->IsActive()) {
// |browser_state_| is not active, WKWebView linked to this browser state
// should not exist or be created.
return nil;
}
if (!web_view_) {
web_view_ = BuildWKWebView();
navigation_delegate_ = [[AccountConsistencyNavigationDelegate alloc]
initWithCallback:base::BindRepeating(&AccountConsistencyService::
FinishedApplyingCookieRequest,
base::Unretained(this), true)];
[web_view_ setNavigationDelegate:navigation_delegate_];
}
return web_view_;
}
WKWebView* AccountConsistencyService::BuildWKWebView() {
return web::BuildWKWebView(CGRectZero, browser_state_);
}
void AccountConsistencyService::ResetWKWebView() {
[web_view_ setNavigationDelegate:nil];
[web_view_ stopLoading];
web_view_ = nil;
navigation_delegate_ = nil;
applying_cookie_requests_ = false;
}
void AccountConsistencyService::AddChromeConnectedCookies() { void AccountConsistencyService::AddChromeConnectedCookies() {
DCHECK(!browser_state_->IsOffTheRecord()); DCHECK(!browser_state_->IsOffTheRecord());
// These cookie request are preventive and not a strong signal (unlike // These cookie request are preventive and not a strong signal (unlike
...@@ -469,7 +408,6 @@ void AccountConsistencyService::AddChromeConnectedCookies() { ...@@ -469,7 +408,6 @@ void AccountConsistencyService::AddChromeConnectedCookies() {
void AccountConsistencyService::OnBrowsingDataRemoved() { void AccountConsistencyService::OnBrowsingDataRemoved() {
// CHROME_CONNECTED cookies have been removed, update internal state // CHROME_CONNECTED cookies have been removed, update internal state
// accordingly. // accordingly.
ResetWKWebView();
for (auto& cookie_request : cookie_requests_) { for (auto& cookie_request : cookie_requests_) {
base::OnceClosure callback(std::move(cookie_request.callback)); base::OnceClosure callback(std::move(cookie_request.callback));
if (!callback.is_null()) { if (!callback.is_null()) {
...@@ -510,9 +448,3 @@ void AccountConsistencyService::OnActive() { ...@@ -510,9 +448,3 @@ void AccountConsistencyService::OnActive() {
// to apply. // to apply.
ApplyCookieRequests(); ApplyCookieRequests();
} }
void AccountConsistencyService::OnInactive() {
// |browser_state_| is now inactive. Stop using |web_view_| and don't create
// a new one until it is active.
ResetWKWebView();
}
...@@ -10,6 +10,8 @@ ...@@ -10,6 +10,8 @@
#include "base/bind.h" #include "base/bind.h"
#include "base/bind_helpers.h" #include "base/bind_helpers.h"
#include "base/test/bind_test_util.h"
#import "base/test/ios/wait_util.h"
#include "base/values.h" #include "base/values.h"
#include "components/content_settings/core/browser/cookie_settings.h" #include "components/content_settings/core/browser/cookie_settings.h"
#include "components/content_settings/core/browser/host_content_settings_map.h" #include "components/content_settings/core/browser/host_content_settings_map.h"
...@@ -49,6 +51,25 @@ NSURL* const kCountryGoogleUrl = [NSURL URLWithString:@"https://google.de/"]; ...@@ -49,6 +51,25 @@ NSURL* const kCountryGoogleUrl = [NSURL URLWithString:@"https://google.de/"];
const char* kGoogleDomain = "google.com"; const char* kGoogleDomain = "google.com";
// Youtube domain. // Youtube domain.
const char* kYoutubeDomain = "youtube.com"; const char* kYoutubeDomain = "youtube.com";
// Google domain where the CHROME_CONNECTED cookie is set/removed.
const char* kCountryGoogleDomain = "google.de";
// Returns true if |cookies| contains a cookie with |name| and |domain|.
//
// Note: If |domain| is the empty string, then it returns true if any cookie
// with |name| is found.
bool ContainsCookie(const std::vector<net::CanonicalCookie>& cookies,
const std::string& name,
const std::string& domain) {
for (const auto& cookie : cookies) {
if (cookie.Name() ==
AccountConsistencyService::kChromeConnectedCookieName) {
if (domain.empty() || cookie.Domain() == domain)
return true;
}
}
return false;
}
// AccountConsistencyService specialization that fakes the creation of the // AccountConsistencyService specialization that fakes the creation of the
// WKWebView in order to mock it. This allows tests to intercept the calls to // WKWebView in order to mock it. This allows tests to intercept the calls to
...@@ -67,14 +88,6 @@ class FakeAccountConsistencyService : public AccountConsistencyService { ...@@ -67,14 +88,6 @@ class FakeAccountConsistencyService : public AccountConsistencyService {
cookie_settings, cookie_settings,
identity_manager) {} identity_manager) {}
private:
WKWebView* BuildWKWebView() override {
if (!mock_web_view_) {
mock_web_view_ = [OCMockObject niceMockForClass:[WKWebView class]];
}
return mock_web_view_;
}
id mock_web_view_;
}; };
// Mock AccountReconcilor to catch call to OnReceivedManageAccountsResponse. // Mock AccountReconcilor to catch call to OnReceivedManageAccountsResponse.
...@@ -130,7 +143,6 @@ class AccountConsistencyServiceTest : public PlatformTest { ...@@ -130,7 +143,6 @@ class AccountConsistencyServiceTest : public PlatformTest {
public: public:
void OnRemoveChromeConnectedCookieFinished() { void OnRemoveChromeConnectedCookieFinished() {
EXPECT_FALSE(remove_cookie_callback_called_); EXPECT_FALSE(remove_cookie_callback_called_);
EXPECT_EQ(0, web_view_load_expection_count_);
remove_cookie_callback_called_ = true; remove_cookie_callback_called_ = true;
} }
...@@ -142,7 +154,6 @@ class AccountConsistencyServiceTest : public PlatformTest { ...@@ -142,7 +154,6 @@ class AccountConsistencyServiceTest : public PlatformTest {
content_settings::CookieSettings::RegisterProfilePrefs(prefs_.registry()); content_settings::CookieSettings::RegisterProfilePrefs(prefs_.registry());
HostContentSettingsMap::RegisterProfilePrefs(prefs_.registry()); HostContentSettingsMap::RegisterProfilePrefs(prefs_.registry());
web_view_load_expection_count_ = 0;
signin_client_.reset( signin_client_.reset(
new TestSigninClient(&prefs_, &test_url_loader_factory_)); new TestSigninClient(&prefs_, &test_url_loader_factory_));
identity_test_env_.reset(new signin::IdentityTestEnvironment( identity_test_env_.reset(new signin::IdentityTestEnvironment(
...@@ -160,8 +171,6 @@ class AccountConsistencyServiceTest : public PlatformTest { ...@@ -160,8 +171,6 @@ class AccountConsistencyServiceTest : public PlatformTest {
} }
void TearDown() override { void TearDown() override {
EXPECT_EQ(0, web_view_load_expection_count_);
EXPECT_OCMOCK_VERIFY(GetMockWKWebView());
account_consistency_service_->Shutdown(); account_consistency_service_->Shutdown();
settings_map_->ShutdownOnUIThread(); settings_map_->ShutdownOnUIThread();
ActiveStateManager::FromBrowserState(&browser_state_)->SetActive(false); ActiveStateManager::FromBrowserState(&browser_state_)->SetActive(false);
...@@ -169,25 +178,6 @@ class AccountConsistencyServiceTest : public PlatformTest { ...@@ -169,25 +178,6 @@ class AccountConsistencyServiceTest : public PlatformTest {
PlatformTest::TearDown(); PlatformTest::TearDown();
} }
// Adds an expectation for |url| to be loaded in the web view of
// |account_consistency_service_|.
// |continue_navigation| controls whether navigation will continue or be
// stopped on page load.
void AddPageLoadedExpectation(NSURL* url, bool continue_navigation) {
++web_view_load_expection_count_;
void (^continueBlock)(NSInvocation*) = ^(NSInvocation* invocation) {
--web_view_load_expection_count_;
if (!continue_navigation)
return;
__unsafe_unretained WKWebView* web_view = nil;
[invocation getArgument:&web_view atIndex:0];
[GetNavigationDelegate() webView:web_view didFinishNavigation:nil];
};
[static_cast<WKWebView*>([[GetMockWKWebView() expect] andDo:continueBlock])
loadHTMLString:[OCMArg any]
baseURL:url];
}
void ResetAccountConsistencyService() { void ResetAccountConsistencyService() {
if (account_consistency_service_) { if (account_consistency_service_) {
account_consistency_service_->Shutdown(); account_consistency_service_->Shutdown();
...@@ -197,28 +187,26 @@ class AccountConsistencyServiceTest : public PlatformTest { ...@@ -197,28 +187,26 @@ class AccountConsistencyServiceTest : public PlatformTest {
identity_test_env_->identity_manager())); identity_test_env_->identity_manager()));
} }
void WaitUntilAllCookieRequestsAreApplied() {
// Spinning the runloop is needed to ensure that the cookie manager requests
// are executed.
base::RunLoop().RunUntilIdle();
if (ActiveStateManager::FromBrowserState(&browser_state_)->IsActive())
EXPECT_TRUE(account_consistency_service_->cookie_requests_.empty());
}
void SignIn() { void SignIn() {
signin::MakePrimaryAccountAvailable(identity_test_env_->identity_manager(), signin::MakePrimaryAccountAvailable(identity_test_env_->identity_manager(),
"user@gmail.com"); "user@gmail.com");
EXPECT_EQ(0, web_view_load_expection_count_); WaitUntilAllCookieRequestsAreApplied();
} }
void SignOutAndSimulateGaiaCookieManagerServiceLogout() { void SignOutAndSimulateGaiaCookieManagerServiceLogout() {
signin::ClearPrimaryAccount(identity_test_env_->identity_manager(), signin::ClearPrimaryAccount(identity_test_env_->identity_manager(),
signin::ClearPrimaryAccountPolicy::DEFAULT); signin::ClearPrimaryAccountPolicy::DEFAULT);
SimulateGaiaCookieManagerServiceLogout(true); SimulateGaiaCookieManagerServiceLogout();
} WaitUntilAllCookieRequestsAreApplied();
id GetWKWebView() { return account_consistency_service_->GetWKWebView(); }
id GetMockWKWebView() {
// Should use BuildWKWebView() to always have the mock instance, even when
// the |account_consistency_service_| is inactive.
return account_consistency_service_->BuildWKWebView();
}
id GetNavigationDelegate() {
return account_consistency_service_->navigation_delegate_;
} }
bool ShouldAddCookieToDomain(const std::string& domain, bool ShouldAddCookieToDomain(const std::string& domain,
...@@ -227,21 +215,53 @@ class AccountConsistencyServiceTest : public PlatformTest { ...@@ -227,21 +215,53 @@ class AccountConsistencyServiceTest : public PlatformTest {
domain, should_check_last_update_time); domain, should_check_last_update_time);
} }
void CheckDomainHasCookie(const std::string& domain) { std::vector<net::CanonicalCookie> GetCookiesInCookieJar() {
std::vector<net::CanonicalCookie> cookies_out;
base::RunLoop run_loop;
network::mojom::CookieManager* cookie_manager =
browser_state_.GetCookieManager();
cookie_manager->GetAllCookies(base::BindOnce(base::BindLambdaForTesting(
[&run_loop,
&cookies_out](const std::vector<net::CanonicalCookie>& cookies) {
cookies_out = cookies;
run_loop.Quit();
})));
run_loop.Run();
return cookies_out;
}
void CheckDomainHasChromeConnectedCookie(const std::string& domain) {
EXPECT_TRUE(ContainsCookie(
GetCookiesInCookieJar(),
AccountConsistencyService::kChromeConnectedCookieName, "." + domain));
EXPECT_GE( EXPECT_GE(
account_consistency_service_->last_cookie_update_map_.count(domain), account_consistency_service_->last_cookie_update_map_.count(domain),
1u); 1u);
} }
void SimulateGaiaCookieManagerServiceLogout( void CheckNoChromeConnectedCookieForDomain(const std::string& domain) {
bool expect_remove_cookie_callback) { EXPECT_FALSE(ContainsCookie(
// Simulate the action of the action GaiaCookieManagerService to cleanup GetCookiesInCookieJar(),
// the cookies once the sign-out is done. AccountConsistencyService::kChromeConnectedCookieName, "." + domain));
remove_cookie_callback_called_ = false; EXPECT_EQ(0U, account_consistency_service_->last_cookie_update_map_.count(
account_consistency_service_->RemoveChromeConnectedCookies(base::BindOnce( domain));
&AccountConsistencyServiceTest::OnRemoveChromeConnectedCookieFinished, }
base::Unretained(this)));
EXPECT_EQ(expect_remove_cookie_callback, remove_cookie_callback_called_); void CheckNoChromeConnectedCookies() {
EXPECT_FALSE(
ContainsCookie(GetCookiesInCookieJar(),
AccountConsistencyService::kChromeConnectedCookieName,
/*domain=*/std::string()));
}
// Simulate the action of the action GaiaCookieManagerService to cleanup
// the cookies once the sign-out is done.
void SimulateGaiaCookieManagerServiceLogout() {
base::RunLoop run_loop;
account_consistency_service_->RemoveChromeConnectedCookies(
run_loop.QuitClosure());
run_loop.Run();
} }
// Creates test threads, necessary for ActiveStateManager that needs a UI // Creates test threads, necessary for ActiveStateManager that needs a UI
...@@ -261,27 +281,29 @@ class AccountConsistencyServiceTest : public PlatformTest { ...@@ -261,27 +281,29 @@ class AccountConsistencyServiceTest : public PlatformTest {
scoped_refptr<HostContentSettingsMap> settings_map_; scoped_refptr<HostContentSettingsMap> settings_map_;
scoped_refptr<content_settings::CookieSettings> cookie_settings_; scoped_refptr<content_settings::CookieSettings> cookie_settings_;
bool remove_cookie_callback_called_; bool remove_cookie_callback_called_;
int web_view_load_expection_count_;
}; };
// Tests whether the WKWebView is actually stopped when the browser state is // Tests that main domains are added to the internal map when cookies are set in
// inactive. // reaction to signin.
TEST_F(AccountConsistencyServiceTest, OnInactive) { TEST_F(AccountConsistencyServiceTest, SigninAddCookieOnMainDomains) {
[[GetMockWKWebView() expect] stopLoading]; SignIn();
// Loads the webview. CheckDomainHasChromeConnectedCookie(kGoogleDomain);
EXPECT_TRUE(GetWKWebView()); CheckDomainHasChromeConnectedCookie(kYoutubeDomain);
ActiveStateManager::FromBrowserState(&browser_state_)->SetActive(false);
const base::DictionaryValue* dict =
prefs_.GetDictionary(AccountConsistencyService::kDomainsWithCookiePref);
EXPECT_EQ(2u, dict->size());
EXPECT_TRUE(dict->GetBooleanWithoutPathExpansion("google.com", nullptr));
EXPECT_TRUE(dict->GetBooleanWithoutPathExpansion("youtube.com", nullptr));
} }
// Tests that cookies that are added during SignIn and subsequent navigations // Tests that cookies that are added during SignIn and subsequent navigations
// are correctly removed during the SignOut. // are correctly removed during the SignOut.
TEST_F(AccountConsistencyServiceTest, SignInSignOut) { TEST_F(AccountConsistencyServiceTest, SignInSignOut) {
// Check that main Google domains are added.
AddPageLoadedExpectation(kGoogleUrl, true /* continue_navigation */);
AddPageLoadedExpectation(kYoutubeUrl, true /* continue_navigation */);
SignIn(); SignIn();
// Check that other Google domains are added on navigation. CheckDomainHasChromeConnectedCookie(kGoogleDomain);
AddPageLoadedExpectation(kCountryGoogleUrl, true /* continue_navigation */); CheckDomainHasChromeConnectedCookie(kYoutubeDomain);
CheckNoChromeConnectedCookieForDomain(kCountryGoogleDomain);
id delegate = id delegate =
[OCMockObject mockForProtocol:@protocol(ManageAccountsDelegate)]; [OCMockObject mockForProtocol:@protocol(ManageAccountsDelegate)];
...@@ -291,23 +313,26 @@ TEST_F(AccountConsistencyServiceTest, SignInSignOut) { ...@@ -291,23 +313,26 @@ TEST_F(AccountConsistencyServiceTest, SignInSignOut) {
statusCode:200 statusCode:200
HTTPVersion:@"HTTP/1.1" HTTPVersion:@"HTTP/1.1"
headerFields:headers]; headerFields:headers];
web_view_load_expection_count_ = 1;
account_consistency_service_->SetWebStateHandler(&web_state_, delegate); account_consistency_service_->SetWebStateHandler(&web_state_, delegate);
EXPECT_TRUE( EXPECT_TRUE(
web_state_.ShouldAllowResponse(response, /* for_main_frame = */ true)); web_state_.ShouldAllowResponse(response, /* for_main_frame = */ true));
web_state_.WebStateDestroyed(); web_state_.WebStateDestroyed();
EXPECT_EQ(0, web_view_load_expection_count_);
// Check that all domains are removed. // Check that cookies was also added for |kCountryGoogleDomain|.
AddPageLoadedExpectation(kGoogleUrl, true /* continue_navigation */); CheckDomainHasChromeConnectedCookie(kGoogleDomain);
AddPageLoadedExpectation(kYoutubeUrl, true /* continue_navigation */); CheckDomainHasChromeConnectedCookie(kYoutubeDomain);
AddPageLoadedExpectation(kCountryGoogleUrl, true /* continue_navigation */); CheckDomainHasChromeConnectedCookie(kCountryGoogleDomain);
SignOutAndSimulateGaiaCookieManagerServiceLogout(); SignOutAndSimulateGaiaCookieManagerServiceLogout();
CheckNoChromeConnectedCookies();
} }
// Tests that signing out with no domains known, still call the callback. // Tests that signing out with no domains known, still call the callback.
TEST_F(AccountConsistencyServiceTest, SignOutWithoutDomains) { TEST_F(AccountConsistencyServiceTest, SignOutWithoutDomains) {
CheckNoChromeConnectedCookies();
SignOutAndSimulateGaiaCookieManagerServiceLogout(); SignOutAndSimulateGaiaCookieManagerServiceLogout();
CheckNoChromeConnectedCookies();
} }
// Tests that pending cookie requests are correctly applied when the browser // Tests that pending cookie requests are correctly applied when the browser
...@@ -315,27 +340,13 @@ TEST_F(AccountConsistencyServiceTest, SignOutWithoutDomains) { ...@@ -315,27 +340,13 @@ TEST_F(AccountConsistencyServiceTest, SignOutWithoutDomains) {
TEST_F(AccountConsistencyServiceTest, ApplyOnActive) { TEST_F(AccountConsistencyServiceTest, ApplyOnActive) {
// No request is made until the browser state is active, then a WKWebView and // No request is made until the browser state is active, then a WKWebView and
// its navigation delegate are created, and the requests are processed. // its navigation delegate are created, and the requests are processed.
[[GetMockWKWebView() expect] setNavigationDelegate:[OCMArg isNotNil]];
ActiveStateManager::FromBrowserState(&browser_state_)->SetActive(false); ActiveStateManager::FromBrowserState(&browser_state_)->SetActive(false);
SignIn(); SignIn();
AddPageLoadedExpectation(kGoogleUrl, true /* continue_navigation */); CheckNoChromeConnectedCookies();
AddPageLoadedExpectation(kYoutubeUrl, true /* continue_navigation */);
ActiveStateManager::FromBrowserState(&browser_state_)->SetActive(true);
}
// Tests that cookie request being processed is correctly cancelled when the
// browser state becomes inactives and correctly re-started later when the
// browser state becomes active.
TEST_F(AccountConsistencyServiceTest, CancelOnInactiveReApplyOnActive) {
// The first request starts to get applied and get cancelled as the browser
// state becomes inactive. It is resumed after the browser state becomes
// active again.
AddPageLoadedExpectation(kGoogleUrl, false /* continue_navigation */);
SignIn();
ActiveStateManager::FromBrowserState(&browser_state_)->SetActive(false);
AddPageLoadedExpectation(kGoogleUrl, true /* continue_navigation */);
AddPageLoadedExpectation(kYoutubeUrl, true /* continue_navigation */);
ActiveStateManager::FromBrowserState(&browser_state_)->SetActive(true); ActiveStateManager::FromBrowserState(&browser_state_)->SetActive(true);
CheckDomainHasChromeConnectedCookie(kGoogleDomain);
CheckDomainHasChromeConnectedCookie(kYoutubeDomain);
} }
// Tests that the X-Chrome-Manage-Accounts header is ignored unless it comes // Tests that the X-Chrome-Manage-Accounts header is ignored unless it comes
...@@ -408,42 +419,30 @@ TEST_F(AccountConsistencyServiceTest, ChromeManageAccountsDefault) { ...@@ -408,42 +419,30 @@ TEST_F(AccountConsistencyServiceTest, ChromeManageAccountsDefault) {
EXPECT_OCMOCK_VERIFY(delegate); EXPECT_OCMOCK_VERIFY(delegate);
} }
// Tests that domains with cookie are added to the prefs only after the request
// has been applied.
TEST_F(AccountConsistencyServiceTest, DomainsWithCookiePrefsOnApplied) {
// Second request is not completely applied. Ensure prefs reflect that.
AddPageLoadedExpectation(kGoogleUrl, true /* continue_navigation */);
AddPageLoadedExpectation(kYoutubeUrl, false /* continue_navigation */);
SignIn();
const base::DictionaryValue* dict =
prefs_.GetDictionary(AccountConsistencyService::kDomainsWithCookiePref);
EXPECT_EQ(1u, dict->size());
EXPECT_TRUE(dict->GetBooleanWithoutPathExpansion("google.com", nullptr));
EXPECT_FALSE(dict->GetBooleanWithoutPathExpansion("youtube.com", nullptr));
}
// Tests that domains with cookie are correctly loaded from the prefs on service // Tests that domains with cookie are correctly loaded from the prefs on service
// startup. // startup.
TEST_F(AccountConsistencyServiceTest, DomainsWithCookieLoadedFromPrefs) { TEST_F(AccountConsistencyServiceTest, DomainsWithCookieLoadedFromPrefs) {
AddPageLoadedExpectation(kGoogleUrl, true /* continue_navigation */);
AddPageLoadedExpectation(kYoutubeUrl, true /* continue_navigation */);
SignIn(); SignIn();
CheckDomainHasChromeConnectedCookie(kGoogleDomain);
CheckDomainHasChromeConnectedCookie(kYoutubeDomain);
ResetAccountConsistencyService(); ResetAccountConsistencyService();
AddPageLoadedExpectation(kGoogleUrl, true /* continue_navigation */); CheckDomainHasChromeConnectedCookie(kGoogleDomain);
AddPageLoadedExpectation(kYoutubeUrl, true /* continue_navigation */); CheckDomainHasChromeConnectedCookie(kYoutubeDomain);
SignOutAndSimulateGaiaCookieManagerServiceLogout(); SignOutAndSimulateGaiaCookieManagerServiceLogout();
CheckNoChromeConnectedCookies();
} }
// Tests that domains with cookie are cleared when browsing data is removed. // Tests that domains with cookie are cleared when browsing data is removed.
TEST_F(AccountConsistencyServiceTest, DomainsClearedOnBrowsingDataRemoved) { TEST_F(AccountConsistencyServiceTest, DomainsClearedOnBrowsingDataRemoved) {
AddPageLoadedExpectation(kGoogleUrl, true /* continue_navigation */);
AddPageLoadedExpectation(kYoutubeUrl, true /* continue_navigation */);
SignIn(); SignIn();
const base::DictionaryValue* dict = CheckDomainHasChromeConnectedCookie(kGoogleDomain);
prefs_.GetDictionary(AccountConsistencyService::kDomainsWithCookiePref); CheckDomainHasChromeConnectedCookie(kYoutubeDomain);
EXPECT_EQ(2u, dict->size()); EXPECT_EQ(
2u,
prefs_.GetDictionary(AccountConsistencyService::kDomainsWithCookiePref)
->size());
// Sets Response to get IdentityManager::Observer::OnAccountsInCookieUpdated // Sets Response to get IdentityManager::Observer::OnAccountsInCookieUpdated
// through GaiaCookieManagerService::OnCookieChange. // through GaiaCookieManagerService::OnCookieChange.
...@@ -456,36 +455,20 @@ TEST_F(AccountConsistencyServiceTest, DomainsClearedOnBrowsingDataRemoved) { ...@@ -456,36 +455,20 @@ TEST_F(AccountConsistencyServiceTest, DomainsClearedOnBrowsingDataRemoved) {
// AccountsCookieMutator::ForceTriggerOnCookieChange and finally // AccountsCookieMutator::ForceTriggerOnCookieChange and finally
// IdentityManager::Observer::OnAccountsInCookieUpdated is called. // IdentityManager::Observer::OnAccountsInCookieUpdated is called.
account_consistency_service_->OnBrowsingDataRemoved(); account_consistency_service_->OnBrowsingDataRemoved();
EXPECT_EQ(
0u,
prefs_.GetDictionary(AccountConsistencyService::kDomainsWithCookiePref)
->size());
run_loop.Run(); run_loop.Run();
dict = // AccountConsistency service is supposed to rebuild the CHROME_CONNECTED
prefs_.GetDictionary(AccountConsistencyService::kDomainsWithCookiePref); // cookies when browsing data is removed.
EXPECT_EQ(0u, dict->size()); CheckDomainHasChromeConnectedCookie(kGoogleDomain);
} CheckDomainHasChromeConnectedCookie(kYoutubeDomain);
EXPECT_EQ(
// Tests that remove cookie call back is called when the signout is interrupted 2u,
// by removing the browser data. prefs_.GetDictionary(AccountConsistencyService::kDomainsWithCookiePref)
TEST_F(AccountConsistencyServiceTest, DomainsClearedOnBrowsingDataRemoved2) { ->size());
AddPageLoadedExpectation(kGoogleUrl, true /* continue_navigation */);
AddPageLoadedExpectation(kYoutubeUrl, true /* continue_navigation */);
SignIn();
AddPageLoadedExpectation(kGoogleUrl, false /* continue_navigation */);
SimulateGaiaCookieManagerServiceLogout(false);
// Sets Response to get IdentityManager::Observer::OnAccountsInCookieUpdated
// through GaiaCookieManagerService::OnCookieChange.
signin::SetListAccountsResponseNoAccounts(&test_url_loader_factory_);
base::RunLoop run_loop;
identity_test_env_->identity_manager_observer()
->SetOnAccountsInCookieUpdatedCallback(run_loop.QuitClosure());
// OnBrowsingDataRemoved triggers
// AccountsCookieMutator::ForceTriggerOnCookieChange and finally
// IdentityManager::Observer::OnAccountsInCookieUpdated is called.
account_consistency_service_->OnBrowsingDataRemoved();
run_loop.Run();
EXPECT_TRUE(remove_cookie_callback_called_);
} }
// Tests that cookie requests are correctly processed or ignored when the update // Tests that cookie requests are correctly processed or ignored when the update
...@@ -494,15 +477,16 @@ TEST_F(AccountConsistencyServiceTest, ShouldAddCookieDontCheckUpdateTime) { ...@@ -494,15 +477,16 @@ TEST_F(AccountConsistencyServiceTest, ShouldAddCookieDontCheckUpdateTime) {
EXPECT_TRUE(ShouldAddCookieToDomain(kGoogleDomain, false)); EXPECT_TRUE(ShouldAddCookieToDomain(kGoogleDomain, false));
EXPECT_TRUE(ShouldAddCookieToDomain(kYoutubeDomain, false)); EXPECT_TRUE(ShouldAddCookieToDomain(kYoutubeDomain, false));
AddPageLoadedExpectation(kGoogleUrl, true /* continue_navigation */);
AddPageLoadedExpectation(kYoutubeUrl, true /* continue_navigation */);
SignIn(); SignIn();
CheckDomainHasChromeConnectedCookie(kGoogleDomain);
CheckDomainHasChromeConnectedCookie(kYoutubeDomain);
EXPECT_FALSE(ShouldAddCookieToDomain(kGoogleDomain, false)); EXPECT_FALSE(ShouldAddCookieToDomain(kGoogleDomain, false));
EXPECT_FALSE(ShouldAddCookieToDomain(kYoutubeDomain, false)); EXPECT_FALSE(ShouldAddCookieToDomain(kYoutubeDomain, false));
ResetAccountConsistencyService(); ResetAccountConsistencyService();
CheckDomainHasChromeConnectedCookie(kGoogleDomain);
CheckDomainHasChromeConnectedCookie(kYoutubeDomain);
EXPECT_FALSE(ShouldAddCookieToDomain(kGoogleDomain, false)); EXPECT_FALSE(ShouldAddCookieToDomain(kGoogleDomain, false));
EXPECT_FALSE(ShouldAddCookieToDomain(kYoutubeDomain, false)); EXPECT_FALSE(ShouldAddCookieToDomain(kYoutubeDomain, false));
} }
...@@ -513,26 +497,17 @@ TEST_F(AccountConsistencyServiceTest, ShouldAddCookieCheckUpdateTime) { ...@@ -513,26 +497,17 @@ TEST_F(AccountConsistencyServiceTest, ShouldAddCookieCheckUpdateTime) {
EXPECT_TRUE(ShouldAddCookieToDomain(kGoogleDomain, true)); EXPECT_TRUE(ShouldAddCookieToDomain(kGoogleDomain, true));
EXPECT_TRUE(ShouldAddCookieToDomain(kYoutubeDomain, true)); EXPECT_TRUE(ShouldAddCookieToDomain(kYoutubeDomain, true));
AddPageLoadedExpectation(kGoogleUrl, true /* continue_navigation */);
AddPageLoadedExpectation(kYoutubeUrl, true /* continue_navigation */);
SignIn(); SignIn();
CheckDomainHasChromeConnectedCookie(kGoogleDomain);
CheckDomainHasChromeConnectedCookie(kYoutubeDomain);
EXPECT_FALSE(ShouldAddCookieToDomain(kGoogleDomain, true)); EXPECT_FALSE(ShouldAddCookieToDomain(kGoogleDomain, true));
EXPECT_FALSE(ShouldAddCookieToDomain(kYoutubeDomain, true)); EXPECT_FALSE(ShouldAddCookieToDomain(kYoutubeDomain, true));
ResetAccountConsistencyService(); ResetAccountConsistencyService();
CheckDomainHasChromeConnectedCookie(kGoogleDomain);
CheckDomainHasChromeConnectedCookie(kYoutubeDomain);
EXPECT_TRUE(ShouldAddCookieToDomain(kGoogleDomain, true)); EXPECT_TRUE(ShouldAddCookieToDomain(kGoogleDomain, true));
EXPECT_TRUE(ShouldAddCookieToDomain(kYoutubeDomain, true)); EXPECT_TRUE(ShouldAddCookieToDomain(kYoutubeDomain, true));
} }
// Tests that main domains are added to the internal map when cookies are set in
// reaction to signin.
TEST_F(AccountConsistencyServiceTest, SigninAddCookieOnMainDomains) {
AddPageLoadedExpectation(kGoogleUrl, true /* continue_navigation */);
AddPageLoadedExpectation(kYoutubeUrl, true /* continue_navigation */);
SignIn();
CheckDomainHasCookie(kGoogleDomain);
CheckDomainHasCookie(kYoutubeDomain);
}
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include "base/bind_helpers.h" #include "base/bind_helpers.h"
#include "base/mac/foundation_util.h" #include "base/mac/foundation_util.h"
#include "base/strings/sys_string_conversions.h" #include "base/strings/sys_string_conversions.h"
#include "components/signin/ios/browser/account_consistency_service.h"
#include "ios/chrome/browser/signin/feature_flags.h" #include "ios/chrome/browser/signin/feature_flags.h"
#include "ios/net/cookies/system_cookie_util.h" #include "ios/net/cookies/system_cookie_util.h"
#include "ios/web/common/features.h" #include "ios/web/common/features.h"
...@@ -20,13 +21,6 @@ ...@@ -20,13 +21,6 @@
#error "This file requires ARC support." #error "This file requires ARC support."
#endif #endif
namespace {
// Name of the cookie that is managed by AccountConsistencyService and is used
// to inform Google web properties that the browser is connected and that Google
// authentication cookies are managed by |AccountReconcilor|).
const char kChromeConnectedCookieName[] = "CHROME_CONNECTED";
}
@interface GaiaAuthFetcherIOSURLSessionDelegate @interface GaiaAuthFetcherIOSURLSessionDelegate
: NSObject <NSURLSessionTaskDelegate> : NSObject <NSURLSessionTaskDelegate>
...@@ -159,8 +153,10 @@ void GaiaAuthFetcherIOSNSURLSessionBridge::FetchPendingRequestWithCookies( ...@@ -159,8 +153,10 @@ void GaiaAuthFetcherIOSNSURLSessionBridge::FetchPendingRequestWithCookies(
// |CHROME_CONNECTED| cookie is attached to all web requests to Google web // |CHROME_CONNECTED| cookie is attached to all web requests to Google web
// properties. Requests initiated from the browser services (e.g. // properties. Requests initiated from the browser services (e.g.
// GaiaCookieManagerService) must not include this cookie. // GaiaCookieManagerService) must not include this cookie.
if (cookie_with_access_result.cookie.Name() == kChromeConnectedCookieName) if (cookie_with_access_result.cookie.Name() ==
AccountConsistencyService::kChromeConnectedCookieName) {
continue; continue;
}
[http_cookies addObject:net::SystemCookieFromCanonicalCookie( [http_cookies addObject:net::SystemCookieFromCanonicalCookie(
cookie_with_access_result.cookie)]; cookie_with_access_result.cookie)];
} }
......
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