tor-browser

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

browser_localStorage_e10s.js (11460B)


      1 const HELPER_PAGE_URL =
      2  "http://example.com/browser/dom/tests/browser/page_localstorage.html";
      3 const HELPER_PAGE_ORIGIN = "http://example.com/";
      4 
      5 let testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
      6 Services.scriptloader.loadSubScript(testDir + "/helper_localStorage.js", this);
      7 
      8 /* import-globals-from helper_localStorage.js */
      9 
     10 // We spin up a ton of child processes.
     11 requestLongerTimeout(4);
     12 
     13 /**
     14 * Verify the basics of our multi-e10s localStorage support.  We are focused on
     15 * whitebox testing two things.  When this is being written, broadcast filtering
     16 * is not in place, but the test is intended to attempt to verify that its
     17 * implementation does not break things.
     18 *
     19 * 1) That pages see the same localStorage state in a timely fashion when
     20 *    engaging in non-conflicting operations.  We are not testing races or
     21 *    conflict resolution; the spec does not cover that.
     22 *
     23 * 2) That there are no edge-cases related to when the Storage instance is
     24 *    created for the page or the StorageCache for the origin.  (StorageCache is
     25 *    what actually backs the Storage binding exposed to the page.)  This
     26 *    matters because the following reasons can exist for them to be created:
     27 *    - Preload, on the basis of knowing the origin uses localStorage.  The
     28 *      interesting edge case is when we have the same origin open in different
     29 *      processes and the origin starts using localStorage when it did not
     30 *      before.  Preload will not have instantiated bindings, which could impact
     31 *      correctness.
     32 *    - The page accessing localStorage for read or write purposes.  This is the
     33 *      obvious, boring one.
     34 *    - The page adding a "storage" listener.  This is less obvious and
     35 *      interacts with the preload edge-case mentioned above.  The page needs to
     36 *      hear "storage" events even if the page has not touched localStorage
     37 *      itself and its origin had nothing stored in localStorage when the page
     38 *      was created.
     39 *
     40 * We use the same simple child page in all tabs that:
     41 * - can be instructed to listen for and record "storage" events
     42 * - can be instructed to issue a series of localStorage writes
     43 * - can be instructed to return the current entire localStorage contents
     44 *
     45 * We open the 5 following tabs:
     46 * - Open a "writer" tab that does not listen for "storage" events and will
     47 *   issue only writes.
     48 * - Open a "listener" tab instructed to listen for "storage" events
     49 *   immediately.  We expect it to capture all events.
     50 * - Open an "reader" tab that does not listen for "storage" events and will
     51 *   only issue reads when instructed.
     52 * - Open a "lateWriteThenListen" tab that initially does nothing.  We will
     53 *   later tell it to issue a write and then listen for events to make sure it
     54 *   captures the later events.
     55 * - Open "lateOpenSeesPreload" tab after we've done everything and ensure that
     56 *   it preloads/precaches the data without us having touched localStorage or
     57 *   added an event listener.
     58 */
     59 add_task(async function () {
     60  await SpecialPowers.pushPrefEnv({
     61    set: [
     62      // Stop the preallocated process manager from speculatively creating
     63      // processes.  Our test explicitly asserts on whether preload happened or
     64      // not for each tab's process.  This information is loaded and latched by
     65      // the StorageDBParent constructor which the child process's
     66      // LocalStorageManager() constructor causes to be created via a call to
     67      // LocalStorageCache::StartDatabase().  Although the service is lazily
     68      // created and should not have been created prior to our opening the tab,
     69      // it's safest to ensure the process simply didn't exist before we ask for
     70      // it.
     71      //
     72      // This is done in conjunction with our use of forceNewProcess when
     73      // opening tabs.  There would be no point if we weren't also requesting a
     74      // new process.
     75      ["dom.ipc.processPrelaunch.enabled", false],
     76      // Enable LocalStorage's testing API so we can explicitly trigger a flush
     77      // when needed.
     78      ["dom.storage.testing", true],
     79    ],
     80  });
     81 
     82  // Ensure that there is no localstorage data or potential false positives for
     83  // localstorage preloads by forcing the origin to be cleared prior to the
     84  // start of our test.
     85  await clearOriginStorageEnsuringNoPreload(HELPER_PAGE_ORIGIN);
     86 
     87  // Make sure mOriginsHavingData gets updated.
     88  await triggerAndWaitForLocalStorageFlush();
     89 
     90  // - Open tabs.  Don't configure any of them yet.
     91  const knownTabs = new KnownTabs();
     92  const writerTab = await openTestTab(
     93    HELPER_PAGE_URL,
     94    "writer",
     95    knownTabs,
     96    true
     97  );
     98  const listenerTab = await openTestTab(
     99    HELPER_PAGE_URL,
    100    "listener",
    101    knownTabs,
    102    true
    103  );
    104  const readerTab = await openTestTab(
    105    HELPER_PAGE_URL,
    106    "reader",
    107    knownTabs,
    108    true
    109  );
    110  const lateWriteThenListenTab = await openTestTab(
    111    HELPER_PAGE_URL,
    112    "lateWriteThenListen",
    113    knownTabs,
    114    true
    115  );
    116 
    117  // Sanity check that preloading did not occur in the tabs.
    118  await verifyTabPreload(writerTab, false, HELPER_PAGE_ORIGIN);
    119  await verifyTabPreload(listenerTab, false, HELPER_PAGE_ORIGIN);
    120  await verifyTabPreload(readerTab, false, HELPER_PAGE_ORIGIN);
    121 
    122  // - Configure the tabs.
    123  const initialSentinel = "initial";
    124  const noSentinelCheck = null;
    125  await recordTabStorageEvents(listenerTab, initialSentinel);
    126 
    127  // - Issue the initial batch of writes and verify.
    128  info("initial writes");
    129  const initialWriteMutations = [
    130    // [key (null=clear), newValue (null=delete), oldValue (verification)]
    131    ["getsCleared", "1", null],
    132    ["alsoGetsCleared", "2", null],
    133    [null, null, null],
    134    ["stays", "3", null],
    135    ["clobbered", "pre", null],
    136    ["getsDeletedLater", "4", null],
    137    ["getsDeletedImmediately", "5", null],
    138    ["getsDeletedImmediately", null, "5"],
    139    ["alsoStays", "6", null],
    140    ["getsDeletedLater", null, "4"],
    141    ["clobbered", "post", "pre"],
    142  ];
    143  const initialWriteState = {
    144    stays: "3",
    145    clobbered: "post",
    146    alsoStays: "6",
    147  };
    148 
    149  await mutateTabStorage(writerTab, initialWriteMutations, initialSentinel);
    150 
    151  // We expect the writer tab to have the correct state because it just did the
    152  // writes.  We do not perform a sentinel-check because the writes should be
    153  // locally available and consistent.
    154  await verifyTabStorageState(writerTab, initialWriteState, noSentinelCheck);
    155  // We expect the listener tab to have heard all events despite preload not
    156  // having occurred and despite not issuing any reads or writes itself.  We
    157  // intentionally check the events before the state because we're most
    158  // interested in adding the listener having had a side-effect of subscribing
    159  // to changes for the process.
    160  //
    161  // We ensure it had a chance to hear all of the events because we told
    162  // recordTabStorageEvents to listen for the given sentinel.  The state check
    163  // then does not need to do a sentinel check.
    164  await verifyTabStorageEvents(
    165    listenerTab,
    166    initialWriteMutations,
    167    initialSentinel
    168  );
    169  await verifyTabStorageState(listenerTab, initialWriteState, noSentinelCheck);
    170  // We expect the reader tab to retrieve the current localStorage state from
    171  // the database.  Because of the above checks, we are confident that the
    172  // writes have hit PBackground and therefore that the (synchronous) state
    173  // retrieval contains all the data we need.  No sentinel-check is required.
    174  await verifyTabStorageState(readerTab, initialWriteState, noSentinelCheck);
    175 
    176  // - Issue second set of writes from lateWriteThenListen
    177  // This tests that our new tab that begins by issuing only writes is building
    178  // on top of the existing state (although we don't verify that until after the
    179  // next set of mutations).  We also verify that the initial "writerTab" that
    180  // was our first tab and started with only writes sees the writes, even though
    181  // it did not add an event listener.
    182 
    183  info("late writes");
    184  const lateWriteSentinel = "lateWrite";
    185  const lateWriteMutations = [
    186    ["lateStays", "10", null],
    187    ["lateClobbered", "latePre", null],
    188    ["lateDeleted", "11", null],
    189    ["lateClobbered", "lastPost", "latePre"],
    190    ["lateDeleted", null, "11"],
    191  ];
    192  const lateWriteState = Object.assign({}, initialWriteState, {
    193    lateStays: "10",
    194    lateClobbered: "lastPost",
    195  });
    196 
    197  await recordTabStorageEvents(listenerTab, lateWriteSentinel);
    198 
    199  await mutateTabStorage(
    200    lateWriteThenListenTab,
    201    lateWriteMutations,
    202    lateWriteSentinel
    203  );
    204 
    205  // Verify the writer tab saw the writes.  It has to wait for the sentinel to
    206  // appear before checking.
    207  await verifyTabStorageState(writerTab, lateWriteState, lateWriteSentinel);
    208  // Wait for the sentinel event before checking the events and then the state.
    209  await verifyTabStorageEvents(
    210    listenerTab,
    211    lateWriteMutations,
    212    lateWriteSentinel
    213  );
    214  await verifyTabStorageState(listenerTab, lateWriteState, noSentinelCheck);
    215  // We need to wait for the sentinel to show up for the reader.
    216  await verifyTabStorageState(readerTab, lateWriteState, lateWriteSentinel);
    217 
    218  // - Issue last set of writes from writerTab.
    219  info("last set of writes");
    220  const lastWriteSentinel = "lastWrite";
    221  const lastWriteMutations = [
    222    ["lastStays", "20", null],
    223    ["lastDeleted", "21", null],
    224    ["lastClobbered", "lastPre", null],
    225    ["lastClobbered", "lastPost", "lastPre"],
    226    ["lastDeleted", null, "21"],
    227  ];
    228  const lastWriteState = Object.assign({}, lateWriteState, {
    229    lastStays: "20",
    230    lastClobbered: "lastPost",
    231  });
    232 
    233  await recordTabStorageEvents(listenerTab, lastWriteSentinel);
    234  await recordTabStorageEvents(lateWriteThenListenTab, lastWriteSentinel);
    235 
    236  await mutateTabStorage(writerTab, lastWriteMutations, lastWriteSentinel);
    237 
    238  // The writer performed the writes, no need to wait for the sentinel.
    239  await verifyTabStorageState(writerTab, lastWriteState, noSentinelCheck);
    240  // Wait for the sentinel event to be received, then check.
    241  await verifyTabStorageEvents(
    242    listenerTab,
    243    lastWriteMutations,
    244    lastWriteSentinel
    245  );
    246  await verifyTabStorageState(listenerTab, lastWriteState, noSentinelCheck);
    247  // We need to wait for the sentinel to show up for the reader.
    248  await verifyTabStorageState(readerTab, lastWriteState, lastWriteSentinel);
    249  // Wait for the sentinel event to be received, then check.
    250  await verifyTabStorageEvents(
    251    lateWriteThenListenTab,
    252    lastWriteMutations,
    253    lastWriteSentinel
    254  );
    255  await verifyTabStorageState(
    256    lateWriteThenListenTab,
    257    lastWriteState,
    258    noSentinelCheck
    259  );
    260 
    261  // - Force a LocalStorage DB flush so mOriginsHavingData is updated.
    262  // mOriginsHavingData is only updated when the storage thread runs its
    263  // accumulated operations during the flush.  If we don't initiate and ensure
    264  // that a flush has occurred before moving on to the next step,
    265  // mOriginsHavingData may not include our origin when it's sent down to the
    266  // child process.
    267  info("flush to make preload check work");
    268  await triggerAndWaitForLocalStorageFlush();
    269 
    270  // - Open a fresh tab and make sure it sees the precache/preload
    271  info("late open preload check");
    272  const lateOpenSeesPreload = await openTestTab(
    273    HELPER_PAGE_URL,
    274    "lateOpenSeesPreload",
    275    knownTabs,
    276    true
    277  );
    278  await verifyTabPreload(lateOpenSeesPreload, true, HELPER_PAGE_ORIGIN);
    279 
    280  // - Clean up.
    281  await cleanupTabs(knownTabs);
    282 
    283  clearOriginStorageEnsuringNoPreload(HELPER_PAGE_ORIGIN);
    284 });