Skip to content

Commit cf1cd9a

Browse files
committed
add cookie test
1 parent 9c8ae83 commit cf1cd9a

File tree

6 files changed

+165
-46
lines changed

6 files changed

+165
-46
lines changed

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

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -136,10 +136,8 @@ scheme.SetNetworkCookie = tObject({
136136
httpOnly: tOptional(tBoolean),
137137
secure: tOptional(tBoolean),
138138
sameSite: tOptional(tEnum(['Strict', 'Lax', 'None'])),
139-
partitionKey: tOptional(tObject({
140-
topLevelSite: tString,
141-
hasCrossSiteAncestor: tBoolean,
142-
})),
139+
topLevelSite: tOptional(tString),
140+
_chromiumHasCrossSiteAncestor: tOptional(tBoolean),
143141
});
144142
scheme.NetworkCookie = tObject({
145143
name: tString,
@@ -150,10 +148,8 @@ scheme.NetworkCookie = tObject({
150148
httpOnly: tBoolean,
151149
secure: tBoolean,
152150
sameSite: tEnum(['Strict', 'Lax', 'None']),
153-
partitionKey: tOptional(tObject({
154-
topLevelSite: tString,
155-
hasCrossSiteAncestor: tBoolean,
156-
})),
151+
topLevelSite: tOptional(tString),
152+
_chromiumHasCrossSiteAncestor: tOptional(tBoolean),
157153
});
158154
scheme.NameValue = tObject({
159155
name: tString,

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

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -388,12 +388,37 @@ export class CRBrowserContext extends BrowserContext {
388388
delete copy.sameParty;
389389
delete copy.sourceScheme;
390390
delete copy.sourcePort;
391+
delete copy.partitionKey;
392+
// If hasCrossSiteAncestor is false, the cookie is a partitioned first party cookie,
393+
// this is Chromium specific, see https://chromestatus.com/feature/5144832583663616
394+
// and https://github.com/explainers-by-googlers/CHIPS-spec.
395+
if (c.partitionKey) {
396+
copy._chromiumHasCrossSiteAncestor = c.partitionKey.hasCrossSiteAncestor;
397+
copy.topLevelSite = c.partitionKey.topLevelSite;
398+
}
391399
return copy as channels.NetworkCookie;
392400
}), urls);
393401
}
394402

395403
async addCookies(cookies: channels.SetNetworkCookie[]) {
396-
await this._browser._session.send('Storage.setCookies', { cookies: network.rewriteCookies(cookies), browserContextId: this._browserContextId });
404+
function toChromiumCookie(cookie: channels.SetNetworkCookie) {
405+
const { topLevelSite, _chromiumHasCrossSiteAncestor, ...rest } = cookie;
406+
if (!topLevelSite)
407+
return cookie;
408+
return {
409+
...rest,
410+
partitionKey: {
411+
topLevelSite,
412+
// _chromiumHasCrossSiteAncestor is non-standard, set it true by default if the cookie is partitioned.
413+
hasCrossSiteAncestor: _chromiumHasCrossSiteAncestor ?? true,
414+
},
415+
};
416+
}
417+
418+
await this._browser._session.send('Storage.setCookies', {
419+
cookies: network.rewriteCookies(cookies.map(toChromiumCookie)),
420+
browserContextId: this._browserContextId
421+
});
397422
}
398423

399424
async doClearCookies() {

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

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -305,10 +305,14 @@ export class FFBrowserContext extends BrowserContext {
305305
}
306306

307307
async addCookies(cookies: channels.SetNetworkCookie[]) {
308-
const cc = network.rewriteCookies(cookies).map(c => ({
309-
...c,
310-
expires: c.expires === -1 ? undefined : c.expires,
311-
}));
308+
const cc = network.rewriteCookies(cookies).map(c => {
309+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
310+
const { _chromiumHasCrossSiteAncestor, topLevelSite, ...rest } = c;
311+
return {
312+
...rest,
313+
expires: c.expires === -1 ? undefined : c.expires,
314+
};
315+
});
312316
await this._browser.session.send('Browser.setCookies', { browserContextId: this._browserContextId, cookies: cc });
313317
}
314318

packages/protocol/src/channels.d.ts

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -260,10 +260,8 @@ export type SetNetworkCookie = {
260260
httpOnly?: boolean,
261261
secure?: boolean,
262262
sameSite?: 'Strict' | 'Lax' | 'None',
263-
partitionKey?: {
264-
topLevelSite: string,
265-
hasCrossSiteAncestor: boolean,
266-
},
263+
topLevelSite?: string,
264+
_chromiumHasCrossSiteAncestor?: boolean,
267265
};
268266

269267
export type NetworkCookie = {
@@ -275,10 +273,8 @@ export type NetworkCookie = {
275273
httpOnly: boolean,
276274
secure: boolean,
277275
sameSite: 'Strict' | 'Lax' | 'None',
278-
partitionKey?: {
279-
topLevelSite: string,
280-
hasCrossSiteAncestor: boolean,
281-
},
276+
topLevelSite?: string,
277+
_chromiumHasCrossSiteAncestor?: boolean,
282278
};
283279

284280
export type NameValue = {

packages/protocol/src/protocol.yml

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -223,11 +223,8 @@ SetNetworkCookie:
223223
- Strict
224224
- Lax
225225
- None
226-
partitionKey:
227-
type: object?
228-
properties:
229-
topLevelSite: string
230-
hasCrossSiteAncestor: boolean
226+
topLevelSite: string?
227+
_chromiumHasCrossSiteAncestor: boolean?
231228

232229

233230
NetworkCookie:
@@ -246,11 +243,8 @@ NetworkCookie:
246243
- Strict
247244
- Lax
248245
- None
249-
partitionKey:
250-
type: object?
251-
properties:
252-
topLevelSite: string
253-
hasCrossSiteAncestor: boolean
246+
topLevelSite: string?
247+
_chromiumHasCrossSiteAncestor: boolean?
254248

255249

256250
NameValue:

tests/library/browsercontext-cookies-third-party.spec.ts

Lines changed: 119 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
import { contextTest, expect } from '../config/browserTest';
1818

19-
import type { Page, BrowserContext } from 'playwright';
19+
import type { Page, BrowserContext, Cookie } from 'playwright';
2020
import type { TestServer } from '../config/testserver';
2121

2222
type TestUrls = {
@@ -63,6 +63,12 @@ test.use({
6363
* frame-partitioned=value
6464
* frame-non-partitioned=value
6565
*
66+
* origin1:
67+
* origin2:
68+
* origin1:
69+
* frame-partitioned=value
70+
* frame-non-partitioned=value
71+
*
6672
* origin1 = httpsServer.PREFIX
6773
* origin2 = httpsServer.CROSS_PROCESS_PREFIX
6874
*/
@@ -93,6 +99,17 @@ function addCommonCookieHandlers(httpsServer: TestServer, urls: TestUrls) {
9399
});
94100
}
95101

102+
function findCookie(cookies: Cookie[], name: string) {
103+
const result = cookies.find(cookie => cookie.name === name);
104+
expect(result, `Cookie ${name} not found in ${JSON.stringify(cookies, null, 2)}`).toBeTruthy();
105+
return result;
106+
}
107+
108+
function expectTopLevelSite(cookies: Cookie[], name: string, topLevelSite: string) {
109+
const cookie = findCookie(cookies, name);
110+
expect(cookie.topLevelSite, `Cookie ${name}`).toBe(topLevelSite);
111+
}
112+
96113
async function runNonPartitionedTest(page: Page, httpsServer: TestServer, browserName: string, isMac: boolean, urls: TestUrls) {
97114
addCommonCookieHandlers(httpsServer, urls);
98115
httpsServer.setRoute('/set-cookie.html', (req, res) => {
@@ -264,6 +281,23 @@ test(`save/load third party 'Partitioned;' cookies`, async ({ page, browserName,
264281

265282
await checkCookies(page);
266283

284+
function checkStorageCookies(cookies: Cookie[]) {
285+
const expectedTopLevelPartitioned = browserName === 'webkit' && isMac ?
286+
undefined :
287+
'https://localhost';
288+
expectTopLevelSite(cookies, 'top-level-partitioned', expectedTopLevelPartitioned);
289+
expectTopLevelSite(cookies, 'top-level-non-partitioned', undefined);
290+
if (browserName === 'webkit' && isMac) {
291+
expect(cookies.find(cookie => cookie.name === 'frame-partitioned')).toBeUndefined();
292+
expect(cookies.find(cookie => cookie.name === 'frame-non-partitioned')).toBeUndefined();
293+
} else {
294+
expectTopLevelSite(cookies, 'frame-partitioned', 'https://127.0.0.1');
295+
expectTopLevelSite(cookies, 'frame-non-partitioned', undefined);
296+
}
297+
}
298+
checkStorageCookies(await page.context().cookies());
299+
checkStorageCookies((await page.context().storageState()).cookies);
300+
267301
await test.step('export via cookies/addCookies', async () => {
268302
const cookies = await page.context().cookies();
269303
const context2 = await browser.newContext();
@@ -280,20 +314,90 @@ test(`save/load third party 'Partitioned;' cookies`, async ({ page, browserName,
280314
});
281315
});
282316

283-
/**
284-
* origin1:
285-
* top-level-partitioned=value
286-
* top-level-non-partitioned=value
287-
*
288-
* origin1:
289-
* origin2:
290-
* origin1:
291-
* frame-partitioned=value
292-
* frame-non-partitioned=value
293-
*
294-
* origin1 = httpsServer.PREFIX
295-
* origin2 = httpsServer.CROSS_PROCESS_PREFIX
296-
*/
317+
test(`add 'Partitioned;' cookie via API`, async ({ page, context, browserName, httpsServer, isMac, urls }) => {
318+
// test.fixme(browserName === 'firefox', 'Firefox cookie partitioning is disabled in Firefox.');
319+
// test.fixme(browserName === 'webkit' && !isMac, 'Linux and Windows WebKit builds do not partition third-party cookies at all.');
320+
addCommonCookieHandlers(httpsServer, urls);
321+
322+
await context.addCookies([
323+
{
324+
name: 'top-level-partitioned',
325+
value: 'value',
326+
domain: 'localhost',
327+
path: '/',
328+
expires: -1,
329+
httpOnly: false,
330+
secure: true,
331+
sameSite: 'None',
332+
topLevelSite: 'https://localhost',
333+
_chromiumHasCrossSiteAncestor: false
334+
} as any,
335+
{
336+
name: 'top-level-non-partitioned',
337+
value: 'value',
338+
domain: 'localhost',
339+
path: '/',
340+
expires: -1,
341+
httpOnly: false,
342+
secure: true,
343+
sameSite: 'None'
344+
},
345+
{
346+
name: 'frame-partitioned',
347+
value: 'value',
348+
domain: 'localhost',
349+
path: '/',
350+
expires: -1,
351+
httpOnly: false,
352+
secure: true,
353+
sameSite: 'None',
354+
topLevelSite: 'https://127.0.0.1',
355+
_chromiumHasCrossSiteAncestor: true
356+
} as any,
357+
{
358+
name: 'frame-non-partitioned',
359+
value: 'value',
360+
domain: 'localhost',
361+
path: '/',
362+
expires: -1,
363+
httpOnly: false,
364+
secure: true,
365+
sameSite: 'None'
366+
}
367+
]);
368+
369+
async function checkCookies(page: Page) {
370+
{
371+
// Check top-level cookie first.
372+
await page.goto(urls.read_origin1);
373+
const expectedTopLevel = browserName === 'webkit' || browserName === 'firefox' ?
374+
'Received cookie: frame-non-partitioned=value; frame-partitioned=value; top-level-non-partitioned=value; top-level-partitioned=value' :
375+
'Received cookie: frame-non-partitioned=value; top-level-non-partitioned=value; top-level-partitioned=value';
376+
expect.soft(await page.locator('body').textContent()).toBe(expectedTopLevel);
377+
}
378+
{
379+
// Check third-party cookie.
380+
await page.goto(urls.read_origin2_origin1);
381+
const frameBody = page.locator('iframe').contentFrame().locator('body');
382+
const expectedThirdParty = browserName === 'webkit' ?
383+
'Received cookie: undefined' : browserName === 'firefox' ?
384+
'Received cookie: frame-non-partitioned=value; frame-partitioned=value; top-level-non-partitioned=value; top-level-partitioned=value' :
385+
'Received cookie: frame-non-partitioned=value; frame-partitioned=value; top-level-non-partitioned=value';
386+
await expect.soft(frameBody).toHaveText(expectedThirdParty, { timeout: 1000 });
387+
}
388+
{
389+
await page.goto(urls.read_origin1_origin2_origin1); // read-origin1-origin2-origin1.html
390+
const frameBody = page.locator('iframe').contentFrame().locator('iframe').contentFrame().locator('body');
391+
const expectedThirdParty = browserName === 'webkit' || browserName === 'firefox' ?
392+
'Received cookie: frame-non-partitioned=value; frame-partitioned=value; top-level-non-partitioned=value; top-level-partitioned=value' :
393+
'Received cookie: frame-non-partitioned=value; top-level-non-partitioned=value';
394+
await expect.soft(frameBody).toHaveText(expectedThirdParty, { timeout: 1000 });
395+
}
396+
}
397+
398+
await checkCookies(page);
399+
});
400+
297401

298402
test(`same origin third party 'Partitioned;' cookie with different origin intermediate iframe`, async ({ page, httpsServer, browser, urls }) => {
299403
addCommonCookieHandlers(httpsServer, urls);

0 commit comments

Comments
 (0)