@@ -26,6 +26,8 @@ const CookieFieldsMapping = {
26
26
value : "value" ,
27
27
} ;
28
28
29
+ const MAX_COOKIE_EXPIRY = Number . MAX_SAFE_INTEGER ;
30
+
29
31
/**
30
32
* Enum of possible partition types supported by the
31
33
* storage.getCookies command.
@@ -157,6 +159,165 @@ class StorageModule extends Module {
157
159
return { cookies, partitionKey } ;
158
160
}
159
161
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
+
160
321
#assertGetCookieFilter( filter ) {
161
322
lazy . assert . object (
162
323
filter ,
@@ -210,17 +371,7 @@ class StorageModule extends Module {
210
371
) ;
211
372
}
212
373
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" ) ;
224
375
225
376
if ( secure !== null ) {
226
377
lazy . assert . boolean (
@@ -237,27 +388,7 @@ class StorageModule extends Module {
237
388
}
238
389
239
390
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" ) ;
261
392
}
262
393
263
394
return {
@@ -341,11 +472,43 @@ class StorageModule extends Module {
341
472
}
342
473
}
343
474
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
+
344
507
/**
345
508
* Deserialize the value to string, since platform API
346
509
* returns cookie's value as a string.
347
510
*/
348
- #deserializeCookieValue ( cookieValue ) {
511
+ #deserializeProtocolBytes ( cookieValue ) {
349
512
const { type, value } = cookieValue ;
350
513
351
514
if ( type === lazy . BytesValueType . String ) {
@@ -460,6 +623,19 @@ class StorageModule extends Module {
460
623
return originAttributes ;
461
624
}
462
625
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
+
463
639
/**
464
640
* Return a cookie store of the storage partition for a given storage partition key.
465
641
*
@@ -532,8 +708,8 @@ class StorageModule extends Module {
532
708
let storedCookieValue = storedCookie [ fieldName ] ;
533
709
534
710
if ( fieldName === "value" ) {
535
- value = this . #deserializeCookieValue ( value ) ;
536
- storedCookieValue = this . #deserializeCookieValue ( storedCookieValue ) ;
711
+ value = this . #deserializeProtocolBytes ( value ) ;
712
+ storedCookieValue = this . #deserializeProtocolBytes ( storedCookieValue ) ;
537
713
}
538
714
539
715
if ( storedCookieValue !== value ) {
0 commit comments