Commit 3bb7f20f authored by allada's avatar allada Committed by Commit bot

[Devtools] Backend support for setCookie

Added the ability to set/update cookie through protocol. This was
modeled after extensions set cookie method found here:
https://developer.chrome.com/extensions/cookies#method-set

BUG=166637
R=dgozman

Review-Url: https://codereview.chromium.org/2221093003
Cr-Commit-Position: refs/heads/master@{#412625}
parent b4a313a7
...@@ -32,7 +32,9 @@ namespace content { ...@@ -32,7 +32,9 @@ namespace content {
namespace devtools { namespace devtools {
namespace network { namespace network {
using Response = DevToolsProtocolClient::Response;
using CookieListCallback = net::CookieStore::GetCookieListCallback; using CookieListCallback = net::CookieStore::GetCookieListCallback;
using SetCookieCallback = net::CookieStore::SetCookiesCallback;
namespace { namespace {
...@@ -126,6 +128,72 @@ void DeleteCookieOnUI( ...@@ -126,6 +128,72 @@ void DeleteCookieOnUI(
callback)); callback));
} }
void CookieSetOnIO(const SetCookieCallback& callback, bool success) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
BrowserThread::PostTask(
BrowserThread::UI,
FROM_HERE,
base::Bind(callback, success));
}
void SetCookieOnIO(
ResourceContext* resource_context,
net::URLRequestContextGetter* context_getter,
const GURL& url,
const std::string& name,
const std::string& value,
const std::string& domain,
const std::string& path,
bool secure,
bool http_only,
net::CookieSameSite same_site,
base::Time expires,
const SetCookieCallback& callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
net::URLRequestContext* request_context =
GetRequestContextOnIO(resource_context, context_getter, url);
bool are_experimental_cookie_features_enabled =
request_context->network_delegate()
->AreExperimentalCookieFeaturesEnabled();
request_context->cookie_store()->SetCookieWithDetailsAsync(
url, name, value, domain, path,
base::Time(),
expires,
base::Time(),
secure,
http_only,
same_site,
are_experimental_cookie_features_enabled,
net::COOKIE_PRIORITY_DEFAULT,
base::Bind(&CookieSetOnIO, callback));
}
void SetCookieOnUI(
ResourceContext* resource_context,
net::URLRequestContextGetter* context_getter,
const GURL& url,
const std::string& name,
const std::string& value,
const std::string& domain,
const std::string& path,
bool secure,
bool http_only,
net::CookieSameSite same_site,
base::Time expires,
const SetCookieCallback& callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
BrowserThread::PostTask(
BrowserThread::IO,
FROM_HERE,
base::Bind(&SetCookieOnIO,
base::Unretained(resource_context),
base::Unretained(context_getter),
url, name, value, domain, path, secure, http_only,
same_site, expires, callback));
}
class GetCookiesCommand { class GetCookiesCommand {
public: public:
explicit GetCookiesCommand( explicit GetCookiesCommand(
...@@ -228,6 +296,50 @@ Response NetworkHandler::GetCookies(DevToolsCommandId command_id) { ...@@ -228,6 +296,50 @@ Response NetworkHandler::GetCookies(DevToolsCommandId command_id) {
return Response::OK(); return Response::OK();
} }
Response NetworkHandler::SetCookie(DevToolsCommandId command_id,
const std::string& url,
const std::string& name,
const std::string& value,
const std::string* domain,
const std::string* path,
bool* secure,
bool* http_only,
const std::string* same_site,
double* expires) {
if (!host_)
return Response::InternalError("Could not connect to view");
net::CookieSameSite same_site_enum = net::CookieSameSite::DEFAULT_MODE;
if (same_site && *same_site == kCookieSameSiteLax)
same_site_enum = net::CookieSameSite::LAX_MODE;
else if (same_site && *same_site == kCookieSameSiteStrict)
same_site_enum = net::CookieSameSite::STRICT_MODE;
base::Time expiration_date;
if (expires)
expiration_date = (*expires == 0)
? base::Time::UnixEpoch()
: base::Time::FromDoubleT(*expires);
SetCookieOnUI(
host_->GetSiteInstance()->GetBrowserContext()->GetResourceContext(),
host_->GetProcess()->GetStoragePartition()->GetURLRequestContext(),
GURL(url), name, value,
domain ? *domain : std::string(), path ? *path : std::string(),
secure ? *secure : false, http_only ? *http_only : false,
same_site_enum, expiration_date,
base::Bind(&NetworkHandler::SendSetCookieResponse,
weak_factory_.GetWeakPtr(),
command_id));
return Response::OK();
}
void NetworkHandler::SendSetCookieResponse(DevToolsCommandId command_id,
bool success) {
client_->SendSetCookieResponse(command_id,
SetCookieResponse::Create()->set_success(success));
}
void NetworkHandler::SendGetCookiesResponse( void NetworkHandler::SendGetCookiesResponse(
DevToolsCommandId command_id, DevToolsCommandId command_id,
const net::CookieList& cookie_list) { const net::CookieList& cookie_list) {
...@@ -247,10 +359,10 @@ void NetworkHandler::SendGetCookiesResponse( ...@@ -247,10 +359,10 @@ void NetworkHandler::SendGetCookiesResponse(
switch (cookie.SameSite()) { switch (cookie.SameSite()) {
case net::CookieSameSite::STRICT_MODE: case net::CookieSameSite::STRICT_MODE:
devtools_cookie->set_same_site(cookie::kSameSiteStrict); devtools_cookie->set_same_site(kCookieSameSiteStrict);
break; break;
case net::CookieSameSite::LAX_MODE: case net::CookieSameSite::LAX_MODE:
devtools_cookie->set_same_site(cookie::kSameSiteLax); devtools_cookie->set_same_site(kCookieSameSiteLax);
break; break;
case net::CookieSameSite::NO_RESTRICTION: case net::CookieSameSite::NO_RESTRICTION:
break; break;
......
...@@ -20,7 +20,6 @@ namespace network { ...@@ -20,7 +20,6 @@ namespace network {
class NetworkHandler { class NetworkHandler {
public: public:
typedef DevToolsProtocolClient::Response Response; typedef DevToolsProtocolClient::Response Response;
NetworkHandler(); NetworkHandler();
virtual ~NetworkHandler(); virtual ~NetworkHandler();
...@@ -34,6 +33,17 @@ class NetworkHandler { ...@@ -34,6 +33,17 @@ class NetworkHandler {
const std::string& cookie_name, const std::string& cookie_name,
const std::string& url); const std::string& url);
Response SetCookie(DevToolsCommandId command_id,
const std::string& url,
const std::string& name,
const std::string& value,
const std::string* domain,
const std::string* path,
bool* secure,
bool* http_only,
const std::string* same_site,
double* expires);
Response CanEmulateNetworkConditions(bool* result); Response CanEmulateNetworkConditions(bool* result);
Response EmulateNetworkConditions(bool offline, Response EmulateNetworkConditions(bool offline,
double latency, double latency,
...@@ -49,6 +59,7 @@ class NetworkHandler { ...@@ -49,6 +59,7 @@ class NetworkHandler {
DevToolsCommandId command_id, DevToolsCommandId command_id,
const net::CookieList& cookie_list); const net::CookieList& cookie_list);
void SendDeleteCookieResponse(DevToolsCommandId command_id); void SendDeleteCookieResponse(DevToolsCommandId command_id);
void SendSetCookieResponse(DevToolsCommandId command_id, bool success);
RenderFrameHostImpl* host_; RenderFrameHostImpl* host_;
std::unique_ptr<Client> client_; std::unique_ptr<Client> client_;
......
Tests that cookies are set, updated and removed.
Test started
Enabling network
Running test: simpleCookieAdd
Setting Cookie
Logging Cookies
Success: true
Num of cookies 1
Cookie:
Domain: 127.0.0.1
Name: foo
Value: bar1
Path: /
HttpOnly: false
Secure: false
Session: true
Running test: simpleCookieChange
Setting Cookie
Logging Cookies
Success: true
Num of cookies 1
Cookie:
Domain: 127.0.0.1
Name: foo
Value: second bar2
Path: /
HttpOnly: false
Secure: false
Session: true
Running test: anotherSimpleCookieAdd
Setting Cookie
Logging Cookies
Success: true
Num of cookies 2
Cookie:
Domain: 127.0.0.1
Name: foo2
Value: bar1
Path: /
HttpOnly: false
Secure: false
Session: true
Cookie:
Domain: 127.0.0.1
Name: foo
Value: second bar2
Path: /
HttpOnly: false
Secure: false
Session: true
Running test: simpleCookieDelete
Deleting Cookie
Logging Cookies
Num of cookies 1
Cookie:
Domain: 127.0.0.1
Name: foo2
Value: bar1
Path: /
HttpOnly: false
Secure: false
Session: true
Running test: deleteAllCookies
Removing All Cookies
Logging Cookies
Num of cookies 0
Running test: sessionCookieAdd
Setting Cookie
Logging Cookies
Success: true
Num of cookies 1
Cookie:
Domain: 127.0.0.1
Name: foo
Value: bar4
Path: /
HttpOnly: false
Secure: false
Session: true
Running test: deleteAllCookies
Removing All Cookies
Logging Cookies
Num of cookies 0
Running test: nonSessionCookieZeroAdd
Setting Cookie
Logging Cookies
Success: true
Num of cookies 0
Running test: deleteAllCookies
Removing All Cookies
Logging Cookies
Num of cookies 0
Running test: nonSessionCookieAdd
Setting Cookie
Logging Cookies
Success: true
Num of cookies 1
Cookie:
Domain: 127.0.0.1
Name: foo
Value: bar6
Path: /
HttpOnly: false
Secure: false
Session: false
Running test: deleteAllCookies
Removing All Cookies
Logging Cookies
Num of cookies 0
Running test: differentOriginCookieAdd
Setting Cookie
Logging Cookies
Success: true
Num of cookies 0
Running test: invalidCookieAddDomain
Setting Cookie
Logging Cookies
Success: false
Num of cookies 0
Running test: invalidCookieAddName
Setting Cookie
Logging Cookies
Success: false
Num of cookies 0
Running test: deleteAllCookies
Removing All Cookies
Logging Cookies
Num of cookies 0
Running test: secureCookieAdd
Setting Cookie
Logging Cookies
Success: false
Num of cookies 0
Running test: deleteAllCookies
Removing All Cookies
Logging Cookies
Num of cookies 0
Running test: cookieAddHttpOnly
Setting Cookie
Logging Cookies
Success: true
Num of cookies 1
Cookie:
Domain: 127.0.0.1
Name: foo
Value: bar
Path: /
HttpOnly: true
Secure: false
Session: true
Running test: deleteAllCookies
Removing All Cookies
Logging Cookies
Num of cookies 0
Running test: cookieAddSameSiteLax
Setting Cookie
Logging Cookies
Success: true
Num of cookies 1
Cookie:
Domain: 127.0.0.1
Name: foo
Value: bar
Path: /
HttpOnly: false
Secure: false
Session: true
Running test: deleteAllCookies
Removing All Cookies
Logging Cookies
Num of cookies 0
Running test: cookieAddSameSiteLax
Setting Cookie
Logging Cookies
Success: true
Num of cookies 1
Cookie:
Domain: 127.0.0.1
Name: foo
Value: bar
Path: /
HttpOnly: false
Secure: false
Session: true
<!DOCTYPE html>
<html>
<head>
<script src="inspector-protocol-test.js"></script>
<script>
function test()
{
InspectorTest.log("Test started");
var testCookies = [
function simpleCookieAdd(done)
{
setCookie({url: "http://127.0.0.1", name: "foo", value: "bar1"}, done);
},
function simpleCookieChange(done)
{
setCookie({url: "http://127.0.0.1", name: "foo", value: "second bar2"}, done);
},
function anotherSimpleCookieAdd(done)
{
setCookie({url: "http://127.0.0.1", name: "foo2", value: "bar1"}, done);
},
function simpleCookieDelete(done)
{
deleteCookie({url: "http://127.0.0.1", cookieName: "foo"}, done);
},
deleteAllCookies,
function sessionCookieAdd(done)
{
setCookie({url: "http://127.0.0.1", name: "foo", value: "bar4", expirationDate: undefined}, done);
},
deleteAllCookies,
function nonSessionCookieZeroAdd(done)
{
setCookie({url: "http://127.0.0.1", name: "foo", value: "bar5", expirationDate: 0}, done);
},
deleteAllCookies,
function nonSessionCookieAdd(done)
{
setCookie({url: "http://127.0.0.1", name: "foo", value: "bar6", expirationDate: new Date().getTime() + 1000000}, done);
},
deleteAllCookies,
function differentOriginCookieAdd(done)
{
// Will result in success but not show up
setCookie({url: "http://example.com", name: "foo", value: "bar7"}, done);
},
function invalidCookieAddDomain(done)
{
setCookie({url: "ht2tp://127.0.0.1", name: "foo", value: "bar8"}, done);
},
function invalidCookieAddName(done)
{
setCookie({url: "http://127.0.0.1", name: "foo\0\r\na", value: "bar9"}, done);
},
deleteAllCookies,
function secureCookieAdd(done)
{
// Will succeed but not be shown because not over https
setCookie({url: "http://127.0.0.1", secure: true, name: "foo", value: "bar"}, done);
},
deleteAllCookies,
function cookieAddHttpOnly(done)
{
setCookie({url: "http://127.0.0.1", httpOnly: true, name: "foo", value: "bar"}, done);
},
deleteAllCookies,
function cookieAddSameSiteLax(done)
{
setCookie({url: "http://127.0.0.1", sameSite: "Lax", name: "foo", value: "bar"}, done);
},
deleteAllCookies,
function cookieAddSameSiteLax(done)
{
setCookie({url: "http://127.0.0.1", sameSite: "Strict", name: "foo", value: "bar"}, done);
}
];
enableNetwork();
function enableNetwork()
{
InspectorTest.log("Enabling network");
InspectorTest.sendCommandOrDie("Network.enable", {}, InspectorTest.runTestSuite(testCookies));
}
function setCookie(cookie, done)
{
InspectorTest.log("Setting Cookie");
InspectorTest.sendCommandOrDie("Network.setCookie", cookie, (response) => logCookies(done, response.success));
}
function deleteCookie(cookie, done)
{
InspectorTest.log("Deleting Cookie");
InspectorTest.sendCommandOrDie("Network.deleteCookie", cookie, () => logCookies(done));
}
function deleteAllCookies(done)
{
InspectorTest.log("Removing All Cookies");
InspectorTest.sendCommandOrDie("Network.getCookies", {}, gotCookiesForDelete.bind(null, done));
}
function gotCookiesForDelete(done, data)
{
var promises = [];
for (var cookie of data.cookies) {
var url = "http://" + cookie.domain + "/" + cookie.path;
promises.push(InspectorTest.sendCommandPromise("Network.deleteCookie", {url: url, cookieName: cookie.name}));
}
Promise.all(promises).then(logCookies.bind(null, done, undefined));
}
function logCookies(done, success)
{
InspectorTest.log("Logging Cookies");
if (success !== undefined)
InspectorTest.log("Success: " + success);
InspectorTest.sendCommandOrDie("Network.getCookies", {}, logReceivedGetCookies.bind(null, done));
}
function logReceivedGetCookies(done, data)
{
InspectorTest.log("Num of cookies " + data.cookies.length);
for (var cookie of data.cookies) {
InspectorTest.log(" Cookie: ");
InspectorTest.log(" Domain: " + cookie.domain);
InspectorTest.log(" Name: " + cookie.name);
InspectorTest.log(" Value: " + cookie.value);
InspectorTest.log(" Path: " + cookie.path);
InspectorTest.log(" HttpOnly: " + cookie.httpOnly);
InspectorTest.log(" Secure: " + cookie.secure);
InspectorTest.log(" Session: " + cookie.session);
}
done();
}
}
</script>
</head>
<body onload="runTest();">
<p>Tests that cookies are set, updated and removed.</p>
</body>
</html>
...@@ -923,6 +923,12 @@ ...@@ -923,6 +923,12 @@
"enum": ["none", "cellular2g", "cellular3g", "cellular4g", "bluetooth", "ethernet", "wifi", "wimax", "other"], "enum": ["none", "cellular2g", "cellular3g", "cellular4g", "bluetooth", "ethernet", "wifi", "wimax", "other"],
"description": "Loading priority of a resource request." "description": "Loading priority of a resource request."
}, },
{
"id": "CookieSameSite",
"type": "string",
"enum": ["Strict", "Lax"],
"description": "Represents the cookie's 'SameSite' status: https://tools.ietf.org/html/draft-west-first-party-cookies"
},
{ {
"id": "ResourceTiming", "id": "ResourceTiming",
"type": "object", "type": "object",
...@@ -1112,12 +1118,12 @@ ...@@ -1112,12 +1118,12 @@
{ "name": "value", "type": "string", "description": "Cookie value." }, { "name": "value", "type": "string", "description": "Cookie value." },
{ "name": "domain", "type": "string", "description": "Cookie domain." }, { "name": "domain", "type": "string", "description": "Cookie domain." },
{ "name": "path", "type": "string", "description": "Cookie path." }, { "name": "path", "type": "string", "description": "Cookie path." },
{ "name": "expires", "type": "number", "description": "Cookie expires." }, { "name": "expires", "type": "number", "description": "Cookie expiration date as the number of seconds since the UNIX epoch." },
{ "name": "size", "type": "integer", "description": "Cookie size." }, { "name": "size", "type": "integer", "description": "Cookie size." },
{ "name": "httpOnly", "type": "boolean", "description": "True if cookie is http-only." }, { "name": "httpOnly", "type": "boolean", "description": "True if cookie is http-only." },
{ "name": "secure", "type": "boolean", "description": "True if cookie is secure." }, { "name": "secure", "type": "boolean", "description": "True if cookie is secure." },
{ "name": "session", "type": "boolean", "description": "True in case of session cookie." }, { "name": "session", "type": "boolean", "description": "True in case of session cookie." },
{ "name": "sameSite", "type": "string", "optional": true, "enum": ["Strict", "Lax"], "description": "Represents the cookies' 'SameSite' status: https://tools.ietf.org/html/draft-west-first-party-cookies" } { "name": "sameSite", "$ref": "CookieSameSite", "optional": true, "description": "Cookie SameSite type." }
], ],
"experimental": true "experimental": true
} }
...@@ -1238,6 +1244,27 @@ ...@@ -1238,6 +1244,27 @@
"async": true, "async": true,
"experimental": true "experimental": true
}, },
{
"name": "setCookie",
"parameters": [
{ "name": "url", "type": "string", "description": "The request-URI to associate with the setting of the cookie. This value can affect the default domain and path values of the created cookie." },
{ "name": "name", "type": "string", "description": "The name of the cookie." },
{ "name": "value", "type": "string", "description": "The value of the cookie." },
{ "name": "domain", "type": "string", "optional": true, "description": "If omitted, the cookie becomes a host-only cookie." },
{ "name": "path", "type": "string", "optional": true, "description": "Defaults to the path portion of the url parameter." },
{ "name": "secure", "type": "boolean", "optional": true, "description": "Defaults ot false." },
{ "name": "httpOnly", "type": "boolean", "optional": true, "description": "Defaults to false." },
{ "name": "sameSite", "$ref": "CookieSameSite", "optional": true, "description": "Defaults to browser default behavior." },
{ "name": "expirationDate", "$ref": "Timestamp", "optional": true, "description": "If omitted, the cookie becomes a session cookie." }
],
"returns": [
{ "name": "success", "type": "boolean", "description": "True if successfully set cookie." }
],
"description": "Sets a cookie with the given cookie data; may overwrite equivalent cookies if they exist.",
"handlers": ["browser"],
"async": true,
"experimental": true
},
{ {
"name": "canEmulateNetworkConditions", "name": "canEmulateNetworkConditions",
"description": "Tells whether emulation of network conditions is supported.", "description": "Tells whether emulation of network conditions is supported.",
......
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