зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1590098 - [remote] Implement basic support for Network.getCookies. r=remote-protocol-reviewers,ato,maja_zf
This patch adds basic support for retrieving cookies, which means that it returns the cookies for the currently active target. Hereby it has the following limitations: 1. It does not walk the frame tree, and as such only returns the cookies from the top-level frame. Support for that will be added once frames can correctly be handled, which means once support for the JSWindowActor API has been landed. 2. The "urls" parameter is not supported because it is unclear right now what it actually does. More investigation is necessary before any implementation can happen. 3. There is no support for the file:// protocol yet. 4. Dot domains aren't taken care of yet. Differential Revision: https://phabricator.services.mozilla.com/D57614 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
3036d033c3
Коммит
2dd64e92cd
|
@ -6,6 +6,14 @@
|
|||
|
||||
var EXPORTED_SYMBOLS = ["Network"];
|
||||
|
||||
const { XPCOMUtils } = ChromeUtils.import(
|
||||
"resource://gre/modules/XPCOMUtils.jsm"
|
||||
);
|
||||
|
||||
XPCOMUtils.defineLazyGlobalGetters(this, ["URL"]);
|
||||
|
||||
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
const { Domain } = ChromeUtils.import(
|
||||
"chrome://remote/content/domains/Domain.jsm"
|
||||
);
|
||||
|
@ -75,6 +83,73 @@ class Network extends Domain {
|
|||
this.enabled = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all browser cookies for the current URL.
|
||||
*
|
||||
* @param {Object} options
|
||||
* @param {Array<string>=} urls
|
||||
* The list of URLs for which applicable cookies will be fetched.
|
||||
* Defaults to the currently open URL.
|
||||
*
|
||||
* @return {Array<Cookie>}
|
||||
* Array of cookie objects.
|
||||
*/
|
||||
// https://cs.chromium.org/chromium/src/content/browser/devtools/protocol/network_handler.cc?type=cs&q=+ComputeCookieURLs&sq=package:chromium&g=0&l=1115
|
||||
async getCookies(options = {}) {
|
||||
// Bug 1605354 - Add support for options.urls
|
||||
const urls = [this.session.target.url];
|
||||
|
||||
const cookies = [];
|
||||
for (let url of urls) {
|
||||
url = new URL(url);
|
||||
|
||||
const secureProtocol = ["https:", "wss:"].includes(url.protocol);
|
||||
|
||||
const cookiesFound = Services.cookies.getCookiesWithOriginAttributes(
|
||||
JSON.stringify({}),
|
||||
url.hostname
|
||||
);
|
||||
|
||||
for (const cookie of cookiesFound) {
|
||||
// Reject secure cookies for non-secure protocols
|
||||
if (cookie.isSecure && !secureProtocol) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Reject cookies which do not match the given path
|
||||
if (!url.pathname.startsWith(cookie.path)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const data = {
|
||||
name: cookie.name,
|
||||
value: cookie.value,
|
||||
domain: cookie.host,
|
||||
path: cookie.path,
|
||||
expires: cookie.isSession ? -1 : cookie.expiry,
|
||||
// The size is the combined length of both the cookie name and value
|
||||
size: cookie.name.length + cookie.value.length,
|
||||
httpOnly: cookie.isHttpOnly,
|
||||
secure: cookie.isSecure,
|
||||
session: cookie.isSession,
|
||||
};
|
||||
|
||||
if (cookie.sameSite) {
|
||||
const sameSiteMap = new Map([
|
||||
[Ci.nsICookie.SAMESITE_LAX, "Lax"],
|
||||
[Ci.nsICookie.SAMESITE_STRICT, "Strict"],
|
||||
]);
|
||||
|
||||
data.sameSite = sameSiteMap.get(cookie.sameSite);
|
||||
}
|
||||
|
||||
cookies.push(data);
|
||||
}
|
||||
}
|
||||
|
||||
return { cookies };
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows overriding user agent with the given string.
|
||||
*
|
||||
|
|
|
@ -3,8 +3,12 @@ tags = remote
|
|||
subsuite = remote
|
||||
prefs = remote.enabled=true
|
||||
support-files =
|
||||
!/remote/test/browser/chrome-remote-interface.js
|
||||
!/remote/test/browser/head.js
|
||||
head.js
|
||||
doc_requestWillBeSent.html
|
||||
file_requestWillBeSent.js
|
||||
head.js
|
||||
sjs-cookies.sjs
|
||||
|
||||
[browser_getCookies.js]
|
||||
[browser_requestWillBeSent.js]
|
||||
|
|
|
@ -0,0 +1,244 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
const SJS_PATH = "/browser/remote/test/browser/network/sjs-cookies.sjs";
|
||||
|
||||
const DEFAULT_HOST = "http://example.org";
|
||||
const ALT_HOST = "http://example.net";
|
||||
const SECURE_HOST = "https://example.com";
|
||||
|
||||
const DEFAULT_URL = `${DEFAULT_HOST}${SJS_PATH}`;
|
||||
|
||||
add_task(async function noCookiesWhenNoneAreSet({ Network }) {
|
||||
const { cookies } = await Network.getCookies({ urls: [DEFAULT_HOST] });
|
||||
is(cookies.length, 0, "No cookies have been found");
|
||||
});
|
||||
|
||||
add_task(async function noCookiesForPristineContext({ Network }) {
|
||||
await loadURL(DEFAULT_URL);
|
||||
|
||||
try {
|
||||
const { cookies } = await Network.getCookies();
|
||||
is(cookies.length, 0, "No cookies have been found");
|
||||
} finally {
|
||||
Services.cookies.removeAll();
|
||||
}
|
||||
});
|
||||
|
||||
add_task(async function allCookiesFromHostWithPort({ Network }) {
|
||||
const PORT_URL = `${DEFAULT_HOST}:8000${SJS_PATH}?name=id&value=1`;
|
||||
await loadURL(PORT_URL);
|
||||
|
||||
const cookie = {
|
||||
name: "id",
|
||||
value: "1",
|
||||
};
|
||||
|
||||
try {
|
||||
const { cookies } = await Network.getCookies();
|
||||
is(cookies.length, 1, "All cookies have been found");
|
||||
assertCookie(cookies[0], cookie);
|
||||
} finally {
|
||||
Services.cookies.removeAll();
|
||||
}
|
||||
});
|
||||
|
||||
add_task(async function allCookiesFromCurrentURL({ Network }) {
|
||||
await loadURL(`${ALT_HOST}${SJS_PATH}?name=user&value=password`);
|
||||
await loadURL(`${DEFAULT_URL}?name=foo&value=bar`);
|
||||
await loadURL(`${DEFAULT_URL}?name=user&value=password`);
|
||||
|
||||
const cookie1 = { name: "foo", value: "bar", domain: "example.org" };
|
||||
const cookie2 = { name: "user", value: "password", domain: "example.org" };
|
||||
|
||||
try {
|
||||
const { cookies } = await Network.getCookies();
|
||||
cookies.sort((a, b) => a.name.localeCompare(b.name));
|
||||
is(cookies.length, 2, "All cookies have been found");
|
||||
assertCookie(cookies[0], cookie1);
|
||||
assertCookie(cookies[1], cookie2);
|
||||
} finally {
|
||||
Services.cookies.removeAll();
|
||||
}
|
||||
});
|
||||
|
||||
add_task(async function secure({ Network }) {
|
||||
await loadURL(`${SECURE_HOST}${SJS_PATH}?name=foo&value=bar&secure`);
|
||||
|
||||
const cookie = {
|
||||
name: "foo",
|
||||
value: "bar",
|
||||
domain: "example.com",
|
||||
secure: true,
|
||||
};
|
||||
|
||||
try {
|
||||
// Cookie returned for secure protocols
|
||||
let result = await Network.getCookies();
|
||||
is(result.cookies.length, 1, "The secure cookie has been found");
|
||||
assertCookie(result.cookies[0], cookie);
|
||||
|
||||
// For unsecure protocols no secure cookies are returned
|
||||
await loadURL(DEFAULT_URL);
|
||||
result = await Network.getCookies();
|
||||
is(result.cookies.length, 0, "No secure cookies have been found");
|
||||
} finally {
|
||||
Services.cookies.removeAll();
|
||||
}
|
||||
});
|
||||
|
||||
add_task(async function expiry({ Network }) {
|
||||
const date = new Date();
|
||||
date.setDate(date.getDate() + 3);
|
||||
|
||||
const encodedDate = encodeURI(date.toUTCString());
|
||||
await loadURL(`${DEFAULT_URL}?name=foo&value=bar&expiry=${encodedDate}`);
|
||||
|
||||
const cookie = {
|
||||
name: "foo",
|
||||
value: "bar",
|
||||
expires: date,
|
||||
session: false,
|
||||
};
|
||||
|
||||
try {
|
||||
const { cookies } = await Network.getCookies();
|
||||
is(cookies.length, 1, "A single cookie has been found");
|
||||
assertCookie(cookies[0], cookie);
|
||||
} finally {
|
||||
Services.cookies.removeAll();
|
||||
}
|
||||
});
|
||||
|
||||
add_task(async function session({ Network }) {
|
||||
await loadURL(`${DEFAULT_URL}?name=foo&value=bar`);
|
||||
|
||||
const cookie = {
|
||||
name: "foo",
|
||||
value: "bar",
|
||||
expiry: -1,
|
||||
session: true,
|
||||
};
|
||||
|
||||
try {
|
||||
const { cookies } = await Network.getCookies();
|
||||
is(cookies.length, 1, "A single cookie has been found");
|
||||
assertCookie(cookies[0], cookie);
|
||||
} finally {
|
||||
Services.cookies.removeAll();
|
||||
}
|
||||
});
|
||||
|
||||
add_task(async function path({ Network }) {
|
||||
const PATH = "/browser/remote/test/browser/";
|
||||
const PARENT_PATH = "/browser/remote/test/";
|
||||
|
||||
await loadURL(`${DEFAULT_URL}?name=foo&value=bar&path=${PATH}`);
|
||||
|
||||
const cookie = {
|
||||
name: "foo",
|
||||
value: "bar",
|
||||
path: PATH,
|
||||
};
|
||||
|
||||
try {
|
||||
console.log("Check exact path");
|
||||
await loadURL(`${DEFAULT_HOST}${PATH}`);
|
||||
let result = await Network.getCookies();
|
||||
is(result.cookies.length, 1, "A single cookie has been found");
|
||||
assertCookie(result.cookies[0], cookie);
|
||||
|
||||
console.log("Check sub path");
|
||||
await loadURL(`${DEFAULT_HOST}${SJS_PATH}`);
|
||||
result = await Network.getCookies();
|
||||
is(result.cookies.length, 1, "A single cookie has been found");
|
||||
assertCookie(result.cookies[0], cookie);
|
||||
|
||||
console.log("Check parent path");
|
||||
await loadURL(`${DEFAULT_HOST}${PARENT_PATH}`);
|
||||
result = await Network.getCookies();
|
||||
is(result.cookies.length, 0, "No cookies have been found");
|
||||
|
||||
console.log("Check non matching path");
|
||||
await loadURL(`${DEFAULT_HOST}/foo/bar`);
|
||||
result = await Network.getCookies();
|
||||
is(result.cookies.length, 0, "No cookies have been found");
|
||||
} finally {
|
||||
Services.cookies.removeAll();
|
||||
}
|
||||
});
|
||||
|
||||
add_task(async function httpOnly({ Network }) {
|
||||
await loadURL(`${DEFAULT_URL}?name=foo&value=bar&httpOnly`);
|
||||
|
||||
const cookie = {
|
||||
name: "foo",
|
||||
value: "bar",
|
||||
httpOnly: true,
|
||||
};
|
||||
|
||||
try {
|
||||
const { cookies } = await Network.getCookies();
|
||||
is(cookies.length, 1, "A single cookie has been found");
|
||||
assertCookie(cookies[0], cookie);
|
||||
} finally {
|
||||
Services.cookies.removeAll();
|
||||
}
|
||||
});
|
||||
|
||||
add_task(async function sameSite({ Network }) {
|
||||
for (const value of ["Lax", "Strict"]) {
|
||||
console.log(`Test cookie with sameSite=${value}`);
|
||||
await loadURL(`${DEFAULT_URL}?name=foo&value=bar&sameSite=${value}`);
|
||||
|
||||
const cookie = {
|
||||
name: "foo",
|
||||
value: "bar",
|
||||
sameSite: value,
|
||||
};
|
||||
|
||||
try {
|
||||
const { cookies } = await Network.getCookies();
|
||||
is(cookies.length, 1, "A single cookie has been found");
|
||||
assertCookie(cookies[0], cookie);
|
||||
} finally {
|
||||
Services.cookies.removeAll();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function assertCookie(cookie, expected = {}) {
|
||||
const {
|
||||
name = "",
|
||||
value = "",
|
||||
domain = "example.org",
|
||||
path = "/",
|
||||
expires = -1,
|
||||
size = name.length + value.length,
|
||||
httpOnly = false,
|
||||
secure = false,
|
||||
session = true,
|
||||
sameSite,
|
||||
} = expected;
|
||||
|
||||
const expectedCookie = {
|
||||
name,
|
||||
value,
|
||||
domain,
|
||||
path,
|
||||
// If expires is set, convert from milliseconds to seconds
|
||||
expires: expires > 0 ? Math.floor(expires.getTime() / 1000) : -1,
|
||||
size,
|
||||
httpOnly,
|
||||
secure,
|
||||
session,
|
||||
};
|
||||
|
||||
if (sameSite) {
|
||||
expectedCookie.sameSite = sameSite;
|
||||
}
|
||||
|
||||
Assert.deepEqual(cookie, expectedCookie);
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// eslint-disable-next-line mozilla/reject-importGlobalProperties
|
||||
Cu.importGlobalProperties(["URLSearchParams"]);
|
||||
|
||||
function handleRequest(request, response) {
|
||||
const queryString = new URLSearchParams(request.queryString);
|
||||
|
||||
response.setStatusLine(request.httpVersion, 200, "OK");
|
||||
response.setHeader("Content-Type", "text/plain; charset=utf-8", false);
|
||||
|
||||
if (queryString.has("name") && queryString.has("value")) {
|
||||
const name = queryString.get("name");
|
||||
const value = queryString.get("value");
|
||||
const path = queryString.get("path") || "/";
|
||||
|
||||
const expiry = queryString.get("expiry");
|
||||
const httpOnly = queryString.has("httpOnly");
|
||||
const secure = queryString.has("secure");
|
||||
const sameSite = queryString.get("sameSite");
|
||||
|
||||
let cookie = `${name}=${value}; Path=${path}`;
|
||||
|
||||
if (expiry) {
|
||||
cookie += `; Expires=${expiry}`;
|
||||
}
|
||||
|
||||
if (httpOnly) {
|
||||
cookie += "; HttpOnly";
|
||||
}
|
||||
|
||||
if (sameSite != undefined) {
|
||||
cookie += `; sameSite=${sameSite}`;
|
||||
}
|
||||
|
||||
if (secure) {
|
||||
cookie += "; Secure";
|
||||
}
|
||||
|
||||
response.setHeader("Set-Cookie", cookie, true);
|
||||
response.write(`Set cookie: ${cookie}`);
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче