toolkit/components/antitracking/test/browser/localStorage.html
author Jan Varga <jan.varga@gmail.com>
Thu, 29 Nov 2018 21:47:45 +0100
changeset 505228 75c28b78f8ee02018ccaec9c2f4035d46182d10e
parent 503528 401763b97e54cd99c6a5ad2a617379da011a57e5
permissions -rw-r--r--
Bug 1286798 - Part 10: Support for storage events; r=asuth,janv Storage events are fired either directly after getting response from synchronous SetItem call or through observers. When a new onstorage event listener is added, we sycnhronously register an observer in the parent process. There's always only one observer actor per content process. PBackgroundLSDatabase is now managed by a new PBackgroundLSObject protocol. PBackgroundLSObject is needed to eliminate the need to pass the principal info and document URI everytime a write operation occurs. Preparation of an observer shares some states with preparation of a datastore, so common stuff now lives in LSRequestBase and preparation of a datastore now implements a nested state machine. This patch was enhanced by asuth to drop observers only when the last storage listener is removed. EventListenerRemoved is invoked on any removal, not just the final removal, so we need to make sure it's the final removal before dropping observer.

<h1>Here a tracker!</h1>
<script>

if (window.opener) {
  SpecialPowers.wrap(document).userInteractionForTesting();
  localStorage.foo = "opener" + Math.random();
  // Don't call window.close immediatelly. It can happen that adding the
  // "storage" event listener below takes more time than usual (it may need to
  // synchronously subscribe in the parent process to receive storage
  // notifications). Spending more time in the initial script can prevent
  // the "load" event from being fired for the window opened by "open and test".
  setTimeout(() => {
    window.close();
  }, 0);
}

if (parent) {
  window.onmessage = e => {
    if (e.data == "test") {
      let status;
      try {
        localStorage.foo = "value" + Math.random();
        status = true;
      } catch (e) {
        status = false;
      }

      parent.postMessage({type: "test", status }, "*");
      return;
    }

    if (e.data == "open") {
      window.open("localStorage.html");
      return;
    }

    if (e.data == "open and test") {
      let w = window.open("localStorage.html");
      w.addEventListener("load", _ => {
        let status;
        try {
          localStorage.foo = "value" + Math.random();
          status = true;
        } catch (e) {
          status = false;
        }

        parent.postMessage({type: "test", status }, "*");
      }, {once: true});
    }
  };

  window.addEventListener("storage", e => {
    let fromOpener = localStorage.foo.startsWith("opener");

    let status;
    try {
      localStorage.foo = "value" + Math.random();
      status = true;
    } catch (e) {
      status = false;
    }

    parent.postMessage({type: "test", status: status && fromOpener }, "*");
  });
}

</script>