Source code
Revision control
Copy as Markdown
Other Tools
// getAndExpireCookiesForDefaultPathTest is a helper method to get and delete
// cookies using echo-cookie.html.
async function getAndExpireCookiesForDefaultPathTest() {
return new Promise((resolve, reject) => {
try {
const iframe = document.createElement('iframe');
iframe.style = 'display: none';
iframe.addEventListener('load', (e) => {
const win = e.target.contentWindow;
const iframeCookies = win.getCookies();
win.expireCookies().then(() => {
document.documentElement.removeChild(iframe);
resolve(iframeCookies);
});
}, {once: true});
iframe.src = '/cookies/resources/echo-cookie.html';
document.documentElement.appendChild(iframe);
} catch (e) {
reject(e);
}
});
}
// getAndExpireCookiesForRedirectTest is a helper method to get and delete
// cookies that were set from a Location header redirect.
async function getAndExpireCookiesForRedirectTest(location) {
return new Promise((resolve, reject) => {
try {
const iframe = document.createElement('iframe');
iframe.style = 'display: none';
const listener = (e) => {
if (typeof e.data == 'object' && 'cookies' in e.data) {
window.removeEventListener('message', listener);
document.documentElement.removeChild(iframe);
resolve(e.data.cookies);
}
};
window.addEventListener('message', listener);
iframe.addEventListener('load', (e) => {
e.target.contentWindow.postMessage('getAndExpireCookiesForRedirectTest', '*');
}, {once: true});
iframe.src = location;
document.documentElement.appendChild(iframe);
} catch (e) {
reject(e);
}
});
}
// httpCookieTest sets a `cookie` (via HTTP), then asserts it was or was not set
// via `expectedValue` (via the DOM). Then cleans it up (via test driver). Most
// tests do not set a Path attribute, so `defaultPath` defaults to true. If the
// cookie values are expected to cause the HTTP request or response to fail, the
// test can be made to pass when this happens via `allowFetchFailure`, which
// defaults to false.
//
// `cookie` may be a single cookie string, or an array of cookie strings, where
// the order of the array items represents the order of the Set-Cookie headers
// sent by the server.
//
// Note: this function has a dependency on testdriver.js. Any test files calling
// it should include testdriver.js and testdriver-vendor.js
function httpCookieTest(cookie, expectedValue, name, defaultPath = true,
allowFetchFailure = false) {
return promise_test((t) => {
var skipAssertions = false;
return new Promise(async (resolve, reject) => {
// The result is ignored as we're expiring cookies for cleaning here.
await getAndExpireCookiesForDefaultPathTest();
await test_driver.delete_all_cookies();
t.add_cleanup(test_driver.delete_all_cookies);
let encodedCookie = encodeURIComponent(JSON.stringify(cookie));
try {
await fetch(`/cookies/resources/cookie.py?set=${encodedCookie}`);
} catch {
if (allowFetchFailure) {
skipAssertions = true;
} else {
reject('Failed to fetch /cookies/resources/cookie.py');
}
}
let cookies = document.cookie;
if (defaultPath) {
// for the tests where a Path is set from the request-uri
// path, we need to go look for cookies in an iframe at that
// default path.
cookies = await getAndExpireCookiesForDefaultPathTest();
}
resolve(cookies);
}).then((cookies) => {
if (skipAssertions) {
return;
}
if (Boolean(expectedValue)) {
assert_equals(cookies, expectedValue, 'The cookie was set as expected.');
} else {
assert_equals(cookies, expectedValue, 'The cookie was rejected.');
}
});
}, name);
}
// This is a variation on httpCookieTest, where a redirect happens via
// the Location header and we check to see if cookies are sent via
// getRedirectedCookies
//
// Note: the locations targeted by this function have a dependency on
// path-redirect-shared.js and should be sure to include it.
function httpRedirectCookieTest(cookie, expectedValue, name, location) {
return promise_test(async (t) => {
// The result is ignored as we're expiring cookies for cleaning here.
await getAndExpireCookiesForRedirectTest(location);
const encodedCookie = encodeURIComponent(JSON.stringify(cookie));
const encodedLocation = encodeURIComponent(location);
const setParams = `?set=${encodedCookie}&location=${encodedLocation}`;
await fetch(`/cookies/resources/cookie.py${setParams}`);
// for the tests where a redirect happens, we need to head
// to that URI to get the cookies (and then delete them there)
const cookies = await getAndExpireCookiesForRedirectTest(location);
if (Boolean(expectedValue)) {
assert_equals(cookies, expectedValue, 'The cookie was set as expected.');
} else {
assert_equals(cookies, expectedValue, 'The cookie was rejected.');
}
}, name);
}
// Sets a `cookie` via the DOM, checks it against `expectedValue` via the DOM,
// then cleans it up via the DOM. This is needed in cases where going through
// HTTP headers may modify the cookie line (e.g. by stripping control
// characters).
//
// Note: this function has a dependency on testdriver.js. Any test files calling
// it should include testdriver.js and testdriver-vendor.js
function domCookieTest(cookie, expectedValue, name) {
return promise_test(async (t) => {
await test_driver.delete_all_cookies();
t.add_cleanup(test_driver.delete_all_cookies);
if (typeof cookie === "string") {
document.cookie = cookie;
} else if (Array.isArray(cookie)) {
for (const singlecookie of cookie) {
document.cookie = singlecookie;
}
} else {
throw new Error('Unexpected type passed into domCookieTest as cookie: ' + typeof cookie);
}
let cookies = document.cookie;
assert_equals(cookies, expectedValue, Boolean(expectedValue) ?
'The cookie was set as expected.' :
'The cookie was rejected.');
}, name);
}
// Returns an array of control characters along with their ASCII codes. Control
// characters are defined by RFC 5234 to be %x00-1F / %x7F.
function getCtlCharacters() {
const ctlCodes = [...Array(0x20).keys()]
.concat([0x7F]);
return ctlCodes.map(i => ({ code: i, chr: String.fromCharCode(i) }))
}
// Returns a cookie string with name set to "t" * nameLength and value
// set to "1" * valueLength. Passing in 0 for either allows for creating
// a name- or value-less cookie.
//
// Note: Cookie length checking should ignore the "=".
function cookieStringWithNameAndValueLengths(nameLength, valueLength) {
return `${"t".repeat(nameLength)}=${"1".repeat(valueLength)}`;
}
// Finds the root window.top.opener and directs test_driver commands to it.
//
// If you see a message like: "Error: Tried to run in a non-testharness window
// without a call to set_test_context." then you probably need to call this.
function setTestContextUsingRootWindow() {
let test_window = window.top;
while (test_window.opener && !test_window.opener.closed) {
test_window = test_window.opener.top;
}
test_driver.set_test_context(test_window);
}