Skip to content

Commit 67e1ebd

Browse files
committed
Bug 1854582 - [bidi] Implement "storage.setCookie" command. r=webdriver-reviewers,whimboo,jdescottes
Differential Revision: https://phabricator.services.mozilla.com/D200187
1 parent f8bba4c commit 67e1ebd

12 files changed

+211
-322
lines changed

remote/webdriver-bidi/modules/root/storage.sys.mjs

+211-35
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ const CookieFieldsMapping = {
2626
value: "value",
2727
};
2828

29+
const MAX_COOKIE_EXPIRY = Number.MAX_SAFE_INTEGER;
30+
2931
/**
3032
* Enum of possible partition types supported by the
3133
* storage.getCookies command.
@@ -157,6 +159,165 @@ class StorageModule extends Module {
157159
return { cookies, partitionKey };
158160
}
159161

162+
/**
163+
* An object representation of the cookie which should be set.
164+
*
165+
* @typedef PartialCookie
166+
*
167+
* @property {string} domain
168+
* @property {number=} expiry
169+
* @property {boolean=} httpOnly
170+
* @property {string} name
171+
* @property {string=} path
172+
* @property {SameSiteType=} sameSite
173+
* @property {boolean=} secure
174+
* @property {number=} size
175+
* @property {Network.BytesValueType} value
176+
*/
177+
178+
/**
179+
* Create a new cookie in a cookie store.
180+
*
181+
* @param {object=} options
182+
* @param {PartialCookie} options.cookie
183+
* An object representation of the cookie which
184+
* should be set.
185+
* @param {PartitionDescriptor=} options.partition
186+
* An object which holds the information which
187+
* should be used to build a partition key.
188+
*
189+
* @returns {PartitionKey}
190+
* An object with the partition key which was used to
191+
* add the cookie.
192+
* @throws {InvalidArgumentError}
193+
* If the provided arguments are not valid.
194+
* @throws {NoSuchFrameError}
195+
* If the provided browsing context cannot be found.
196+
* @throws {UnableToSetCookieError}
197+
* If the cookie was not added.
198+
* @throws {UnsupportedOperationError}
199+
* Raised when the command is called with `userContext` as
200+
* in `partition` argument.
201+
*/
202+
async setCookie(options = {}) {
203+
const { cookie: cookieSpec, partition: partitionSpec = null } = options;
204+
lazy.assert.object(
205+
cookieSpec,
206+
`Expected "cookie" to be an object, got ${cookieSpec}`
207+
);
208+
209+
const {
210+
domain,
211+
expiry = null,
212+
httpOnly = null,
213+
name,
214+
path = null,
215+
sameSite = null,
216+
secure = null,
217+
value,
218+
} = cookieSpec;
219+
this.#assertCookie({
220+
domain,
221+
expiry,
222+
httpOnly,
223+
name,
224+
path,
225+
sameSite,
226+
secure,
227+
value,
228+
});
229+
this.#assertPartition(partitionSpec);
230+
231+
const partitionKey = this.#expandStoragePartitionSpec(partitionSpec);
232+
233+
// The cookie store is defined by originAttributes.
234+
const originAttributes = this.#getOriginAttributes(partitionKey);
235+
236+
const deserializedValue = this.#deserializeProtocolBytes(value);
237+
238+
// The XPCOM interface requires to be specified if a cookie is session.
239+
const isSession = expiry === null;
240+
241+
let schemeType;
242+
if (secure) {
243+
schemeType = Ci.nsICookie.SCHEME_HTTPS;
244+
} else {
245+
schemeType = Ci.nsICookie.SCHEME_HTTP;
246+
}
247+
248+
try {
249+
Services.cookies.add(
250+
domain,
251+
path === null ? "/" : path,
252+
name,
253+
deserializedValue,
254+
secure === null ? false : secure,
255+
httpOnly === null ? false : httpOnly,
256+
isSession,
257+
// The XPCOM interface requires the expiry field even for session cookies.
258+
expiry === null ? MAX_COOKIE_EXPIRY : expiry,
259+
originAttributes,
260+
this.#getSameSitePlatformProperty(sameSite),
261+
schemeType
262+
);
263+
} catch (e) {
264+
throw new lazy.error.UnableToSetCookieError(e);
265+
}
266+
267+
// Bug 1875255. Exchange platform id for Webdriver BiDi id for the user context to return it to the client.
268+
// For now we use platform user context id for returning cookies for a specific browsing context in the platform API,
269+
// but we can not return it directly to the client, so for now we just remove it from the response.
270+
delete partitionKey.userContext;
271+
272+
return { partitionKey };
273+
}
274+
275+
#assertCookie(cookie) {
276+
lazy.assert.object(
277+
cookie,
278+
`Expected "cookie" to be an object, got ${cookie}`
279+
);
280+
281+
const { domain, expiry, httpOnly, name, path, sameSite, secure, value } =
282+
cookie;
283+
284+
lazy.assert.string(
285+
domain,
286+
`Expected "domain" to be a string, got ${domain}`
287+
);
288+
289+
lazy.assert.string(name, `Expected "name" to be a string, got ${name}`);
290+
291+
this.#assertValue(value);
292+
293+
if (expiry !== null) {
294+
lazy.assert.positiveInteger(
295+
expiry,
296+
`Expected "expiry" to be a positive number, got ${expiry}`
297+
);
298+
}
299+
300+
if (httpOnly !== null) {
301+
lazy.assert.boolean(
302+
httpOnly,
303+
`Expected "httpOnly" to be a boolean, got ${httpOnly}`
304+
);
305+
}
306+
307+
if (path !== null) {
308+
lazy.assert.string(path, `Expected "path" to be a string, got ${path}`);
309+
}
310+
311+
this.#assertSameSite(sameSite);
312+
313+
if (secure !== null) {
314+
lazy.assert.boolean(
315+
secure,
316+
`Expected "secure" to be a boolean, got ${secure}`
317+
);
318+
}
319+
}
320+
160321
#assertGetCookieFilter(filter) {
161322
lazy.assert.object(
162323
filter,
@@ -210,17 +371,7 @@ class StorageModule extends Module {
210371
);
211372
}
212373

