browser_sessionStorage.js (9373B)
1 /* Any copyright is dedicated to the Public Domain. 2 * http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 "use strict"; 5 6 const RAND = Math.random(); 7 const URL = 8 "http://mochi.test:8888/browser/" + 9 "browser/components/sessionstore/test/browser_sessionStorage.html" + 10 "?" + 11 RAND; 12 13 const HAS_FIRST_PARTY_DOMAIN = [ 14 Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN, 15 ].includes(Services.prefs.getIntPref("network.cookie.cookieBehavior")); 16 const OUTER_ORIGIN = "http://mochi.test:8888"; 17 const FIRST_PARTY_DOMAIN = escape("(http,mochi.test)"); 18 const INNER_ORIGIN = HAS_FIRST_PARTY_DOMAIN 19 ? `http://example.com^partitionKey=${FIRST_PARTY_DOMAIN}` 20 : "http://example.com"; 21 const SECURE_INNER_ORIGIN = HAS_FIRST_PARTY_DOMAIN 22 ? `https://example.com^partitionKey=${FIRST_PARTY_DOMAIN}` 23 : "https://example.com"; 24 25 const OUTER_VALUE = "outer-value-" + RAND; 26 const INNER_VALUE = "inner-value-" + RAND; 27 28 /** 29 * This test ensures that setting, modifying and restoring sessionStorage data 30 * works as expected. 31 */ 32 add_task(async function session_storage() { 33 let tab = BrowserTestUtils.addTab(gBrowser, URL); 34 let browser = tab.linkedBrowser; 35 await promiseBrowserLoaded(browser); 36 37 // Flush to make sure chrome received all data. 38 await TabStateFlusher.flush(browser); 39 40 let { storage } = JSON.parse(ss.getTabState(tab)); 41 is( 42 storage[INNER_ORIGIN].test, 43 INNER_VALUE, 44 "sessionStorage data for example.com has been serialized correctly" 45 ); 46 is( 47 storage[OUTER_ORIGIN].test, 48 OUTER_VALUE, 49 "sessionStorage data for mochi.test has been serialized correctly" 50 ); 51 52 // Ensure that modifying sessionStore values works for the inner frame only. 53 await modifySessionStorage(browser, { test: "modified1" }, { frameIndex: 0 }); 54 await TabStateFlusher.flush(browser); 55 56 ({ storage } = JSON.parse(ss.getTabState(tab))); 57 is( 58 storage[INNER_ORIGIN].test, 59 "modified1", 60 "sessionStorage data for example.com has been serialized correctly" 61 ); 62 is( 63 storage[OUTER_ORIGIN].test, 64 OUTER_VALUE, 65 "sessionStorage data for mochi.test has been serialized correctly" 66 ); 67 68 // Ensure that modifying sessionStore values works for both frames. 69 await modifySessionStorage(browser, { test: "modified" }); 70 await modifySessionStorage(browser, { test: "modified2" }, { frameIndex: 0 }); 71 await TabStateFlusher.flush(browser); 72 73 ({ storage } = JSON.parse(ss.getTabState(tab))); 74 is( 75 storage[INNER_ORIGIN].test, 76 "modified2", 77 "sessionStorage data for example.com has been serialized correctly" 78 ); 79 is( 80 storage[OUTER_ORIGIN].test, 81 "modified", 82 "sessionStorage data for mochi.test has been serialized correctly" 83 ); 84 85 // Test that duplicating a tab works. 86 let tab2 = gBrowser.duplicateTab(tab); 87 let browser2 = tab2.linkedBrowser; 88 // See bug 2001322, need to wait for iframe of tab2 to finish loading 89 const subframeLoaded = BrowserTestUtils.browserLoaded(tab2.linkedBrowser, { 90 includeSubFrames: true, 91 wantLoad: url => url.startsWith("http://example.com"), 92 }); 93 await Promise.all([promiseTabRestored(tab2), subframeLoaded]); 94 95 // Flush to make sure chrome received all data. 96 await TabStateFlusher.flush(browser2); 97 98 ({ storage } = JSON.parse(ss.getTabState(tab2))); 99 is( 100 storage[INNER_ORIGIN].test, 101 "modified2", 102 "sessionStorage data for example.com has been duplicated correctly" 103 ); 104 is( 105 storage[OUTER_ORIGIN].test, 106 "modified", 107 "sessionStorage data for mochi.test has been duplicated correctly" 108 ); 109 110 // Ensure that the content script retains restored data 111 // (by e.g. duplicateTab) and sends it along with new data. 112 await modifySessionStorage(browser2, { test: "modified3" }); 113 await TabStateFlusher.flush(browser2); 114 115 ({ storage } = JSON.parse(ss.getTabState(tab2))); 116 is( 117 storage[INNER_ORIGIN].test, 118 "modified2", 119 "sessionStorage data for example.com has been duplicated correctly" 120 ); 121 is( 122 storage[OUTER_ORIGIN].test, 123 "modified3", 124 "sessionStorage data for mochi.test has been duplicated correctly" 125 ); 126 127 // Check that loading a new URL discards data. 128 BrowserTestUtils.startLoadingURIString(browser2, "http://mochi.test:8888/"); 129 await promiseBrowserLoaded(browser2); 130 await TabStateFlusher.flush(browser2); 131 132 ({ storage } = JSON.parse(ss.getTabState(tab2))); 133 is( 134 storage[OUTER_ORIGIN].test, 135 "modified3", 136 "navigating retains correct storage data" 137 ); 138 139 is( 140 storage[INNER_ORIGIN].test, 141 "modified2", 142 "sessionStorage data for example.com wasn't discarded after top-level same-site navigation" 143 ); 144 145 // Test that clearing the data in the first tab works properly within 146 // the subframe 147 await modifySessionStorage(browser, {}, { frameIndex: 0 }); 148 await TabStateFlusher.flush(browser); 149 ({ storage } = JSON.parse(ss.getTabState(tab))); 150 is( 151 storage[INNER_ORIGIN], 152 undefined, 153 "sessionStorage data for example.com has been cleared correctly" 154 ); 155 156 // Test that clearing the data in the first tab works properly within 157 // the top-level frame 158 await modifySessionStorage(browser, {}); 159 await TabStateFlusher.flush(browser); 160 ({ storage } = JSON.parse(ss.getTabState(tab))); 161 ok( 162 storage === null || storage === undefined, 163 "sessionStorage data for the entire tab has been cleared correctly" 164 ); 165 166 // Clean up. 167 BrowserTestUtils.removeTab(tab); 168 BrowserTestUtils.removeTab(tab2); 169 }); 170 171 /** 172 * This test ensures that purging domain data also purges data from the 173 * sessionStorage data collected for tabs. 174 */ 175 add_task(async function purge_domain() { 176 let tab = BrowserTestUtils.addTab(gBrowser, URL); 177 let browser = tab.linkedBrowser; 178 await promiseBrowserLoaded(browser); 179 180 // Purge data for "mochi.test". 181 await purgeDomainData(browser, "mochi.test"); 182 183 // Flush to make sure chrome received all data. 184 await TabStateFlusher.flush(browser); 185 186 let { storage } = JSON.parse(ss.getTabState(tab)); 187 ok( 188 !storage[OUTER_ORIGIN], 189 "sessionStorage data for mochi.test has been purged" 190 ); 191 is( 192 storage[INNER_ORIGIN].test, 193 INNER_VALUE, 194 "sessionStorage data for example.com has been preserved" 195 ); 196 197 BrowserTestUtils.removeTab(tab); 198 }); 199 200 /** 201 * This test ensures that collecting sessionStorage data respects the privacy 202 * levels as set by the user. 203 */ 204 add_task(async function respect_privacy_level() { 205 let tab = BrowserTestUtils.addTab(gBrowser, URL + "&secure"); 206 await promiseBrowserLoaded(tab.linkedBrowser); 207 await TabStateFlusher.flush(tab.linkedBrowser); 208 await promiseRemoveTabAndSessionState(tab); 209 210 let [ 211 { 212 state: { storage }, 213 }, 214 ] = ss.getClosedTabDataForWindow(window); 215 is( 216 storage[OUTER_ORIGIN].test, 217 OUTER_VALUE, 218 "http sessionStorage data has been saved" 219 ); 220 is( 221 storage[SECURE_INNER_ORIGIN].test, 222 INNER_VALUE, 223 "https sessionStorage data has been saved" 224 ); 225 226 // Disable saving data for encrypted sites. 227 Services.prefs.setIntPref("browser.sessionstore.privacy_level", 1); 228 229 tab = BrowserTestUtils.addTab(gBrowser, URL + "&secure"); 230 await promiseBrowserLoaded(tab.linkedBrowser); 231 await TabStateFlusher.flush(tab.linkedBrowser); 232 await promiseRemoveTabAndSessionState(tab); 233 234 [ 235 { 236 state: { storage }, 237 }, 238 ] = ss.getClosedTabDataForWindow(window); 239 is( 240 storage[OUTER_ORIGIN].test, 241 OUTER_VALUE, 242 "http sessionStorage data has been saved" 243 ); 244 ok( 245 !storage[SECURE_INNER_ORIGIN], 246 "https sessionStorage data has *not* been saved" 247 ); 248 249 // Disable saving data for any site. 250 Services.prefs.setIntPref("browser.sessionstore.privacy_level", 2); 251 252 // Check that duplicating a tab copies all private data. 253 tab = BrowserTestUtils.addTab(gBrowser, URL + "&secure"); 254 await promiseBrowserLoaded(tab.linkedBrowser); 255 let tab2 = gBrowser.duplicateTab(tab); 256 // See bug 2001322, need to wait for iframe of tab2 to finish loading 257 const subframeLoaded = BrowserTestUtils.browserLoaded(tab2.linkedBrowser, { 258 includeSubFrames: true, 259 wantLoad: url => url.startsWith("https://example.com"), 260 }); 261 await Promise.all([promiseTabRestored(tab2), subframeLoaded]); 262 await promiseRemoveTabAndSessionState(tab); 263 264 // With privacy_level=2 the |tab| shouldn't have any sessionStorage data. 265 [ 266 { 267 state: { storage }, 268 }, 269 ] = ss.getClosedTabDataForWindow(window); 270 ok(!storage, "sessionStorage data has *not* been saved"); 271 272 // Remove all closed tabs before continuing with the next test. 273 // As Date.now() isn't monotonic we might sometimes check 274 // the wrong closedTabData entry. 275 forgetClosedTabs(window); 276 277 // Restore the default privacy level and close the duplicated tab. 278 Services.prefs.clearUserPref("browser.sessionstore.privacy_level"); 279 await promiseRemoveTabAndSessionState(tab2); 280 281 // With privacy_level=0 the duplicated |tab2| should persist all data. 282 [ 283 { 284 state: { storage }, 285 }, 286 ] = ss.getClosedTabDataForWindow(window); 287 is( 288 storage[OUTER_ORIGIN].test, 289 OUTER_VALUE, 290 "http sessionStorage data has been saved" 291 ); 292 is( 293 storage[SECURE_INNER_ORIGIN].test, 294 INNER_VALUE, 295 "https sessionStorage data has been saved" 296 ); 297 }); 298 299 function purgeDomainData(browser, domain) { 300 return new Promise(resolve => { 301 Services.clearData.deleteDataFromHost( 302 domain, 303 true, 304 Services.clearData.CLEAR_HISTORY, 305 resolve 306 ); 307 }); 308 }