tor-browser

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

browser_localStorage_fis.js (20528B)


      1 const HELPER_PAGE_URL =
      2  "https://example.com/browser/dom/tests/browser/page_localstorage.html";
      3 const HELPER_PAGE_COOP_COEP_URL =
      4  "https://example.com/browser/dom/tests/browser/page_localstorage_coop+coep.html";
      5 const HELPER_PAGE_ORIGIN = "https://example.com/";
      6 
      7 let testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
      8 Services.scriptloader.loadSubScript(testDir + "/helper_localStorage.js", this);
      9 
     10 /* import-globals-from helper_localStorage.js */
     11 
     12 // We spin up a ton of child processes.
     13 requestLongerTimeout(4);
     14 
     15 /**
     16 * Verify the basics of our multi-e10s localStorage support with fission.
     17 * We are focused on whitebox testing two things.
     18 * When this is being written, broadcast filtering is not in place, but the test
     19 * is intended to attempt to verify that its implementation does not break things.
     20 *
     21 * 1) That pages see the same localStorage state in a timely fashion when
     22 *    engaging in non-conflicting operations.  We are not testing races or
     23 *    conflict resolution; the spec does not cover that.
     24 *
     25 * 2) That there are no edge-cases related to when the Storage instance is
     26 *    created for the page or the StorageCache for the origin.  (StorageCache is
     27 *    what actually backs the Storage binding exposed to the page.)  This
     28 *    matters because the following reasons can exist for them to be created:
     29 *    - Preload, on the basis of knowing the origin uses localStorage.  The
     30 *      interesting edge case is when we have the same origin open in different
     31 *      processes and the origin starts using localStorage when it did not
     32 *      before.  Preload will not have instantiated bindings, which could impact
     33 *      correctness.
     34 *    - The page accessing localStorage for read or write purposes.  This is the
     35 *      obvious, boring one.
     36 *    - The page adding a "storage" listener.  This is less obvious and
     37 *      interacts with the preload edge-case mentioned above.  The page needs to
     38 *      hear "storage" events even if the page has not touched localStorage
     39 *      itself and its origin had nothing stored in localStorage when the page
     40 *      was created.
     41 *
     42 * According to current fission implementation, same origin pages will be loaded
     43 * by the same process, which process type is webIsolated=. And thanks to
     44 * Cross-Origin-Opener-Policy and Cross-Origin-Embedder-Policy headers support,
     45 * it is possible to load the same origin page by a special process, which type
     46 * is webCOOP+COEP=. These are the only two processes can be used to test
     47 * localStroage consistency between tabs in different tabs.
     48 *
     49 * We use the two child pages for testing, page_localstorage.html and
     50 * page_localstorage_coop+coep.html. Their content are the same, but
     51 * page_localstorage_coop+coep.html will be loaded with its ^headers^ file.
     52 * These pages provide followings
     53 * - can be instructed to listen for and record "storage" events
     54 * - can be instructed to issue a series of localStorage writes
     55 * - can be instructed to return the current entire localStorage contents
     56 *
     57 * To test localStorage consistency, four subtests are used.
     58 * Test case 1: one writer tab and one reader tab
     59 *   The writer tab issues a series of write operations, then verify the
     60 *   localStorage contents from the reader tab.
     61 *
     62 * Test case 2: one writer tab and one listener tab
     63 *   The writer tab issues a series of write operations, then verify the recorded
     64 *   storage events from the listener tab.
     65 *
     66 * Test case 3: one writeThenRead tab and one readThenWrite tab
     67 *   The writeThenRead first issues a series write of operations, and then verify
     68 *   the recorded storage events and localStorage contents from readThenWrite
     69 *   tab. After that readThenWrite tab issues a series of write operations, then
     70 *   verify the results from writeThenRead tab.
     71 *
     72 * Test case 4: one writer tab and one lateOpenSeesPreload tab
     73 *   The writer tab issues a series write of operations. Then open the
     74 *   lateOpenSeesPreload tab to make sure preloads exists.
     75 */
     76 
     77 /**
     78 * Shared constants for test cases
     79 */
     80 const noSentinelCheck = null;
     81 const initialSentinel = "initial";
     82 const initialWriteMutations = [
     83  // [key (null=clear), newValue (null=delete), oldValue (verification)]
     84  ["getsCleared", "1", null],
     85  ["alsoGetsCleared", "2", null],
     86  [null, null, null],
     87  ["stays", "3", null],
     88  ["clobbered", "pre", null],
     89  ["getsDeletedLater", "4", null],
     90  ["getsDeletedImmediately", "5", null],
     91  ["getsDeletedImmediately", null, "5"],
     92  ["alsoStays", "6", null],
     93  ["getsDeletedLater", null, "4"],
     94  ["clobbered", "post", "pre"],
     95 ];
     96 const initialWriteState = {
     97  stays: "3",
     98  clobbered: "post",
     99  alsoStays: "6",
    100 };
    101 
    102 const lastWriteSentinel = "lastWrite";
    103 const lastWriteMutations = [
    104  ["lastStays", "20", null],
    105  ["lastDeleted", "21", null],
    106  ["lastClobbered", "lastPre", null],
    107  ["lastClobbered", "lastPost", "lastPre"],
    108  ["lastDeleted", null, "21"],
    109 ];
    110 const lastWriteState = Object.assign({}, initialWriteState, {
    111  lastStays: "20",
    112  lastClobbered: "lastPost",
    113 });
    114 
    115 /**
    116 * Test case 1: one writer tab and one reader tab
    117 * Test steps
    118 *   1. Clear origin storage to make sure no data and preloads.
    119 *   2. Open the writer and reader tabs and verify preloads do not exist.
    120 *      Open writer tab in webIsolated= process
    121 *      Open reader tab in webCOOP+COEP= process
    122 *   3. Issue a series write operations in the writer tab, and then verify the
    123 *      storage state on the tab.
    124 *   4. Verify the storage state on the reader tab.
    125 *   5. Issue another series write operations in the writer tab, and then verify
    126 *      the storage state on the tab.
    127 *   6. Verify the storage state on the reader tab.
    128 *   7. Close tabs and clear origin storage.
    129 */
    130 add_task(async function () {
    131  if (!Services.domStorageManager.nextGenLocalStorageEnabled) {
    132    ok(true, "Test ignored when the next gen local storage is not enabled.");
    133    return;
    134  }
    135 
    136  await SpecialPowers.pushPrefEnv({
    137    set: [
    138      // Stop the preallocated process manager from speculatively creating
    139      // processes.  Our test explicitly asserts on whether preload happened or
    140      // not for each tab's process.  This information is loaded and latched by
    141      // the StorageDBParent constructor which the child process's
    142      // LocalStorageManager() constructor causes to be created via a call to
    143      // LocalStorageCache::StartDatabase().  Although the service is lazily
    144      // created and should not have been created prior to our opening the tab,
    145      // it's safest to ensure the process simply didn't exist before we ask for
    146      // it.
    147      //
    148      // This is done in conjunction with our use of forceNewProcess when
    149      // opening tabs.  There would be no point if we weren't also requesting a
    150      // new process.
    151      ["dom.ipc.processPrelaunch.enabled", false],
    152      // Enable LocalStorage's testing API so we can explicitly trigger a flush
    153      // when needed.
    154      ["dom.storage.testing", true],
    155    ],
    156  });
    157 
    158  // Ensure that there is no localstorage data or potential false positives for
    159  // localstorage preloads by forcing the origin to be cleared prior to the
    160  // start of our test.
    161  await clearOriginStorageEnsuringNoPreload(HELPER_PAGE_ORIGIN);
    162 
    163  // Make sure mOriginsHavingData gets updated.
    164  await triggerAndWaitForLocalStorageFlush();
    165 
    166  // - Open tabs.  Don't configure any of them yet.
    167  const knownTabs = new KnownTabs();
    168  const writerTab = await openTestTab(
    169    HELPER_PAGE_URL,
    170    "writer",
    171    knownTabs,
    172    true
    173  );
    174  const readerTab = await openTestTab(
    175    HELPER_PAGE_COOP_COEP_URL,
    176    "reader",
    177    knownTabs,
    178    true
    179  );
    180  // Sanity check that preloading did not occur in the tabs.
    181  await verifyTabPreload(writerTab, false, HELPER_PAGE_ORIGIN);
    182  await verifyTabPreload(readerTab, false, HELPER_PAGE_ORIGIN);
    183 
    184  // - Issue the initial batch of writes and verify.
    185  info("initial writes");
    186  await mutateTabStorage(writerTab, initialWriteMutations, initialSentinel);
    187 
    188  // We expect the writer tab to have the correct state because it just did the
    189  // writes.  We do not perform a sentinel-check because the writes should be
    190  // locally available and consistent.
    191  await verifyTabStorageState(writerTab, initialWriteState, noSentinelCheck);
    192  // We expect the reader tab to retrieve the current localStorage state from
    193  // the database.
    194  await verifyTabStorageState(readerTab, initialWriteState, initialSentinel);
    195 
    196  // - Issue last set of writes from writerTab.
    197  info("last set of writes");
    198  await mutateTabStorage(writerTab, lastWriteMutations, lastWriteSentinel);
    199 
    200  // The writer performed the writes, no need to wait for the sentinel.
    201  await verifyTabStorageState(writerTab, lastWriteState, noSentinelCheck);
    202  // We need to wait for the sentinel to show up for the reader.
    203  await verifyTabStorageState(readerTab, lastWriteState, lastWriteSentinel);
    204 
    205  // - Clean up.
    206  await cleanupTabs(knownTabs);
    207 
    208  clearOriginStorageEnsuringNoPreload(HELPER_PAGE_ORIGIN);
    209 });
    210 
    211 /**
    212 * Test case 2: one writer tab and one linsener tab
    213 * Test steps
    214 *   1. Clear origin storage to make sure no data and preloads.
    215 *   2. Open the writer and listener tabs and verify preloads do not exist.
    216 *      Open writer tab in webIsolated= process
    217 *      Open listener tab in webCOOP+COEP= process
    218 *   3. Ask the listener tab to listen and record storage events.
    219 *   4. Issue a series write operations in the writer tab, and then verify the
    220 *      storage state on the tab.
    221 *   5. Verify the storage events record from the listener tab is as expected.
    222 *   6. Verify the storage state on the listener tab.
    223 *   7. Ask the listener tab to listen and record storage events.
    224 *   8. Issue another series write operations in the writer tab, and then verify
    225 *      the storage state on the tab.
    226 *   9. Verify the storage events record from the listener tab is as expected.
    227 *   10. Verify the storage state on the listener tab.
    228 *   11. Close tabs and clear origin storage.
    229 */
    230 add_task(async function () {
    231  if (!Services.domStorageManager.nextGenLocalStorageEnabled) {
    232    ok(true, "Test ignored when the next gen local storage is not enabled.");
    233    return;
    234  }
    235 
    236  await SpecialPowers.pushPrefEnv({
    237    set: [
    238      ["dom.ipc.processPrelaunch.enabled", false],
    239      ["dom.storage.testing", true],
    240    ],
    241  });
    242 
    243  // Ensure that there is no localstorage data or potential false positives for
    244  // localstorage preloads by forcing the origin to be cleared prior to the
    245  // start of our test.
    246  await clearOriginStorageEnsuringNoPreload(HELPER_PAGE_ORIGIN);
    247 
    248  // Make sure mOriginsHavingData gets updated.
    249  await triggerAndWaitForLocalStorageFlush();
    250 
    251  // - Open tabs.  Don't configure any of them yet.
    252  const knownTabs = new KnownTabs();
    253  const writerTab = await openTestTab(
    254    HELPER_PAGE_URL,
    255    "writer",
    256    knownTabs,
    257    true
    258  );
    259  const listenerTab = await openTestTab(
    260    HELPER_PAGE_COOP_COEP_URL,
    261    "listener",
    262    knownTabs,
    263    true
    264  );
    265  // Sanity check that preloading did not occur in the tabs.
    266  await verifyTabPreload(writerTab, false, HELPER_PAGE_ORIGIN);
    267  await verifyTabPreload(listenerTab, false, HELPER_PAGE_ORIGIN);
    268 
    269  // - Ask the listener tab to listen and record the storage events..
    270  await recordTabStorageEvents(listenerTab, initialSentinel);
    271 
    272  // - Issue the initial batch of writes and verify.
    273  info("initial writes");
    274  await mutateTabStorage(writerTab, initialWriteMutations, initialSentinel);
    275 
    276  // We expect the writer tab to have the correct state because it just did the
    277  // writes.  We do not perform a sentinel-check because the writes should be
    278  // locally available and consistent.
    279  await verifyTabStorageState(writerTab, initialWriteState, noSentinelCheck);
    280  // We expect the listener tab to have heard all events despite preload not
    281  // having occurred and despite not issuing any reads or writes itself.  We
    282  // intentionally check the events before the state because we're most
    283  // interested in adding the listener having had a side-effect of subscribing
    284  // to changes for the process.
    285  //
    286  // We ensure it had a chance to hear all of the events because we told
    287  // recordTabStorageEvents to listen for the given sentinel.  The state check
    288  // then does not need to do a sentinel check.
    289  await verifyTabStorageEvents(
    290    listenerTab,
    291    initialWriteMutations,
    292    initialSentinel
    293  );
    294  await verifyTabStorageState(listenerTab, initialWriteState, noSentinelCheck);
    295 
    296  // - Ask the listener tab to listen and record the storage events.
    297  await recordTabStorageEvents(listenerTab, lastWriteSentinel);
    298 
    299  // - Issue last set of writes from writerTab.
    300  info("last set of writes");
    301  await mutateTabStorage(writerTab, lastWriteMutations, lastWriteSentinel);
    302 
    303  // The writer performed the writes, no need to wait for the sentinel.
    304  await verifyTabStorageState(writerTab, lastWriteState, noSentinelCheck);
    305  // Wait for the sentinel event to be received, then check.
    306  await verifyTabStorageEvents(
    307    listenerTab,
    308    lastWriteMutations,
    309    lastWriteSentinel
    310  );
    311  await verifyTabStorageState(listenerTab, lastWriteState, noSentinelCheck);
    312 
    313  // - Clean up.
    314  await cleanupTabs(knownTabs);
    315 
    316  clearOriginStorageEnsuringNoPreload(HELPER_PAGE_ORIGIN);
    317 });
    318 
    319 /**
    320 * Test case 3: one writeThenRead tab and one readThenWrite tab
    321 * Test steps
    322 *   1. Clear origin storage to make sure no data and preloads.
    323 *   2. Open the writeThenRead and readThenWrite tabs and verify preloads do not
    324 *      exist.
    325 *      Open writeThenRead tab in webIsolated= process
    326 *      Open readThenWrite tab in webCOOP+COEP= process
    327 *   3. Ask the readThenWrite tab to listen and record storage events.
    328 *   4. Issue a series write operations in the writeThenRead tab, and then verify
    329 *      the storage state on the tab.
    330 *   5. Verify the storage events record from the readThenWrite tab is as
    331 *      expected.
    332 *   6. Verify the storage state on the readThenWrite tab.
    333 *   7. Ask the writeThenRead tab to listen and record storage events.
    334 *   8. Issue another series write operations in the readThenWrite tab, and then
    335 *      verify the storage state on the tab.
    336 *   9. Verify the storage events record from the writeThenRead tab is as
    337 *      expected.
    338 *   10. Verify the storage state on the writeThenRead tab.
    339 *   11. Close tabs and clear origin storage.
    340 */
    341 add_task(async function () {
    342  if (!Services.domStorageManager.nextGenLocalStorageEnabled) {
    343    ok(true, "Test ignored when the next gen local storage is not enabled.");
    344    return;
    345  }
    346 
    347  await SpecialPowers.pushPrefEnv({
    348    set: [
    349      ["dom.ipc.processPrelaunch.enabled", false],
    350      ["dom.storage.testing", true],
    351    ],
    352  });
    353 
    354  // Ensure that there is no localstorage data or potential false positives for
    355  // localstorage preloads by forcing the origin to be cleared prior to the
    356  // start of our test.
    357  await clearOriginStorageEnsuringNoPreload(HELPER_PAGE_ORIGIN);
    358 
    359  // Make sure mOriginsHavingData gets updated.
    360  await triggerAndWaitForLocalStorageFlush();
    361 
    362  // - Open tabs.  Don't configure any of them yet.
    363  const knownTabs = new KnownTabs();
    364  const writeThenReadTab = await openTestTab(
    365    HELPER_PAGE_URL,
    366    "writerthenread",
    367    knownTabs,
    368    true
    369  );
    370  const readThenWriteTab = await openTestTab(
    371    HELPER_PAGE_COOP_COEP_URL,
    372    "readthenwrite",
    373    knownTabs,
    374    true
    375  );
    376  // Sanity check that preloading did not occur in the tabs.
    377  await verifyTabPreload(writeThenReadTab, false, HELPER_PAGE_ORIGIN);
    378  await verifyTabPreload(readThenWriteTab, false, HELPER_PAGE_ORIGIN);
    379 
    380  // - Ask readThenWrite tab to listen and record storageEvents.
    381  await recordTabStorageEvents(readThenWriteTab, initialSentinel);
    382 
    383  // - Issue the initial batch of writes and verify.
    384  info("initial writes");
    385  await mutateTabStorage(
    386    writeThenReadTab,
    387    initialWriteMutations,
    388    initialSentinel
    389  );
    390 
    391  // We expect the writer tab to have the correct state because it just did the
    392  // writes.  We do not perform a sentinel-check because the writes should be
    393  // locally available and consistent.
    394  await verifyTabStorageState(
    395    writeThenReadTab,
    396    initialWriteState,
    397    noSentinelCheck
    398  );
    399 
    400  // We expect the listener tab to have heard all events despite preload not
    401  // having occurred and despite not issuing any reads or writes itself.  We
    402  // intentionally check the events before the state because we're most
    403  // interested in adding the listener having had a side-effect of subscribing
    404  // to changes for the process.
    405  //
    406  // We ensure it had a chance to hear all of the events because we told
    407  // recordTabStorageEvents to listen for the given sentinel.  The state check
    408  // then does not need to do a sentinel check.
    409  await verifyTabStorageEvents(
    410    readThenWriteTab,
    411    initialWriteMutations,
    412    initialSentinel
    413  );
    414  await verifyTabStorageState(
    415    readThenWriteTab,
    416    initialWriteState,
    417    noSentinelCheck
    418  );
    419 
    420  // - Issue last set of writes from writerTab.
    421  info("last set of writes");
    422  await recordTabStorageEvents(writeThenReadTab, lastWriteSentinel);
    423 
    424  await mutateTabStorage(
    425    readThenWriteTab,
    426    lastWriteMutations,
    427    lastWriteSentinel
    428  );
    429 
    430  // The writer performed the writes, no need to wait for the sentinel.
    431  await verifyTabStorageState(
    432    readThenWriteTab,
    433    lastWriteState,
    434    noSentinelCheck
    435  );
    436  // Wait for the sentinel event to be received, then check.
    437  await verifyTabStorageEvents(
    438    writeThenReadTab,
    439    lastWriteMutations,
    440    lastWriteSentinel
    441  );
    442  await verifyTabStorageState(
    443    writeThenReadTab,
    444    lastWriteState,
    445    noSentinelCheck
    446  );
    447 
    448  // - Clean up.
    449  await cleanupTabs(knownTabs);
    450 
    451  clearOriginStorageEnsuringNoPreload(HELPER_PAGE_ORIGIN);
    452 });
    453 
    454 /**
    455 * Test case 4: one writerRead tab and one lateOpenSeesPreload tab
    456 * Test steps
    457 *   1. Clear origin storage to make sure no data and preloads.
    458 *   2. Open the writer tab and verify preloads do not exist.
    459 *      Open writer tab in webIsolated= process
    460 *   3. Issue a series write operations in the writer tab, and then verify the
    461 *      storage state on the tab.
    462 *   4. Issue another series write operations in the writer tab, and then verify
    463 *      the storage state on the tab.
    464 *   5. Open lateOpenSeesPreload tab in webCOOP+COEP process
    465 *   6. Verify the preloads on the lateOpenSeesPreload tab
    466 *   7. Close tabs and clear origin storage.
    467 */
    468 add_task(async function () {
    469  await SpecialPowers.pushPrefEnv({
    470    set: [
    471      ["dom.ipc.processPrelaunch.enabled", false],
    472      ["dom.storage.testing", true],
    473    ],
    474  });
    475 
    476  // Ensure that there is no localstorage data or potential false positives for
    477  // localstorage preloads by forcing the origin to be cleared prior to the
    478  // start of our test.
    479  await clearOriginStorageEnsuringNoPreload(HELPER_PAGE_ORIGIN);
    480 
    481  // Make sure mOriginsHavingData gets updated.
    482  await triggerAndWaitForLocalStorageFlush();
    483 
    484  // - Open tabs.  Don't configure any of them yet.
    485  const knownTabs = new KnownTabs();
    486  const writerTab = await openTestTab(
    487    HELPER_PAGE_URL,
    488    "writer",
    489    knownTabs,
    490    true
    491  );
    492  // Sanity check that preloading did not occur in the tabs.
    493  await verifyTabPreload(writerTab, false, HELPER_PAGE_ORIGIN);
    494 
    495  // - Configure the tabs.
    496 
    497  // - Issue the initial batch of writes and verify.
    498  info("initial writes");
    499  await mutateTabStorage(writerTab, initialWriteMutations, initialSentinel);
    500 
    501  // We expect the writer tab to have the correct state because it just did the
    502  // writes.  We do not perform a sentinel-check because the writes should be
    503  // locally available and consistent.
    504  await verifyTabStorageState(writerTab, initialWriteState, noSentinelCheck);
    505 
    506  // - Force a LocalStorage DB flush so mOriginsHavingData is updated.
    507  // mOriginsHavingData is only updated when the storage thread runs its
    508  // accumulated operations during the flush.  If we don't initiate and ensure
    509  // that a flush has occurred before moving on to the next step,
    510  // mOriginsHavingData may not include our origin when it's sent down to the
    511  // child process.
    512  info("flush to make preload check work");
    513  await triggerAndWaitForLocalStorageFlush();
    514 
    515  // - Open a fresh tab and make sure it sees the precache/preload
    516  info("late open preload check");
    517  const lateOpenSeesPreload = await openTestTab(
    518    HELPER_PAGE_COOP_COEP_URL,
    519    "lateOpenSeesPreload",
    520    knownTabs,
    521    true
    522  );
    523  await verifyTabPreload(lateOpenSeesPreload, true, HELPER_PAGE_ORIGIN);
    524 
    525  // - Clean up.
    526  await cleanupTabs(knownTabs);
    527 
    528  clearOriginStorageEnsuringNoPreload(HELPER_PAGE_ORIGIN);
    529 });