213-
if (sameSite !== null) {
214-
lazy.assert.string(
215-
sameSite,
216-
`Expected "filter.sameSite" to be a string, got ${sameSite}`
217-
);
218-
const sameSiteTypeValue = Object.values(SameSiteType);
219-
lazy.assert.that(
220-
sameSite => sameSiteTypeValue.includes(sameSite),
221-
`Expected "filter.sameSite" to be one of ${sameSiteTypeValue}, got ${sameSite}`
222-
)(sameSite);
223-
}
374+
this.#assertSameSite(sameSite, "filter.sameSite");
224375

225376
if (secure !== null) {
226377
lazy.assert.boolean(
@@ -237,27 +388,7 @@ class StorageModule extends Module {
237388
}
238389

239390
if (value !== null) {
240-
lazy.assert.object(
241-
value,
242-
`Expected "filter.value" to be an object, got ${value}`
243-
);
244-
245-
const { type, value: protocolBytesValue } = value;
246-
247-
lazy.assert.string(
248-
type,
249-
`Expected "filter.value.type" to be string, got ${type}`
250-
);
251-
const bytesValueTypeValue = Object.values(lazy.BytesValueType);
252-
lazy.assert.that(
253-
type => bytesValueTypeValue.includes(type),
254-
`Expected "filter.value.type" to be one of ${bytesValueTypeValue}, got ${type}`
255-
)(type);
256-
257-
lazy.assert.string(
258-
protocolBytesValue,
259-
`Expected "filter.value.value" to be string, got ${protocolBytesValue}`
260-
);
391+
this.#assertValue(value, "filter.value");
261392
}
262393

263394
return {
@@ -341,11 +472,43 @@ class StorageModule extends Module {
341472
}
342473
}
343474

475+
#assertSameSite(sameSite, fieldName = "sameSite") {
476+
if (sameSite !== null) {
477+
const sameSiteTypeValue = Object.values(SameSiteType);
478+
lazy.assert.in(
479+
sameSite,
480+
sameSiteTypeValue,
481+
`Expected "${fieldName}" to be one of ${sameSiteTypeValue}, got ${sameSite}`
482+
);
483+
}
484+
}
485+
486+
#assertValue(value, fieldName = "value") {
487+
lazy.assert.object(
488+
value,
489+
`Expected "${fieldName}" to be an object, got ${value}`
490+
);
491+
492+
const { type, value: protocolBytesValue } = value;
493+
494+
const bytesValueTypeValue = Object.values(lazy.BytesValueType);
495+
lazy.assert.in(
496+
type,
497+
bytesValueTypeValue,
498+
`Expected "${fieldName}.type" to be one of ${bytesValueTypeValue}, got ${type}`
499+
);
500+
501+
lazy.assert.string(
502+
protocolBytesValue,
503+
`Expected "${fieldName}.value" to be string, got ${protocolBytesValue}`
504+
);
505+
}
506+
344507
/**
345508
* Deserialize the value to string, since platform API
346509
* returns cookie's value as a string.
347510
*/
348-
#deserializeCookieValue(cookieValue) {
511+
#deserializeProtocolBytes(cookieValue) {
349512
const { type, value } = cookieValue;
350513

