Skip to content

Commit c930e67

Browse files
authored
feat(cookie): export/import chips cookies (#36168)
1 parent 237de4f commit c930e67

File tree

10 files changed

+556
-58
lines changed

10 files changed

+556
-58
lines changed

docs/src/api/class-browsercontext.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,7 @@ await context.AddCookiesAsync(new[] { cookie1, cookie2 });
363363
- `httpOnly` ?<[boolean]> Optional.
364364
- `secure` ?<[boolean]> Optional.
365365
- `sameSite` ?<[SameSiteAttribute]<"Strict"|"Lax"|"None">> Optional.
366+
- `partitionKey` ?<[string]> For partitioned third-party cookies (aka [CHIPS](https://developer.mozilla.org/en-US/docs/Web/Privacy/Guides/Privacy_sandbox/Partitioned_cookies)), the partition key. Optional.
366367

367368
## async method: BrowserContext.addInitScript
368369
* since: v1.8
@@ -602,6 +603,7 @@ The default browser context cannot be closed.
602603
- `httpOnly` <[boolean]>
603604
- `secure` <[boolean]>
604605
- `sameSite` <[SameSiteAttribute]<"Strict"|"Lax"|"None">>
606+
- `partitionKey` ?<[string]>
605607

606608
If no URLs are specified, this method returns all cookies. If URLs are specified, only cookies that affect those URLs
607609
are returned.
@@ -1504,6 +1506,7 @@ Whether to emulate network being offline for the browser context.
15041506
- `httpOnly` <[boolean]>
15051507
- `secure` <[boolean]>
15061508
- `sameSite` <[SameSiteAttribute]<"Strict"|"Lax"|"None">>
1509+
- `partitionKey` ?<[string]>
15071510
- `origins` <[Array]<[Object]>>
15081511
- `origin` <[string]>
15091512
- `localStorage` <[Array]<[Object]>>

packages/playwright-client/types/types.d.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8830,6 +8830,13 @@ export interface BrowserContext {
88308830
* Optional.
88318831
*/
88328832
sameSite?: "Strict"|"Lax"|"None";
8833+
8834+
/**
8835+
* For partitioned third-party cookies (aka
8836+
* [CHIPS](https://developer.mozilla.org/en-US/docs/Web/Privacy/Guides/Privacy_sandbox/Partitioned_cookies)), the
8837+
* partition key. Optional.
8838+
*/
8839+
partitionKey?: string;
88338840
}>): Promise<void>;
88348841

88358842
/**
@@ -9305,6 +9312,8 @@ export interface BrowserContext {
93059312
secure: boolean;
93069313

93079314
sameSite: "Strict"|"Lax"|"None";
9315+
9316+
partitionKey?: string;
93089317
}>;
93099318

93109319
origins: Array<{
@@ -22502,6 +22511,8 @@ export interface Cookie {
2250222511
secure: boolean;
2250322512

2250422513
sameSite: "Strict"|"Lax"|"None";
22514+
22515+
partitionKey?: string;
2250522516
}
2250622517

2250722518
interface PageWaitForSelectorOptions {

packages/playwright-core/src/protocol/validator.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,8 @@ scheme.SetNetworkCookie = tObject({
136136
httpOnly: tOptional(tBoolean),
137137
secure: tOptional(tBoolean),
138138
sameSite: tOptional(tEnum(['Strict', 'Lax', 'None'])),
139+
partitionKey: tOptional(tString),
140+
_crHasCrossSiteAncestor: tOptional(tBoolean),
139141
});
140142
scheme.NetworkCookie = tObject({
141143
name: tString,
@@ -146,6 +148,8 @@ scheme.NetworkCookie = tObject({
146148
httpOnly: tBoolean,
147149
secure: tBoolean,
148150
sameSite: tEnum(['Strict', 'Lax', 'None']),
151+
partitionKey: tOptional(tString),
152+
_crHasCrossSiteAncestor: tOptional(tBoolean),
149153
});
150154
scheme.NameValue = tObject({
151155
name: tString,

packages/playwright-core/src/server/chromium/crBrowser.ts

Lines changed: 46 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -381,19 +381,56 @@ export class CRBrowserContext extends BrowserContext {
381381
async doGetCookies(urls: string[]): Promise<channels.NetworkCookie[]> {
382382
const { cookies } = await this._browser._session.send('Storage.getCookies', { browserContextId: this._browserContextId });
383383
return network.filterCookies(cookies.map(c => {
384-
const copy: any = { sameSite: 'Lax', ...c };
385-
delete copy.size;
386-
delete copy.priority;
387-
delete copy.session;
388-
delete copy.sameParty;
389-
delete copy.sourceScheme;
390-
delete copy.sourcePort;
391-
return copy as channels.NetworkCookie;
384+
const { name, value, domain, path, expires, httpOnly, secure, sameSite } = c;
385+
const copy: channels.NetworkCookie = {
386+
name,
387+
value,
388+
domain,
389+
path,
390+
expires,
391+
httpOnly,
392+
secure,
393+
sameSite: sameSite ?? 'Lax',
394+
};
395+
// If hasCrossSiteAncestor is false, the cookie is a partitioned first party cookie,
396+
// this is Chromium specific, see https://chromestatus.com/feature/5144832583663616
397+
// and https://github.com/explainers-by-googlers/CHIPS-spec.
398+
if (c.partitionKey) {
399+
copy._crHasCrossSiteAncestor = c.partitionKey.hasCrossSiteAncestor;
400+
copy.partitionKey = c.partitionKey.topLevelSite;
401+
}
402+
return copy;
392403
}), urls);
393404
}
394405

395406
async addCookies(cookies: channels.SetNetworkCookie[]) {
396-
await this._browser._session.send('Storage.setCookies', { cookies: network.rewriteCookies(cookies), browserContextId: this._browserContextId });
407+
function toChromiumCookie(cookie: channels.SetNetworkCookie) {
408+
const { name, value, url, domain, path, expires, httpOnly, secure, sameSite, partitionKey, _crHasCrossSiteAncestor } = cookie;
409+
const copy: Protocol.Network.CookieParam = {
410+
name,
411+
value,
412+
url,
413+
domain,
414+
path,
415+
expires,
416+
httpOnly,
417+
secure,
418+
sameSite
419+
};
420+
if (partitionKey) {
421+
copy.partitionKey = {
422+
topLevelSite: partitionKey,
423+
// _crHasCrossSiteAncestor is non-standard, set it true by default if the cookie is partitioned.
424+
hasCrossSiteAncestor: _crHasCrossSiteAncestor ?? true,
425+
};
426+
}
427+
return copy;
428+
}
429+
430+
await this._browser._session.send('Storage.setCookies', {
431+
cookies: network.rewriteCookies(cookies).map(toChromiumCookie),
432+
browserContextId: this._browserContextId
433+
});
397434
}
398435

399436
async doClearCookies() {

packages/playwright-core/src/server/firefox/ffBrowser.ts

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -297,18 +297,35 @@ export class FFBrowserContext extends BrowserContext {
297297
async doGetCookies(urls: string[]): Promise<channels.NetworkCookie[]> {
298298
const { cookies } = await this._browser.session.send('Browser.getCookies', { browserContextId: this._browserContextId });
299299
return network.filterCookies(cookies.map(c => {
300-
const copy: any = { ... c };
301-
delete copy.size;
302-
delete copy.session;
303-
return copy as channels.NetworkCookie;
300+
const { name, value, domain, path, expires, httpOnly, secure, sameSite } = c;
301+
return {
302+
name,
303+
value,
304+
domain,
305+
path,
306+
expires,
307+
httpOnly,
308+
secure,
309+
sameSite,
310+
};
304311
}), urls);
305312
}
306313

307314
async addCookies(cookies: channels.SetNetworkCookie[]) {
308-
const cc = network.rewriteCookies(cookies).map(c => ({
309-
...c,
310-
expires: c.expires === -1 ? undefined : c.expires,
311-
}));
315+
const cc = network.rewriteCookies(cookies).map(c => {
316+
const { name, value, url, domain, path, expires, httpOnly, secure, sameSite } = c;
317+
return {
318+
name,
319+
value,
320+
url,
321+
domain,
322+
path,
323+
expires: expires === -1 ? undefined : expires,
324+
httpOnly,
325+
secure,
326+
sameSite
327+
};
328+
});
312329
await this._browser.session.send('Browser.setCookies', { browserContextId: this._browserContextId, cookies: cc });
313330
}
314331

packages/playwright-core/src/server/webkit/wkBrowser.ts

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -255,19 +255,37 @@ export class WKBrowserContext extends BrowserContext {
255255
async doGetCookies(urls: string[]): Promise<channels.NetworkCookie[]> {
256256
const { cookies } = await this._browser._browserSession.send('Playwright.getAllCookies', { browserContextId: this._browserContextId });
257257
return network.filterCookies(cookies.map((c: channels.NetworkCookie) => {
258-
const copy: any = { ... c };
259-
copy.expires = c.expires === -1 ? -1 : c.expires / 1000;
260-
delete copy.session;
261-
return copy as channels.NetworkCookie;
258+
const { name, value, domain, path, expires, httpOnly, secure, sameSite } = c;
259+
const copy: channels.NetworkCookie = {
260+
name,
261+
value,
262+
domain,
263+
path,
264+
expires: expires === -1 ? -1 : expires / 1000,
265+
httpOnly,
266+
secure,
267+
sameSite,
268+
};
269+
return copy;
262270
}), urls);
263271
}
264272

265273
async addCookies(cookies: channels.SetNetworkCookie[]) {
266-
const cc = network.rewriteCookies(cookies).map(c => ({
267-
...c,
268-
session: c.expires === -1 || c.expires === undefined,
269-
expires: c.expires && c.expires !== -1 ? c.expires * 1000 : c.expires,
270-
})) as Protocol.Playwright.SetCookieParam[];
274+
const cc = network.rewriteCookies(cookies).map(c => {
275+
const { name, value, domain, path, expires, httpOnly, secure, sameSite } = c;
276+
const copy: Protocol.Playwright.SetCookieParam = {
277+
name,
278+
value,
279+
domain: domain!,
280+
path: path!,
281+
expires: expires && expires !== -1 ? expires * 1000 : expires,
282+
httpOnly,
283+
secure,
284+
sameSite,
285+
session: expires === -1 || expires === undefined,
286+
};
287+
return copy;
288+
});
271289
await this._browser._browserSession.send('Playwright.setCookies', { cookies: cc, browserContextId: this._browserContextId });
272290
}
273291

packages/playwright-core/types/types.d.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8830,6 +8830,13 @@ export interface BrowserContext {
88308830
* Optional.
88318831
*/
88328832
sameSite?: "Strict"|"Lax"|"None";
8833+
8834+
/**
8835+
* For partitioned third-party cookies (aka
8836+
* [CHIPS](https://developer.mozilla.org/en-US/docs/Web/Privacy/Guides/Privacy_sandbox/Partitioned_cookies)), the
8837+
* partition key. Optional.
8838+
*/
8839+
partitionKey?: string;
88338840
}>): Promise<void>;
88348841

