summaryrefslogtreecommitdiffstats
path: root/dom/serviceworkers/test/utils.js
blob: 28be239593e52d02380e81c79c118152918ecd30 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
function waitForState(worker, state, context) {
  return new Promise(resolve => {
    function onStateChange() {
      if (worker.state === state) {
        worker.removeEventListener("statechange", onStateChange);
        resolve(context);
      }
    }

    // First add an event listener, so we won't miss any change that happens
    // before we check the current state.
    worker.addEventListener("statechange", onStateChange);

    // Now check if the worker is already in the desired state.
    onStateChange();
  });
}

/**
 * Helper for browser tests to issue register calls from the content global and
 * wait for the SW to progress to the active state, as most tests desire.
 * From the ContentTask.spawn, use via
 * `content.wrappedJSObject.registerAndWaitForActive`.
 */
async function registerAndWaitForActive(script, maybeScope) {
  console.log("...calling register");
  let opts = undefined;
  if (maybeScope) {
    opts = { scope: maybeScope };
  }
  const reg = await navigator.serviceWorker.register(script, opts);
  // Unless registration resurrection happens, the SW should be in the
  // installing slot.
  console.log("...waiting for activation");
  await waitForState(reg.installing, "activated", reg);
  console.log("...activated!");
  return reg;
}

/**
 * Helper to create an iframe with the given URL and return the first
 * postMessage payload received.  This is intended to be used when creating
 * cross-origin iframes.
 *
 * A promise will be returned that resolves with the payload of the postMessage
 * call.
 */
function createIframeAndWaitForMessage(url) {
  const iframe = document.createElement("iframe");
  document.body.appendChild(iframe);
  return new Promise(resolve => {
    window.addEventListener(
      "message",
      event => {
        resolve(event.data);
      },
      { once: true }
    );
    iframe.src = url;
  });
}

/**
 * Helper to create a nested iframe into the iframe created by
 * createIframeAndWaitForMessage().
 *
 * A promise will be returned that resolves with the payload of the postMessage
 * call.
 */
function createNestedIframeAndWaitForMessage(url) {
  const iframe = document.getElementsByTagName("iframe")[0];
  iframe.contentWindow.postMessage("create nested iframe", "*");
  return new Promise(resolve => {
    window.addEventListener(
      "message",
      event => {
        resolve(event.data);
      },
      { once: true }
    );
  });
}

async function unregisterAll() {
  const registrations = await navigator.serviceWorker.getRegistrations();
  for (const reg of registrations) {
    await reg.unregister();
  }
}

/**
 * Make a blob that contains random data and therefore shouldn't compress all
 * that well.
 */
function makeRandomBlob(size) {
  const arr = new Uint8Array(size);
  let offset = 0;
  /**
   * getRandomValues will only provide a maximum of 64k of data at a time and
   * will error if we ask for more, so using a while loop for get a random value
   * which much larger than 64k.
   * https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues#exceptions
   */
  while (offset < size) {
    const nextSize = Math.min(size - offset, 65536);
    window.crypto.getRandomValues(new Uint8Array(arr.buffer, offset, nextSize));
    offset += nextSize;
  }
  return new Blob([arr], { type: "application/octet-stream" });
}

async function fillStorage(cacheBytes, idbBytes) {
  // ## Fill Cache API Storage
  const cache = await caches.open("filler");
  await cache.put("fill", new Response(makeRandomBlob(cacheBytes)));

  // ## Fill IDB
  const storeName = "filler";
  let db = await new Promise((resolve, reject) => {
    let openReq = indexedDB.open("filler", 1);
    openReq.onerror = event => {
      reject(event.target.error);
    };
    openReq.onsuccess = event => {
      resolve(event.target.result);
    };
    openReq.onupgradeneeded = event => {
      const useDB = event.target.result;
      useDB.onerror = error => {
        reject(error);
      };
      const store = useDB.createObjectStore(storeName);
      store.put({ blob: makeRandomBlob(idbBytes) }, "filler-blob");
    };
  });
}