351514
if (type === lazy.BytesValueType.String) {
@@ -460,6 +623,19 @@ class StorageModule extends Module {
460623
return originAttributes;
461624
}
462625

626+
#getSameSitePlatformProperty(sameSite) {
627+
switch (sameSite) {
628+
case "lax": {
629+
return Ci.nsICookie.SAMESITE_LAX;
630+
}
631+
case "strict": {
632+
return Ci.nsICookie.SAMESITE_STRICT;
633+
}
634+
}
635+
636+
return Ci.nsICookie.SAMESITE_NONE;
637+
}
638+
463639
/**
464640
* Return a cookie store of the storage partition for a given storage partition key.
465641
*
@@ -532,8 +708,8 @@ class StorageModule extends Module {
532708
let storedCookieValue = storedCookie[fieldName];
533709

534710
if (fieldName === "value") {
535-
value = this.#deserializeCookieValue(value);
536-
storedCookieValue = this.#deserializeCookieValue(storedCookieValue);
711+
value = this.#deserializeProtocolBytes(value);
712+
storedCookieValue = this.#deserializeProtocolBytes(storedCookieValue);
537713
}
538714

539715
if (storedCookieValue !== value) {

testing/web-platform/meta/webdriver/tests/bidi/storage/set_cookie/cookie_domain.py.ini

-14
This file was deleted.

testing/web-platform/meta/webdriver/tests/bidi/storage/set_cookie/cookie_expiry.py.ini

-9
This file was deleted.

testing/web-platform/meta/webdriver/tests/bidi/storage/set_cookie/cookie_http_only.py.ini

-9
This file was deleted.

testing/web-platform/meta/webdriver/tests/bidi/storage/set_cookie/cookie_name.py.ini

-9
This file was deleted.

testing/web-platform/meta/webdriver/tests/bidi/storage/set_cookie/cookie_path.py.ini

-12
This file was deleted.

testing/web-platform/meta/webdriver/tests/bidi/storage/set_cookie/cookie_same_site.py.ini

-12
This file was deleted.

testing/web-platform/meta/webdriver/tests/bidi/storage/set_cookie/cookie_secure.py.ini

-9
This file was deleted.

testing/web-platform/meta/webdriver/tests/bidi/storage/set_cookie/cookie_value.py.ini

-6
This file was deleted.

0 commit comments

Comments
 (0)