@@ -23,64 +23,129 @@ test.use({
2323 ignoreHTTPSErrors : true ,
2424} ) ;
2525
26- test ( `third party non-partitioned cookies` , async ( { page , browserName , httpsServer, isMac } ) => {
27- httpsServer . setRoute ( '/empty .html', ( req , res ) => {
28- res . setHeader ( 'Set-Cookie ', `name=value; SameSite=None; Path=/; Secure;` ) ;
26+ function addCommonCookieHandlers ( httpsServer : TestServer ) {
27+ // '/set-cookie .html' handlers are added in the tests.
28+ httpsServer . setRoute ( '/read-cookie.html ', ( req , res ) => {
2929 res . setHeader ( 'Content-Type' , 'text/html' ) ;
3030 const cookies = req . headers . cookie ?. split ( ';' ) . map ( c => c . trim ( ) ) . sort ( ) . join ( '; ' ) ;
3131 res . end ( `Received cookie: ${ cookies } ` ) ;
3232 } ) ;
33- httpsServer . setRoute ( '/with-frame.html' , ( req , res ) => {
33+ httpsServer . setRoute ( '/frame-set-cookie.html' , ( req , res ) => {
34+ res . setHeader ( 'Content-Type' , 'text/html' ) ;
35+ res . end ( `<iframe src='${ httpsServer . PREFIX } /set-cookie.html'></iframe>` ) ;
36+ } ) ;
37+ httpsServer . setRoute ( '/frame-read-cookie.html' , ( req , res ) => {
3438 res . setHeader ( 'Content-Type' , 'text/html' ) ;
35- res . end ( `<iframe src='${ httpsServer . PREFIX } /empty .html'></iframe>` ) ;
39+ res . end ( `<iframe src='${ httpsServer . PREFIX } /read-cookie .html'></iframe>` ) ;
3640 } ) ;
41+ }
3742
38- await page . goto ( httpsServer . EMPTY_PAGE ) ;
39- await page . goto ( httpsServer . EMPTY_PAGE ) ;
40- expect ( await page . locator ( 'body' ) . textContent ( ) ) . toBe ( 'Received cookie: name=value' ) ;
43+ async function runNonPartitionedTest ( page : Page , httpsServer : TestServer , browserName : string , isMac : boolean ) {
44+ addCommonCookieHandlers ( httpsServer ) ;
45+ httpsServer . setRoute ( '/set-cookie.html' , ( req , res ) => {
46+ res . setHeader ( 'Set-Cookie' , `name=value${ req . headers . referer ? '-third-party' : '-top-level' } ; SameSite=None; Path=/; Secure;` ) ;
47+ res . setHeader ( 'Content-Type' , 'text/html' ) ;
48+ res . end ( ) ;
49+ } ) ;
4150
42- await page . goto ( httpsServer . CROSS_PROCESS_PREFIX + '/with-frame.html' ) ;
51+ await page . goto ( httpsServer . PREFIX + '/set-cookie.html' ) ;
52+ await page . goto ( httpsServer . PREFIX + '/read-cookie.html' ) ;
53+ expect ( await page . locator ( 'body' ) . textContent ( ) ) . toBe ( 'Received cookie: name=value-top-level' ) ;
54+
55+ await page . goto ( httpsServer . CROSS_PROCESS_PREFIX + '/frame-read-cookie.html' ) ;
4356 const frameBody = page . locator ( 'iframe' ) . contentFrame ( ) . locator ( 'body' ) ;
4457
4558 // WebKit does not support third-party cookies without a 'Partition' attribute.
4659 if ( browserName === 'webkit' && isMac )
4760 await expect ( frameBody ) . toHaveText ( 'Received cookie: undefined' ) ;
4861 else
49- await expect ( frameBody ) . toHaveText ( 'Received cookie: name=value' ) ;
62+ await expect ( frameBody ) . toHaveText ( 'Received cookie: name=value-top-level' ) ;
63+
64+ // Set cookie and do second navigation.
65+ await page . goto ( httpsServer . CROSS_PROCESS_PREFIX + '/frame-set-cookie.html' ) ;
66+ await page . goto ( httpsServer . CROSS_PROCESS_PREFIX + '/frame-read-cookie.html' ) ;
67+ const expectedThirdParty = browserName === 'webkit' ?
68+ 'Received cookie: undefined' :
69+ 'Received cookie: name=value-third-party' ;
70+ await expect ( frameBody ) . toHaveText ( expectedThirdParty ) ;
71+
72+ // Check again the top-level cookie.
73+ await page . goto ( httpsServer . PREFIX + '/read-cookie.html' ) ;
74+ const expectedTopLevel = browserName === 'webkit' && isMac ?
75+ 'Received cookie: name=value-top-level' :
76+ 'Received cookie: name=value-third-party' ;
77+ expect ( await page . locator ( 'body' ) . textContent ( ) ) . toBe ( expectedTopLevel ) ;
78+
79+ return {
80+ expectedTopLevel,
81+ expectedThirdParty,
82+ } ;
83+ }
84+
85+ test ( `third party non-partitioned cookies` , async ( { page, browserName, httpsServer, isMac, browser } ) => {
86+ await runNonPartitionedTest ( page , httpsServer , browserName , isMac ) ;
5087} ) ;
5188
52- test ( `third party 'Partitioned;' cookies` , async ( { page, browserName, httpsServer, isMac } ) => {
53- httpsServer . setRoute ( '/empty.html' , ( req , res ) => {
89+ test ( `save/load third party non-partitioned cookies` , async ( { page, browserName, httpsServer, isMac, browser } ) => {
90+ // Run the test to populate the cookies.
91+ const { expectedTopLevel, expectedThirdParty } = await runNonPartitionedTest ( page , httpsServer , browserName , isMac ) ;
92+
93+ async function checkCookies ( page : Page ) {
94+ // Check top-level cookie first.
95+ await page . goto ( httpsServer . PREFIX + '/read-cookie.html' ) ;
96+ expect . soft ( await page . locator ( 'body' ) . textContent ( ) ) . toBe ( expectedTopLevel ) ;
97+
98+ // Check third-party cookie.
99+ await page . goto ( httpsServer . CROSS_PROCESS_PREFIX + '/frame-read-cookie.html' ) ;
100+ const frameBody = page . locator ( 'iframe' ) . contentFrame ( ) . locator ( 'body' ) ;
101+ await expect . soft ( frameBody ) . toHaveText ( expectedThirdParty ) ;
102+ }
103+
104+ await checkCookies ( page ) ;
105+
106+ await test . step ( 'export via cookies/addCookies' , async ( ) => {
107+ const cookies = await page . context ( ) . cookies ( ) ;
108+ const context2 = await browser . newContext ( ) ;
109+ await context2 . addCookies ( cookies ) ;
110+ const page2 = await context2 . newPage ( ) ;
111+ await checkCookies ( page2 ) ;
112+ } ) ;
113+
114+ await test . step ( 'export via storageState' , async ( ) => {
115+ const storageState = await page . context ( ) . storageState ( ) ;
116+ const context3 = await browser . newContext ( { storageState } ) ;
117+ const page3 = await context3 . newPage ( ) ;
118+ await checkCookies ( page3 ) ;
119+ } ) ;
120+ } ) ;
121+
122+ async function runPartitionedTest ( page : Page , httpsServer : TestServer , browserName : string , isMac : boolean ) {
123+ addCommonCookieHandlers ( httpsServer ) ;
124+ httpsServer . setRoute ( '/set-cookie.html' , ( req , res ) => {
54125 res . setHeader ( 'Set-Cookie' , [
55- `name=value; SameSite=None; Path=/; Secure; Partitioned;` ,
126+ `name=value${ req . headers . referer ? '-partitioned' : '-top-level' } ; SameSite=None; Path=/; Secure; Partitioned;` ,
56127 `nonPartitionedName=value; SameSite=None; Path=/; Secure;`
57128 ] ) ;
58- res . setHeader ( 'Content-Type' , 'text/html' ) ;
59- const cookies = req . headers . cookie ?. split ( ';' ) . map ( c => c . trim ( ) ) . sort ( ) . join ( '; ' ) ;
60- res . end ( `Received cookie: ${ cookies } ` ) ;
61- } ) ;
62- httpsServer . setRoute ( '/with-frame.html' , ( req , res ) => {
63- res . setHeader ( 'Content-Type' , 'text/html' ) ;
64- res . end ( `<iframe src='${ httpsServer . PREFIX } /empty.html'></iframe>` ) ;
129+ res . end ( ) ;
65130 } ) ;
66131
67- await page . goto ( httpsServer . EMPTY_PAGE ) ;
68- await page . goto ( httpsServer . EMPTY_PAGE ) ;
69- expect ( await page . locator ( 'body' ) . textContent ( ) ) . toBe ( 'Received cookie: name=value; nonPartitionedName=value' ) ;
132+ await page . goto ( httpsServer . PREFIX + '/set-cookie.html' ) ;
133+ await page . goto ( httpsServer . PREFIX + '/read-cookie.html' ) ;
134+ expect ( await page . locator ( 'body' ) . textContent ( ) ) . toBe ( 'Received cookie: name=value-top-level ; nonPartitionedName=value' ) ;
70135
71- await page . goto ( httpsServer . CROSS_PROCESS_PREFIX + '/with- frame.html' ) ;
136+ await page . goto ( httpsServer . CROSS_PROCESS_PREFIX + '/frame-read-cookie .html' ) ;
72137 const frameBody = page . locator ( 'iframe' ) . contentFrame ( ) . locator ( 'body' ) ;
73138
74139 // Firefox cookie partitioning is disabled in Firefox.
75140 // TODO: reenable cookie partitioning?
76141 if ( browserName === 'firefox' ) {
77- await expect ( frameBody ) . toHaveText ( 'Received cookie: name=value; nonPartitionedName=value' ) ;
142+ await expect ( frameBody ) . toHaveText ( 'Received cookie: name=value-top-level ; nonPartitionedName=value' ) ;
78143 return ;
79144 }
80145
81146 // Linux and Windows WebKit builds do not partition third-party cookies at all.
82147 if ( browserName === 'webkit' && ! isMac ) {
83- await expect ( frameBody ) . toHaveText ( 'Received cookie: name=value; nonPartitionedName=value' ) ;
148+ await expect ( frameBody ) . toHaveText ( 'Received cookie: name=value-top-level ; nonPartitionedName=value' ) ;
84149 return ;
85150 }
86151
@@ -98,11 +163,101 @@ test(`third party 'Partitioned;' cookies`, async ({ page, browserName, httpsServ
98163 // - sets the third-party cookie for the top-level context
99164 // Second navigation:
100165 // - sends the cookie as it was just set for the (top-level site, iframe url) partition.
101- await page . goto ( httpsServer . CROSS_PROCESS_PREFIX + '/with-frame.html' ) ;
166+ await page . goto ( httpsServer . CROSS_PROCESS_PREFIX + '/frame-set-cookie.html' ) ;
167+ await page . goto ( httpsServer . CROSS_PROCESS_PREFIX + '/frame-read-cookie.html' ) ;
102168 if ( browserName === 'webkit' )
103169 await expect ( frameBody ) . toHaveText ( 'Received cookie: undefined' ) ;
104170 else
105- await expect ( frameBody ) . toHaveText ( 'Received cookie: name=value; nonPartitionedName=value' ) ;
171+ await expect ( frameBody ) . toHaveText ( 'Received cookie: name=value-partitioned; nonPartitionedName=value' ) ;
172+ }
173+
174+ test ( `third party 'Partitioned;' cookies` , async ( { page, browserName, httpsServer, isMac, browser } ) => {
175+ await runPartitionedTest ( page , httpsServer , browserName , isMac ) ;
176+ } ) ;
177+
178+ test ( `save/load third party 'Partitioned;' cookies` , async ( { page, browserName, httpsServer, isMac, browser } ) => {
179+ test . fixme ( browserName === 'firefox' , 'Firefox cookie partitioning is disabled in Firefox.' ) ;
180+ test . fixme ( browserName === 'webkit' && ! isMac , 'Linux and Windows WebKit builds do not partition third-party cookies at all.' ) ;
181+
182+ await runPartitionedTest ( page , httpsServer , browserName , isMac ) ;
183+
184+ async function checkCookies ( page : Page ) {
185+ // Check top-level cookie first.
186+ await page . goto ( httpsServer . PREFIX + '/read-cookie.html' ) ;
187+ expect . soft ( await page . locator ( 'body' ) . textContent ( ) ) . toBe ( 'Received cookie: name=value-top-level; nonPartitionedName=value' ) ;
188+
189+ // Check third-party cookie.
190+ await page . goto ( httpsServer . CROSS_PROCESS_PREFIX + '/frame-read-cookie.html' ) ;
191+ const frameBody = page . locator ( 'iframe' ) . contentFrame ( ) . locator ( 'body' ) ;
192+ if ( browserName === 'webkit' )
193+ await expect . soft ( frameBody ) . toHaveText ( 'Received cookie: undefined' ) ;
194+ else
195+ await expect . soft ( frameBody ) . toHaveText ( 'Received cookie: name=value-partitioned; nonPartitionedName=value' ) ;
196+ }
197+
198+ await checkCookies ( page ) ;
199+
200+ await test . step ( 'export via cookies/addCookies' , async ( ) => {
201+ const cookies = await page . context ( ) . cookies ( ) ;
202+ const context2 = await browser . newContext ( ) ;
203+ await context2 . addCookies ( cookies ) ;
204+ const page2 = await context2 . newPage ( ) ;
205+ await checkCookies ( page2 ) ;
206+ } ) ;
207+
208+ await test . step ( 'export via storageState' , async ( ) => {
209+ const storageState = await page . context ( ) . storageState ( ) ;
210+ const context3 = await browser . newContext ( { storageState } ) ;
211+ const page3 = await context3 . newPage ( ) ;
212+ await checkCookies ( page3 ) ;
213+ } ) ;
214+ } ) ;
215+
216+ test ( `same origin third party 'Partitioned;' cookie with different origin intermediate iframe` , async ( { page, browserName, httpsServer, isMac, browser } ) => {
217+ addCommonCookieHandlers ( httpsServer ) ;
218+ httpsServer . setRoute ( '/set-cookie.html' , ( req , res ) => {
219+ res . setHeader ( 'Set-Cookie' , [
220+ `name=value${ req . headers . referer ? '-partitioned' : '-top-level' } ; SameSite=None; Path=/; Secure; Partitioned;` ,
221+ `nonPartitionedName=value; SameSite=None; Path=/; Secure;`
222+ ] ) ;
223+ res . end ( ) ;
224+ } ) ;
225+ // main frame: origin1 -> iframe1: origin2 -> iframe2: origin1
226+ // In this case the cookie in iframe2 will have hasCrossSiteAncestor=true even though
227+ // the innermost frame is in origin1.
228+ httpsServer . setRoute ( '/top-frame-set-cookie.html' , ( req , res ) => {
229+ res . setHeader ( 'Content-Type' , 'text/html' ) ;
230+ res . end ( `<iframe src='${ httpsServer . CROSS_PROCESS_PREFIX } /frame-set-cookie.html'></iframe>` ) ;
231+ } ) ;
232+ httpsServer . setRoute ( '/top-frame-read-cookie.html' , ( req , res ) => {
233+ res . setHeader ( 'Content-Type' , 'text/html' ) ;
234+ res . end ( `<iframe src='${ httpsServer . CROSS_PROCESS_PREFIX } /frame-read-cookie.html'></iframe>` ) ;
235+ } ) ;
236+
237+ await page . goto ( httpsServer . PREFIX + '/top-frame-set-cookie.html' ) ;
238+
239+ async function checkCookies ( page : Page ) {
240+ await page . goto ( httpsServer . PREFIX + '/top-frame-read-cookie.html' ) ;
241+ const frameBody = page . locator ( 'iframe' ) . contentFrame ( ) . locator ( 'iframe' ) . contentFrame ( ) . locator ( 'body' ) ;
242+ await expect . soft ( frameBody ) . toHaveText ( 'Received cookie: name=value-partitioned; nonPartitionedName=value' ) ;
243+ }
244+
245+ await checkCookies ( page ) ;
246+
247+ await test . step ( 'export via cookies/addCookies' , async ( ) => {
248+ const cookies = await page . context ( ) . cookies ( ) ;
249+ const context2 = await browser . newContext ( ) ;
250+ await context2 . addCookies ( cookies ) ;
251+ const page2 = await context2 . newPage ( ) ;
252+ await checkCookies ( page2 ) ;
253+ } ) ;
254+
255+ await test . step ( 'export via storageState' , async ( ) => {
256+ const storageState = await page . context ( ) . storageState ( ) ;
257+ const context3 = await browser . newContext ( { storageState } ) ;
258+ const page3 = await context3 . newPage ( ) ;
259+ await checkCookies ( page3 ) ;
260+ } ) ;
106261} ) ;
107262
108263test ( 'should be able to send third party cookies via an iframe' , async ( { browser, httpsServer, browserName, isMac } ) => {
0 commit comments