88358842
/**
@@ -9305,6 +9312,8 @@ export interface BrowserContext {
93059312
secure: boolean;
93069313

93079314
sameSite: "Strict"|"Lax"|"None";
9315+
9316+
partitionKey?: string;
93089317
}>;
93099318

93109319
origins: Array<{
@@ -22502,6 +22511,8 @@ export interface Cookie {
2250222511
secure: boolean;
2250322512

2250422513
sameSite: "Strict"|"Lax"|"None";
22514+
22515+
partitionKey?: string;
2250522516
}
2250622517

2250722518
interface PageWaitForSelectorOptions {

packages/protocol/src/channels.d.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,8 @@ export type SetNetworkCookie = {
260260
httpOnly?: boolean,
261261
secure?: boolean,
262262
sameSite?: 'Strict' | 'Lax' | 'None',
263+
partitionKey?: string,
264+
_crHasCrossSiteAncestor?: boolean,
263265
};
264266

265267
export type NetworkCookie = {
@@ -271,6 +273,8 @@ export type NetworkCookie = {
271273
httpOnly: boolean,
272274
secure: boolean,
273275
sameSite: 'Strict' | 'Lax' | 'None',
276+
partitionKey?: string,
277+
_crHasCrossSiteAncestor?: boolean,
274278
};
275279

276280
export type NameValue = {

packages/protocol/src/protocol.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,8 @@ SetNetworkCookie:
223223
- Strict
224224
- Lax
225225
- None
226+
partitionKey: string?
227+
_crHasCrossSiteAncestor: boolean?
226228

227229

228230
NetworkCookie:
@@ -241,6 +243,8 @@ NetworkCookie:
241243
- Strict
242244
- Lax
243245
- None
246+
partitionKey: string?
247+
_crHasCrossSiteAncestor: boolean?
244248

245249

246250
NameValue:

0 commit comments

Comments
 (0)