gecko-dev/testing/marionette/cookie.js

205 строки
6.3 KiB
JavaScript

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const {interfaces: Ci, utils: Cu} = Components;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("chrome://marionette/content/assert.js");
const {
InvalidCookieDomainError,
pprint,
} = Cu.import("chrome://marionette/content/error.js", {});
this.EXPORTED_SYMBOLS = ["cookie"];
const IPV4_PORT_EXPR = /:\d+$/;
/** @namespace */
this.cookie = {
manager: Services.cookies,
};
/**
* @name Cookie
*
* @return {Object.<string, (number|boolean|string)>
*/
/**
* Unmarshal a JSON Object to a cookie representation.
*
* Effectively this will run validation checks on |json|, which will
* produce the errors expected by WebDriver if the input is not valid.
*
* @param {Object.<string, (number|boolean|string)>} json
* Cookie to be deserialised. <var>name</var> and <var>value</var>
* are required fields which must be strings. The <var>path</var> and
* <var>domain</var> fields are optional, but must be a string if
* provided.
* The <var>secure</var>, <var>httpOnly</var>, and
* <var>session</var>fields are similarly optional, but must be
* booleans. Likewise, the <var>expiry</var> field is optional but
* must be unsigned integer.
*
* @return {Cookie}
* Valid cookie object.
*
* @throws {InvalidArgumentError}
* If any of the properties are invalid.
*/
cookie.fromJSON = function(json) {
let newCookie = {};
assert.object(json, pprint`Expected cookie object, got ${json}`);
newCookie.name = assert.string(json.name, "Cookie name must be string");
newCookie.value = assert.string(json.value, "Cookie value must be string");
if (typeof json.domain != "undefined") {
let domain = assert.string(json.domain, "Cookie domain must be string");
if (domain.substring(0, 1) !== ".") {
// make sure that this is stored as a domain cookie
domain = "." + domain;
}
newCookie.domain = domain;
}
if (typeof json.path != "undefined") {
newCookie.path = assert.string(json.path, "Cookie path must be string");
}
if (typeof json.secure != "undefined") {
newCookie.secure = assert.boolean(json.secure, "Cookie secure flag must be boolean");
}
if (typeof json.httpOnly != "undefined") {
newCookie.httpOnly = assert.boolean(json.httpOnly, "Cookie httpOnly flag must be boolean");
}
if (typeof json.session != "undefined") {
newCookie.session = assert.boolean(json.session, "Cookie session flag must be boolean");
}
if (typeof json.expiry != "undefined") {
newCookie.expiry = assert.positiveInteger(json.expiry, "Cookie expiry must be a positive integer");
}
return newCookie;
};
/**
* Insert cookie to the cookie store.
*
* @param {Cookie} newCookie
* Cookie to add.
* @param {string=} restrictToHost
* Perform test that <var>newCookie</var>'s domain matches this.
*
* @throws {TypeError}
* If <var>name</var>, <var>value</var>, or <var>domain</var> are
* not present and of the correct type.
* @throws {InvalidCookieDomainError}
* If <var>restrictToHost</var> is set and <var>newCookie</var>'s
* domain does not match.
*/
cookie.add = function(newCookie, {restrictToHost = null} = {}) {
assert.string(newCookie.name, "Cookie name must be string");
assert.string(newCookie.value, "Cookie value must be string");
assert.string(newCookie.domain, "Cookie domain must be string");
if (typeof newCookie.path == "undefined") {
newCookie.path = "/";
}
if (typeof newCookie.expiry == "undefined") {
// twenty years into the future
let date = new Date();
let now = new Date(Date.now());
date.setYear(now.getFullYear() + 20);
newCookie.expiry = date.getTime() / 1000;
}
if (restrictToHost) {
if (!restrictToHost.endsWith(newCookie.domain) &&
("." + restrictToHost) !== newCookie.domain) {
throw new InvalidCookieDomainError(
`Cookies may only be set ` +
`for the current domain (${restrictToHost})`);
}
}
// remove port from domain, if present.
// unfortunately this catches IPv6 addresses by mistake
// TODO: Bug 814416
newCookie.domain = newCookie.domain.replace(IPV4_PORT_EXPR, "");
cookie.manager.add(
newCookie.domain,
newCookie.path,
newCookie.name,
newCookie.value,
newCookie.secure,
newCookie.httpOnly,
newCookie.session,
newCookie.expiry,
{} /* origin attributes */);
};
/**
* Remove cookie from the cookie store.
*
* @param {Cookie} toDelete
* Cookie to remove.
*/
cookie.remove = function(toDelete) {
cookie.manager.remove(
toDelete.domain,
toDelete.name,
toDelete.path,
false,
{} /* originAttributes */);
};
/**
* Iterates over the cookies for the current <var>host</var>. You may
* optionally filter for specific paths on that <var>host</var> by
* specifying a path in <var>currentPath</var>.
*
* @param {string} host
* Hostname to retrieve cookies for.
* @param {string=} [currentPath="/"] currentPath
* Optionally filter the cookies for <var>host</var> for the
* specific path. Defaults to "<tt>/</tt>", meaning all cookies
* for <var>host</var> are included.
*
* @return {Iterable.<Cookie>}
* Iterator.
*/
cookie.iter = function* (host, currentPath = "/") {
assert.string(host, "host must be string");
assert.string(currentPath, "currentPath must be string");
const isForCurrentPath = path => currentPath.indexOf(path) != -1;
let en = cookie.manager.getCookiesFromHost(host, {});
while (en.hasMoreElements()) {
let cookie = en.getNext().QueryInterface(Ci.nsICookie2);
// take the hostname and progressively shorten
let hostname = host;
do {
if ((cookie.host == "." + hostname || cookie.host == hostname) &&
isForCurrentPath(cookie.path)) {
yield {
"name": cookie.name,
"value": cookie.value,
"path": cookie.path,
"domain": cookie.host,
"secure": cookie.isSecure,
"httpOnly": cookie.isHttpOnly,
"expiry": cookie.expiry,
};
}
hostname = hostname.replace(/^.*?\./, "");
} while (hostname.indexOf(".") != -1);
}
};