Commit e040790c authored by Nate Fischer's avatar Nate Fischer Committed by Commit Bot

AW: implement setCookie for the network service

This implements CookieManager#setCookie for the network service code
path, plumbing SetCanonicalCookie through AwCookieManagerWrapper.

Along with this change, this re-implements #setCookie for the legacy
code path, to use SetCanonicalCookieAsync instead of
SetCookieWithOptionsAsync (to share code between the new and old code
paths).

This splits apart a test, which I found to be useful when debugging
(since the "good URL" and "bad URL" case are actually quite different).

This removes a bunch of tests from the test filter, and adds in new
breakages (mainly due to #hasCookies() not being implemented yet).

This also updates some TODOs, to point at finer-grained crbugs I've
filed since the last CL.

Bug: 933460, 931641, 902641, 933462, 893580
Test: run_webview_instrumentation_test_apk -f=CookieManagerTest#* \
Test: --enable-features=NetworkService,NetworkServiceInProcess
Cq-Include-Trybots: master.tryserver.chromium.android:android_mojo
Change-Id: I3f29a209aa9a442d273b8673c01848801f298ee8
Reviewed-on: https://chromium-review.googlesource.com/c/1478470Reviewed-by: default avatarJohn Abd-El-Malek <jam@chromium.org>
Reviewed-by: default avatarRichard Coles <torne@chromium.org>
Commit-Queue: Nate Fischer <ntfschr@chromium.org>
Cr-Commit-Position: refs/heads/master@{#634460}
parent eea4064a
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
#include "base/synchronization/waitable_event.h" #include "base/synchronization/waitable_event.h"
#include "base/threading/thread.h" #include "base/threading/thread.h"
#include "base/threading/thread_restrictions.h" #include "base/threading/thread_restrictions.h"
#include "base/time/time.h"
#include "content/public/browser/browser_thread.h" #include "content/public/browser/browser_thread.h"
#include "content/public/browser/cookie_store_factory.h" #include "content/public/browser/cookie_store_factory.h"
#include "jni/AwCookieManager_jni.h" #include "jni/AwCookieManager_jni.h"
...@@ -95,6 +96,38 @@ class BoolCookieCallbackHolder { ...@@ -95,6 +96,38 @@ class BoolCookieCallbackHolder {
namespace { namespace {
// Copied from net/cookies/cookie_util.h for consistency and backwards
// compatibility.
const int kVlogSetCookies = 7;
// TODO(ntfschr): see if we can turn this into OnceCallback.
// http://crbug.com/932535.
void MaybeRunCookieCallback(base::RepeatingCallback<void(bool)> callback,
const bool& result) {
if (callback)
std::move(callback).Run(result);
}
GURL MaybeFixUpSchemeForSecureCookie(const GURL& host,
const std::string& value) {
// Log message for catching strict secure cookies related bugs.
// TODO(sgurun) temporary. Add UMA stats to monitor, and remove afterwards.
// http://crbug.com/933981.
if (host.is_valid() &&
(!host.has_scheme() || host.SchemeIs(url::kHttpScheme))) {
net::ParsedCookie parsed_cookie(value);
if (parsed_cookie.IsValid() && parsed_cookie.IsSecure()) {
LOG(WARNING) << "Strict Secure Cookie policy does not allow setting a "
"secure cookie for "
<< host.spec();
GURL::Replacements replace_host;
replace_host.SetSchemeStr("https");
return host.ReplaceComponents(replace_host);
}
}
return host;
}
// Construct a closure which signals a waitable event if and when the closure // Construct a closure which signals a waitable event if and when the closure
// is called the waitable event must still exist. // is called the waitable event must still exist.
static base::RepeatingClosure SignalEventClosure(WaitableEvent* completion) { static base::RepeatingClosure SignalEventClosure(WaitableEvent* completion) {
...@@ -308,26 +341,63 @@ void CookieManager::SetCookieHelper( ...@@ -308,26 +341,63 @@ void CookieManager::SetCookieHelper(
net::CookieOptions options; net::CookieOptions options;
options.set_include_httponly(); options.set_include_httponly();
// Log message for catching strict secure cookies related bugs. const GURL& new_host = MaybeFixUpSchemeForSecureCookie(host, value);
// TODO(sgurun) temporary. Add UMA stats to monitor, and remove afterwards.
if (host.is_valid() && // The below is copied from net::CookieMonster::SetCookieWithOptions. We do
(!host.has_scheme() || host.SchemeIs(url::kHttpScheme))) { // this because we have strict requirements to keep behavior identical across
net::ParsedCookie parsed_cookie(value); // WebView versions.
if (parsed_cookie.IsValid() && parsed_cookie.IsSecure()) {
LOG(WARNING) << "Strict Secure Cookie policy does not allow setting a " // If this scheme is not cookieable, then we should not set a cookie for it.
"secure cookie for " // Instead, invoke the callback and indicate failure.
<< host.spec(); if (!HasCookieableScheme(new_host)) {
GURL::Replacements replace_host; MaybeRunCookieCallback(std::move(callback), false);
replace_host.SetSchemeStr("https"); return;
GURL new_host = host.ReplaceComponents(replace_host); }
GetCookieStore()->SetCookieWithOptionsAsync(new_host, value, options,
StatusToBool(callback)); VLOG(kVlogSetCookies) << "SetCookie() line: " << value;
net::CanonicalCookie::CookieInclusionStatus status;
std::unique_ptr<net::CanonicalCookie> cc(net::CanonicalCookie::Create(
new_host, value, base::Time::Now(), options, &status));
if (status != net::CanonicalCookie::CookieInclusionStatus::INCLUDE) {
DCHECK(!cc);
VLOG(kVlogSetCookies) << "WARNING: Failed to allocate CanonicalCookie";
MaybeRunCookieCallback(std::move(callback), false);
return; return;
} }
DCHECK(cc);
// Note: CookieStore and network::CookieManager have different signatures: one
// accepts a boolean callback while the other (recently) changed to accept a
// CookieInclusionStatus callback. WebView only cares about boolean success,
// which is why we use StatusToBool. This is temporary technical debt until we
// fully launch the Network Service code path.
if (base::FeatureList::IsEnabled(network::features::kNetworkService)) {
// *cc.get() is safe, because network::CookieManager::SetCanonicalCookie
// will make a copy before our smart pointer goes out of scope.
GetCookieManagerWrapper()->SetCanonicalCookie(
*cc.get(), new_host.SchemeIsCryptographic(),
!options.exclude_httponly(), std::move(callback));
} else {
GetCookieStore()->SetCanonicalCookieAsync(
std::move(cc), new_host.SchemeIsCryptographic(),
!options.exclude_httponly(), StatusToBool(callback));
} }
}
GetCookieStore()->SetCookieWithOptionsAsync(host, value, options, bool CookieManager::HasCookieableScheme(const GURL& host) {
StatusToBool(callback)); for (int i = 0; i < net::CookieMonster::kDefaultCookieableSchemesCount; ++i) {
if (host.SchemeIs(net::CookieMonster::kDefaultCookieableSchemes[i])) {
return true;
}
}
if (host.SchemeIsFile() && AllowFileSchemeCookies()) {
return true;
}
return false;
} }
std::string CookieManager::GetCookie(const GURL& host) { std::string CookieManager::GetCookie(const GURL& host) {
......
...@@ -113,6 +113,9 @@ class CookieManager { ...@@ -113,6 +113,9 @@ class CookieManager {
bool* result, bool* result,
const net::CookieList& cookies, const net::CookieList& cookies,
const net::CookieStatusList& excluded_cookies); const net::CookieStatusList& excluded_cookies);
// Determine if cookies can be set for |host|, based on its scheme. This is
// based on net::CookieMonster::HasCookieableScheme.
bool HasCookieableScheme(const GURL& host);
// This protects the following two bools, as they're used on multiple threads. // This protects the following two bools, as they're used on multiple threads.
base::Lock accept_file_scheme_cookies_lock_; base::Lock accept_file_scheme_cookies_lock_;
......
...@@ -26,8 +26,19 @@ void AwCookieManagerWrapper::GetCookieList( ...@@ -26,8 +26,19 @@ void AwCookieManagerWrapper::GetCookieList(
const net::CookieOptions& cookie_options, const net::CookieOptions& cookie_options,
GetCookieListCallback callback) { GetCookieListCallback callback) {
// TODO(ntfschr): handle the case where content layer isn't initialized yet // TODO(ntfschr): handle the case where content layer isn't initialized yet
// (http://crbug.com/902641). // (http://crbug.com/933461).
cookie_manager_->GetCookieList(url, cookie_options, std::move(callback)); cookie_manager_->GetCookieList(url, cookie_options, std::move(callback));
} }
void AwCookieManagerWrapper::SetCanonicalCookie(
const net::CanonicalCookie& cc,
bool secure_source,
bool modify_http_only,
SetCanonicalCookieCallback callback) {
// TODO(ntfschr): handle the case where content layer isn't initialized yet
// (http://crbug.com/933461).
cookie_manager_->SetCanonicalCookie(cc, secure_source, modify_http_only,
std::move(callback));
}
} // namespace android_webview } // namespace android_webview
...@@ -11,9 +11,6 @@ ...@@ -11,9 +11,6 @@
namespace android_webview { namespace android_webview {
using GetCookieListCallback =
base::OnceCallback<void(const std::vector<net::CanonicalCookie>&)>;
// AwCookieManagerWrapper is a thin wrapper around // AwCookieManagerWrapper is a thin wrapper around
// network::mojom::CookieManager. This lives on the CookieStore TaskRunner. This // network::mojom::CookieManager. This lives on the CookieStore TaskRunner. This
// class's main responsibility is to support the CookieManager APIs before it // class's main responsibility is to support the CookieManager APIs before it
...@@ -24,17 +21,30 @@ class AwCookieManagerWrapper { ...@@ -24,17 +21,30 @@ class AwCookieManagerWrapper {
AwCookieManagerWrapper(); AwCookieManagerWrapper();
~AwCookieManagerWrapper(); ~AwCookieManagerWrapper();
// We redefine these type aliases for consistency and readability. These are
// originally defined by generated mojo code in
// out/<folder>/gen/services/network/public/mojom/cookie_manager.mojom.h.
using GetCookieListCallback =
network::mojom::CookieManager::GetCookieListCallback;
using SetCanonicalCookieCallback =
network::mojom::CookieManager::SetCanonicalCookieCallback;
// Called when content layer starts up, to pass in a NetworkContextPtr for us // Called when content layer starts up, to pass in a NetworkContextPtr for us
// to use for Cookies APIs. // to use for Cookies APIs.
void SetMojoCookieManager( void SetMojoCookieManager(
network::mojom::CookieManagerPtrInfo cookie_manager_info); network::mojom::CookieManagerPtrInfo cookie_manager_info);
// Thin wrappers around network::mojom::CookieManager APIs. // Thin wrappers around network::mojom::CookieManager APIs.
// TODO(ntfschr): implement the other APIs we need (http://crbug.com/902641). // TODO(ntfschr): implement the other APIs we need (http://crbug.com/933462).
void GetCookieList(const GURL& url, void GetCookieList(const GURL& url,
const net::CookieOptions& cookie_options, const net::CookieOptions& cookie_options,
GetCookieListCallback callback); GetCookieListCallback callback);
void SetCanonicalCookie(const net::CanonicalCookie& cc,
bool secure_source,
bool modify_http_only,
SetCanonicalCookieCallback);
private: private:
// A CookieManagerPtr which is cloned from the NetworkContext's // A CookieManagerPtr which is cloned from the NetworkContext's
// CookieManagerPtr (but, lives on this thread). // CookieManagerPtr (but, lives on this thread).
......
...@@ -299,10 +299,9 @@ public class CookieManagerTest { ...@@ -299,10 +299,9 @@ public class CookieManagerTest {
@Test @Test
@MediumTest @MediumTest
@Feature({"AndroidWebView", "Privacy"}) @Feature({"AndroidWebView", "Privacy"})
public void testSetCookieCallback() throws Throwable { public void testSetCookieCallback_goodUrl() throws Throwable {
final String url = "http://www.example.com"; final String url = "http://www.example.com";
final String cookie = "name=test"; final String cookie = "name=test";
final String brokenUrl = "foo";
final TestCallback<Boolean> callback = new TestCallback<Boolean>(); final TestCallback<Boolean> callback = new TestCallback<Boolean>();
int callCount = callback.getOnResultHelper().getCallCount(); int callCount = callback.getOnResultHelper().getCallCount();
...@@ -311,13 +310,23 @@ public class CookieManagerTest { ...@@ -311,13 +310,23 @@ public class CookieManagerTest {
callback.getOnResultHelper().waitForCallback(callCount); callback.getOnResultHelper().waitForCallback(callCount);
Assert.assertTrue(callback.getValue()); Assert.assertTrue(callback.getValue());
Assert.assertEquals(cookie, mCookieManager.getCookie(url)); Assert.assertEquals(cookie, mCookieManager.getCookie(url));
}
callCount = callback.getOnResultHelper().getCallCount(); @Test
@MediumTest
@Feature({"AndroidWebView", "Privacy"})
public void testSetCookieCallback_badUrl() throws Throwable {
final String cookie = "name=test";
final String brokenUrl = "foo";
final TestCallback<Boolean> callback = new TestCallback<Boolean>();
int callCount = callback.getOnResultHelper().getCallCount();
setCookieOnUiThread(brokenUrl, cookie, callback); setCookieOnUiThread(brokenUrl, cookie, callback);
callback.getOnResultHelper().waitForCallback(callCount); callback.getOnResultHelper().waitForCallback(callCount);
Assert.assertFalse(callback.getValue()); Assert.assertFalse("Cookie should not be set for bad URLs", callback.getValue());
Assert.assertEquals(null, mCookieManager.getCookie(brokenUrl)); Assert.assertNull("getCookie should be null if cookie hasn't been set",
mCookieManager.getCookie(brokenUrl));
} }
@Test @Test
...@@ -333,6 +342,7 @@ public class CookieManagerTest { ...@@ -333,6 +342,7 @@ public class CookieManagerTest {
mCookieManager.setCookie(url, cookie, null); mCookieManager.setCookie(url, cookie, null);
AwActivityTestRule.pollInstrumentationThread(() -> mCookieManager.hasCookies()); AwActivityTestRule.pollInstrumentationThread(() -> mCookieManager.hasCookies());
Assert.assertEquals(cookie, mCookieManager.getCookie(url));
} }
@Test @Test
......
...@@ -20,35 +20,29 @@ ...@@ -20,35 +20,29 @@
-org.chromium.android_webview.test.AwContentsClientShouldInterceptRequestTest.testLoadDataWithBaseUrlTriggersShouldInterceptRequest -org.chromium.android_webview.test.AwContentsClientShouldInterceptRequestTest.testLoadDataWithBaseUrlTriggersShouldInterceptRequest
# https://crbug.com/893575 # https://crbug.com/893575
-org.chromium.android_webview.test.CookieManagerStartupTest.testShouldInterceptRequestDeadlock
-org.chromium.android_webview.test.CookieManagerStartupTest.testStartup -org.chromium.android_webview.test.CookieManagerStartupTest.testStartup
# https://crbug.com/893576 # https://crbug.com/893576
-org.chromium.android_webview.test.CookieManagerTest.testAcceptCookie_falseWontSetCookies -org.chromium.android_webview.test.CookieManagerTest.testAcceptCookie_falseWontSetCookies
-org.chromium.android_webview.test.CookieManagerTest.testAcceptFileSchemeCookies -org.chromium.android_webview.test.CookieManagerTest.testAcceptFileSchemeCookies
-org.chromium.android_webview.test.CookieManagerTest.testEmbedderCanSeeRestrictedCookies
-org.chromium.android_webview.test.CookieManagerTest.testRejectFileSchemeCookies
-org.chromium.android_webview.test.CookieManagerTest.testThirdPartyCookie -org.chromium.android_webview.test.CookieManagerTest.testThirdPartyCookie
-org.chromium.android_webview.test.CookieManagerTest.testThirdPartyCookieForWebSocketEnabledCase
-org.chromium.android_webview.test.CookieManagerTest.testThirdPartyCookiesArePerWebview -org.chromium.android_webview.test.CookieManagerTest.testThirdPartyCookiesArePerWebview
-org.chromium.android_webview.test.CookieManagerTest.testThirdPartyJavascriptCookie
# https://crbug.com/931641 # https://crbug.com/931641
-org.chromium.android_webview.test.CookieManagerTest.testRemoveSessionCookiesNullCallback -org.chromium.android_webview.test.CookieManagerTest.testRemoveSessionCookiesNullCallback
-org.chromium.android_webview.test.CookieManagerTest.testSetSecureCookieForHttpUrl
-org.chromium.android_webview.test.CookieManagerTest.testSetCookie
-org.chromium.android_webview.test.CookieManagerTest.testThirdPartyCookieForWebSocketDisabledCase -org.chromium.android_webview.test.CookieManagerTest.testThirdPartyCookieForWebSocketDisabledCase
-org.chromium.android_webview.test.CookieManagerTest.testSetCookieWithDomainForUrlWithTrailingSemicolonInCookie
-org.chromium.android_webview.test.CookieManagerTest.testSetCookieWithDomainForUrl
-org.chromium.android_webview.test.CookieManagerTest.testRemoveSessionCookies -org.chromium.android_webview.test.CookieManagerTest.testRemoveSessionCookies
-org.chromium.android_webview.test.CookieManagerTest.testSetCookieCallback
-org.chromium.android_webview.test.CookieManagerTest.testRemoveSessionCookiesCallback -org.chromium.android_webview.test.CookieManagerTest.testRemoveSessionCookiesCallback
-org.chromium.android_webview.test.CookieManagerTest.testSetCookieWithDomainForUrlAndExistingDomainAttribute
-org.chromium.android_webview.test.CookieManagerTest.testCookieExpiration # https://crbug.com/933629
-org.chromium.android_webview.test.CookieManagerTest.testCookiesExpire
-org.chromium.android_webview.test.CookieManagerTest.testHasCookie
-org.chromium.android_webview.test.CookieManagerTest.testRemoveAllCookies
-org.chromium.android_webview.test.CookieManagerTest.testRemoveAllCookiesCallback
-org.chromium.android_webview.test.CookieManagerTest.testSetCookieNullCallback
# https://crbug.com/893580 # https://crbug.com/893580
-org.chromium.android_webview.test.LoadDataWithBaseUrlTest.testLoadDataWithBaseUrlAccessingFile -org.chromium.android_webview.test.LoadDataWithBaseUrlTest.testLoadDataWithBaseUrlAccessingFile
-org.chromium.android_webview.test.LoadDataWithBaseUrlTest.testThirdPartyCookieInIframe
# https://crbug.com/893582 # https://crbug.com/893582
-org.chromium.android_webview.test.SafeBrowsingTest.testSafeBrowsingDontProceedCausesNetworkErrorForMainFrame -org.chromium.android_webview.test.SafeBrowsingTest.testSafeBrowsingDontProceedCausesNetworkErrorForMainFrame
......
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