tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }