| 
              (default settings) | brave 1.63 | chrome 122.0 | edge 122.0 | firefox 123.0 | librewolf 123.0-1 | mullvad 13.0 | opera 107.0 | safari 17.3 | tor 13.0 | ungoogled 122.0 | vivaldi 6.6 | 
|---|---|---|---|---|---|---|---|---|---|---|---|
| State Partitioning testsWhich browsers isolate websites to prevent them from sharing data to track you? A common vulnerability of web browsers is that they allow tracking companies to 'tag' your browser with some data ('state') that identifies you. When third-party trackers are embedded in websites, they can see this identifying data as you browse to different websites. Fortunately, it is possible for this category of leaks to be fixed by partitioning all data stored in the browser such that no data can be shared between websites.
         | |||||||||||
| Alt-Svc Alt-Svc allows the server to indicate to the web browser that a resource should be loaded on a different server. Because this is a persistent setting, it could be used to track users across websites if it is not correctly partitioned.
               | write: async () => {
      // Clear Alt-Svc caching first.
      let responseText = "";
      for (let i = 0; i < 3; ++i) {
        await fetch(altSvcOrigin + "/clear");
        await sleepMs(100);
      }
      responseText = await fetchText(altSvcOrigin + "/protocol");
      console.log("after clear:", responseText);
      // Store "h3" state in Alt-Svc cache
      for (let i = 0; i < 3; ++i) {
        await fetch(altSvcOrigin + "/set");
        await sleepMs(100);
      }
      responseText = await fetchText(altSvcOrigin + "/protocol");
      console.log("after set:", responseText);
    }
read: async () => {
      const protocol = await fetchText(altSvcOrigin + "/protocol");
      if ((new URL(location)).searchParams.get("thirdparty") === "same") {
        if (protocol !== "h3") {
          throw new Error("Unsupported");
        }
      }
      return protocol;
    }
result, same first party: h3, h3, h3
result, different first party: h2, h2, h2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async () => {
      // Clear Alt-Svc caching first.
      let responseText = "";
      for (let i = 0; i < 3; ++i) {
        await fetch(altSvcOrigin + "/clear");
        await sleepMs(100);
      }
      responseText = await fetchText(altSvcOrigin + "/protocol");
      console.log("after clear:", responseText);
      // Store "h3" state in Alt-Svc cache
      for (let i = 0; i < 3; ++i) {
        await fetch(altSvcOrigin + "/set");
        await sleepMs(100);
      }
      responseText = await fetchText(altSvcOrigin + "/protocol");
      console.log("after set:", responseText);
    }
read: async () => {
      const protocol = await fetchText(altSvcOrigin + "/protocol");
      if ((new URL(location)).searchParams.get("thirdparty") === "same") {
        if (protocol !== "h3") {
          throw new Error("Unsupported");
        }
      }
      return protocol;
    }
result, same first party: h3, h3
result, different first party: h2, h2
unsupported: false, false
passed: true, true
test failed: false, false | write: async () => {
      // Clear Alt-Svc caching first.
      let responseText = "";
      for (let i = 0; i < 3; ++i) {
        await fetch(altSvcOrigin + "/clear");
        await sleepMs(100);
      }
      responseText = await fetchText(altSvcOrigin + "/protocol");
      console.log("after clear:", responseText);
      // Store "h3" state in Alt-Svc cache
      for (let i = 0; i < 3; ++i) {
        await fetch(altSvcOrigin + "/set");
        await sleepMs(100);
      }
      responseText = await fetchText(altSvcOrigin + "/protocol");
      console.log("after set:", responseText);
    }
read: async () => {
      const protocol = await fetchText(altSvcOrigin + "/protocol");
      if ((new URL(location)).searchParams.get("thirdparty") === "same") {
        if (protocol !== "h3") {
          throw new Error("Unsupported");
        }
      }
      return protocol;
    }
result, same first party: h3, h3
result, different first party: h2, h2
unsupported: false, false
passed: true, true
test failed: false, false | write: async () => {
      // Clear Alt-Svc caching first.
      let responseText = "";
      for (let i = 0; i < 3; ++i) {
        await fetch(altSvcOrigin + "/clear");
        await sleepMs(100);
      }
      responseText = await fetchText(altSvcOrigin + "/protocol");
      console.log("after clear:", responseText);
      // Store "h3" state in Alt-Svc cache
      for (let i = 0; i < 3; ++i) {
        await fetch(altSvcOrigin + "/set");
        await sleepMs(100);
      }
      responseText = await fetchText(altSvcOrigin + "/protocol");
      console.log("after set:", responseText);
    }
read: async () => {
      const protocol = await fetchText(altSvcOrigin + "/protocol");
      if ((new URL(location)).searchParams.get("thirdparty") === "same") {
        if (protocol !== "h3") {
          throw new Error("Unsupported");
        }
      }
      return protocol;
    }
result, same first party: h3, h3
result, different first party: h2, h2
unsupported: false, false
passed: true, true
test failed: false, false | write: async () => {
      // Clear Alt-Svc caching first.
      let responseText = "";
      for (let i = 0; i < 3; ++i) {
        await fetch(altSvcOrigin + "/clear");
        await sleepMs(100);
      }
      responseText = await fetchText(altSvcOrigin + "/protocol");
      console.log("after clear:", responseText);
      // Store "h3" state in Alt-Svc cache
      for (let i = 0; i < 3; ++i) {
        await fetch(altSvcOrigin + "/set");
        await sleepMs(100);
      }
      responseText = await fetchText(altSvcOrigin + "/protocol");
      console.log("after set:", responseText);
    }
read: async () => {
      const protocol = await fetchText(altSvcOrigin + "/protocol");
      if ((new URL(location)).searchParams.get("thirdparty") === "same") {
        if (protocol !== "h3") {
          throw new Error("Unsupported");
        }
      }
      return protocol;
    }
result, same first party: h3, h3, h3
result, different first party: h2, h2, h2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async () => {
      // Clear Alt-Svc caching first.
      let responseText = "";
      for (let i = 0; i < 3; ++i) {
        await fetch(altSvcOrigin + "/clear");
        await sleepMs(100);
      }
      responseText = await fetchText(altSvcOrigin + "/protocol");
      console.log("after clear:", responseText);
      // Store "h3" state in Alt-Svc cache
      for (let i = 0; i < 3; ++i) {
        await fetch(altSvcOrigin + "/set");
        await sleepMs(100);
      }
      responseText = await fetchText(altSvcOrigin + "/protocol");
      console.log("after set:", responseText);
    }
read: async () => {
      const protocol = await fetchText(altSvcOrigin + "/protocol");
      if ((new URL(location)).searchParams.get("thirdparty") === "same") {
        if (protocol !== "h3") {
          throw new Error("Unsupported");
        }
      }
      return protocol;
    }
result, same first party: h3, h3, h3
result, different first party: h2, h2, h2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async () => {
      // Clear Alt-Svc caching first.
      let responseText = "";
      for (let i = 0; i < 3; ++i) {
        await fetch(altSvcOrigin + "/clear");
        await sleepMs(100);
      }
      responseText = await fetchText(altSvcOrigin + "/protocol");
      console.log("after clear:", responseText);
      // Store "h3" state in Alt-Svc cache
      for (let i = 0; i < 3; ++i) {
        await fetch(altSvcOrigin + "/set");
        await sleepMs(100);
      }
      responseText = await fetchText(altSvcOrigin + "/protocol");
      console.log("after set:", responseText);
    }
read: async () => {
      const protocol = await fetchText(altSvcOrigin + "/protocol");
      if ((new URL(location)).searchParams.get("thirdparty") === "same") {
        if (protocol !== "h3") {
          throw new Error("Unsupported");
        }
      }
      return protocol;
    }
result, same first party: h3, h3
result, different first party: h2, h2
unsupported: false, false
passed: true, true
test failed: false, false | write: async () => {
      // Clear Alt-Svc caching first.
      let responseText = "";
      for (let i = 0; i < 3; ++i) {
        await fetch(altSvcOrigin + "/clear");
        await sleepMs(100);
      }
      responseText = await fetchText(altSvcOrigin + "/protocol");
      console.log("after clear:", responseText);
      // Store "h3" state in Alt-Svc cache
      for (let i = 0; i < 3; ++i) {
        await fetch(altSvcOrigin + "/set");
        await sleepMs(100);
      }
      responseText = await fetchText(altSvcOrigin + "/protocol");
      console.log("after set:", responseText);
    }
read: async () => {
      const protocol = await fetchText(altSvcOrigin + "/protocol");
      if ((new URL(location)).searchParams.get("thirdparty") === "same") {
        if (protocol !== "h3") {
          throw new Error("Unsupported");
        }
      }
      return protocol;
    }
result, same first party: 
Error: Unsupported,
Error: Unsupported
result, different first party: h3, h3
unsupported: true, true
passed: undefined
test failed: false, false | write: async () => {
      // Clear Alt-Svc caching first.
      let responseText = "";
      for (let i = 0; i < 3; ++i) {
        await fetch(altSvcOrigin + "/clear");
        await sleepMs(100);
      }
      responseText = await fetchText(altSvcOrigin + "/protocol");
      console.log("after clear:", responseText);
      // Store "h3" state in Alt-Svc cache
      for (let i = 0; i < 3; ++i) {
        await fetch(altSvcOrigin + "/set");
        await sleepMs(100);
      }
      responseText = await fetchText(altSvcOrigin + "/protocol");
      console.log("after set:", responseText);
    }
read: async () => {
      const protocol = await fetchText(altSvcOrigin + "/protocol");
      if ((new URL(location)).searchParams.get("thirdparty") === "same") {
        if (protocol !== "h3") {
          throw new Error("Unsupported");
        }
      }
      return protocol;
    }
result, same first party: 
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
result, different first party: h2, h2, h2
unsupported: true, true, true
passed: undefined
test failed: false, false, false | write: async () => {
      // Clear Alt-Svc caching first.
      let responseText = "";
      for (let i = 0; i < 3; ++i) {
        await fetch(altSvcOrigin + "/clear");
        await sleepMs(100);
      }
      responseText = await fetchText(altSvcOrigin + "/protocol");
      console.log("after clear:", responseText);
      // Store "h3" state in Alt-Svc cache
      for (let i = 0; i < 3; ++i) {
        await fetch(altSvcOrigin + "/set");
        await sleepMs(100);
      }
      responseText = await fetchText(altSvcOrigin + "/protocol");
      console.log("after set:", responseText);
    }
read: async () => {
      const protocol = await fetchText(altSvcOrigin + "/protocol");
      if ((new URL(location)).searchParams.get("thirdparty") === "same") {
        if (protocol !== "h3") {
          throw new Error("Unsupported");
        }
      }
      return protocol;
    }
result, same first party: h3, h3
result, different first party: h2, h2
unsupported: false, false
passed: true, true
test failed: false, false | write: async () => {
      // Clear Alt-Svc caching first.
      let responseText = "";
      for (let i = 0; i < 3; ++i) {
        await fetch(altSvcOrigin + "/clear");
        await sleepMs(100);
      }
      responseText = await fetchText(altSvcOrigin + "/protocol");
      console.log("after clear:", responseText);
      // Store "h3" state in Alt-Svc cache
      for (let i = 0; i < 3; ++i) {
        await fetch(altSvcOrigin + "/set");
        await sleepMs(100);
      }
      responseText = await fetchText(altSvcOrigin + "/protocol");
      console.log("after set:", responseText);
    }
read: async () => {
      const protocol = await fetchText(altSvcOrigin + "/protocol");
      if ((new URL(location)).searchParams.get("thirdparty") === "same") {
        if (protocol !== "h3") {
          throw new Error("Unsupported");
        }
      }
      return protocol;
    }
result, same first party: h3, h3
result, different first party: h2, h2
unsupported: false, false
passed: true, true
test failed: false, false | 
| blob A 'blob URL' is a local reference to some raw data. Trackers can use a blob URL to share data between websites.
               | write: (secret) => {
      try {
        let blobURL = URL.createObjectURL(new Blob([secret]));
        fetch(`${baseURI}blob?mode=write&key=${secret}&blobUrl=${encodeURIComponent(blobURL)}`);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: async (secret) => {
      let response = await fetch(`${baseURI}blob?mode=read&key=${secret}`);
      let result = await response.json();
      let blobUrl = decodeURIComponent(result.blobUrl);
      let blobResponse = await fetch(blobUrl);
      return blobResponse.text();
    }
result, same first party: 
3133a4ca-686b-4047-91c3-9a7e181c53f7,
49872d4a-b01d-4af8-8cbe-85c379a609e2,
58bb2b32-4f83-47bb-91c8-96dce39cd97a
result, different first party: 
Error: Failed to fetch,
Error: Failed to fetch,
Error: Failed to fetch
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (secret) => {
      try {
        let blobURL = URL.createObjectURL(new Blob([secret]));
        fetch(`${baseURI}blob?mode=write&key=${secret}&blobUrl=${encodeURIComponent(blobURL)}`);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: async (secret) => {
      let response = await fetch(`${baseURI}blob?mode=read&key=${secret}`);
      let result = await response.json();
      let blobUrl = decodeURIComponent(result.blobUrl);
      let blobResponse = await fetch(blobUrl);
      return blobResponse.text();
    }
result, same first party: 
e8485c10-5a92-474a-bd16-de23f46ee0e2,
cd5b479f-1529-4f43-a586-810ed83aeb8b
result, different first party: 
e8485c10-5a92-474a-bd16-de23f46ee0e2,
cd5b479f-1529-4f43-a586-810ed83aeb8b
unsupported: false, false
passed: false, false
test failed: false, false | write: (secret) => {
      try {
        let blobURL = URL.createObjectURL(new Blob([secret]));
        fetch(`${baseURI}blob?mode=write&key=${secret}&blobUrl=${encodeURIComponent(blobURL)}`);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: async (secret) => {
      let response = await fetch(`${baseURI}blob?mode=read&key=${secret}`);
      let result = await response.json();
      let blobUrl = decodeURIComponent(result.blobUrl);
      let blobResponse = await fetch(blobUrl);
      return blobResponse.text();
    }
result, same first party: 
e96973da-bf72-4eb1-b6ab-a4e6c6775fb4,
67fb6941-7c51-4727-b485-317211e65ae0
result, different first party: 
e96973da-bf72-4eb1-b6ab-a4e6c6775fb4,
67fb6941-7c51-4727-b485-317211e65ae0
unsupported: false, false
passed: false, false
test failed: false, false | write: (secret) => {
      try {
        let blobURL = URL.createObjectURL(new Blob([secret]));
        fetch(`${baseURI}blob?mode=write&key=${secret}&blobUrl=${encodeURIComponent(blobURL)}`);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: async (secret) => {
      let response = await fetch(`${baseURI}blob?mode=read&key=${secret}`);
      let result = await response.json();
      let blobUrl = decodeURIComponent(result.blobUrl);
      let blobResponse = await fetch(blobUrl);
      return blobResponse.text();
    }
result, same first party: 
94f1fd6d-f642-403e-8ef8-11180b18b1f6,
2bf6571e-4385-4b68-a076-f6868cbe4954
result, different first party: 
Error: NetworkError when attempting to fetch resource.,
Error: NetworkError when attempting to fetch resource.
unsupported: false, false
passed: true, true
test failed: false, false | write: (secret) => {
      try {
        let blobURL = URL.createObjectURL(new Blob([secret]));
        fetch(`${baseURI}blob?mode=write&key=${secret}&blobUrl=${encodeURIComponent(blobURL)}`);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: async (secret) => {
      let response = await fetch(`${baseURI}blob?mode=read&key=${secret}`);
      let result = await response.json();
      let blobUrl = decodeURIComponent(result.blobUrl);
      let blobResponse = await fetch(blobUrl);
      return blobResponse.text();
    }
result, same first party: 
da67bebb-5652-441a-816e-1206b25c0225,
0bad71fe-de93-4434-bfa0-bb7f0db62eea,
6aba94ed-010e-45af-892c-dd670cd8d134
result, different first party: 
Error: NetworkError when attempting to fetch resource.,
Error: NetworkError when attempting to fetch resource.,
Error: NetworkError when attempting to fetch resource.
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (secret) => {
      try {
        let blobURL = URL.createObjectURL(new Blob([secret]));
        fetch(`${baseURI}blob?mode=write&key=${secret}&blobUrl=${encodeURIComponent(blobURL)}`);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: async (secret) => {
      let response = await fetch(`${baseURI}blob?mode=read&key=${secret}`);
      let result = await response.json();
      let blobUrl = decodeURIComponent(result.blobUrl);
      let blobResponse = await fetch(blobUrl);
      return blobResponse.text();
    }
result, same first party: 
9dd63e63-3256-4352-99f6-805b6f48eb8c,
b7cb77db-aca9-4570-923e-b81da9175421,
736f05a1-05e6-491e-8dad-a6647bd52dda
result, different first party: 
Error: NetworkError when attempting to fetch resource.,
Error: NetworkError when attempting to fetch resource.,
Error: NetworkError when attempting to fetch resource.
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (secret) => {
      try {
        let blobURL = URL.createObjectURL(new Blob([secret]));
        fetch(`${baseURI}blob?mode=write&key=${secret}&blobUrl=${encodeURIComponent(blobURL)}`);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: async (secret) => {
      let response = await fetch(`${baseURI}blob?mode=read&key=${secret}`);
      let result = await response.json();
      let blobUrl = decodeURIComponent(result.blobUrl);
      let blobResponse = await fetch(blobUrl);
      return blobResponse.text();
    }
result, same first party: 
50c6a369-0b5c-4de5-a70b-a0516825119e,
49e0d8b1-c126-4b6b-be66-6ba7acfd85a9
result, different first party: 
50c6a369-0b5c-4de5-a70b-a0516825119e,
49e0d8b1-c126-4b6b-be66-6ba7acfd85a9
unsupported: false, false
passed: false, false
test failed: false, false | write: (secret) => {
      try {
        let blobURL = URL.createObjectURL(new Blob([secret]));
        fetch(`${baseURI}blob?mode=write&key=${secret}&blobUrl=${encodeURIComponent(blobURL)}`);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: async (secret) => {
      let response = await fetch(`${baseURI}blob?mode=read&key=${secret}`);
      let result = await response.json();
      let blobUrl = decodeURIComponent(result.blobUrl);
      let blobResponse = await fetch(blobUrl);
      return blobResponse.text();
    }
result, same first party: 
6b07cf4a-a332-4a7c-90df-fbc3dc862f86,
2d40813a-a6c5-4a6b-bb1c-f4d01a399217
result, different first party: 
Error: Load failed,
Error: Load failed
unsupported: false, false
passed: true, true
test failed: false, false | write: (secret) => {
      try {
        let blobURL = URL.createObjectURL(new Blob([secret]));
        fetch(`${baseURI}blob?mode=write&key=${secret}&blobUrl=${encodeURIComponent(blobURL)}`);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: async (secret) => {
      let response = await fetch(`${baseURI}blob?mode=read&key=${secret}`);
      let result = await response.json();
      let blobUrl = decodeURIComponent(result.blobUrl);
      let blobResponse = await fetch(blobUrl);
      return blobResponse.text();
    }
result, same first party: 
3dc5c317-3ea7-42b4-be71-7902bcc4eb27,
4932f81b-b746-4e4b-b97d-c0b827047888,
3e016f09-5749-4084-a136-e9b829c1a824
result, different first party: 
Error: NetworkError when attempting to fetch resource.,
Error: NetworkError when attempting to fetch resource.,
Error: NetworkError when attempting to fetch resource.
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (secret) => {
      try {
        let blobURL = URL.createObjectURL(new Blob([secret]));
        fetch(`${baseURI}blob?mode=write&key=${secret}&blobUrl=${encodeURIComponent(blobURL)}`);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: async (secret) => {
      let response = await fetch(`${baseURI}blob?mode=read&key=${secret}`);
      let result = await response.json();
      let blobUrl = decodeURIComponent(result.blobUrl);
      let blobResponse = await fetch(blobUrl);
      return blobResponse.text();
    }
result, same first party: 
3242a94e-3344-4cb9-ba7c-ebedaf5d6e01,
87918123-fc52-4196-9d0c-a2cfc3f826d4
result, different first party: 
3242a94e-3344-4cb9-ba7c-ebedaf5d6e01,
87918123-fc52-4196-9d0c-a2cfc3f826d4
unsupported: false, false
passed: false, false
test failed: false, false | write: (secret) => {
      try {
        let blobURL = URL.createObjectURL(new Blob([secret]));
        fetch(`${baseURI}blob?mode=write&key=${secret}&blobUrl=${encodeURIComponent(blobURL)}`);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: async (secret) => {
      let response = await fetch(`${baseURI}blob?mode=read&key=${secret}`);
      let result = await response.json();
      let blobUrl = decodeURIComponent(result.blobUrl);
      let blobResponse = await fetch(blobUrl);
      return blobResponse.text();
    }
result, same first party: 
5c052838-a55e-4690-83c6-b7d572146713,
49042d99-5b0e-4f37-82f3-78de245eea34
result, different first party: 
5c052838-a55e-4690-83c6-b7d572146713,
49042d99-5b0e-4f37-82f3-78de245eea34
unsupported: false, false
passed: false, false
test failed: false, false | 
| BroadcastChannel A BroadcastChannel is designed to send messages between tabs. In some browsers it can be used for cross-site communication and tracking.
               | write: (secret) => {
      try {
        let bc = new BroadcastChannel("secrets");
        bc.onmessage = (event) => {
          if (event.data === "request") {
            bc.postMessage(secret);
          }
        };
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: () =>
      new Promise((resolve, reject) => {
        let bc = new BroadcastChannel("secrets");
        bc.onmessage = (event) => {
          if (event.data !== "request") {
            resolve(event.data);
          }
        };
        bc.postMessage("request");
        setTimeout(() => reject({message: "no BroadcastChannel message"}), 3000);
      })
result, same first party: 
3133a4ca-686b-4047-91c3-9a7e181c53f7,
49872d4a-b01d-4af8-8cbe-85c379a609e2,
58bb2b32-4f83-47bb-91c8-96dce39cd97a
result, different first party: 
Error: no BroadcastChannel message,
Error: no BroadcastChannel message,
Error: no BroadcastChannel message
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (secret) => {
      try {
        let bc = new BroadcastChannel("secrets");
        bc.onmessage = (event) => {
          if (event.data === "request") {
            bc.postMessage(secret);
          }
        };
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: () =>
      new Promise((resolve, reject) => {
        let bc = new BroadcastChannel("secrets");
        bc.onmessage = (event) => {
          if (event.data !== "request") {
            resolve(event.data);
          }
        };
        bc.postMessage("request");
        setTimeout(() => reject({message: "no BroadcastChannel message"}), 3000);
      })
result, same first party: 
e8485c10-5a92-474a-bd16-de23f46ee0e2,
cd5b479f-1529-4f43-a586-810ed83aeb8b
result, different first party: 
Error: no BroadcastChannel message,
Error: no BroadcastChannel message
unsupported: false, false
passed: true, true
test failed: false, false | write: (secret) => {
      try {
        let bc = new BroadcastChannel("secrets");
        bc.onmessage = (event) => {
          if (event.data === "request") {
            bc.postMessage(secret);
          }
        };
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: () =>
      new Promise((resolve, reject) => {
        let bc = new BroadcastChannel("secrets");
        bc.onmessage = (event) => {
          if (event.data !== "request") {
            resolve(event.data);
          }
        };
        bc.postMessage("request");
        setTimeout(() => reject({message: "no BroadcastChannel message"}), 3000);
      })
result, same first party: 
e96973da-bf72-4eb1-b6ab-a4e6c6775fb4,
67fb6941-7c51-4727-b485-317211e65ae0
result, different first party: 
Error: no BroadcastChannel message,
Error: no BroadcastChannel message
unsupported: false, false
passed: true, true
test failed: false, false | write: (secret) => {
      try {
        let bc = new BroadcastChannel("secrets");
        bc.onmessage = (event) => {
          if (event.data === "request") {
            bc.postMessage(secret);
          }
        };
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: () =>
      new Promise((resolve, reject) => {
        let bc = new BroadcastChannel("secrets");
        bc.onmessage = (event) => {
          if (event.data !== "request") {
            resolve(event.data);
          }
        };
        bc.postMessage("request");
        setTimeout(() => reject({message: "no BroadcastChannel message"}), 3000);
      })
result, same first party: 
94f1fd6d-f642-403e-8ef8-11180b18b1f6,
2bf6571e-4385-4b68-a076-f6868cbe4954
result, different first party: 
Error: no BroadcastChannel message,
Error: no BroadcastChannel message
unsupported: false, false
passed: true, true
test failed: false, false | write: (secret) => {
      try {
        let bc = new BroadcastChannel("secrets");
        bc.onmessage = (event) => {
          if (event.data === "request") {
            bc.postMessage(secret);
          }
        };
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: () =>
      new Promise((resolve, reject) => {
        let bc = new BroadcastChannel("secrets");
        bc.onmessage = (event) => {
          if (event.data !== "request") {
            resolve(event.data);
          }
        };
        bc.postMessage("request");
        setTimeout(() => reject({message: "no BroadcastChannel message"}), 3000);
      })
result, same first party: 
da67bebb-5652-441a-816e-1206b25c0225,
0bad71fe-de93-4434-bfa0-bb7f0db62eea,
6aba94ed-010e-45af-892c-dd670cd8d134
result, different first party: 
Error: no BroadcastChannel message,
Error: no BroadcastChannel message,
Error: no BroadcastChannel message
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (secret) => {
      try {
        let bc = new BroadcastChannel("secrets");
        bc.onmessage = (event) => {
          if (event.data === "request") {
            bc.postMessage(secret);
          }
        };
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: () =>
      new Promise((resolve, reject) => {
        let bc = new BroadcastChannel("secrets");
        bc.onmessage = (event) => {
          if (event.data !== "request") {
            resolve(event.data);
          }
        };
        bc.postMessage("request");
        setTimeout(() => reject({message: "no BroadcastChannel message"}), 3000);
      })
result, same first party: 
9dd63e63-3256-4352-99f6-805b6f48eb8c,
b7cb77db-aca9-4570-923e-b81da9175421,
736f05a1-05e6-491e-8dad-a6647bd52dda
result, different first party: 
Error: The operation is insecure.,
Error: The operation is insecure.,
Error: The operation is insecure.
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (secret) => {
      try {
        let bc = new BroadcastChannel("secrets");
        bc.onmessage = (event) => {
          if (event.data === "request") {
            bc.postMessage(secret);
          }
        };
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: () =>
      new Promise((resolve, reject) => {
        let bc = new BroadcastChannel("secrets");
        bc.onmessage = (event) => {
          if (event.data !== "request") {
            resolve(event.data);
          }
        };
        bc.postMessage("request");
        setTimeout(() => reject({message: "no BroadcastChannel message"}), 3000);
      })
result, same first party: 
50c6a369-0b5c-4de5-a70b-a0516825119e,
49e0d8b1-c126-4b6b-be66-6ba7acfd85a9
result, different first party: 
Error: no BroadcastChannel message,
Error: no BroadcastChannel message
unsupported: false, false
passed: true, true
test failed: false, false | write: (secret) => {
      try {
        let bc = new BroadcastChannel("secrets");
        bc.onmessage = (event) => {
          if (event.data === "request") {
            bc.postMessage(secret);
          }
        };
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: () =>
      new Promise((resolve, reject) => {
        let bc = new BroadcastChannel("secrets");
        bc.onmessage = (event) => {
          if (event.data !== "request") {
            resolve(event.data);
          }
        };
        bc.postMessage("request");
        setTimeout(() => reject({message: "no BroadcastChannel message"}), 3000);
      })
result, same first party: 
6b07cf4a-a332-4a7c-90df-fbc3dc862f86,
2d40813a-a6c5-4a6b-bb1c-f4d01a399217
result, different first party: 
Error: no BroadcastChannel message,
Error: no BroadcastChannel message
unsupported: false, false
passed: true, true
test failed: false, false | write: (secret) => {
      try {
        let bc = new BroadcastChannel("secrets");
        bc.onmessage = (event) => {
          if (event.data === "request") {
            bc.postMessage(secret);
          }
        };
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: () =>
      new Promise((resolve, reject) => {
        let bc = new BroadcastChannel("secrets");
        bc.onmessage = (event) => {
          if (event.data !== "request") {
            resolve(event.data);
          }
        };
        bc.postMessage("request");
        setTimeout(() => reject({message: "no BroadcastChannel message"}), 3000);
      })
result, same first party: 
3dc5c317-3ea7-42b4-be71-7902bcc4eb27,
4932f81b-b746-4e4b-b97d-c0b827047888,
3e016f09-5749-4084-a136-e9b829c1a824
result, different first party: 
Error: The operation is insecure.,
Error: The operation is insecure.,
Error: The operation is insecure.
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (secret) => {
      try {
        let bc = new BroadcastChannel("secrets");
        bc.onmessage = (event) => {
          if (event.data === "request") {
            bc.postMessage(secret);
          }
        };
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: () =>
      new Promise((resolve, reject) => {
        let bc = new BroadcastChannel("secrets");
        bc.onmessage = (event) => {
          if (event.data !== "request") {
            resolve(event.data);
          }
        };
        bc.postMessage("request");
        setTimeout(() => reject({message: "no BroadcastChannel message"}), 3000);
      })
result, same first party: 
3242a94e-3344-4cb9-ba7c-ebedaf5d6e01,
87918123-fc52-4196-9d0c-a2cfc3f826d4
result, different first party: 
Error: no BroadcastChannel message,
Error: no BroadcastChannel message
unsupported: false, false
passed: true, true
test failed: false, false | write: (secret) => {
      try {
        let bc = new BroadcastChannel("secrets");
        bc.onmessage = (event) => {
          if (event.data === "request") {
            bc.postMessage(secret);
          }
        };
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: () =>
      new Promise((resolve, reject) => {
        let bc = new BroadcastChannel("secrets");
        bc.onmessage = (event) => {
          if (event.data !== "request") {
            resolve(event.data);
          }
        };
        bc.postMessage("request");
        setTimeout(() => reject({message: "no BroadcastChannel message"}), 3000);
      })
result, same first party: 
5c052838-a55e-4690-83c6-b7d572146713,
49042d99-5b0e-4f37-82f3-78de245eea34
result, different first party: 
Error: no BroadcastChannel message,
Error: no BroadcastChannel message
unsupported: false, false
passed: true, true
test failed: false, false | 
| CacheStorage The Cache API is a content storage mechanism originally introduced to support ServiceWorkers. If the same Cache object is accessible to multiple websites, it can be abused to track users.
               | write: async (key) => {
      try {
        let cache = await caches.open("supercookies");
        cache.addAll([`test.css?key=${key}`]);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      let cache = await caches.open("supercookies");
      let cacheKeys = await cache.keys();
      let url = cacheKeys[0].url;
      return (new URL(url)).searchParams.get("key");
    }
result, same first party: 
3133a4ca-686b-4047-91c3-9a7e181c53f7,
49872d4a-b01d-4af8-8cbe-85c379a609e2,
58bb2b32-4f83-47bb-91c8-96dce39cd97a
result, different first party: 
Error: Failed to execute 'open' on 'CacheStorage': An attempt was made to break through the security policy of the user agent.,
Error: Failed to execute 'open' on 'CacheStorage': An attempt was made to break through the security policy of the user agent.,
Error: Failed to execute 'open' on 'CacheStorage': An attempt was made to break through the security policy of the user agent.
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (key) => {
      try {
        let cache = await caches.open("supercookies");
        cache.addAll([`test.css?key=${key}`]);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      let cache = await caches.open("supercookies");
      let cacheKeys = await cache.keys();
      let url = cacheKeys[0].url;
      return (new URL(url)).searchParams.get("key");
    }
result, same first party: 
e8485c10-5a92-474a-bd16-de23f46ee0e2,
cd5b479f-1529-4f43-a586-810ed83aeb8b
result, different first party: 
Error: Cannot read properties of undefined (reading 'url'),
Error: Cannot read properties of undefined (reading 'url')
unsupported: false, false
passed: true, true
test failed: false, false | write: async (key) => {
      try {
        let cache = await caches.open("supercookies");
        cache.addAll([`test.css?key=${key}`]);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      let cache = await caches.open("supercookies");
      let cacheKeys = await cache.keys();
      let url = cacheKeys[0].url;
      return (new URL(url)).searchParams.get("key");
    }
result, same first party: 
e96973da-bf72-4eb1-b6ab-a4e6c6775fb4,
67fb6941-7c51-4727-b485-317211e65ae0
result, different first party: 
Error: Cannot read properties of undefined (reading 'url'),
Error: Cannot read properties of undefined (reading 'url')
unsupported: false, false
passed: true, true
test failed: false, false | write: async (key) => {
      try {
        let cache = await caches.open("supercookies");
        cache.addAll([`test.css?key=${key}`]);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      let cache = await caches.open("supercookies");
      let cacheKeys = await cache.keys();
      let url = cacheKeys[0].url;
      return (new URL(url)).searchParams.get("key");
    }
result, same first party: 
94f1fd6d-f642-403e-8ef8-11180b18b1f6,
2bf6571e-4385-4b68-a076-f6868cbe4954
result, different first party: 
Error: cacheKeys[0] is undefined,
Error: cacheKeys[0] is undefined
unsupported: false, false
passed: true, true
test failed: false, false | write: async (key) => {
      try {
        let cache = await caches.open("supercookies");
        cache.addAll([`test.css?key=${key}`]);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      let cache = await caches.open("supercookies");
      let cacheKeys = await cache.keys();
      let url = cacheKeys[0].url;
      return (new URL(url)).searchParams.get("key");
    }
result, same first party: 
da67bebb-5652-441a-816e-1206b25c0225,
0bad71fe-de93-4434-bfa0-bb7f0db62eea,
6aba94ed-010e-45af-892c-dd670cd8d134
result, different first party: 
Error: cacheKeys[0] is undefined,
Error: cacheKeys[0] is undefined,
Error: cacheKeys[0] is undefined
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (key) => {
      try {
        let cache = await caches.open("supercookies");
        cache.addAll([`test.css?key=${key}`]);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      let cache = await caches.open("supercookies");
      let cacheKeys = await cache.keys();
      let url = cacheKeys[0].url;
      return (new URL(url)).searchParams.get("key");
    }
result, same first party: 
Error: The operation is insecure.,
Error: The operation is insecure.,
Error: The operation is insecure.
result, different first party: 
Error: The operation is insecure.,
Error: The operation is insecure.,
Error: The operation is insecure.
unsupported: true, true, true
passed: undefined
test failed: false, false, false | write: async (key) => {
      try {
        let cache = await caches.open("supercookies");
        cache.addAll([`test.css?key=${key}`]);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      let cache = await caches.open("supercookies");
      let cacheKeys = await cache.keys();
      let url = cacheKeys[0].url;
      return (new URL(url)).searchParams.get("key");
    }
result, same first party: 
50c6a369-0b5c-4de5-a70b-a0516825119e,
49e0d8b1-c126-4b6b-be66-6ba7acfd85a9
result, different first party: 
Error: Cannot read properties of undefined (reading 'url'),
Error: Cannot read properties of undefined (reading 'url')
unsupported: false, false
passed: true, true
test failed: false, false | write: async (key) => {
      try {
        let cache = await caches.open("supercookies");
        cache.addAll([`test.css?key=${key}`]);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      let cache = await caches.open("supercookies");
      let cacheKeys = await cache.keys();
      let url = cacheKeys[0].url;
      return (new URL(url)).searchParams.get("key");
    }
result, same first party: 
6b07cf4a-a332-4a7c-90df-fbc3dc862f86,
2d40813a-a6c5-4a6b-bb1c-f4d01a399217
result, different first party: 
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url')
unsupported: false, false
passed: true, true
test failed: false, false | write: async (key) => {
      try {
        let cache = await caches.open("supercookies");
        cache.addAll([`test.css?key=${key}`]);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      let cache = await caches.open("supercookies");
      let cacheKeys = await cache.keys();
      let url = cacheKeys[0].url;
      return (new URL(url)).searchParams.get("key");
    }
result, same first party: 
Error: The operation is insecure.,
Error: The operation is insecure.,
Error: The operation is insecure.
result, different first party: 
Error: The operation is insecure.,
Error: The operation is insecure.,
Error: The operation is insecure.
unsupported: true, true, true
passed: undefined
test failed: false, false, false | write: async (key) => {
      try {
        let cache = await caches.open("supercookies");
        cache.addAll([`test.css?key=${key}`]);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      let cache = await caches.open("supercookies");
      let cacheKeys = await cache.keys();
      let url = cacheKeys[0].url;
      return (new URL(url)).searchParams.get("key");
    }
result, same first party: 
3242a94e-3344-4cb9-ba7c-ebedaf5d6e01,
87918123-fc52-4196-9d0c-a2cfc3f826d4
result, different first party: 
Error: Cannot read properties of undefined (reading 'url'),
Error: Cannot read properties of undefined (reading 'url')
unsupported: false, false
passed: true, true
test failed: false, false | write: async (key) => {
      try {
        let cache = await caches.open("supercookies");
        cache.addAll([`test.css?key=${key}`]);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      let cache = await caches.open("supercookies");
      let cacheKeys = await cache.keys();
      let url = cacheKeys[0].url;
      return (new URL(url)).searchParams.get("key");
    }
result, same first party: 
5c052838-a55e-4690-83c6-b7d572146713,
49042d99-5b0e-4f37-82f3-78de245eea34
result, different first party: 
Error: Cannot read properties of undefined (reading 'url'),
Error: Cannot read properties of undefined (reading 'url')
unsupported: false, false
passed: true, true
test failed: false, false | 
| cookie (HTTP) The cookie, first introduced by Netscape in 1994, is a small amount of data stored by your browser on a website's behalf. It has legitimate uses, but it is also the classic cross-site tracking mechanism, and today still the most popular method of tracking users across websites. Browsers can stop cookies from being used for cross-site tracking by either blocking or partitioning them.
               | write: async (secret) => {
      // Request a page that will send an HTTPOnly 'set-cookie' response header with secret value.
      await fetch(`${baseURI}cookie?secret=${secret}_http`);
    }
read: async () => {
      // Test if we now send a requests with a 'cookie' header containing the secret.
      let response = await fetch(`${baseURI}headers`);
      let cookie = (await response.json())["cookie"];
      return cookie ? cookie.match(/secret=([\w-]+)/)[1]: null;
    }
result, same first party: 
3133a4ca-686b-4047-91c3-9a7e181c53f7_http,
49872d4a-b01d-4af8-8cbe-85c379a609e2_http,
58bb2b32-4f83-47bb-91c8-96dce39cd97a_http
result, different first party: , , 
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (secret) => {
      // Request a page that will send an HTTPOnly 'set-cookie' response header with secret value.
      await fetch(`${baseURI}cookie?secret=${secret}_http`);
    }
read: async () => {
      // Test if we now send a requests with a 'cookie' header containing the secret.
      let response = await fetch(`${baseURI}headers`);
      let cookie = (await response.json())["cookie"];
      return cookie ? cookie.match(/secret=([\w-]+)/)[1]: null;
    }
result, same first party: 
e8485c10-5a92-474a-bd16-de23f46ee0e2_http,
cd5b479f-1529-4f43-a586-810ed83aeb8b_http
result, different first party: 
e8485c10-5a92-474a-bd16-de23f46ee0e2_http,
cd5b479f-1529-4f43-a586-810ed83aeb8b_http
unsupported: false, false
passed: false, false
test failed: false, false | write: async (secret) => {
      // Request a page that will send an HTTPOnly 'set-cookie' response header with secret value.
      await fetch(`${baseURI}cookie?secret=${secret}_http`);
    }
read: async () => {
      // Test if we now send a requests with a 'cookie' header containing the secret.
      let response = await fetch(`${baseURI}headers`);
      let cookie = (await response.json())["cookie"];
      return cookie ? cookie.match(/secret=([\w-]+)/)[1]: null;
    }
result, same first party: 
e96973da-bf72-4eb1-b6ab-a4e6c6775fb4_http,
67fb6941-7c51-4727-b485-317211e65ae0_http
result, different first party: 
e96973da-bf72-4eb1-b6ab-a4e6c6775fb4_http,
67fb6941-7c51-4727-b485-317211e65ae0_http
unsupported: false, false
passed: false, false
test failed: false, false | write: async (secret) => {
      // Request a page that will send an HTTPOnly 'set-cookie' response header with secret value.
      await fetch(`${baseURI}cookie?secret=${secret}_http`);
    }
read: async () => {
      // Test if we now send a requests with a 'cookie' header containing the secret.
      let response = await fetch(`${baseURI}headers`);
      let cookie = (await response.json())["cookie"];
      return cookie ? cookie.match(/secret=([\w-]+)/)[1]: null;
    }
result, same first party: 
94f1fd6d-f642-403e-8ef8-11180b18b1f6_http,
2bf6571e-4385-4b68-a076-f6868cbe4954_http
result, different first party: , 
unsupported: false, false
passed: true, true
test failed: false, false | write: async (secret) => {
      // Request a page that will send an HTTPOnly 'set-cookie' response header with secret value.
      await fetch(`${baseURI}cookie?secret=${secret}_http`);
    }
read: async () => {
      // Test if we now send a requests with a 'cookie' header containing the secret.
      let response = await fetch(`${baseURI}headers`);
      let cookie = (await response.json())["cookie"];
      return cookie ? cookie.match(/secret=([\w-]+)/)[1]: null;
    }
result, same first party: 
da67bebb-5652-441a-816e-1206b25c0225_http,
0bad71fe-de93-4434-bfa0-bb7f0db62eea_http,
6aba94ed-010e-45af-892c-dd670cd8d134_http
result, different first party: , , 
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (secret) => {
      // Request a page that will send an HTTPOnly 'set-cookie' response header with secret value.
      await fetch(`${baseURI}cookie?secret=${secret}_http`);
    }
read: async () => {
      // Test if we now send a requests with a 'cookie' header containing the secret.
      let response = await fetch(`${baseURI}headers`);
      let cookie = (await response.json())["cookie"];
      return cookie ? cookie.match(/secret=([\w-]+)/)[1]: null;
    }
result, same first party: 
9dd63e63-3256-4352-99f6-805b6f48eb8c_http,
b7cb77db-aca9-4570-923e-b81da9175421_http,
736f05a1-05e6-491e-8dad-a6647bd52dda_http
result, different first party: , , 
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (secret) => {
      // Request a page that will send an HTTPOnly 'set-cookie' response header with secret value.
      await fetch(`${baseURI}cookie?secret=${secret}_http`);
    }
read: async () => {
      // Test if we now send a requests with a 'cookie' header containing the secret.
      let response = await fetch(`${baseURI}headers`);
      let cookie = (await response.json())["cookie"];
      return cookie ? cookie.match(/secret=([\w-]+)/)[1]: null;
    }
result, same first party: 
50c6a369-0b5c-4de5-a70b-a0516825119e_http,
49e0d8b1-c126-4b6b-be66-6ba7acfd85a9_http
result, different first party: 
50c6a369-0b5c-4de5-a70b-a0516825119e_http,
49e0d8b1-c126-4b6b-be66-6ba7acfd85a9_http
unsupported: false, false
passed: false, false
test failed: false, false | write: async (secret) => {
      // Request a page that will send an HTTPOnly 'set-cookie' response header with secret value.
      await fetch(`${baseURI}cookie?secret=${secret}_http`);
    }
read: async () => {
      // Test if we now send a requests with a 'cookie' header containing the secret.
      let response = await fetch(`${baseURI}headers`);
      let cookie = (await response.json())["cookie"];
      return cookie ? cookie.match(/secret=([\w-]+)/)[1]: null;
    }
result, same first party: 
6b07cf4a-a332-4a7c-90df-fbc3dc862f86_http,
2d40813a-a6c5-4a6b-bb1c-f4d01a399217_http
result, different first party: , 
unsupported: false, false
passed: true, true
test failed: false, false | write: async (secret) => {
      // Request a page that will send an HTTPOnly 'set-cookie' response header with secret value.
      await fetch(`${baseURI}cookie?secret=${secret}_http`);
    }
read: async () => {
      // Test if we now send a requests with a 'cookie' header containing the secret.
      let response = await fetch(`${baseURI}headers`);
      let cookie = (await response.json())["cookie"];
      return cookie ? cookie.match(/secret=([\w-]+)/)[1]: null;
    }
result, same first party: 
3dc5c317-3ea7-42b4-be71-7902bcc4eb27_http,
4932f81b-b746-4e4b-b97d-c0b827047888_http,
3e016f09-5749-4084-a136-e9b829c1a824_http
result, different first party: , , 
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (secret) => {
      // Request a page that will send an HTTPOnly 'set-cookie' response header with secret value.
      await fetch(`${baseURI}cookie?secret=${secret}_http`);
    }
read: async () => {
      // Test if we now send a requests with a 'cookie' header containing the secret.
      let response = await fetch(`${baseURI}headers`);
      let cookie = (await response.json())["cookie"];
      return cookie ? cookie.match(/secret=([\w-]+)/)[1]: null;
    }
result, same first party: 
3242a94e-3344-4cb9-ba7c-ebedaf5d6e01_http,
87918123-fc52-4196-9d0c-a2cfc3f826d4_http
result, different first party: , 
unsupported: false, false
passed: true, true
test failed: false, false | write: async (secret) => {
      // Request a page that will send an HTTPOnly 'set-cookie' response header with secret value.
      await fetch(`${baseURI}cookie?secret=${secret}_http`);
    }
read: async () => {
      // Test if we now send a requests with a 'cookie' header containing the secret.
      let response = await fetch(`${baseURI}headers`);
      let cookie = (await response.json())["cookie"];
      return cookie ? cookie.match(/secret=([\w-]+)/)[1]: null;
    }
result, same first party: 
5c052838-a55e-4690-83c6-b7d572146713_http,
49042d99-5b0e-4f37-82f3-78de245eea34_http
result, different first party: 
5c052838-a55e-4690-83c6-b7d572146713_http,
49042d99-5b0e-4f37-82f3-78de245eea34_http
unsupported: false, false
passed: false, false
test failed: false, false | 
| cookie (JS) The cookie, first introduced by Netscape in 1994, is a small amount of data stored by your browser on a website's behalf. It has legitimate uses, but it is also the classic cross-site tracking mechanism, and today still the most popular method of tracking users across websites. Browsers can stop cookies from being used for cross-site tracking by either blocking or partitioning them.
               | write: (secret) => {
      document.cookie = `secret=${secret}_js; max-age=3600; SameSite=None; Secure`;
    }
read: () => document.cookie ? document.cookie.match(/secret=([\w-]+)/)[1] : null
result, same first party: 
3133a4ca-686b-4047-91c3-9a7e181c53f7_js,
49872d4a-b01d-4af8-8cbe-85c379a609e2_js,
58bb2b32-4f83-47bb-91c8-96dce39cd97a_js
result, different first party: , , 
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (secret) => {
      document.cookie = `secret=${secret}_js; max-age=3600; SameSite=None; Secure`;
    }
read: () => document.cookie ? document.cookie.match(/secret=([\w-]+)/)[1] : null
result, same first party: 
e8485c10-5a92-474a-bd16-de23f46ee0e2_js,
cd5b479f-1529-4f43-a586-810ed83aeb8b_js
result, different first party: 
e8485c10-5a92-474a-bd16-de23f46ee0e2_js,
cd5b479f-1529-4f43-a586-810ed83aeb8b_js
unsupported: false, false
passed: false, false
test failed: false, false | write: (secret) => {
      document.cookie = `secret=${secret}_js; max-age=3600; SameSite=None; Secure`;
    }
read: () => document.cookie ? document.cookie.match(/secret=([\w-]+)/)[1] : null
result, same first party: 
e96973da-bf72-4eb1-b6ab-a4e6c6775fb4_js,
67fb6941-7c51-4727-b485-317211e65ae0_js
result, different first party: 
e96973da-bf72-4eb1-b6ab-a4e6c6775fb4_js,
67fb6941-7c51-4727-b485-317211e65ae0_js
unsupported: false, false
passed: false, false
test failed: false, false | write: (secret) => {
      document.cookie = `secret=${secret}_js; max-age=3600; SameSite=None; Secure`;
    }
read: () => document.cookie ? document.cookie.match(/secret=([\w-]+)/)[1] : null
result, same first party: 
94f1fd6d-f642-403e-8ef8-11180b18b1f6_js,
2bf6571e-4385-4b68-a076-f6868cbe4954_js
result, different first party: , 
unsupported: false, false
passed: true, true
test failed: false, false | write: (secret) => {
      document.cookie = `secret=${secret}_js; max-age=3600; SameSite=None; Secure`;
    }
read: () => document.cookie ? document.cookie.match(/secret=([\w-]+)/)[1] : null
result, same first party: 
da67bebb-5652-441a-816e-1206b25c0225_js,
0bad71fe-de93-4434-bfa0-bb7f0db62eea_js,
6aba94ed-010e-45af-892c-dd670cd8d134_js
result, different first party: , , 
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (secret) => {
      document.cookie = `secret=${secret}_js; max-age=3600; SameSite=None; Secure`;
    }
read: () => document.cookie ? document.cookie.match(/secret=([\w-]+)/)[1] : null
result, same first party: 
9dd63e63-3256-4352-99f6-805b6f48eb8c_js,
b7cb77db-aca9-4570-923e-b81da9175421_js,
736f05a1-05e6-491e-8dad-a6647bd52dda_js
result, different first party: , , 
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (secret) => {
      document.cookie = `secret=${secret}_js; max-age=3600; SameSite=None; Secure`;
    }
read: () => document.cookie ? document.cookie.match(/secret=([\w-]+)/)[1] : null
result, same first party: 
50c6a369-0b5c-4de5-a70b-a0516825119e_js,
49e0d8b1-c126-4b6b-be66-6ba7acfd85a9_js
result, different first party: 
50c6a369-0b5c-4de5-a70b-a0516825119e_js,
49e0d8b1-c126-4b6b-be66-6ba7acfd85a9_js
unsupported: false, false
passed: false, false
test failed: false, false | write: (secret) => {
      document.cookie = `secret=${secret}_js; max-age=3600; SameSite=None; Secure`;
    }
read: () => document.cookie ? document.cookie.match(/secret=([\w-]+)/)[1] : null
result, same first party: 
6b07cf4a-a332-4a7c-90df-fbc3dc862f86_js,
2d40813a-a6c5-4a6b-bb1c-f4d01a399217_js
result, different first party: , 
unsupported: false, false
passed: true, true
test failed: false, false | write: (secret) => {
      document.cookie = `secret=${secret}_js; max-age=3600; SameSite=None; Secure`;
    }
read: () => document.cookie ? document.cookie.match(/secret=([\w-]+)/)[1] : null
result, same first party: 
3dc5c317-3ea7-42b4-be71-7902bcc4eb27_js,
4932f81b-b746-4e4b-b97d-c0b827047888_js,
3e016f09-5749-4084-a136-e9b829c1a824_js
result, different first party: , , 
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (secret) => {
      document.cookie = `secret=${secret}_js; max-age=3600; SameSite=None; Secure`;
    }
read: () => document.cookie ? document.cookie.match(/secret=([\w-]+)/)[1] : null
result, same first party: 
3242a94e-3344-4cb9-ba7c-ebedaf5d6e01_js,
87918123-fc52-4196-9d0c-a2cfc3f826d4_js
result, different first party: , 
unsupported: false, false
passed: true, true
test failed: false, false | write: (secret) => {
      document.cookie = `secret=${secret}_js; max-age=3600; SameSite=None; Secure`;
    }
read: () => document.cookie ? document.cookie.match(/secret=([\w-]+)/)[1] : null
result, same first party: 
5c052838-a55e-4690-83c6-b7d572146713_js,
49042d99-5b0e-4f37-82f3-78de245eea34_js
result, different first party: 
5c052838-a55e-4690-83c6-b7d572146713_js,
49042d99-5b0e-4f37-82f3-78de245eea34_js
unsupported: false, false
passed: false, false
test failed: false, false | 
| CookieStore The Cookie Store API is an alternative asynchronous API for managing cookies, supported by some browsers.
               | write: (data) => {
      const msPerHour = 60 * 60 * 1000;
      if (!window.cookieStore) {
        throw new Error("Unsupported");
      }
      window.cookieStore.set({
        name: "partition_test",
        value: data,
        expires: Date.now() + msPerHour,
        sameSite: "none"
      });
    }
read: async () => {
      if (!window.cookieStore) {
        throw new Error("Unsupported");
      }
      const cookie = await window.cookieStore.get("partition_test");
      if (!cookie) {
        return null;
      }
      return cookie.value;
    }
result, same first party: 
3133a4ca-686b-4047-91c3-9a7e181c53f7,
49872d4a-b01d-4af8-8cbe-85c379a609e2,
58bb2b32-4f83-47bb-91c8-96dce39cd97a
result, different first party: , , 
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (data) => {
      const msPerHour = 60 * 60 * 1000;
      if (!window.cookieStore) {
        throw new Error("Unsupported");
      }
      window.cookieStore.set({
        name: "partition_test",
        value: data,
        expires: Date.now() + msPerHour,
        sameSite: "none"
      });
    }
read: async () => {
      if (!window.cookieStore) {
        throw new Error("Unsupported");
      }
      const cookie = await window.cookieStore.get("partition_test");
      if (!cookie) {
        return null;
      }
      return cookie.value;
    }
result, same first party: 
e8485c10-5a92-474a-bd16-de23f46ee0e2,
cd5b479f-1529-4f43-a586-810ed83aeb8b
result, different first party: 
e8485c10-5a92-474a-bd16-de23f46ee0e2,
cd5b479f-1529-4f43-a586-810ed83aeb8b
unsupported: false, false
passed: false, false
test failed: false, false | write: (data) => {
      const msPerHour = 60 * 60 * 1000;
      if (!window.cookieStore) {
        throw new Error("Unsupported");
      }
      window.cookieStore.set({
        name: "partition_test",
        value: data,
        expires: Date.now() + msPerHour,
        sameSite: "none"
      });
    }
read: async () => {
      if (!window.cookieStore) {
        throw new Error("Unsupported");
      }
      const cookie = await window.cookieStore.get("partition_test");
      if (!cookie) {
        return null;
      }
      return cookie.value;
    }
result, same first party: 
e96973da-bf72-4eb1-b6ab-a4e6c6775fb4,
67fb6941-7c51-4727-b485-317211e65ae0
result, different first party: 
e96973da-bf72-4eb1-b6ab-a4e6c6775fb4,
67fb6941-7c51-4727-b485-317211e65ae0
unsupported: false, false
passed: false, false
test failed: false, false | write: (data) => {
      const msPerHour = 60 * 60 * 1000;
      if (!window.cookieStore) {
        throw new Error("Unsupported");
      }
      window.cookieStore.set({
        name: "partition_test",
        value: data,
        expires: Date.now() + msPerHour,
        sameSite: "none"
      });
    }
read: async () => {
      if (!window.cookieStore) {
        throw new Error("Unsupported");
      }
      const cookie = await window.cookieStore.get("partition_test");
      if (!cookie) {
        return null;
      }
      return cookie.value;
    }
result, same first party: 
Error: Unsupported,
Error: Unsupported
result, different first party: 
Error: Unsupported,
Error: Unsupported
unsupported: true, true
passed: undefined
test failed: false, false | write: (data) => {
      const msPerHour = 60 * 60 * 1000;
      if (!window.cookieStore) {
        throw new Error("Unsupported");
      }
      window.cookieStore.set({
        name: "partition_test",
        value: data,
        expires: Date.now() + msPerHour,
        sameSite: "none"
      });
    }
read: async () => {
      if (!window.cookieStore) {
        throw new Error("Unsupported");
      }
      const cookie = await window.cookieStore.get("partition_test");
      if (!cookie) {
        return null;
      }
      return cookie.value;
    }
result, same first party: 
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
result, different first party: 
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
unsupported: true, true, true
passed: undefined
test failed: false, false, false | write: (data) => {
      const msPerHour = 60 * 60 * 1000;
      if (!window.cookieStore) {
        throw new Error("Unsupported");
      }
      window.cookieStore.set({
        name: "partition_test",
        value: data,
        expires: Date.now() + msPerHour,
        sameSite: "none"
      });
    }
read: async () => {
      if (!window.cookieStore) {
        throw new Error("Unsupported");
      }
      const cookie = await window.cookieStore.get("partition_test");
      if (!cookie) {
        return null;
      }
      return cookie.value;
    }
result, same first party: 
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
result, different first party: 
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
unsupported: true, true, true
passed: undefined
test failed: false, false, false | write: (data) => {
      const msPerHour = 60 * 60 * 1000;
      if (!window.cookieStore) {
        throw new Error("Unsupported");
      }
      window.cookieStore.set({
        name: "partition_test",
        value: data,
        expires: Date.now() + msPerHour,
        sameSite: "none"
      });
    }
read: async () => {
      if (!window.cookieStore) {
        throw new Error("Unsupported");
      }
      const cookie = await window.cookieStore.get("partition_test");
      if (!cookie) {
        return null;
      }
      return cookie.value;
    }
result, same first party: 
50c6a369-0b5c-4de5-a70b-a0516825119e,
49e0d8b1-c126-4b6b-be66-6ba7acfd85a9
result, different first party: 
50c6a369-0b5c-4de5-a70b-a0516825119e,
49e0d8b1-c126-4b6b-be66-6ba7acfd85a9
unsupported: false, false
passed: false, false
test failed: false, false | write: (data) => {
      const msPerHour = 60 * 60 * 1000;
      if (!window.cookieStore) {
        throw new Error("Unsupported");
      }
      window.cookieStore.set({
        name: "partition_test",
        value: data,
        expires: Date.now() + msPerHour,
        sameSite: "none"
      });
    }
read: async () => {
      if (!window.cookieStore) {
        throw new Error("Unsupported");
      }
      const cookie = await window.cookieStore.get("partition_test");
      if (!cookie) {
        return null;
      }
      return cookie.value;
    }
result, same first party: 
Error: Unsupported,
Error: Unsupported
result, different first party: 
Error: Unsupported,
Error: Unsupported
unsupported: true, true
passed: undefined
test failed: false, false | write: (data) => {
      const msPerHour = 60 * 60 * 1000;
      if (!window.cookieStore) {
        throw new Error("Unsupported");
      }
      window.cookieStore.set({
        name: "partition_test",
        value: data,
        expires: Date.now() + msPerHour,
        sameSite: "none"
      });
    }
read: async () => {
      if (!window.cookieStore) {
        throw new Error("Unsupported");
      }
      const cookie = await window.cookieStore.get("partition_test");
      if (!cookie) {
        return null;
      }
      return cookie.value;
    }
result, same first party: 
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
result, different first party: 
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
unsupported: true, true, true
passed: undefined
test failed: false, false, false | write: (data) => {
      const msPerHour = 60 * 60 * 1000;
      if (!window.cookieStore) {
        throw new Error("Unsupported");
      }
      window.cookieStore.set({
        name: "partition_test",
        value: data,
        expires: Date.now() + msPerHour,
        sameSite: "none"
      });
    }
read: async () => {
      if (!window.cookieStore) {
        throw new Error("Unsupported");
      }
      const cookie = await window.cookieStore.get("partition_test");
      if (!cookie) {
        return null;
      }
      return cookie.value;
    }
result, same first party: 
3242a94e-3344-4cb9-ba7c-ebedaf5d6e01,
87918123-fc52-4196-9d0c-a2cfc3f826d4
result, different first party: , 
unsupported: false, false
passed: true, true
test failed: false, false | write: (data) => {
      const msPerHour = 60 * 60 * 1000;
      if (!window.cookieStore) {
        throw new Error("Unsupported");
      }
      window.cookieStore.set({
        name: "partition_test",
        value: data,
        expires: Date.now() + msPerHour,
        sameSite: "none"
      });
    }
read: async () => {
      if (!window.cookieStore) {
        throw new Error("Unsupported");
      }
      const cookie = await window.cookieStore.get("partition_test");
      if (!cookie) {
        return null;
      }
      return cookie.value;
    }
result, same first party: 
5c052838-a55e-4690-83c6-b7d572146713,
49042d99-5b0e-4f37-82f3-78de245eea34
result, different first party: 
5c052838-a55e-4690-83c6-b7d572146713,
49042d99-5b0e-4f37-82f3-78de245eea34
unsupported: false, false
passed: false, false
test failed: false, false | 
| CSS cache CSS stylesheets are cached, and if that cache is shared between websites, it can be used to track users across sites.
               | write: async (key) => {
      const href = testURI("resource", "css", key);
      const head = document.getElementsByTagName("head")[0];
      head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
      const testElement = document.querySelector("#css");
      let fontFamily;
      while (true) {
        await sleepMs(100);
        fontFamily = getComputedStyle(testElement).fontFamily;
        if (fontFamily.startsWith("fake")) {
          break;
        }
      }
      console.log(fontFamily);
      return key;
    }
read: async (key) => {
      const href = testURI("resource", "css", key);
      const head = document.getElementsByTagName("head")[0];
      head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
      const testElement = document.querySelector("#css");
      let fontFamily;
      while (true) {
        await sleepMs(100);
        fontFamily = getComputedStyle(testElement).fontFamily;
        if (fontFamily.startsWith("fake")) {
          break;
        }
      }
      console.log(fontFamily);
      return fontFamily;
    }
result, same first party: 
fake_6115171383856657,
fake_052148203702226725,
fake_46454090916436064
result, different first party: 
fake_27462044088298754,
fake_8771279885375369,
fake_14804366774406885
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (key) => {
      const href = testURI("resource", "css", key);
      const head = document.getElementsByTagName("head")[0];
      head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
      const testElement = document.querySelector("#css");
      let fontFamily;
      while (true) {
        await sleepMs(100);
        fontFamily = getComputedStyle(testElement).fontFamily;
        if (fontFamily.startsWith("fake")) {
          break;
        }
      }
      console.log(fontFamily);
      return key;
    }
read: async (key) => {
      const href = testURI("resource", "css", key);
      const head = document.getElementsByTagName("head")[0];
      head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
      const testElement = document.querySelector("#css");
      let fontFamily;
      while (true) {
        await sleepMs(100);
        fontFamily = getComputedStyle(testElement).fontFamily;
        if (fontFamily.startsWith("fake")) {
          break;
        }
      }
      console.log(fontFamily);
      return fontFamily;
    }
result, same first party: 
fake_8020977657571622,
fake_7346673966936372
result, different first party: 
fake_48575446640659026,
fake_3545149319808709
unsupported: false, false
passed: true, true
test failed: false, false | write: async (key) => {
      const href = testURI("resource", "css", key);
      const head = document.getElementsByTagName("head")[0];
      head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
      const testElement = document.querySelector("#css");
      let fontFamily;
      while (true) {
        await sleepMs(100);
        fontFamily = getComputedStyle(testElement).fontFamily;
        if (fontFamily.startsWith("fake")) {
          break;
        }
      }
      console.log(fontFamily);
      return key;
    }
read: async (key) => {
      const href = testURI("resource", "css", key);
      const head = document.getElementsByTagName("head")[0];
      head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
      const testElement = document.querySelector("#css");
      let fontFamily;
      while (true) {
        await sleepMs(100);
        fontFamily = getComputedStyle(testElement).fontFamily;
        if (fontFamily.startsWith("fake")) {
          break;
        }
      }
      console.log(fontFamily);
      return fontFamily;
    }
result, same first party: 
fake_48160521716136495,
fake_09709340505421982
result, different first party: 
fake_1453438429035514,
fake_5970474222747941
unsupported: false, false
passed: true, true
test failed: false, false | write: async (key) => {
      const href = testURI("resource", "css", key);
      const head = document.getElementsByTagName("head")[0];
      head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
      const testElement = document.querySelector("#css");
      let fontFamily;
      while (true) {
        await sleepMs(100);
        fontFamily = getComputedStyle(testElement).fontFamily;
        if (fontFamily.startsWith("fake")) {
          break;
        }
      }
      console.log(fontFamily);
      return key;
    }
read: async (key) => {
      const href = testURI("resource", "css", key);
      const head = document.getElementsByTagName("head")[0];
      head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
      const testElement = document.querySelector("#css");
      let fontFamily;
      while (true) {
        await sleepMs(100);
        fontFamily = getComputedStyle(testElement).fontFamily;
        if (fontFamily.startsWith("fake")) {
          break;
        }
      }
      console.log(fontFamily);
      return fontFamily;
    }
result, same first party: 
fake_7484681749560473,
fake_7977853181782064
result, different first party: 
fake_7860559844699049,
fake_21364024842810392
unsupported: false, false
passed: true, true
test failed: false, false | write: async (key) => {
      const href = testURI("resource", "css", key);
      const head = document.getElementsByTagName("head")[0];
      head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
      const testElement = document.querySelector("#css");
      let fontFamily;
      while (true) {
        await sleepMs(100);
        fontFamily = getComputedStyle(testElement).fontFamily;
        if (fontFamily.startsWith("fake")) {
          break;
        }
      }
      console.log(fontFamily);
      return key;
    }
read: async (key) => {
      const href = testURI("resource", "css", key);
      const head = document.getElementsByTagName("head")[0];
      head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
      const testElement = document.querySelector("#css");
      let fontFamily;
      while (true) {
        await sleepMs(100);
        fontFamily = getComputedStyle(testElement).fontFamily;
        if (fontFamily.startsWith("fake")) {
          break;
        }
      }
      console.log(fontFamily);
      return fontFamily;
    }
result, same first party: 
fake_9599451386444993,
fake_09548662500965466,
fake_03710528834473337
result, different first party: 
fake_7102080825722721,
fake_9665252069620958,
fake_21817744698795605
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (key) => {
      const href = testURI("resource", "css", key);
      const head = document.getElementsByTagName("head")[0];
      head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
      const testElement = document.querySelector("#css");
      let fontFamily;
      while (true) {
        await sleepMs(100);
        fontFamily = getComputedStyle(testElement).fontFamily;
        if (fontFamily.startsWith("fake")) {
          break;
        }
      }
      console.log(fontFamily);
      return key;
    }
read: async (key) => {
      const href = testURI("resource", "css", key);
      const head = document.getElementsByTagName("head")[0];
      head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
      const testElement = document.querySelector("#css");
      let fontFamily;
      while (true) {
        await sleepMs(100);
        fontFamily = getComputedStyle(testElement).fontFamily;
        if (fontFamily.startsWith("fake")) {
          break;
        }
      }
      console.log(fontFamily);
      return fontFamily;
    }
result, same first party: 
fake_12777076868804826,
fake_9518816592256487,
fake_8839325914031502
result, different first party: 
fake_3800393206845962,
fake_1949691825974349,
fake_7130035623342659
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (key) => {
      const href = testURI("resource", "css", key);
      const head = document.getElementsByTagName("head")[0];
      head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
      const testElement = document.querySelector("#css");
      let fontFamily;
      while (true) {
        await sleepMs(100);
        fontFamily = getComputedStyle(testElement).fontFamily;
        if (fontFamily.startsWith("fake")) {
          break;
        }
      }
      console.log(fontFamily);
      return key;
    }
read: async (key) => {
      const href = testURI("resource", "css", key);
      const head = document.getElementsByTagName("head")[0];
      head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
      const testElement = document.querySelector("#css");
      let fontFamily;
      while (true) {
        await sleepMs(100);
        fontFamily = getComputedStyle(testElement).fontFamily;
        if (fontFamily.startsWith("fake")) {
          break;
        }
      }
      console.log(fontFamily);
      return fontFamily;
    }
result, same first party: 
fake_23890638371159456,
fake_6199473574505661
result, different first party: 
fake_33669692198952195,
fake_7382586247052028
unsupported: false, false
passed: true, true
test failed: false, false | write: async (key) => {
      const href = testURI("resource", "css", key);
      const head = document.getElementsByTagName("head")[0];
      head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
      const testElement = document.querySelector("#css");
      let fontFamily;
      while (true) {
        await sleepMs(100);
        fontFamily = getComputedStyle(testElement).fontFamily;
        if (fontFamily.startsWith("fake")) {
          break;
        }
      }
      console.log(fontFamily);
      return key;
    }
read: async (key) => {
      const href = testURI("resource", "css", key);
      const head = document.getElementsByTagName("head")[0];
      head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
      const testElement = document.querySelector("#css");
      let fontFamily;
      while (true) {
        await sleepMs(100);
        fontFamily = getComputedStyle(testElement).fontFamily;
        if (fontFamily.startsWith("fake")) {
          break;
        }
      }
      console.log(fontFamily);
      return fontFamily;
    }
result, same first party: 
fake_5582883318796807,
fake_5217851920367649
result, different first party: 
fake_35839607458191924,
fake_413425797885115
unsupported: false, false
passed: true, true
test failed: false, false | write: async (key) => {
      const href = testURI("resource", "css", key);
      const head = document.getElementsByTagName("head")[0];
      head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
      const testElement = document.querySelector("#css");
      let fontFamily;
      while (true) {
        await sleepMs(100);
        fontFamily = getComputedStyle(testElement).fontFamily;
        if (fontFamily.startsWith("fake")) {
          break;
        }
      }
      console.log(fontFamily);
      return key;
    }
read: async (key) => {
      const href = testURI("resource", "css", key);
      const head = document.getElementsByTagName("head")[0];
      head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
      const testElement = document.querySelector("#css");
      let fontFamily;
      while (true) {
        await sleepMs(100);
        fontFamily = getComputedStyle(testElement).fontFamily;
        if (fontFamily.startsWith("fake")) {
          break;
        }
      }
      console.log(fontFamily);
      return fontFamily;
    }
result, same first party: 
fake_6371459004822237,
fake_1720101312560336,
fake_5362574549535613
result, different first party: 
fake_053092148632338576,
fake_32730676193021946,
fake_11190452516616456
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (key) => {
      const href = testURI("resource", "css", key);
      const head = document.getElementsByTagName("head")[0];
      head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
      const testElement = document.querySelector("#css");
      let fontFamily;
      while (true) {
        await sleepMs(100);
        fontFamily = getComputedStyle(testElement).fontFamily;
        if (fontFamily.startsWith("fake")) {
          break;
        }
      }
      console.log(fontFamily);
      return key;
    }
read: async (key) => {
      const href = testURI("resource", "css", key);
      const head = document.getElementsByTagName("head")[0];
      head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
      const testElement = document.querySelector("#css");
      let fontFamily;
      while (true) {
        await sleepMs(100);
        fontFamily = getComputedStyle(testElement).fontFamily;
        if (fontFamily.startsWith("fake")) {
          break;
        }
      }
      console.log(fontFamily);
      return fontFamily;
    }
result, same first party: 
fake_9988111991432336,
fake_4925750102200077
result, different first party: 
fake_8242274422244522,
fake_8230453138493643
unsupported: false, false
passed: true, true
test failed: false, false | write: async (key) => {
      const href = testURI("resource", "css", key);
      const head = document.getElementsByTagName("head")[0];
      head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
      const testElement = document.querySelector("#css");
      let fontFamily;
      while (true) {
        await sleepMs(100);
        fontFamily = getComputedStyle(testElement).fontFamily;
        if (fontFamily.startsWith("fake")) {
          break;
        }
      }
      console.log(fontFamily);
      return key;
    }
read: async (key) => {
      const href = testURI("resource", "css", key);
      const head = document.getElementsByTagName("head")[0];
      head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
      const testElement = document.querySelector("#css");
      let fontFamily;
      while (true) {
        await sleepMs(100);
        fontFamily = getComputedStyle(testElement).fontFamily;
        if (fontFamily.startsWith("fake")) {
          break;
        }
      }
      console.log(fontFamily);
      return fontFamily;
    }
result, same first party: 
fake_08872427247646386,
fake_1498794322959769
result, different first party: 
fake_2510896842537145,
fake_7747782912613754
unsupported: false, false
passed: true, true
test failed: false, false | 
| favicon cache A favicon is an icon that represents a website, typically shown in browser tab and bookmarks menu. If the favicon cache is not partitioned, it can be used to track users across websites.
               | write: (key) => key
read: async (key) => {
      // Wait for the favicon to load (defined in supercookies.html)
      await sleepMs(2000);
      let response = await fetch(
        testURI("ctr", "favicon", key), {"cache": "reload"});
      let count = (await response.text()).trim();
      if (count === "0") {
        throw new Error("No requests received");
      }
      return count;
    }
result, same first party: 1, 1, 1
result, different first party: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (key) => key
read: async (key) => {
      // Wait for the favicon to load (defined in supercookies.html)
      await sleepMs(2000);
      let response = await fetch(
        testURI("ctr", "favicon", key), {"cache": "reload"});
      let count = (await response.text()).trim();
      if (count === "0") {
        throw new Error("No requests received");
      }
      return count;
    }
result, same first party: 1, 1
result, different first party: 2, 2
unsupported: false, false
passed: true, true
test failed: false, false | write: (key) => key
read: async (key) => {
      // Wait for the favicon to load (defined in supercookies.html)
      await sleepMs(2000);
      let response = await fetch(
        testURI("ctr", "favicon", key), {"cache": "reload"});
      let count = (await response.text()).trim();
      if (count === "0") {
        throw new Error("No requests received");
      }
      return count;
    }
result, same first party: 1, 1
result, different first party: 2, 2
unsupported: false, false
passed: true, true
test failed: false, false | write: (key) => key
read: async (key) => {
      // Wait for the favicon to load (defined in supercookies.html)
      await sleepMs(2000);
      let response = await fetch(
        testURI("ctr", "favicon", key), {"cache": "reload"});
      let count = (await response.text()).trim();
      if (count === "0") {
        throw new Error("No requests received");
      }
      return count;
    }
result, same first party: 1, 1
result, different first party: 2, 2
unsupported: false, false
passed: true, true
test failed: false, false | write: (key) => key
read: async (key) => {
      // Wait for the favicon to load (defined in supercookies.html)
      await sleepMs(2000);
      let response = await fetch(
        testURI("ctr", "favicon", key), {"cache": "reload"});
      let count = (await response.text()).trim();
      if (count === "0") {
        throw new Error("No requests received");
      }
      return count;
    }
result, same first party: 1, 1, 1
result, different first party: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (key) => key
read: async (key) => {
      // Wait for the favicon to load (defined in supercookies.html)
      await sleepMs(2000);
      let response = await fetch(
        testURI("ctr", "favicon", key), {"cache": "reload"});
      let count = (await response.text()).trim();
      if (count === "0") {
        throw new Error("No requests received");
      }
      return count;
    }
result, same first party: 1, 1, 1
result, different first party: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (key) => key
read: async (key) => {
      // Wait for the favicon to load (defined in supercookies.html)
      await sleepMs(2000);
      let response = await fetch(
        testURI("ctr", "favicon", key), {"cache": "reload"});
      let count = (await response.text()).trim();
      if (count === "0") {
        throw new Error("No requests received");
      }
      return count;
    }
result, same first party: 1, 1
result, different first party: 2, 2
unsupported: false, false
passed: true, true
test failed: false, false | write: (key) => key
read: async (key) => {
      // Wait for the favicon to load (defined in supercookies.html)
      await sleepMs(2000);
      let response = await fetch(
        testURI("ctr", "favicon", key), {"cache": "reload"});
      let count = (await response.text()).trim();
      if (count === "0") {
        throw new Error("No requests received");
      }
      return count;
    }
result, same first party: 1, 1
result, different first party: 2, 2
unsupported: false, false
passed: true, true
test failed: false, false | write: (key) => key
read: async (key) => {
      // Wait for the favicon to load (defined in supercookies.html)
      await sleepMs(2000);
      let response = await fetch(
        testURI("ctr", "favicon", key), {"cache": "reload"});
      let count = (await response.text()).trim();
      if (count === "0") {
        throw new Error("No requests received");
      }
      return count;
    }
result, same first party: 1, 1, 1
result, different first party: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (key) => key
read: async (key) => {
      // Wait for the favicon to load (defined in supercookies.html)
      await sleepMs(2000);
      let response = await fetch(
        testURI("ctr", "favicon", key), {"cache": "reload"});
      let count = (await response.text()).trim();
      if (count === "0") {
        throw new Error("No requests received");
      }
      return count;
    }
result, same first party: 1, 1
result, different first party: 2, 2
unsupported: false, false
passed: true, true
test failed: false, false | write: (key) => key
read: async (key) => {
      // Wait for the favicon to load (defined in supercookies.html)
      await sleepMs(2000);
      let response = await fetch(
        testURI("ctr", "favicon", key), {"cache": "reload"});
      let count = (await response.text()).trim();
      if (count === "0") {
        throw new Error("No requests received");
      }
      return count;
    }
result, same first party: 2, 2
result, different first party: 3, 3
unsupported: false, false
passed: true, true
test failed: false, false | 
| fetch cache When a resource is received via the Fetch API, it is frequently cached. That cache can potentially be abused for cross-site tracking.
               | write: async (key) => {
      let response = await fetch(testURI("resource", "fetch", key),
                                 {cache: "force-cache"});
      return key;
    }
read: async (key) => {
      let response = await fetch(testURI("resource", "fetch", key),
                                 {cache: "force-cache"});
      let countResponse = await fetch(testURI("ctr", "fetch", key),
                                      {cache: "reload"});
      return (await countResponse.text()).trim();
    }
result, same first party: 1, 1, 1
result, different first party: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (key) => {
      let response = await fetch(testURI("resource", "fetch", key),
                                 {cache: "force-cache"});
      return key;
    }
read: async (key) => {
      let response = await fetch(testURI("resource", "fetch", key),
                                 {cache: "force-cache"});
      let countResponse = await fetch(testURI("ctr", "fetch", key),
                                      {cache: "reload"});
      return (await countResponse.text()).trim();
    }
result, same first party: 1, 1
result, different first party: 2, 2
unsupported: false, false
passed: true, true
test failed: false, false | write: async (key) => {
      let response = await fetch(testURI("resource", "fetch", key),
                                 {cache: "force-cache"});
      return key;
    }
read: async (key) => {
      let response = await fetch(testURI("resource", "fetch", key),
                                 {cache: "force-cache"});
      let countResponse = await fetch(testURI("ctr", "fetch", key),
                                      {cache: "reload"});
      return (await countResponse.text()).trim();
    }
result, same first party: 1, 1
result, different first party: 2, 2
unsupported: false, false
passed: true, true
test failed: false, false | write: async (key) => {
      let response = await fetch(testURI("resource", "fetch", key),
                                 {cache: "force-cache"});
      return key;
    }
read: async (key) => {
      let response = await fetch(testURI("resource", "fetch", key),
                                 {cache: "force-cache"});
      let countResponse = await fetch(testURI("ctr", "fetch", key),
                                      {cache: "reload"});
      return (await countResponse.text()).trim();
    }
result, same first party: 1, 1
result, different first party: 2, 2
unsupported: false, false
passed: true, true
test failed: false, false | write: async (key) => {
      let response = await fetch(testURI("resource", "fetch", key),
                                 {cache: "force-cache"});
      return key;
    }
read: async (key) => {
      let response = await fetch(testURI("resource", "fetch", key),
                                 {cache: "force-cache"});
      let countResponse = await fetch(testURI("ctr", "fetch", key),
                                      {cache: "reload"});
      return (await countResponse.text()).trim();
    }
result, same first party: 1, 1, 1
result, different first party: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (key) => {
      let response = await fetch(testURI("resource", "fetch", key),
                                 {cache: "force-cache"});
      return key;
    }
read: async (key) => {
      let response = await fetch(testURI("resource", "fetch", key),
                                 {cache: "force-cache"});
      let countResponse = await fetch(testURI("ctr", "fetch", key),
                                      {cache: "reload"});
      return (await countResponse.text()).trim();
    }
result, same first party: 1, 1, 1
result, different first party: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (key) => {
      let response = await fetch(testURI("resource", "fetch", key),
                                 {cache: "force-cache"});
      return key;
    }
read: async (key) => {
      let response = await fetch(testURI("resource", "fetch", key),
                                 {cache: "force-cache"});
      let countResponse = await fetch(testURI("ctr", "fetch", key),
                                      {cache: "reload"});
      return (await countResponse.text()).trim();
    }
result, same first party: 1, 1
result, different first party: 2, 2
unsupported: false, false
passed: true, true
test failed: false, false | write: async (key) => {
      let response = await fetch(testURI("resource", "fetch", key),
                                 {cache: "force-cache"});
      return key;
    }
read: async (key) => {
      let response = await fetch(testURI("resource", "fetch", key),
                                 {cache: "force-cache"});
      let countResponse = await fetch(testURI("ctr", "fetch", key),
                                      {cache: "reload"});
      return (await countResponse.text()).trim();
    }
result, same first party: 1, 1
result, different first party: 2, 2
unsupported: false, false
passed: true, true
test failed: false, false | write: async (key) => {
      let response = await fetch(testURI("resource", "fetch", key),
                                 {cache: "force-cache"});
      return key;
    }
read: async (key) => {
      let response = await fetch(testURI("resource", "fetch", key),
                                 {cache: "force-cache"});
      let countResponse = await fetch(testURI("ctr", "fetch", key),
                                      {cache: "reload"});
      return (await countResponse.text()).trim();
    }
result, same first party: 1, 1, 1
result, different first party: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (key) => {
      let response = await fetch(testURI("resource", "fetch", key),
                                 {cache: "force-cache"});
      return key;
    }
read: async (key) => {
      let response = await fetch(testURI("resource", "fetch", key),
                                 {cache: "force-cache"});
      let countResponse = await fetch(testURI("ctr", "fetch", key),
                                      {cache: "reload"});
      return (await countResponse.text()).trim();
    }
result, same first party: 1, 1
result, different first party: 2, 2
unsupported: false, false
passed: true, true
test failed: false, false | write: async (key) => {
      let response = await fetch(testURI("resource", "fetch", key),
                                 {cache: "force-cache"});
      return key;
    }
read: async (key) => {
      let response = await fetch(testURI("resource", "fetch", key),
                                 {cache: "force-cache"});
      let countResponse = await fetch(testURI("ctr", "fetch", key),
                                      {cache: "reload"});
      return (await countResponse.text()).trim();
    }
result, same first party: 1, 1
result, different first party: 2, 2
unsupported: false, false
passed: true, true
test failed: false, false | 
| font cache Web fonts are sometimes stored in their own cache, which is vulnerable to being abused for cross-site tracking.
               | write: async (key) => {
      let style = document.createElement("style");
      style.type='text/css';
      let fontURI = testURI("resource", "font", key);
      style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } body { font-family: "myFont" }`;
      document.getElementsByTagName("head")[0].appendChild(style);
      return key;
    }
read: async (key) => {
      const text = document.createElement("span");
      text.id = "text";
      text.innerText = "test";
      document.body.appendChild(text);
      const originalWidth = text.getBoundingClientRect().width;
      let style = document.createElement("style");
      style.type='text/css';
      let fontURI = testURI("resource", "font", key);
      style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } #text { font-family: "myFont" }`;
      document.getElementsByTagName("head")[0].appendChild(style);
      let newWidth;
      do {
        await sleepMs(100);
        newWidth = text.getBoundingClientRect().width;
      } while (newWidth < 0 || newWidth === originalWidth)
      let response = await fetch(
        testURI("ctr", "font", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same first party: 1, 1, 1
result, different first party: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (key) => {
      let style = document.createElement("style");
      style.type='text/css';
      let fontURI = testURI("resource", "font", key);
      style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } body { font-family: "myFont" }`;
      document.getElementsByTagName("head")[0].appendChild(style);
      return key;
    }
read: async (key) => {
      const text = document.createElement("span");
      text.id = "text";
      text.innerText = "test";
      document.body.appendChild(text);
      const originalWidth = text.getBoundingClientRect().width;
      let style = document.createElement("style");
      style.type='text/css';
      let fontURI = testURI("resource", "font", key);
      style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } #text { font-family: "myFont" }`;
      document.getElementsByTagName("head")[0].appendChild(style);
      let newWidth;
      do {
        await sleepMs(100);
        newWidth = text.getBoundingClientRect().width;
      } while (newWidth < 0 || newWidth === originalWidth)
      let response = await fetch(
        testURI("ctr", "font", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same first party: 1, 1
result, different first party: 2, 2
unsupported: false, false
passed: true, true
test failed: false, false | write: async (key) => {
      let style = document.createElement("style");
      style.type='text/css';
      let fontURI = testURI("resource", "font", key);
      style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } body { font-family: "myFont" }`;
      document.getElementsByTagName("head")[0].appendChild(style);
      return key;
    }
read: async (key) => {
      const text = document.createElement("span");
      text.id = "text";
      text.innerText = "test";
      document.body.appendChild(text);
      const originalWidth = text.getBoundingClientRect().width;
      let style = document.createElement("style");
      style.type='text/css';
      let fontURI = testURI("resource", "font", key);
      style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } #text { font-family: "myFont" }`;
      document.getElementsByTagName("head")[0].appendChild(style);
      let newWidth;
      do {
        await sleepMs(100);
        newWidth = text.getBoundingClientRect().width;
      } while (newWidth < 0 || newWidth === originalWidth)
      let response = await fetch(
        testURI("ctr", "font", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same first party: 1, 1
result, different first party: 2, 2
unsupported: false, false
passed: true, true
test failed: false, false | write: async (key) => {
      let style = document.createElement("style");
      style.type='text/css';
      let fontURI = testURI("resource", "font", key);
      style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } body { font-family: "myFont" }`;
      document.getElementsByTagName("head")[0].appendChild(style);
      return key;
    }
read: async (key) => {
      const text = document.createElement("span");
      text.id = "text";
      text.innerText = "test";
      document.body.appendChild(text);
      const originalWidth = text.getBoundingClientRect().width;
      let style = document.createElement("style");
      style.type='text/css';
      let fontURI = testURI("resource", "font", key);
      style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } #text { font-family: "myFont" }`;
      document.getElementsByTagName("head")[0].appendChild(style);
      let newWidth;
      do {
        await sleepMs(100);
        newWidth = text.getBoundingClientRect().width;
      } while (newWidth < 0 || newWidth === originalWidth)
      let response = await fetch(
        testURI("ctr", "font", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same first party: 1, 1
result, different first party: 2, 2
unsupported: false, false
passed: true, true
test failed: false, false | write: async (key) => {
      let style = document.createElement("style");
      style.type='text/css';
      let fontURI = testURI("resource", "font", key);
      style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } body { font-family: "myFont" }`;
      document.getElementsByTagName("head")[0].appendChild(style);
      return key;
    }
read: async (key) => {
      const text = document.createElement("span");
      text.id = "text";
      text.innerText = "test";
      document.body.appendChild(text);
      const originalWidth = text.getBoundingClientRect().width;
      let style = document.createElement("style");
      style.type='text/css';
      let fontURI = testURI("resource", "font", key);
      style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } #text { font-family: "myFont" }`;
      document.getElementsByTagName("head")[0].appendChild(style);
      let newWidth;
      do {
        await sleepMs(100);
        newWidth = text.getBoundingClientRect().width;
      } while (newWidth < 0 || newWidth === originalWidth)
      let response = await fetch(
        testURI("ctr", "font", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same first party: 1, 1, 1
result, different first party: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (key) => {
      let style = document.createElement("style");
      style.type='text/css';
      let fontURI = testURI("resource", "font", key);
      style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } body { font-family: "myFont" }`;
      document.getElementsByTagName("head")[0].appendChild(style);
      return key;
    }
read: async (key) => {
      const text = document.createElement("span");
      text.id = "text";
      text.innerText = "test";
      document.body.appendChild(text);
      const originalWidth = text.getBoundingClientRect().width;
      let style = document.createElement("style");
      style.type='text/css';
      let fontURI = testURI("resource", "font", key);
      style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } #text { font-family: "myFont" }`;
      document.getElementsByTagName("head")[0].appendChild(style);
      let newWidth;
      do {
        await sleepMs(100);
        newWidth = text.getBoundingClientRect().width;
      } while (newWidth < 0 || newWidth === originalWidth)
      let response = await fetch(
        testURI("ctr", "font", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same first party: 1, 1, 1
result, different first party: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (key) => {
      let style = document.createElement("style");
      style.type='text/css';
      let fontURI = testURI("resource", "font", key);
      style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } body { font-family: "myFont" }`;
      document.getElementsByTagName("head")[0].appendChild(style);
      return key;
    }
read: async (key) => {
      const text = document.createElement("span");
      text.id = "text";
      text.innerText = "test";
      document.body.appendChild(text);
      const originalWidth = text.getBoundingClientRect().width;
      let style = document.createElement("style");
      style.type='text/css';
      let fontURI = testURI("resource", "font", key);
      style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } #text { font-family: "myFont" }`;
      document.getElementsByTagName("head")[0].appendChild(style);
      let newWidth;
      do {
        await sleepMs(100);
        newWidth = text.getBoundingClientRect().width;
      } while (newWidth < 0 || newWidth === originalWidth)
      let response = await fetch(
        testURI("ctr", "font", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same first party: 1, 1
result, different first party: 2, 2
unsupported: false, false
passed: true, true
test failed: false, false | write: async (key) => {
      let style = document.createElement("style");
      style.type='text/css';
      let fontURI = testURI("resource", "font", key);
      style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } body { font-family: "myFont" }`;
      document.getElementsByTagName("head")[0].appendChild(style);
      return key;
    }
read: async (key) => {
      const text = document.createElement("span");
      text.id = "text";
      text.innerText = "test";
      document.body.appendChild(text);
      const originalWidth = text.getBoundingClientRect().width;
      let style = document.createElement("style");
      style.type='text/css';
      let fontURI = testURI("resource", "font", key);
      style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } #text { font-family: "myFont" }`;
      document.getElementsByTagName("head")[0].appendChild(style);
      let newWidth;
      do {
        await sleepMs(100);
        newWidth = text.getBoundingClientRect().width;
      } while (newWidth < 0 || newWidth === originalWidth)
      let response = await fetch(
        testURI("ctr", "font", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same first party: 2, 2
result, different first party: 3, 3
unsupported: false, false
passed: true, true
test failed: false, false | write: async (key) => {
      let style = document.createElement("style");
      style.type='text/css';
      let fontURI = testURI("resource", "font", key);
      style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } body { font-family: "myFont" }`;
      document.getElementsByTagName("head")[0].appendChild(style);
      return key;
    }
read: async (key) => {
      const text = document.createElement("span");
      text.id = "text";
      text.innerText = "test";
      document.body.appendChild(text);
      const originalWidth = text.getBoundingClientRect().width;
      let style = document.createElement("style");
      style.type='text/css';
      let fontURI = testURI("resource", "font", key);
      style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } #text { font-family: "myFont" }`;
      document.getElementsByTagName("head")[0].appendChild(style);
      let newWidth;
      do {
        await sleepMs(100);
        newWidth = text.getBoundingClientRect().width;
      } while (newWidth < 0 || newWidth === originalWidth)
      let response = await fetch(
        testURI("ctr", "font", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same first party: 1, 1, 1
result, different first party: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (key) => {
      let style = document.createElement("style");
      style.type='text/css';
      let fontURI = testURI("resource", "font", key);
      style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } body { font-family: "myFont" }`;
      document.getElementsByTagName("head")[0].appendChild(style);
      return key;
    }
read: async (key) => {
      const text = document.createElement("span");
      text.id = "text";
      text.innerText = "test";
      document.body.appendChild(text);
      const originalWidth = text.getBoundingClientRect().width;
      let style = document.createElement("style");
      style.type='text/css';
      let fontURI = testURI("resource", "font", key);
      style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } #text { font-family: "myFont" }`;
      document.getElementsByTagName("head")[0].appendChild(style);
      let newWidth;
      do {
        await sleepMs(100);
        newWidth = text.getBoundingClientRect().width;
      } while (newWidth < 0 || newWidth === originalWidth)
      let response = await fetch(
        testURI("ctr", "font", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same first party: 1, 1
result, different first party: 2, 2
unsupported: false, false
passed: true, true
test failed: false, false | write: async (key) => {
      let style = document.createElement("style");
      style.type='text/css';
      let fontURI = testURI("resource", "font", key);
      style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } body { font-family: "myFont" }`;
      document.getElementsByTagName("head")[0].appendChild(style);
      return key;
    }
read: async (key) => {
      const text = document.createElement("span");
      text.id = "text";
      text.innerText = "test";
      document.body.appendChild(text);
      const originalWidth = text.getBoundingClientRect().width;
      let style = document.createElement("style");
      style.type='text/css';
      let fontURI = testURI("resource", "font", key);
      style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } #text { font-family: "myFont" }`;
      document.getElementsByTagName("head")[0].appendChild(style);
      let newWidth;
      do {
        await sleepMs(100);
        newWidth = text.getBoundingClientRect().width;
      } while (newWidth < 0 || newWidth === originalWidth)
      let response = await fetch(
        testURI("ctr", "font", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same first party: 1, 1
result, different first party: 2, 2
unsupported: false, false
passed: true, true
test failed: false, false | 
| getDirectory navigator.storage.getDirectory exposes a location for storing files to web content. In some cases, these files may be shared across tabs.
               | write: async (secret) => {
      try {
        const root = await navigator.storage.getDirectory();
        const fileHandle = await root.getFileHandle("secret.txt", { create: true });
        const stream = await fileHandle.createWritable();
        await stream.write(secret);
        await stream.close();
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      try {
        const root = await navigator.storage.getDirectory();
        const fileHandle = await root.getFileHandle("secret.txt");
        const file = await fileHandle.getFile();
        return file.text();
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
result, same first party: 
3133a4ca-686b-4047-91c3-9a7e181c53f7,
49872d4a-b01d-4af8-8cbe-85c379a609e2,
58bb2b32-4f83-47bb-91c8-96dce39cd97a
result, different first party: 
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (secret) => {
      try {
        const root = await navigator.storage.getDirectory();
        const fileHandle = await root.getFileHandle("secret.txt", { create: true });
        const stream = await fileHandle.createWritable();
        await stream.write(secret);
        await stream.close();
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      try {
        const root = await navigator.storage.getDirectory();
        const fileHandle = await root.getFileHandle("secret.txt");
        const file = await fileHandle.getFile();
        return file.text();
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
result, same first party: 
e8485c10-5a92-474a-bd16-de23f46ee0e2,
cd5b479f-1529-4f43-a586-810ed83aeb8b
result, different first party: 
Error: Unsupported,
Error: Unsupported
unsupported: false, false
passed: true, true
test failed: false, false | write: async (secret) => {
      try {
        const root = await navigator.storage.getDirectory();
        const fileHandle = await root.getFileHandle("secret.txt", { create: true });
        const stream = await fileHandle.createWritable();
        await stream.write(secret);
        await stream.close();
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      try {
        const root = await navigator.storage.getDirectory();
        const fileHandle = await root.getFileHandle("secret.txt");
        const file = await fileHandle.getFile();
        return file.text();
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
result, same first party: 
e96973da-bf72-4eb1-b6ab-a4e6c6775fb4,
67fb6941-7c51-4727-b485-317211e65ae0
result, different first party: 
Error: Unsupported,
Error: Unsupported
unsupported: false, false
passed: true, true
test failed: false, false | write: async (secret) => {
      try {
        const root = await navigator.storage.getDirectory();
        const fileHandle = await root.getFileHandle("secret.txt", { create: true });
        const stream = await fileHandle.createWritable();
        await stream.write(secret);
        await stream.close();
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      try {
        const root = await navigator.storage.getDirectory();
        const fileHandle = await root.getFileHandle("secret.txt");
        const file = await fileHandle.getFile();
        return file.text();
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
result, same first party: 
94f1fd6d-f642-403e-8ef8-11180b18b1f6,
2bf6571e-4385-4b68-a076-f6868cbe4954
result, different first party: 
Error: Unsupported,
Error: Unsupported
unsupported: false, false
passed: true, true
test failed: false, false | write: async (secret) => {
      try {
        const root = await navigator.storage.getDirectory();
        const fileHandle = await root.getFileHandle("secret.txt", { create: true });
        const stream = await fileHandle.createWritable();
        await stream.write(secret);
        await stream.close();
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      try {
        const root = await navigator.storage.getDirectory();
        const fileHandle = await root.getFileHandle("secret.txt");
        const file = await fileHandle.getFile();
        return file.text();
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
result, same first party: 
da67bebb-5652-441a-816e-1206b25c0225,
0bad71fe-de93-4434-bfa0-bb7f0db62eea,
6aba94ed-010e-45af-892c-dd670cd8d134
result, different first party: 
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (secret) => {
      try {
        const root = await navigator.storage.getDirectory();
        const fileHandle = await root.getFileHandle("secret.txt", { create: true });
        const stream = await fileHandle.createWritable();
        await stream.write(secret);
        await stream.close();
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      try {
        const root = await navigator.storage.getDirectory();
        const fileHandle = await root.getFileHandle("secret.txt");
        const file = await fileHandle.getFile();
        return file.text();
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
result, same first party: 
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
result, different first party: 
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
unsupported: true, true, true
passed: undefined
test failed: false, false, false | write: async (secret) => {
      try {
        const root = await navigator.storage.getDirectory();
        const fileHandle = await root.getFileHandle("secret.txt", { create: true });
        const stream = await fileHandle.createWritable();
        await stream.write(secret);
        await stream.close();
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      try {
        const root = await navigator.storage.getDirectory();
        const fileHandle = await root.getFileHandle("secret.txt");
        const file = await fileHandle.getFile();
        return file.text();
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
result, same first party: 
50c6a369-0b5c-4de5-a70b-a0516825119e,
49e0d8b1-c126-4b6b-be66-6ba7acfd85a9
result, different first party: 
Error: Unsupported,
Error: Unsupported
unsupported: false, false
passed: true, true
test failed: false, false | write: async (secret) => {
      try {
        const root = await navigator.storage.getDirectory();
        const fileHandle = await root.getFileHandle("secret.txt", { create: true });
        const stream = await fileHandle.createWritable();
        await stream.write(secret);
        await stream.close();
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      try {
        const root = await navigator.storage.getDirectory();
        const fileHandle = await root.getFileHandle("secret.txt");
        const file = await fileHandle.getFile();
        return file.text();
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
result, same first party: , 
result, different first party: 
Error: Unsupported,
Error: Unsupported
unsupported: true, true
passed: undefined
test failed: false, false | write: async (secret) => {
      try {
        const root = await navigator.storage.getDirectory();
        const fileHandle = await root.getFileHandle("secret.txt", { create: true });
        const stream = await fileHandle.createWritable();
        await stream.write(secret);
        await stream.close();
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      try {
        const root = await navigator.storage.getDirectory();
        const fileHandle = await root.getFileHandle("secret.txt");
        const file = await fileHandle.getFile();
        return file.text();
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
result, same first party: 
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
result, different first party: 
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
unsupported: true, true, true
passed: undefined
test failed: false, false, false | write: async (secret) => {
      try {
        const root = await navigator.storage.getDirectory();
        const fileHandle = await root.getFileHandle("secret.txt", { create: true });
        const stream = await fileHandle.createWritable();
        await stream.write(secret);
        await stream.close();
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      try {
        const root = await navigator.storage.getDirectory();
        const fileHandle = await root.getFileHandle("secret.txt");
        const file = await fileHandle.getFile();
        return file.text();
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
result, same first party: 
3242a94e-3344-4cb9-ba7c-ebedaf5d6e01,
87918123-fc52-4196-9d0c-a2cfc3f826d4
result, different first party: 
Error: Unsupported,
Error: Unsupported
unsupported: false, false
passed: true, true
test failed: false, false | write: async (secret) => {
      try {
        const root = await navigator.storage.getDirectory();
        const fileHandle = await root.getFileHandle("secret.txt", { create: true });
        const stream = await fileHandle.createWritable();
        await stream.write(secret);
        await stream.close();
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      try {
        const root = await navigator.storage.getDirectory();
        const fileHandle = await root.getFileHandle("secret.txt");
        const file = await fileHandle.getFile();
        return file.text();
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
result, same first party: 
5c052838-a55e-4690-83c6-b7d572146713,
49042d99-5b0e-4f37-82f3-78de245eea34
result, different first party: 
Error: Unsupported,
Error: Unsupported
unsupported: false, false
passed: true, true
test failed: false, false | 
| H1 connection HTTP/1.x are the classic web connection protocols. If these connections are re-used across websites, they can be used to track users.
               | write: async (secret) => {
      await fetch(`https://h1.privacytests2.org:8901/?mode=write&secret=${secret}`, {cache: "no-store"});
    }
read: async () => {
      let response = await fetch(`https://h1.privacytests2.org:8901/?mode=read`, {cache: "no-store"});
      return await response.text();
    }
result, same first party: 
3133a4ca-686b-4047-91c3-9a7e181c53f7,
49872d4a-b01d-4af8-8cbe-85c379a609e2,
58bb2b32-4f83-47bb-91c8-96dce39cd97a
result, different first party: , , 
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (secret) => {
      await fetch(`https://h1.privacytests2.org:8901/?mode=write&secret=${secret}`, {cache: "no-store"});
    }
read: async () => {
      let response = await fetch(`https://h1.privacytests2.org:8901/?mode=read`, {cache: "no-store"});
      return await response.text();
    }
result, same first party: 
e8485c10-5a92-474a-bd16-de23f46ee0e2,
cd5b479f-1529-4f43-a586-810ed83aeb8b
result, different first party: , 
unsupported: false, false
passed: true, true
test failed: false, false | write: async (secret) => {
      await fetch(`https://h1.privacytests2.org:8901/?mode=write&secret=${secret}`, {cache: "no-store"});
    }
read: async () => {
      let response = await fetch(`https://h1.privacytests2.org:8901/?mode=read`, {cache: "no-store"});
      return await response.text();
    }
result, same first party: 
e96973da-bf72-4eb1-b6ab-a4e6c6775fb4,
67fb6941-7c51-4727-b485-317211e65ae0
result, different first party: , 
unsupported: false, false
passed: true, true
test failed: false, false | write: async (secret) => {
      await fetch(`https://h1.privacytests2.org:8901/?mode=write&secret=${secret}`, {cache: "no-store"});
    }
read: async () => {
      let response = await fetch(`https://h1.privacytests2.org:8901/?mode=read`, {cache: "no-store"});
      return await response.text();
    }
result, same first party: 
94f1fd6d-f642-403e-8ef8-11180b18b1f6,
2bf6571e-4385-4b68-a076-f6868cbe4954
result, different first party: , 
unsupported: false, false
passed: true, true
test failed: false, false | write: async (secret) => {
      await fetch(`https://h1.privacytests2.org:8901/?mode=write&secret=${secret}`, {cache: "no-store"});
    }
read: async () => {
      let response = await fetch(`https://h1.privacytests2.org:8901/?mode=read`, {cache: "no-store"});
      return await response.text();
    }
result, same first party: 
da67bebb-5652-441a-816e-1206b25c0225,
0bad71fe-de93-4434-bfa0-bb7f0db62eea,
6aba94ed-010e-45af-892c-dd670cd8d134
result, different first party: , , 
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (secret) => {
      await fetch(`https://h1.privacytests2.org:8901/?mode=write&secret=${secret}`, {cache: "no-store"});
    }
read: async () => {
      let response = await fetch(`https://h1.privacytests2.org:8901/?mode=read`, {cache: "no-store"});
      return await response.text();
    }
result, same first party: 
9dd63e63-3256-4352-99f6-805b6f48eb8c,
b7cb77db-aca9-4570-923e-b81da9175421,
736f05a1-05e6-491e-8dad-a6647bd52dda
result, different first party: , , 
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (secret) => {
      await fetch(`https://h1.privacytests2.org:8901/?mode=write&secret=${secret}`, {cache: "no-store"});
    }
read: async () => {
      let response = await fetch(`https://h1.privacytests2.org:8901/?mode=read`, {cache: "no-store"});
      return await response.text();
    }
result, same first party: 
50c6a369-0b5c-4de5-a70b-a0516825119e,
49e0d8b1-c126-4b6b-be66-6ba7acfd85a9
result, different first party: , 
unsupported: false, false
passed: true, true
test failed: false, false | write: async (secret) => {
      await fetch(`https://h1.privacytests2.org:8901/?mode=write&secret=${secret}`, {cache: "no-store"});
    }
read: async () => {
      let response = await fetch(`https://h1.privacytests2.org:8901/?mode=read`, {cache: "no-store"});
      return await response.text();
    }
result, same first party: 
6b07cf4a-a332-4a7c-90df-fbc3dc862f86,
2d40813a-a6c5-4a6b-bb1c-f4d01a399217
result, different first party: , 
unsupported: false, false
passed: true, true
test failed: false, false | write: async (secret) => {
      await fetch(`https://h1.privacytests2.org:8901/?mode=write&secret=${secret}`, {cache: "no-store"});
    }
read: async () => {
      let response = await fetch(`https://h1.privacytests2.org:8901/?mode=read`, {cache: "no-store"});
      return await response.text();
    }
result, same first party: 
3dc5c317-3ea7-42b4-be71-7902bcc4eb27,
4932f81b-b746-4e4b-b97d-c0b827047888,
3e016f09-5749-4084-a136-e9b829c1a824
result, different first party: , , 
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (secret) => {
      await fetch(`https://h1.privacytests2.org:8901/?mode=write&secret=${secret}`, {cache: "no-store"});
    }
read: async () => {
      let response = await fetch(`https://h1.privacytests2.org:8901/?mode=read`, {cache: "no-store"});
      return await response.text();
    }
result, same first party: 
3242a94e-3344-4cb9-ba7c-ebedaf5d6e01,
87918123-fc52-4196-9d0c-a2cfc3f826d4
result, different first party: , 
unsupported: false, false
passed: true, true
test failed: false, false | write: async (secret) => {
      await fetch(`https://h1.privacytests2.org:8901/?mode=write&secret=${secret}`, {cache: "no-store"});
    }
read: async () => {
      let response = await fetch(`https://h1.privacytests2.org:8901/?mode=read`, {cache: "no-store"});
      return await response.text();
    }
result, same first party: 
5c052838-a55e-4690-83c6-b7d572146713,
49042d99-5b0e-4f37-82f3-78de245eea34
result, different first party: , 
unsupported: false, false
passed: true, true
test failed: false, false | 
| H2 connection HTTP/2 is a web connection protocol introduced in 2015. Some browsers re-use HTTP/2 connections across websites and can thus be used to track users.
               | write: async (secret) => {
      await fetch(`https://h2.privacytests2.org:8902/?mode=write&secret=${secret}`, {cache: "no-store"});
    }
read: async () => {
      let response = await fetch(`https://h2.privacytests2.org:8902/?mode=read`, {cache: "no-store"});
      return await response.text();
    }
result, same first party: 
3133a4ca-686b-4047-91c3-9a7e181c53f7,
49872d4a-b01d-4af8-8cbe-85c379a609e2,
58bb2b32-4f83-47bb-91c8-96dce39cd97a
result, different first party: , , 
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (secret) => {
      await fetch(`https://h2.privacytests2.org:8902/?mode=write&secret=${secret}`, {cache: "no-store"});
    }
read: async () => {
      let response = await fetch(`https://h2.privacytests2.org:8902/?mode=read`, {cache: "no-store"});
      return await response.text();
    }
result, same first party: 
e8485c10-5a92-474a-bd16-de23f46ee0e2,
cd5b479f-1529-4f43-a586-810ed83aeb8b
result, different first party: , 
unsupported: false, false
passed: true, true
test failed: false, false | write: async (secret) => {
      await fetch(`https://h2.privacytests2.org:8902/?mode=write&secret=${secret}`, {cache: "no-store"});
    }
read: async () => {
      let response = await fetch(`https://h2.privacytests2.org:8902/?mode=read`, {cache: "no-store"});
      return await response.text();
    }
result, same first party: 
e96973da-bf72-4eb1-b6ab-a4e6c6775fb4,
67fb6941-7c51-4727-b485-317211e65ae0
result, different first party: , 
unsupported: false, false
passed: true, true
test failed: false, false | write: async (secret) => {
      await fetch(`https://h2.privacytests2.org:8902/?mode=write&secret=${secret}`, {cache: "no-store"});
    }
read: async () => {
      let response = await fetch(`https://h2.privacytests2.org:8902/?mode=read`, {cache: "no-store"});
      return await response.text();
    }
result, same first party: 
94f1fd6d-f642-403e-8ef8-11180b18b1f6,
2bf6571e-4385-4b68-a076-f6868cbe4954
result, different first party: , 
unsupported: false, false
passed: true, true
test failed: false, false | write: async (secret) => {
      await fetch(`https://h2.privacytests2.org:8902/?mode=write&secret=${secret}`, {cache: "no-store"});
    }
read: async () => {
      let response = await fetch(`https://h2.privacytests2.org:8902/?mode=read`, {cache: "no-store"});
      return await response.text();
    }
result, same first party: 
da67bebb-5652-441a-816e-1206b25c0225,
0bad71fe-de93-4434-bfa0-bb7f0db62eea,
6aba94ed-010e-45af-892c-dd670cd8d134
result, different first party: , , 
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (secret) => {
      await fetch(`https://h2.privacytests2.org:8902/?mode=write&secret=${secret}`, {cache: "no-store"});
    }
read: async () => {
      let response = await fetch(`https://h2.privacytests2.org:8902/?mode=read`, {cache: "no-store"});
      return await response.text();
    }
result, same first party: 
9dd63e63-3256-4352-99f6-805b6f48eb8c,
b7cb77db-aca9-4570-923e-b81da9175421,
736f05a1-05e6-491e-8dad-a6647bd52dda
result, different first party: , , 
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (secret) => {
      await fetch(`https://h2.privacytests2.org:8902/?mode=write&secret=${secret}`, {cache: "no-store"});
    }
read: async () => {
      let response = await fetch(`https://h2.privacytests2.org:8902/?mode=read`, {cache: "no-store"});
      return await response.text();
    }
result, same first party: 
50c6a369-0b5c-4de5-a70b-a0516825119e,
49e0d8b1-c126-4b6b-be66-6ba7acfd85a9
result, different first party: , 
unsupported: false, false
passed: true, true
test failed: false, false | write: async (secret) => {
      await fetch(`https://h2.privacytests2.org:8902/?mode=write&secret=${secret}`, {cache: "no-store"});
    }
read: async () => {
      let response = await fetch(`https://h2.privacytests2.org:8902/?mode=read`, {cache: "no-store"});
      return await response.text();
    }
result, same first party: 
6b07cf4a-a332-4a7c-90df-fbc3dc862f86,
2d40813a-a6c5-4a6b-bb1c-f4d01a399217
result, different first party: , 
unsupported: false, false
passed: true, true
test failed: false, false | write: async (secret) => {
      await fetch(`https://h2.privacytests2.org:8902/?mode=write&secret=${secret}`, {cache: "no-store"});
    }
read: async () => {
      let response = await fetch(`https://h2.privacytests2.org:8902/?mode=read`, {cache: "no-store"});
      return await response.text();
    }
result, same first party: 
3dc5c317-3ea7-42b4-be71-7902bcc4eb27,
4932f81b-b746-4e4b-b97d-c0b827047888,
3e016f09-5749-4084-a136-e9b829c1a824
result, different first party: , , 
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (secret) => {
      await fetch(`https://h2.privacytests2.org:8902/?mode=write&secret=${secret}`, {cache: "no-store"});
    }
read: async () => {
      let response = await fetch(`https://h2.privacytests2.org:8902/?mode=read`, {cache: "no-store"});
      return await response.text();
    }
result, same first party: 
3242a94e-3344-4cb9-ba7c-ebedaf5d6e01,
87918123-fc52-4196-9d0c-a2cfc3f826d4
result, different first party: , 
unsupported: false, false
passed: true, true
test failed: false, false | write: async (secret) => {
      await fetch(`https://h2.privacytests2.org:8902/?mode=write&secret=${secret}`, {cache: "no-store"});
    }
read: async () => {
      let response = await fetch(`https://h2.privacytests2.org:8902/?mode=read`, {cache: "no-store"});
      return await response.text();
    }
result, same first party: 
5c052838-a55e-4690-83c6-b7d572146713,
49042d99-5b0e-4f37-82f3-78de245eea34
result, different first party: , 
unsupported: false, false
passed: true, true
test failed: false, false | 
| H3 connection HTTP/3 is a new standard HTTP connection protocol, still in draft but widely supported by browsers. If it is not partitioned, it can be used to track users across websites.
               | write: async (secret) => {
      // Ensure that we can switch over to h3 via alt-svc:
      for (let i = 0; i<3; ++i) {
        await fetch(`https://h3.privacytests2.org:4434/connection_id`, {cache: "no-store"});
        await sleepMs(500);
      }
      // Are we now connecting over h3?
      let response = await fetch(`https://h3.privacytests2.org:4434/connection_id`, {cache: "no-store"});
      let text = await response.text();
      // Empty response text indicates we are not connecting over h3:
      if (text.trim() === "") {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      let response = await fetch(`https://h3.privacytests2.org:4434/connection_id`);
      return await response.text();
    }
result, same first party: 
aa9665021891c639ba7b1b2df1000728,
1a89fd430b6ff41b4524e336be739d1d,
9e54a82e54ade5d6ef422e94aa362628
result, different first party: , , 
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (secret) => {
      // Ensure that we can switch over to h3 via alt-svc:
      for (let i = 0; i<3; ++i) {
        await fetch(`https://h3.privacytests2.org:4434/connection_id`, {cache: "no-store"});
        await sleepMs(500);
      }
      // Are we now connecting over h3?
      let response = await fetch(`https://h3.privacytests2.org:4434/connection_id`, {cache: "no-store"});
      let text = await response.text();
      // Empty response text indicates we are not connecting over h3:
      if (text.trim() === "") {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      let response = await fetch(`https://h3.privacytests2.org:4434/connection_id`);
      return await response.text();
    }
result, same first party: 
a46201be125d90afd7b24773bd94eb50,
5ff32321bafd0ca72f408d63e1794c3f
result, different first party: , 
unsupported: false, false
passed: true, true
test failed: false, false | write: async (secret) => {
      // Ensure that we can switch over to h3 via alt-svc:
      for (let i = 0; i<3; ++i) {
        await fetch(`https://h3.privacytests2.org:4434/connection_id`, {cache: "no-store"});
        await sleepMs(500);
      }
      // Are we now connecting over h3?
      let response = await fetch(`https://h3.privacytests2.org:4434/connection_id`, {cache: "no-store"});
      let text = await response.text();
      // Empty response text indicates we are not connecting over h3:
      if (text.trim() === "") {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      let response = await fetch(`https://h3.privacytests2.org:4434/connection_id`);
      return await response.text();
    }
result, same first party: 
ee61cca50f1db9f9789b3c64a9922473,
528fd6ca46d014c4c33de3d5e934a83e
result, different first party: , 
unsupported: false, false
passed: true, true
test failed: false, false | write: async (secret) => {
      // Ensure that we can switch over to h3 via alt-svc:
      for (let i = 0; i<3; ++i) {
        await fetch(`https://h3.privacytests2.org:4434/connection_id`, {cache: "no-store"});
        await sleepMs(500);
      }
      // Are we now connecting over h3?
      let response = await fetch(`https://h3.privacytests2.org:4434/connection_id`, {cache: "no-store"});
      let text = await response.text();
      // Empty response text indicates we are not connecting over h3:
      if (text.trim() === "") {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      let response = await fetch(`https://h3.privacytests2.org:4434/connection_id`);
      return await response.text();
    }
result, same first party: 
b498f552f71b0519ec105209f96280ef,
4afb5d0285c3e68b9d79c412517a50a5
result, different first party: , 
unsupported: false, false
passed: true, true
test failed: false, false | write: async (secret) => {
      // Ensure that we can switch over to h3 via alt-svc:
      for (let i = 0; i<3; ++i) {
        await fetch(`https://h3.privacytests2.org:4434/connection_id`, {cache: "no-store"});
        await sleepMs(500);
      }
      // Are we now connecting over h3?
      let response = await fetch(`https://h3.privacytests2.org:4434/connection_id`, {cache: "no-store"});
      let text = await response.text();
      // Empty response text indicates we are not connecting over h3:
      if (text.trim() === "") {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      let response = await fetch(`https://h3.privacytests2.org:4434/connection_id`);
      return await response.text();
    }
result, same first party: 
52fb11e4212a9ef8939f7433a433d39b,
b44b5a1cd72565e486c6f3de2e4f2c68,
6c742c11b7b86f54ca3deb2ae4984a10
result, different first party: , , 
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (secret) => {
      // Ensure that we can switch over to h3 via alt-svc:
      for (let i = 0; i<3; ++i) {
        await fetch(`https://h3.privacytests2.org:4434/connection_id`, {cache: "no-store"});
        await sleepMs(500);
      }
      // Are we now connecting over h3?
      let response = await fetch(`https://h3.privacytests2.org:4434/connection_id`, {cache: "no-store"});
      let text = await response.text();
      // Empty response text indicates we are not connecting over h3:
      if (text.trim() === "") {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      let response = await fetch(`https://h3.privacytests2.org:4434/connection_id`);
      return await response.text();
    }
result, same first party: 
776b005fcf193b96c0055df8bdc9482a,
c50cb6ba4601e5d2724c68938b3609e3,
777cf3f527b302659f1344b3f373532d
result, different first party: , , 
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (secret) => {
      // Ensure that we can switch over to h3 via alt-svc:
      for (let i = 0; i<3; ++i) {
        await fetch(`https://h3.privacytests2.org:4434/connection_id`, {cache: "no-store"});
        await sleepMs(500);
      }
      // Are we now connecting over h3?
      let response = await fetch(`https://h3.privacytests2.org:4434/connection_id`, {cache: "no-store"});
      let text = await response.text();
      // Empty response text indicates we are not connecting over h3:
      if (text.trim() === "") {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      let response = await fetch(`https://h3.privacytests2.org:4434/connection_id`);
      return await response.text();
    }
result, same first party: 
50f133410de33bfec8486fd16a1fec45,
9e46c09dd2d1f4c586cf100ce41a2cb3
result, different first party: , 
unsupported: false, false
passed: true, true
test failed: false, false | write: async (secret) => {
      // Ensure that we can switch over to h3 via alt-svc:
      for (let i = 0; i<3; ++i) {
        await fetch(`https://h3.privacytests2.org:4434/connection_id`, {cache: "no-store"});
        await sleepMs(500);
      }
      // Are we now connecting over h3?
      let response = await fetch(`https://h3.privacytests2.org:4434/connection_id`, {cache: "no-store"});
      let text = await response.text();
      // Empty response text indicates we are not connecting over h3:
      if (text.trim() === "") {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      let response = await fetch(`https://h3.privacytests2.org:4434/connection_id`);
      return await response.text();
    }
result, same first party: 
497ead4653345d696a47088d6904af65,
beee5e12ade9155f2f6212346b992bbf
result, different first party: 
5ebd279c13222799fbddee028ef41890,
8c99b5a0aca8460e092fb4ebbdd8de9f
unsupported: false, false
passed: true, true
test failed: false, false | write: async (secret) => {
      // Ensure that we can switch over to h3 via alt-svc:
      for (let i = 0; i<3; ++i) {
        await fetch(`https://h3.privacytests2.org:4434/connection_id`, {cache: "no-store"});
        await sleepMs(500);
      }
      // Are we now connecting over h3?
      let response = await fetch(`https://h3.privacytests2.org:4434/connection_id`, {cache: "no-store"});
      let text = await response.text();
      // Empty response text indicates we are not connecting over h3:
      if (text.trim() === "") {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      let response = await fetch(`https://h3.privacytests2.org:4434/connection_id`);
      return await response.text();
    }
result, same first party: , , 
result, different first party: , , 
unsupported: true, true, true
passed: undefined
test failed: false, false, false | write: async (secret) => {
      // Ensure that we can switch over to h3 via alt-svc:
      for (let i = 0; i<3; ++i) {
        await fetch(`https://h3.privacytests2.org:4434/connection_id`, {cache: "no-store"});
        await sleepMs(500);
      }
      // Are we now connecting over h3?
      let response = await fetch(`https://h3.privacytests2.org:4434/connection_id`, {cache: "no-store"});
      let text = await response.text();
      // Empty response text indicates we are not connecting over h3:
      if (text.trim() === "") {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      let response = await fetch(`https://h3.privacytests2.org:4434/connection_id`);
      return await response.text();
    }
result, same first party: 
df3cbf8d84e345ff9d6686f8914843f3,
f00c9048bea19441f032baf1cff98a45
result, different first party: , 
unsupported: false, false
passed: true, true
test failed: false, false | write: async (secret) => {
      // Ensure that we can switch over to h3 via alt-svc:
      for (let i = 0; i<3; ++i) {
        await fetch(`https://h3.privacytests2.org:4434/connection_id`, {cache: "no-store"});
        await sleepMs(500);
      }
      // Are we now connecting over h3?
      let response = await fetch(`https://h3.privacytests2.org:4434/connection_id`, {cache: "no-store"});
      let text = await response.text();
      // Empty response text indicates we are not connecting over h3:
      if (text.trim() === "") {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      let response = await fetch(`https://h3.privacytests2.org:4434/connection_id`);
      return await response.text();
    }
result, same first party: 
059a4de3c4e749d974b835b2d54aaaf2,
09a63fb820114862eedd6271635adb04
result, different first party: , 
unsupported: false, false
passed: true, true
test failed: false, false | 
| HSTS cache The HTTP Strict-Transport-Security response header allows a website to signal that it should only be accessed via HTTPS. The browser remembers this directive in a database, but if this database is not partitioned, then it can be used to track users across websites."
               | write: set HSTS flag read: read HSTS flag result, same first party: not tested, not tested, not tested result, different first party: Used http, Used http, Used http unsupported: false, false, false passed: true, true, true test failed: false, false, false | write: set HSTS flag read: read HSTS flag result, same first party: not tested, not tested result, different first party: Upgraded to https, Upgraded to https unsupported: false, false passed: false, false test failed: false, false | write: set HSTS flag read: read HSTS flag result, same first party: not tested, not tested result, different first party: Upgraded to https, Upgraded to https unsupported: false, false passed: false, false test failed: false, false | write: set HSTS flag read: read HSTS flag result, same first party: not tested, not tested result, different first party: Used http, Used http unsupported: false, false passed: true, true test failed: false, false | write: null read: null result, same first party: , , result, different first party: HTTPS used by default; no HSTS cache issue expected, HTTPS used by default; no HSTS cache issue expected, HTTPS used by default; no HSTS cache issue expected unsupported: , , passed: true, true, true test failed: false, false, false | write: null read: null result, same first party: , , result, different first party: HTTPS used by default; no HSTS cache issue expected, HTTPS used by default; no HSTS cache issue expected, HTTPS used by default; no HSTS cache issue expected unsupported: , , passed: true, true, true test failed: false, false, false | write: set HSTS flag read: read HSTS flag result, same first party: not tested, not tested result, different first party: Upgraded to https, Upgraded to https unsupported: false, false passed: false, false test failed: false, false | write: set HSTS flag read: read HSTS flag result, same first party: not tested, not tested result, different first party: Used http, Used http unsupported: false, false passed: true, true test failed: false, false | write: null read: null result, same first party: , , result, different first party: HTTPS used by default; no HSTS cache issue expected, HTTPS used by default; no HSTS cache issue expected, HTTPS used by default; no HSTS cache issue expected unsupported: , , passed: true, true, true test failed: false, false, false | write: set HSTS flag read: read HSTS flag result, same first party: not tested, not tested result, different first party: Upgraded to https, Upgraded to https unsupported: false, false passed: false, false test failed: false, false | write: set HSTS flag read: read HSTS flag result, same first party: not tested, not tested result, different first party: Upgraded to https, Upgraded to https unsupported: false, false passed: false, false test failed: false, false | 
| HSTS cache (fetch) The HTTP Strict-Transport-Security response header allows a website to signal that it should only be accessed via HTTPS. The browser remembers this directive in a database, but if this database is not partitioned, then it can be used to track users across websites."
               | write: set HSTS flag read: read HSTS flag result, same first party: not tested, not tested, not tested result, different first party: Used http, Used http, Used http unsupported: false, false, false passed: true, true, true test failed: false, false, false | write: set HSTS flag read: read HSTS flag result, same first party: not tested, not tested result, different first party: Upgraded to https, Upgraded to https unsupported: false, false passed: false, false test failed: false, false | write: set HSTS flag read: read HSTS flag result, same first party: not tested, not tested result, different first party: Upgraded to https, Upgraded to https unsupported: false, false passed: false, false test failed: false, false | write: set HSTS flag read: read HSTS flag result, same first party: not tested, not tested result, different first party: Used http, Used http unsupported: false, false passed: true, true test failed: false, false | write: null read: null result, same first party: , , result, different first party: HTTPS used by default; no HSTS cache issue expected, HTTPS used by default; no HSTS cache issue expected, HTTPS used by default; no HSTS cache issue expected unsupported: , , passed: true, true, true test failed: false, false, false | write: null read: null result, same first party: , , result, different first party: HTTPS used by default; no HSTS cache issue expected, HTTPS used by default; no HSTS cache issue expected, HTTPS used by default; no HSTS cache issue expected unsupported: , , passed: true, true, true test failed: false, false, false | write: set HSTS flag read: read HSTS flag result, same first party: not tested, not tested result, different first party: Upgraded to https, Upgraded to https unsupported: false, false passed: false, false test failed: false, false | write: set HSTS flag read: read HSTS flag result, same first party: not tested, not tested result, different first party: Used http, Used http unsupported: false, false passed: true, true test failed: false, false | write: null read: null result, same first party: , , result, different first party: HTTPS used by default; no HSTS cache issue expected, HTTPS used by default; no HSTS cache issue expected, HTTPS used by default; no HSTS cache issue expected unsupported: , , passed: true, true, true test failed: false, false, false | write: set HSTS flag read: read HSTS flag result, same first party: not tested, not tested result, different first party: Upgraded to https, Upgraded to https unsupported: false, false passed: false, false test failed: false, false | write: set HSTS flag read: read HSTS flag result, same first party: not tested, not tested result, different first party: Upgraded to https, Upgraded to https unsupported: false, false passed: false, false test failed: false, false | 
| iframe cache An iframe is an element in a web page than allows websites to embed a second web page. Caching of this web page could be abused for cross-site tracking.
               | write: (key) => new Promise((resolve, reject) => {
      let iframe = document.createElement("iframe");
      document.body.appendChild(iframe);
      iframe.addEventListener("load", () => resolve(key), {once: true});
      iframe.src = testURI("resource", "page", key);
    })
read: async (key) => {
      let iframe = document.createElement("iframe");
      document.body.appendChild(iframe);
      let iframeLoadPromise = new Promise((resolve, reject) => {
        iframe.addEventListener("load", resolve, {once: true});
      });
      let address = testURI("resource", "page", key);
      iframe.src = address;
      await iframeLoadPromise;
      let response = await fetch(
        testURI("ctr", "page", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same first party: 1, 1, 1
result, different first party: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (key) => new Promise((resolve, reject) => {
      let iframe = document.createElement("iframe");
      document.body.appendChild(iframe);
      iframe.addEventListener("load", () => resolve(key), {once: true});
      iframe.src = testURI("resource", "page", key);
    })
read: async (key) => {
      let iframe = document.createElement("iframe");
      document.body.appendChild(iframe);
      let iframeLoadPromise = new Promise((resolve, reject) => {
        iframe.addEventListener("load", resolve, {once: true});
      });
      let address = testURI("resource", "page", key);
      iframe.src = address;
      await iframeLoadPromise;
      let response = await fetch(
        testURI("ctr", "page", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same first party: 1, 1
result, different first party: 2, 2
unsupported: false, false
passed: true, true
test failed: false, false | write: (key) => new Promise((resolve, reject) => {
      let iframe = document.createElement("iframe");
      document.body.appendChild(iframe);
      iframe.addEventListener("load", () => resolve(key), {once: true});
      iframe.src = testURI("resource", "page", key);
    })
read: async (key) => {
      let iframe = document.createElement("iframe");
      document.body.appendChild(iframe);
      let iframeLoadPromise = new Promise((resolve, reject) => {
        iframe.addEventListener("load", resolve, {once: true});
      });
      let address = testURI("resource", "page", key);
      iframe.src = address;
      await iframeLoadPromise;
      let response = await fetch(
        testURI("ctr", "page", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same first party: 1, 1
result, different first party: 2, 2
unsupported: false, false
passed: true, true
test failed: false, false | write: (key) => new Promise((resolve, reject) => {
      let iframe = document.createElement("iframe");
      document.body.appendChild(iframe);
      iframe.addEventListener("load", () => resolve(key), {once: true});
      iframe.src = testURI("resource", "page", key);
    })
read: async (key) => {
      let iframe = document.createElement("iframe");
      document.body.appendChild(iframe);
      let iframeLoadPromise = new Promise((resolve, reject) => {
        iframe.addEventListener("load", resolve, {once: true});
      });
      let address = testURI("resource", "page", key);
      iframe.src = address;
      await iframeLoadPromise;
      let response = await fetch(
        testURI("ctr", "page", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same first party: 1, 1
result, different first party: 2, 2
unsupported: false, false
passed: true, true
test failed: false, false | write: (key) => new Promise((resolve, reject) => {
      let iframe = document.createElement("iframe");
      document.body.appendChild(iframe);
      iframe.addEventListener("load", () => resolve(key), {once: true});
      iframe.src = testURI("resource", "page", key);
    })
read: async (key) => {
      let iframe = document.createElement("iframe");
      document.body.appendChild(iframe);
      let iframeLoadPromise = new Promise((resolve, reject) => {
        iframe.addEventListener("load", resolve, {once: true});
      });
      let address = testURI("resource", "page", key);
      iframe.src = address;
      await iframeLoadPromise;
      let response = await fetch(
        testURI("ctr", "page", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same first party: 1, 1, 1
result, different first party: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (key) => new Promise((resolve, reject) => {
      let iframe = document.createElement("iframe");
      document.body.appendChild(iframe);
      iframe.addEventListener("load", () => resolve(key), {once: true});
      iframe.src = testURI("resource", "page", key);
    })
read: async (key) => {
      let iframe = document.createElement("iframe");
      document.body.appendChild(iframe);
      let iframeLoadPromise = new Promise((resolve, reject) => {
        iframe.addEventListener("load", resolve, {once: true});
      });
      let address = testURI("resource", "page", key);
      iframe.src = address;
      await iframeLoadPromise;
      let response = await fetch(
        testURI("ctr", "page", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same first party: 1, 1, 1
result, different first party: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (key) => new Promise((resolve, reject) => {
      let iframe = document.createElement("iframe");
      document.body.appendChild(iframe);
      iframe.addEventListener("load", () => resolve(key), {once: true});
      iframe.src = testURI("resource", "page", key);
    })
read: async (key) => {
      let iframe = document.createElement("iframe");
      document.body.appendChild(iframe);
      let iframeLoadPromise = new Promise((resolve, reject) => {
        iframe.addEventListener("load", resolve, {once: true});
      });
      let address = testURI("resource", "page", key);
      iframe.src = address;
      await iframeLoadPromise;
      let response = await fetch(
        testURI("ctr", "page", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same first party: 1, 1
result, different first party: 2, 2
unsupported: false, false
passed: true, true
test failed: false, false | write: (key) => new Promise((resolve, reject) => {
      let iframe = document.createElement("iframe");
      document.body.appendChild(iframe);
      iframe.addEventListener("load", () => resolve(key), {once: true});
      iframe.src = testURI("resource", "page", key);
    })
read: async (key) => {
      let iframe = document.createElement("iframe");
      document.body.appendChild(iframe);
      let iframeLoadPromise = new Promise((resolve, reject) => {
        iframe.addEventListener("load", resolve, {once: true});
      });
      let address = testURI("resource", "page", key);
      iframe.src = address;
      await iframeLoadPromise;
      let response = await fetch(
        testURI("ctr", "page", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same first party: 1, 1
result, different first party: 2, 2
unsupported: false, false
passed: true, true
test failed: false, false | write: (key) => new Promise((resolve, reject) => {
      let iframe = document.createElement("iframe");
      document.body.appendChild(iframe);
      iframe.addEventListener("load", () => resolve(key), {once: true});
      iframe.src = testURI("resource", "page", key);
    })
read: async (key) => {
      let iframe = document.createElement("iframe");
      document.body.appendChild(iframe);
      let iframeLoadPromise = new Promise((resolve, reject) => {
        iframe.addEventListener("load", resolve, {once: true});
      });
      let address = testURI("resource", "page", key);
      iframe.src = address;
      await iframeLoadPromise;
      let response = await fetch(
        testURI("ctr", "page", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same first party: 1, 1, 1
result, different first party: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (key) => new Promise((resolve, reject) => {
      let iframe = document.createElement("iframe");
      document.body.appendChild(iframe);
      iframe.addEventListener("load", () => resolve(key), {once: true});
      iframe.src = testURI("resource", "page", key);
    })
read: async (key) => {
      let iframe = document.createElement("iframe");
      document.body.appendChild(iframe);
      let iframeLoadPromise = new Promise((resolve, reject) => {
        iframe.addEventListener("load", resolve, {once: true});
      });
      let address = testURI("resource", "page", key);
      iframe.src = address;
      await iframeLoadPromise;
      let response = await fetch(
        testURI("ctr", "page", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same first party: 1, 1
result, different first party: 2, 2
unsupported: false, false
passed: true, true
test failed: false, false | write: (key) => new Promise((resolve, reject) => {
      let iframe = document.createElement("iframe");
      document.body.appendChild(iframe);
      iframe.addEventListener("load", () => resolve(key), {once: true});
      iframe.src = testURI("resource", "page", key);
    })
read: async (key) => {
      let iframe = document.createElement("iframe");
      document.body.appendChild(iframe);
      let iframeLoadPromise = new Promise((resolve, reject) => {
        iframe.addEventListener("load", resolve, {once: true});
      });
      let address = testURI("resource", "page", key);
      iframe.src = address;
      await iframeLoadPromise;
      let response = await fetch(
        testURI("ctr", "page", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same first party: 1, 1
result, different first party: 2, 2
unsupported: false, false
passed: true, true
test failed: false, false | 
| image cache Caching of images in web browsers is a standard behavior. But if that cache leaks between websites, it can be abused for cross-site tracking.
               | write: (key) => new Promise((resolve, reject) => {
      let img = document.createElement("img");
      document.body.appendChild(img);
      img.addEventListener("load", () => resolve(key), {once: true});
      img.src = testURI("resource", "image", key);
    })
read: async (key) => {
      let img = document.createElement("img");
      document.body.appendChild(img);
      let imgLoadPromise = new Promise((resolve, reject) => {
        img.addEventListener("load", resolve, {once: true});
      });
      img.src = testURI("resource", "image", key);
      await imgLoadPromise;
      let response = await fetch(
        testURI("ctr", "image", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same first party: 1, 1, 1
result, different first party: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (key) => new Promise((resolve, reject) => {
      let img = document.createElement("img");
      document.body.appendChild(img);
      img.addEventListener("load", () => resolve(key), {once: true});
      img.src = testURI("resource", "image", key);
    })
read: async (key) => {
      let img = document.createElement("img");
      document.body.appendChild(img);
      let imgLoadPromise = new Promise((resolve, reject) => {
        img.addEventListener("load", resolve, {once: true});
      });
      img.src = testURI("resource", "image", key);
      await imgLoadPromise;
      let response = await fetch(
        testURI("ctr", "image", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same first party: 1, 1
result, different first party: 2, 2
unsupported: false, false
passed: true, true
test failed: false, false | write: (key) => new Promise((resolve, reject) => {
      let img = document.createElement("img");
      document.body.appendChild(img);
      img.addEventListener("load", () => resolve(key), {once: true});
      img.src = testURI("resource", "image", key);
    })
read: async (key) => {
      let img = document.createElement("img");
      document.body.appendChild(img);
      let imgLoadPromise = new Promise((resolve, reject) => {
        img.addEventListener("load", resolve, {once: true});
      });
      img.src = testURI("resource", "image", key);
      await imgLoadPromise;
      let response = await fetch(
        testURI("ctr", "image", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same first party: 1, 1
result, different first party: 2, 2
unsupported: false, false
passed: true, true
test failed: false, false | write: (key) => new Promise((resolve, reject) => {
      let img = document.createElement("img");
      document.body.appendChild(img);
      img.addEventListener("load", () => resolve(key), {once: true});
      img.src = testURI("resource", "image", key);
    })
read: async (key) => {
      let img = document.createElement("img");
      document.body.appendChild(img);
      let imgLoadPromise = new Promise((resolve, reject) => {
        img.addEventListener("load", resolve, {once: true});
      });
      img.src = testURI("resource", "image", key);
      await imgLoadPromise;
      let response = await fetch(
        testURI("ctr", "image", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same first party: 1, 1
result, different first party: 2, 2
unsupported: false, false
passed: true, true
test failed: false, false | write: (key) => new Promise((resolve, reject) => {
      let img = document.createElement("img");
      document.body.appendChild(img);
      img.addEventListener("load", () => resolve(key), {once: true});
      img.src = testURI("resource", "image", key);
    })
read: async (key) => {
      let img = document.createElement("img");
      document.body.appendChild(img);
      let imgLoadPromise = new Promise((resolve, reject) => {
        img.addEventListener("load", resolve, {once: true});
      });
      img.src = testURI("resource", "image", key);
      await imgLoadPromise;
      let response = await fetch(
        testURI("ctr", "image", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same first party: 1, 1, 1
result, different first party: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (key) => new Promise((resolve, reject) => {
      let img = document.createElement("img");
      document.body.appendChild(img);
      img.addEventListener("load", () => resolve(key), {once: true});
      img.src = testURI("resource", "image", key);
    })
read: async (key) => {
      let img = document.createElement("img");
      document.body.appendChild(img);
      let imgLoadPromise = new Promise((resolve, reject) => {
        img.addEventListener("load", resolve, {once: true});
      });
      img.src = testURI("resource", "image", key);
      await imgLoadPromise;
      let response = await fetch(
        testURI("ctr", "image", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same first party: 1, 1, 1
result, different first party: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (key) => new Promise((resolve, reject) => {
      let img = document.createElement("img");
      document.body.appendChild(img);
      img.addEventListener("load", () => resolve(key), {once: true});
      img.src = testURI("resource", "image", key);
    })
read: async (key) => {
      let img = document.createElement("img");
      document.body.appendChild(img);
      let imgLoadPromise = new Promise((resolve, reject) => {
        img.addEventListener("load", resolve, {once: true});
      });
      img.src = testURI("resource", "image", key);
      await imgLoadPromise;
      let response = await fetch(
        testURI("ctr", "image", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same first party: 1, 1
result, different first party: 2, 2
unsupported: false, false
passed: true, true
test failed: false, false | write: (key) => new Promise((resolve, reject) => {
      let img = document.createElement("img");
      document.body.appendChild(img);
      img.addEventListener("load", () => resolve(key), {once: true});
      img.src = testURI("resource", "image", key);
    })
read: async (key) => {
      let img = document.createElement("img");
      document.body.appendChild(img);
      let imgLoadPromise = new Promise((resolve, reject) => {
        img.addEventListener("load", resolve, {once: true});
      });
      img.src = testURI("resource", "image", key);
      await imgLoadPromise;
      let response = await fetch(
        testURI("ctr", "image", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same first party: 2, 2
result, different first party: 3, 3
unsupported: false, false
passed: true, true
test failed: false, false | write: (key) => new Promise((resolve, reject) => {
      let img = document.createElement("img");
      document.body.appendChild(img);
      img.addEventListener("load", () => resolve(key), {once: true});
      img.src = testURI("resource", "image", key);
    })
read: async (key) => {
      let img = document.createElement("img");
      document.body.appendChild(img);
      let imgLoadPromise = new Promise((resolve, reject) => {
        img.addEventListener("load", resolve, {once: true});
      });
      img.src = testURI("resource", "image", key);
      await imgLoadPromise;
      let response = await fetch(
        testURI("ctr", "image", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same first party: 1, 1, 1
result, different first party: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (key) => new Promise((resolve, reject) => {
      let img = document.createElement("img");
      document.body.appendChild(img);
      img.addEventListener("load", () => resolve(key), {once: true});
      img.src = testURI("resource", "image", key);
    })
read: async (key) => {
      let img = document.createElement("img");
      document.body.appendChild(img);
      let imgLoadPromise = new Promise((resolve, reject) => {
        img.addEventListener("load", resolve, {once: true});
      });
      img.src = testURI("resource", "image", key);
      await imgLoadPromise;
      let response = await fetch(
        testURI("ctr", "image", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same first party: 1, 1
result, different first party: 2, 2
unsupported: false, false
passed: true, true
test failed: false, false | write: (key) => new Promise((resolve, reject) => {
      let img = document.createElement("img");
      document.body.appendChild(img);
      img.addEventListener("load", () => resolve(key), {once: true});
      img.src = testURI("resource", "image", key);
    })
read: async (key) => {
      let img = document.createElement("img");
      document.body.appendChild(img);
      let imgLoadPromise = new Promise((resolve, reject) => {
        img.addEventListener("load", resolve, {once: true});
      });
      img.src = testURI("resource", "image", key);
      await imgLoadPromise;
      let response = await fetch(
        testURI("ctr", "image", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same first party: 1, 1
result, different first party: 2, 2
unsupported: false, false
passed: true, true
test failed: false, false | 
| indexedDB The IndexedDB API exposes a transactional database to web pages. That database can be used to track users across websites, unless it is partitioned.
               | write: async (secret) => {
      try {
        return await IdbKeyVal.set("secret", secret);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: () => IdbKeyVal.get("secret")
result, same first party: 
3133a4ca-686b-4047-91c3-9a7e181c53f7,
49872d4a-b01d-4af8-8cbe-85c379a609e2,
58bb2b32-4f83-47bb-91c8-96dce39cd97a
result, different first party: 
Error: The user denied permission to access the database.,
Error: The user denied permission to access the database.,
Error: The user denied permission to access the database.
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (secret) => {
      try {
        return await IdbKeyVal.set("secret", secret);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: () => IdbKeyVal.get("secret")
result, same first party: 
e8485c10-5a92-474a-bd16-de23f46ee0e2,
cd5b479f-1529-4f43-a586-810ed83aeb8b
result, different first party: undefined
unsupported: false, false
passed: true, true
test failed: false, false | write: async (secret) => {
      try {
        return await IdbKeyVal.set("secret", secret);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: () => IdbKeyVal.get("secret")
result, same first party: 
e96973da-bf72-4eb1-b6ab-a4e6c6775fb4,
67fb6941-7c51-4727-b485-317211e65ae0
result, different first party: undefined
unsupported: false, false
passed: true, true
test failed: false, false | write: async (secret) => {
      try {
        return await IdbKeyVal.set("secret", secret);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: () => IdbKeyVal.get("secret")
result, same first party: 
94f1fd6d-f642-403e-8ef8-11180b18b1f6,
2bf6571e-4385-4b68-a076-f6868cbe4954
result, different first party: undefined
unsupported: false, false
passed: true, true
test failed: false, false | write: async (secret) => {
      try {
        return await IdbKeyVal.set("secret", secret);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: () => IdbKeyVal.get("secret")
result, same first party: 
da67bebb-5652-441a-816e-1206b25c0225,
0bad71fe-de93-4434-bfa0-bb7f0db62eea,
6aba94ed-010e-45af-892c-dd670cd8d134
result, different first party: undefined
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (secret) => {
      try {
        return await IdbKeyVal.set("secret", secret);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: () => IdbKeyVal.get("secret")
result, same first party: 
9dd63e63-3256-4352-99f6-805b6f48eb8c,
b7cb77db-aca9-4570-923e-b81da9175421,
736f05a1-05e6-491e-8dad-a6647bd52dda
result, different first party: 
Error: The operation is insecure.,
Error: The operation is insecure.,
Error: The operation is insecure.
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (secret) => {
      try {
        return await IdbKeyVal.set("secret", secret);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: () => IdbKeyVal.get("secret")
result, same first party: 
50c6a369-0b5c-4de5-a70b-a0516825119e,
49e0d8b1-c126-4b6b-be66-6ba7acfd85a9
result, different first party: undefined
unsupported: false, false
passed: true, true
test failed: false, false | write: async (secret) => {
      try {
        return await IdbKeyVal.set("secret", secret);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: () => IdbKeyVal.get("secret")
result, same first party: 
6b07cf4a-a332-4a7c-90df-fbc3dc862f86,
2d40813a-a6c5-4a6b-bb1c-f4d01a399217
result, different first party: undefined
unsupported: false, false
passed: true, true
test failed: false, false | write: async (secret) => {
      try {
        return await IdbKeyVal.set("secret", secret);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: () => IdbKeyVal.get("secret")
result, same first party: 
3dc5c317-3ea7-42b4-be71-7902bcc4eb27,
4932f81b-b746-4e4b-b97d-c0b827047888,
3e016f09-5749-4084-a136-e9b829c1a824
result, different first party: 
Error: The operation is insecure.,
Error: The operation is insecure.,
Error: The operation is insecure.
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (secret) => {
      try {
        return await IdbKeyVal.set("secret", secret);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: () => IdbKeyVal.get("secret")
result, same first party: 
3242a94e-3344-4cb9-ba7c-ebedaf5d6e01,
87918123-fc52-4196-9d0c-a2cfc3f826d4
result, different first party: undefined
unsupported: false, false
passed: true, true
test failed: false, false | write: async (secret) => {
      try {
        return await IdbKeyVal.set("secret", secret);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: () => IdbKeyVal.get("secret")
result, same first party: 
5c052838-a55e-4690-83c6-b7d572146713,
49042d99-5b0e-4f37-82f3-78de245eea34
result, different first party: undefined
unsupported: false, false
passed: true, true
test failed: false, false | 
| localStorage The localStorage API gives websites access to a key-value database that will remain available across visits. If the localStorage API is not partitioned or blocked, it can also be used to track users across websites.
               | write: (secret) => localStorage.setItem("secret", secret)
read: () => localStorage.getItem("secret")
result, same first party: 
3133a4ca-686b-4047-91c3-9a7e181c53f7,
49872d4a-b01d-4af8-8cbe-85c379a609e2,
58bb2b32-4f83-47bb-91c8-96dce39cd97a
result, different first party: , , 
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (secret) => localStorage.setItem("secret", secret)
read: () => localStorage.getItem("secret")
result, same first party: 
e8485c10-5a92-474a-bd16-de23f46ee0e2,
cd5b479f-1529-4f43-a586-810ed83aeb8b
result, different first party: , 
unsupported: false, false
passed: true, true
test failed: false, false | write: (secret) => localStorage.setItem("secret", secret)
read: () => localStorage.getItem("secret")
result, same first party: 
e96973da-bf72-4eb1-b6ab-a4e6c6775fb4,
67fb6941-7c51-4727-b485-317211e65ae0
result, different first party: , 
unsupported: false, false
passed: true, true
test failed: false, false | write: (secret) => localStorage.setItem("secret", secret)
read: () => localStorage.getItem("secret")
result, same first party: 
94f1fd6d-f642-403e-8ef8-11180b18b1f6,
2bf6571e-4385-4b68-a076-f6868cbe4954
result, different first party: , 
unsupported: false, false
passed: true, true
test failed: false, false | write: (secret) => localStorage.setItem("secret", secret)
read: () => localStorage.getItem("secret")
result, same first party: 
da67bebb-5652-441a-816e-1206b25c0225,
0bad71fe-de93-4434-bfa0-bb7f0db62eea,
6aba94ed-010e-45af-892c-dd670cd8d134
result, different first party: , , 
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (secret) => localStorage.setItem("secret", secret)
read: () => localStorage.getItem("secret")
result, same first party: 
9dd63e63-3256-4352-99f6-805b6f48eb8c,
b7cb77db-aca9-4570-923e-b81da9175421,
736f05a1-05e6-491e-8dad-a6647bd52dda
result, different first party: 
Error: The operation is insecure.,
Error: The operation is insecure.,
Error: The operation is insecure.
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (secret) => localStorage.setItem("secret", secret)
read: () => localStorage.getItem("secret")
result, same first party: 
50c6a369-0b5c-4de5-a70b-a0516825119e,
49e0d8b1-c126-4b6b-be66-6ba7acfd85a9
result, different first party: , 
unsupported: false, false
passed: true, true
test failed: false, false | write: (secret) => localStorage.setItem("secret", secret)
read: () => localStorage.getItem("secret")
result, same first party: 
6b07cf4a-a332-4a7c-90df-fbc3dc862f86,
2d40813a-a6c5-4a6b-bb1c-f4d01a399217
result, different first party: , 
unsupported: false, false
passed: true, true
test failed: false, false | write: (secret) => localStorage.setItem("secret", secret)
read: () => localStorage.getItem("secret")
result, same first party: 
3dc5c317-3ea7-42b4-be71-7902bcc4eb27,
4932f81b-b746-4e4b-b97d-c0b827047888,
3e016f09-5749-4084-a136-e9b829c1a824
result, different first party: 
Error: The operation is insecure.,
Error: The operation is insecure.,
Error: The operation is insecure.
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (secret) => localStorage.setItem("secret", secret)
read: () => localStorage.getItem("secret")
result, same first party: 
3242a94e-3344-4cb9-ba7c-ebedaf5d6e01,
87918123-fc52-4196-9d0c-a2cfc3f826d4
result, different first party: , 
unsupported: false, false
passed: true, true
test failed: false, false | write: (secret) => localStorage.setItem("secret", secret)
read: () => localStorage.getItem("secret")
result, same first party: 
5c052838-a55e-4690-83c6-b7d572146713,
49042d99-5b0e-4f37-82f3-78de245eea34
result, different first party: , 
unsupported: false, false
passed: true, true
test failed: false, false | 
| locks navigator.locks (only supported in some browsers) allows scripts on multiple tabs to coordinate. If this API is not partitioned, it can be used for cross-site tracking.
               | write: async (key) => {
      if (navigator.locks) {
        navigator.locks.request(key, lock => new Promise((f,r) => {}));
        let queryResult = await navigator.locks.query();
        return queryResult.held[0].clientId;
      } else {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      if (navigator.locks) {
        let queryResult = await navigator.locks.query();
        return queryResult.held[0].name;
      }
    }
result, same first party: 
3133a4ca-686b-4047-91c3-9a7e181c53f7,
49872d4a-b01d-4af8-8cbe-85c379a609e2,
58bb2b32-4f83-47bb-91c8-96dce39cd97a
result, different first party: 
Error: The request was denied.,
Error: The request was denied.,
Error: The request was denied.
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (key) => {
      if (navigator.locks) {
        navigator.locks.request(key, lock => new Promise((f,r) => {}));
        let queryResult = await navigator.locks.query();
        return queryResult.held[0].clientId;
      } else {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      if (navigator.locks) {
        let queryResult = await navigator.locks.query();
        return queryResult.held[0].name;
      }
    }
result, same first party: 
e8485c10-5a92-474a-bd16-de23f46ee0e2,
cd5b479f-1529-4f43-a586-810ed83aeb8b
result, different first party: 
Error: Cannot read properties of undefined (reading 'name'),
Error: Cannot read properties of undefined (reading 'name')
unsupported: false, false
passed: true, true
test failed: false, false | write: async (key) => {
      if (navigator.locks) {
        navigator.locks.request(key, lock => new Promise((f,r) => {}));
        let queryResult = await navigator.locks.query();
        return queryResult.held[0].clientId;
      } else {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      if (navigator.locks) {
        let queryResult = await navigator.locks.query();
        return queryResult.held[0].name;
      }
    }
result, same first party: 
e96973da-bf72-4eb1-b6ab-a4e6c6775fb4,
67fb6941-7c51-4727-b485-317211e65ae0
result, different first party: 
Error: Cannot read properties of undefined (reading 'name'),
Error: Cannot read properties of undefined (reading 'name')
unsupported: false, false
passed: true, true
test failed: false, false | write: async (key) => {
      if (navigator.locks) {
        navigator.locks.request(key, lock => new Promise((f,r) => {}));
        let queryResult = await navigator.locks.query();
        return queryResult.held[0].clientId;
      } else {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      if (navigator.locks) {
        let queryResult = await navigator.locks.query();
        return queryResult.held[0].name;
      }
    }
result, same first party: 
94f1fd6d-f642-403e-8ef8-11180b18b1f6,
2bf6571e-4385-4b68-a076-f6868cbe4954
result, different first party: 
Error: LockManager.query: query() is not allowed in this context,
Error: LockManager.query: query() is not allowed in this context
unsupported: false, false
passed: true, true
test failed: false, false | write: async (key) => {
      if (navigator.locks) {
        navigator.locks.request(key, lock => new Promise((f,r) => {}));
        let queryResult = await navigator.locks.query();
        return queryResult.held[0].clientId;
      } else {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      if (navigator.locks) {
        let queryResult = await navigator.locks.query();
        return queryResult.held[0].name;
      }
    }
result, same first party: 
da67bebb-5652-441a-816e-1206b25c0225,
0bad71fe-de93-4434-bfa0-bb7f0db62eea,
6aba94ed-010e-45af-892c-dd670cd8d134
result, different first party: 
Error: LockManager.query: query() is not allowed in this context,
Error: LockManager.query: query() is not allowed in this context,
Error: LockManager.query: query() is not allowed in this context
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (key) => {
      if (navigator.locks) {
        navigator.locks.request(key, lock => new Promise((f,r) => {}));
        let queryResult = await navigator.locks.query();
        return queryResult.held[0].clientId;
      } else {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      if (navigator.locks) {
        let queryResult = await navigator.locks.query();
        return queryResult.held[0].name;
      }
    }
result, same first party: 
9dd63e63-3256-4352-99f6-805b6f48eb8c,
b7cb77db-aca9-4570-923e-b81da9175421,
736f05a1-05e6-491e-8dad-a6647bd52dda
result, different first party: 
Error: LockManager.query: query() is not allowed in this context,
Error: LockManager.query: query() is not allowed in this context,
Error: LockManager.query: query() is not allowed in this context
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (key) => {
      if (navigator.locks) {
        navigator.locks.request(key, lock => new Promise((f,r) => {}));
        let queryResult = await navigator.locks.query();
        return queryResult.held[0].clientId;
      } else {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      if (navigator.locks) {
        let queryResult = await navigator.locks.query();
        return queryResult.held[0].name;
      }
    }
result, same first party: 
50c6a369-0b5c-4de5-a70b-a0516825119e,
49e0d8b1-c126-4b6b-be66-6ba7acfd85a9
result, different first party: 
Error: Cannot read properties of undefined (reading 'name'),
Error: Cannot read properties of undefined (reading 'name')
unsupported: false, false
passed: true, true
test failed: false, false | write: async (key) => {
      if (navigator.locks) {
        navigator.locks.request(key, lock => new Promise((f,r) => {}));
        let queryResult = await navigator.locks.query();
        return queryResult.held[0].clientId;
      } else {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      if (navigator.locks) {
        let queryResult = await navigator.locks.query();
        return queryResult.held[0].name;
      }
    }
result, same first party: 
6b07cf4a-a332-4a7c-90df-fbc3dc862f86,
2d40813a-a6c5-4a6b-bb1c-f4d01a399217
result, different first party: 
Error: undefined is not an object (evaluating 'queryResult.held[0].name'),
Error: undefined is not an object (evaluating 'queryResult.held[0].name')
unsupported: false, false
passed: true, true
test failed: false, false | write: async (key) => {
      if (navigator.locks) {
        navigator.locks.request(key, lock => new Promise((f,r) => {}));
        let queryResult = await navigator.locks.query();
        return queryResult.held[0].clientId;
      } else {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      if (navigator.locks) {
        let queryResult = await navigator.locks.query();
        return queryResult.held[0].name;
      }
    }
result, same first party: 
3dc5c317-3ea7-42b4-be71-7902bcc4eb27,
4932f81b-b746-4e4b-b97d-c0b827047888,
3e016f09-5749-4084-a136-e9b829c1a824
result, different first party: 
Error: LockManager.query: query() is not allowed in this context,
Error: LockManager.query: query() is not allowed in this context,
Error: LockManager.query: query() is not allowed in this context
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (key) => {
      if (navigator.locks) {
        navigator.locks.request(key, lock => new Promise((f,r) => {}));
        let queryResult = await navigator.locks.query();
        return queryResult.held[0].clientId;
      } else {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      if (navigator.locks) {
        let queryResult = await navigator.locks.query();
        return queryResult.held[0].name;
      }
    }
result, same first party: 
3242a94e-3344-4cb9-ba7c-ebedaf5d6e01,
87918123-fc52-4196-9d0c-a2cfc3f826d4
result, different first party: 
Error: Cannot read properties of undefined (reading 'name'),
Error: Cannot read properties of undefined (reading 'name')
unsupported: false, false
passed: true, true
test failed: false, false | write: async (key) => {
      if (navigator.locks) {
        navigator.locks.request(key, lock => new Promise((f,r) => {}));
        let queryResult = await navigator.locks.query();
        return queryResult.held[0].clientId;
      } else {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      if (navigator.locks) {
        let queryResult = await navigator.locks.query();
        return queryResult.held[0].name;
      }
    }
result, same first party: 
5c052838-a55e-4690-83c6-b7d572146713,
49042d99-5b0e-4f37-82f3-78de245eea34
result, different first party: 
Error: Cannot read properties of undefined (reading 'name'),
Error: Cannot read properties of undefined (reading 'name')
unsupported: false, false
passed: true, true
test failed: false, false | 
| prefetch cache A <link rel='prefetch'...> suggests to browsers they should fetch a resource ahead of time and cache it. But if browsers don't partition this cache, it can be used to track users across websites.
               | write: async (key) => {
      let link = document.createElement("link");
      link.rel = "prefetch";
      link.href = testURI("resource", "prefetch", key);
      document.getElementsByTagName("head")[0].appendChild(link);
      return key;
    }
read: async (key) => {
      let link = document.createElement("link");
      link.rel = "prefetch";
      link.href = testURI("resource", "prefetch", key);
      document.getElementsByTagName("head")[0].appendChild(link);
      await sleepMs(500);
      let response = await fetch(
        testURI("ctr", "prefetch", key), {"cache": "reload"});
      let countString = (await response.text()).trim();
      if (parseInt(countString) === 0) {
        throw new Error("No requests received");
      }
      return countString;
    }
result, same first party: 1, 1, 1
result, different first party: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (key) => {
      let link = document.createElement("link");
      link.rel = "prefetch";
      link.href = testURI("resource", "prefetch", key);
      document.getElementsByTagName("head")[0].appendChild(link);
      return key;
    }
read: async (key) => {
      let link = document.createElement("link");
      link.rel = "prefetch";
      link.href = testURI("resource", "prefetch", key);
      document.getElementsByTagName("head")[0].appendChild(link);
      await sleepMs(500);
      let response = await fetch(
        testURI("ctr", "prefetch", key), {"cache": "reload"});
      let countString = (await response.text()).trim();
      if (parseInt(countString) === 0) {
        throw new Error("No requests received");
      }
      return countString;
    }
result, same first party: 1, 1
result, different first party: 2, 2
unsupported: false, false
passed: true, true
test failed: false, false | write: async (key) => {
      let link = document.createElement("link");
      link.rel = "prefetch";
      link.href = testURI("resource", "prefetch", key);
      document.getElementsByTagName("head")[0].appendChild(link);
      return key;
    }
read: async (key) => {
      let link = document.createElement("link");
      link.rel = "prefetch";
      link.href = testURI("resource", "prefetch", key);
      document.getElementsByTagName("head")[0].appendChild(link);
      await sleepMs(500);
      let response = await fetch(
        testURI("ctr", "prefetch", key), {"cache": "reload"});
      let countString = (await response.text()).trim();
      if (parseInt(countString) === 0) {
        throw new Error("No requests received");
      }
      return countString;
    }
result, same first party: 1, 1
result, different first party: 2, 2
unsupported: false, false
passed: true, true
test failed: false, false | write: async (key) => {
      let link = document.createElement("link");
      link.rel = "prefetch";
      link.href = testURI("resource", "prefetch", key);
      document.getElementsByTagName("head")[0].appendChild(link);
      return key;
    }
read: async (key) => {
      let link = document.createElement("link");
      link.rel = "prefetch";
      link.href = testURI("resource", "prefetch", key);
      document.getElementsByTagName("head")[0].appendChild(link);
      await sleepMs(500);
      let response = await fetch(
        testURI("ctr", "prefetch", key), {"cache": "reload"});
      let countString = (await response.text()).trim();
      if (parseInt(countString) === 0) {
        throw new Error("No requests received");
      }
      return countString;
    }
result, same first party: 1, 1
result, different first party: 2, 2
unsupported: false, false
passed: true, true
test failed: false, false | write: async (key) => {
      let link = document.createElement("link");
      link.rel = "prefetch";
      link.href = testURI("resource", "prefetch", key);
      document.getElementsByTagName("head")[0].appendChild(link);
      return key;
    }
read: async (key) => {
      let link = document.createElement("link");
      link.rel = "prefetch";
      link.href = testURI("resource", "prefetch", key);
      document.getElementsByTagName("head")[0].appendChild(link);
      await sleepMs(500);
      let response = await fetch(
        testURI("ctr", "prefetch", key), {"cache": "reload"});
      let countString = (await response.text()).trim();
      if (parseInt(countString) === 0) {
        throw new Error("No requests received");
      }
      return countString;
    }
result, same first party: 
Error: No requests received,
Error: No requests received,
Error: No requests received
result, different first party: 
Error: No requests received,
Error: No requests received,
Error: No requests received
unsupported: true, true, true
passed: undefined
test failed: false, false, false | write: async (key) => {
      let link = document.createElement("link");
      link.rel = "prefetch";
      link.href = testURI("resource", "prefetch", key);
      document.getElementsByTagName("head")[0].appendChild(link);
      return key;
    }
read: async (key) => {
      let link = document.createElement("link");
      link.rel = "prefetch";
      link.href = testURI("resource", "prefetch", key);
      document.getElementsByTagName("head")[0].appendChild(link);
      await sleepMs(500);
      let response = await fetch(
        testURI("ctr", "prefetch", key), {"cache": "reload"});
      let countString = (await response.text()).trim();
      if (parseInt(countString) === 0) {
        throw new Error("No requests received");
      }
      return countString;
    }
result, same first party: 
Error: No requests received,
Error: No requests received,
Error: No requests received
result, different first party: 
Error: No requests received,
Error: No requests received,
Error: No requests received
unsupported: true, true, true
passed: undefined
test failed: false, false, false | write: async (key) => {
      let link = document.createElement("link");
      link.rel = "prefetch";
      link.href = testURI("resource", "prefetch", key);
      document.getElementsByTagName("head")[0].appendChild(link);
      return key;
    }
read: async (key) => {
      let link = document.createElement("link");
      link.rel = "prefetch";
      link.href = testURI("resource", "prefetch", key);
      document.getElementsByTagName("head")[0].appendChild(link);
      await sleepMs(500);
      let response = await fetch(
        testURI("ctr", "prefetch", key), {"cache": "reload"});
      let countString = (await response.text()).trim();
      if (parseInt(countString) === 0) {
        throw new Error("No requests received");
      }
      return countString;
    }
result, same first party: 1, 1
result, different first party: 2, 2
unsupported: false, false
passed: true, true
test failed: false, false | write: async (key) => {
      let link = document.createElement("link");
      link.rel = "prefetch";
      link.href = testURI("resource", "prefetch", key);
      document.getElementsByTagName("head")[0].appendChild(link);
      return key;
    }
read: async (key) => {
      let link = document.createElement("link");
      link.rel = "prefetch";
      link.href = testURI("resource", "prefetch", key);
      document.getElementsByTagName("head")[0].appendChild(link);
      await sleepMs(500);
      let response = await fetch(
        testURI("ctr", "prefetch", key), {"cache": "reload"});
      let countString = (await response.text()).trim();
      if (parseInt(countString) === 0) {
        throw new Error("No requests received");
      }
      return countString;
    }
result, same first party: 
Error: No requests received,
Error: No requests received
result, different first party: 
Error: No requests received,
Error: No requests received
unsupported: true, true
passed: undefined
test failed: false, false | write: async (key) => {
      let link = document.createElement("link");
      link.rel = "prefetch";
      link.href = testURI("resource", "prefetch", key);
      document.getElementsByTagName("head")[0].appendChild(link);
      return key;
    }
read: async (key) => {
      let link = document.createElement("link");
      link.rel = "prefetch";
      link.href = testURI("resource", "prefetch", key);
      document.getElementsByTagName("head")[0].appendChild(link);
      await sleepMs(500);
      let response = await fetch(
        testURI("ctr", "prefetch", key), {"cache": "reload"});
      let countString = (await response.text()).trim();
      if (parseInt(countString) === 0) {
        throw new Error("No requests received");
      }
      return countString;
    }
result, same first party: 
Error: No requests received,
Error: No requests received,
Error: No requests received
result, different first party: 
Error: No requests received,
Error: No requests received,
Error: No requests received
unsupported: true, true, true
passed: undefined
test failed: false, false, false | write: async (key) => {
      let link = document.createElement("link");
      link.rel = "prefetch";
      link.href = testURI("resource", "prefetch", key);
      document.getElementsByTagName("head")[0].appendChild(link);
      return key;
    }
read: async (key) => {
      let link = document.createElement("link");
      link.rel = "prefetch";
      link.href = testURI("resource", "prefetch", key);
      document.getElementsByTagName("head")[0].appendChild(link);
      await sleepMs(500);
      let response = await fetch(
        testURI("ctr", "prefetch", key), {"cache": "reload"});
      let countString = (await response.text()).trim();
      if (parseInt(countString) === 0) {
        throw new Error("No requests received");
      }
      return countString;
    }
result, same first party: 1, 1
result, different first party: 2, 2
unsupported: false, false
passed: true, true
test failed: false, false | write: async (key) => {
      let link = document.createElement("link");
      link.rel = "prefetch";
      link.href = testURI("resource", "prefetch", key);
      document.getElementsByTagName("head")[0].appendChild(link);
      return key;
    }
read: async (key) => {
      let link = document.createElement("link");
      link.rel = "prefetch";
      link.href = testURI("resource", "prefetch", key);
      document.getElementsByTagName("head")[0].appendChild(link);
      await sleepMs(500);
      let response = await fetch(
        testURI("ctr", "prefetch", key), {"cache": "reload"});
      let countString = (await response.text()).trim();
      if (parseInt(countString) === 0) {
        throw new Error("No requests received");
      }
      return countString;
    }
result, same first party: 1, 1
result, different first party: 2, 2
unsupported: false, false
passed: true, true
test failed: false, false | 
| script cache Caching of scripts in web browsers is a standard behavior. But if that cache leaks between websites, it can be abused for cross-site tracking.
               | write: (key) => new Promise((resolve, reject) => {
      let script = document.createElement("script");
      document.body.appendChild(script);
      script.addEventListener("load", () => resolve(key), {once: true});
      script.src = testURI("resource", "script", key);
    })
read: async (key) => {
      let script = document.createElement("script");
      document.body.appendChild(script);
      let scriptLoadPromise = new Promise((resolve, reject) => {
        script.addEventListener("load", resolve, {once: true});
      });
      script.src = testURI("resource", "script", key);
      await scriptLoadPromise;
      let response = await fetch(
        testURI("ctr", "script", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same first party: 1, 1, 1
result, different first party: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (key) => new Promise((resolve, reject) => {
      let script = document.createElement("script");
      document.body.appendChild(script);
      script.addEventListener("load", () => resolve(key), {once: true});
      script.src = testURI("resource", "script", key);
    })
read: async (key) => {
      let script = document.createElement("script");
      document.body.appendChild(script);
      let scriptLoadPromise = new Promise((resolve, reject) => {
        script.addEventListener("load", resolve, {once: true});
      });
      script.src = testURI("resource", "script", key);
      await scriptLoadPromise;
      let response = await fetch(
        testURI("ctr", "script", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same first party: 1, 1
result, different first party: 2, 2
unsupported: false, false
passed: true, true
test failed: false, false | write: (key) => new Promise((resolve, reject) => {
      let script = document.createElement("script");
      document.body.appendChild(script);
      script.addEventListener("load", () => resolve(key), {once: true});
      script.src = testURI("resource", "script", key);
    })
read: async (key) => {
      let script = document.createElement("script");
      document.body.appendChild(script);
      let scriptLoadPromise = new Promise((resolve, reject) => {
        script.addEventListener("load", resolve, {once: true});
      });
      script.src = testURI("resource", "script", key);
      await scriptLoadPromise;
      let response = await fetch(
        testURI("ctr", "script", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same first party: 1, 1
result, different first party: 2, 2
unsupported: false, false
passed: true, true
test failed: false, false | write: (key) => new Promise((resolve, reject) => {
      let script = document.createElement("script");
      document.body.appendChild(script);
      script.addEventListener("load", () => resolve(key), {once: true});
      script.src = testURI("resource", "script", key);
    })
read: async (key) => {
      let script = document.createElement("script");
      document.body.appendChild(script);
      let scriptLoadPromise = new Promise((resolve, reject) => {
        script.addEventListener("load", resolve, {once: true});
      });
      script.src = testURI("resource", "script", key);
      await scriptLoadPromise;
      let response = await fetch(
        testURI("ctr", "script", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same first party: 1, 1
result, different first party: 2, 2
unsupported: false, false
passed: true, true
test failed: false, false | write: (key) => new Promise((resolve, reject) => {
      let script = document.createElement("script");
      document.body.appendChild(script);
      script.addEventListener("load", () => resolve(key), {once: true});
      script.src = testURI("resource", "script", key);
    })
read: async (key) => {
      let script = document.createElement("script");
      document.body.appendChild(script);
      let scriptLoadPromise = new Promise((resolve, reject) => {
        script.addEventListener("load", resolve, {once: true});
      });
      script.src = testURI("resource", "script", key);
      await scriptLoadPromise;
      let response = await fetch(
        testURI("ctr", "script", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same first party: 1, 1, 1
result, different first party: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (key) => new Promise((resolve, reject) => {
      let script = document.createElement("script");
      document.body.appendChild(script);
      script.addEventListener("load", () => resolve(key), {once: true});
      script.src = testURI("resource", "script", key);
    })
read: async (key) => {
      let script = document.createElement("script");
      document.body.appendChild(script);
      let scriptLoadPromise = new Promise((resolve, reject) => {
        script.addEventListener("load", resolve, {once: true});
      });
      script.src = testURI("resource", "script", key);
      await scriptLoadPromise;
      let response = await fetch(
        testURI("ctr", "script", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same first party: 1, 1, 1
result, different first party: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (key) => new Promise((resolve, reject) => {
      let script = document.createElement("script");
      document.body.appendChild(script);
      script.addEventListener("load", () => resolve(key), {once: true});
      script.src = testURI("resource", "script", key);
    })
read: async (key) => {
      let script = document.createElement("script");
      document.body.appendChild(script);
      let scriptLoadPromise = new Promise((resolve, reject) => {
        script.addEventListener("load", resolve, {once: true});
      });
      script.src = testURI("resource", "script", key);
      await scriptLoadPromise;
      let response = await fetch(
        testURI("ctr", "script", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same first party: 1, 1
result, different first party: 2, 2
unsupported: false, false
passed: true, true
test failed: false, false | write: (key) => new Promise((resolve, reject) => {
      let script = document.createElement("script");
      document.body.appendChild(script);
      script.addEventListener("load", () => resolve(key), {once: true});
      script.src = testURI("resource", "script", key);
    })
read: async (key) => {
      let script = document.createElement("script");
      document.body.appendChild(script);
      let scriptLoadPromise = new Promise((resolve, reject) => {
        script.addEventListener("load", resolve, {once: true});
      });
      script.src = testURI("resource", "script", key);
      await scriptLoadPromise;
      let response = await fetch(
        testURI("ctr", "script", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same first party: 2, 2
result, different first party: 3, 3
unsupported: false, false
passed: true, true
test failed: false, false | write: (key) => new Promise((resolve, reject) => {
      let script = document.createElement("script");
      document.body.appendChild(script);
      script.addEventListener("load", () => resolve(key), {once: true});
      script.src = testURI("resource", "script", key);
    })
read: async (key) => {
      let script = document.createElement("script");
      document.body.appendChild(script);
      let scriptLoadPromise = new Promise((resolve, reject) => {
        script.addEventListener("load", resolve, {once: true});
      });
      script.src = testURI("resource", "script", key);
      await scriptLoadPromise;
      let response = await fetch(
        testURI("ctr", "script", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same first party: 1, 1, 1
result, different first party: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (key) => new Promise((resolve, reject) => {
      let script = document.createElement("script");
      document.body.appendChild(script);
      script.addEventListener("load", () => resolve(key), {once: true});
      script.src = testURI("resource", "script", key);
    })
read: async (key) => {
      let script = document.createElement("script");
      document.body.appendChild(script);
      let scriptLoadPromise = new Promise((resolve, reject) => {
        script.addEventListener("load", resolve, {once: true});
      });
      script.src = testURI("resource", "script", key);
      await scriptLoadPromise;
      let response = await fetch(
        testURI("ctr", "script", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same first party: 1, 1
result, different first party: 2, 2
unsupported: false, false
passed: true, true
test failed: false, false | write: (key) => new Promise((resolve, reject) => {
      let script = document.createElement("script");
      document.body.appendChild(script);
      script.addEventListener("load", () => resolve(key), {once: true});
      script.src = testURI("resource", "script", key);
    })
read: async (key) => {
      let script = document.createElement("script");
      document.body.appendChild(script);
      let scriptLoadPromise = new Promise((resolve, reject) => {
        script.addEventListener("load", resolve, {once: true});
      });
      script.src = testURI("resource", "script", key);
      await scriptLoadPromise;
      let response = await fetch(
        testURI("ctr", "script", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same first party: 1, 1
result, different first party: 2, 2
unsupported: false, false
passed: true, true
test failed: false, false | 
| ServiceWorker The ServiceWorker API allows websites to run code in the background and store content in the browser for offline use. If a ServiceWorker can be accessed from multiple websites, it can be abused to track users across sites.
               | write: async (key) => {
      if (!navigator.serviceWorker) {
        throw new Error("Unsupported");
      }
      let registration = await navigator.serviceWorker.register(
        'serviceWorker.js');
      console.log(registration);
      await navigator.serviceWorker.ready;
      console.log("service worker ready");
      await sleepMs(100);
      await fetch(`serviceworker-write?secret=${key}`);
    }
read: async () => {
      console.log("trying to register the serviceworker now...");
      const registration = await Promise.race([
        navigator.serviceWorker.register('serviceWorker.js'),
        sleepMs(500)
      ]);
      if (registration === undefined) {
        // We timed out or otherwise failed.
        throw new Error("ServiceWorker registration failed");
      }
      console.log(registration);
      await navigator.serviceWorker.ready;
      console.log("service worker ready");
      await sleepMs(100);
      let response = await fetch("serviceworker-read");
      return await response.text();
    }
result, same first party: 
<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>nginx/1.18.0 (Ubuntu)</center>
</body>
</html>
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
,
<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>nginx/1.18.0 (Ubuntu)</center>
</body>
</html>
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
,
<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>nginx/1.18.0 (Ubuntu)</center>
</body>
</html>
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
result, different first party: 
Error: Failed to register a ServiceWorker for scope ('https://test-pages.privacytests2.org/') with script ('https://test-pages.privacytests2.org/serviceWorker.js'): The user denied permission to use Service Worker.,
Error: Failed to register a ServiceWorker for scope ('https://test-pages.privacytests2.org/') with script ('https://test-pages.privacytests2.org/serviceWorker.js'): The user denied permission to use Service Worker.,
Error: Failed to register a ServiceWorker for scope ('https://test-pages.privacytests2.org/') with script ('https://test-pages.privacytests2.org/serviceWorker.js'): The user denied permission to use Service Worker.
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (key) => {
      if (!navigator.serviceWorker) {
        throw new Error("Unsupported");
      }
      let registration = await navigator.serviceWorker.register(
        'serviceWorker.js');
      console.log(registration);
      await navigator.serviceWorker.ready;
      console.log("service worker ready");
      await sleepMs(100);
      await fetch(`serviceworker-write?secret=${key}`);
    }
read: async () => {
      console.log("trying to register the serviceworker now...");
      const registration = await Promise.race([
        navigator.serviceWorker.register('serviceWorker.js'),
        sleepMs(500)
      ]);
      if (registration === undefined) {
        // We timed out or otherwise failed.
        throw new Error("ServiceWorker registration failed");
      }
      console.log(registration);
      await navigator.serviceWorker.ready;
      console.log("service worker ready");
      await sleepMs(100);
      let response = await fetch("serviceworker-read");
      return await response.text();
    }
result, same first party: 
<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>nginx/1.18.0 (Ubuntu)</center>
</body>
</html>
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
,
<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>nginx/1.18.0 (Ubuntu)</center>
</body>
</html>
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
result, different first party: , 
unsupported: false, false
passed: true, true
test failed: false, false | write: async (key) => {
      if (!navigator.serviceWorker) {
        throw new Error("Unsupported");
      }
      let registration = await navigator.serviceWorker.register(
        'serviceWorker.js');
      console.log(registration);
      await navigator.serviceWorker.ready;
      console.log("service worker ready");
      await sleepMs(100);
      await fetch(`serviceworker-write?secret=${key}`);
    }
read: async () => {
      console.log("trying to register the serviceworker now...");
      const registration = await Promise.race([
        navigator.serviceWorker.register('serviceWorker.js'),
        sleepMs(500)
      ]);
      if (registration === undefined) {
        // We timed out or otherwise failed.
        throw new Error("ServiceWorker registration failed");
      }
      console.log(registration);
      await navigator.serviceWorker.ready;
      console.log("service worker ready");
      await sleepMs(100);
      let response = await fetch("serviceworker-read");
      return await response.text();
    }
result, same first party: 
<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>nginx/1.18.0 (Ubuntu)</center>
</body>
</html>
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
,
<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>nginx/1.18.0 (Ubuntu)</center>
</body>
</html>
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
result, different first party: , 
unsupported: false, false
passed: true, true
test failed: false, false | write: async (key) => {
      if (!navigator.serviceWorker) {
        throw new Error("Unsupported");
      }
      let registration = await navigator.serviceWorker.register(
        'serviceWorker.js');
      console.log(registration);
      await navigator.serviceWorker.ready;
      console.log("service worker ready");
      await sleepMs(100);
      await fetch(`serviceworker-write?secret=${key}`);
    }
read: async () => {
      console.log("trying to register the serviceworker now...");
      const registration = await Promise.race([
        navigator.serviceWorker.register('serviceWorker.js'),
        sleepMs(500)
      ]);
      if (registration === undefined) {
        // We timed out or otherwise failed.
        throw new Error("ServiceWorker registration failed");
      }
      console.log(registration);
      await navigator.serviceWorker.ready;
      console.log("service worker ready");
      await sleepMs(100);
      let response = await fetch("serviceworker-read");
      return await response.text();
    }
result, same first party: , 
result, different first party: , 
unsupported: false, false
passed: undefined
test failed: true, true | write: async (key) => {
      if (!navigator.serviceWorker) {
        throw new Error("Unsupported");
      }
      let registration = await navigator.serviceWorker.register(
        'serviceWorker.js');
      console.log(registration);
      await navigator.serviceWorker.ready;
      console.log("service worker ready");
      await sleepMs(100);
      await fetch(`serviceworker-write?secret=${key}`);
    }
read: async () => {
      console.log("trying to register the serviceworker now...");
      const registration = await Promise.race([
        navigator.serviceWorker.register('serviceWorker.js'),
        sleepMs(500)
      ]);
      if (registration === undefined) {
        // We timed out or otherwise failed.
        throw new Error("ServiceWorker registration failed");
      }
      console.log(registration);
      await navigator.serviceWorker.ready;
      console.log("service worker ready");
      await sleepMs(100);
      let response = await fetch("serviceworker-read");
      return await response.text();
    }
result, same first party: , , 
result, different first party: , , 
unsupported: false, false, false
passed: undefined
test failed: true, true, true | write: async (key) => {
      if (!navigator.serviceWorker) {
        throw new Error("Unsupported");
      }
      let registration = await navigator.serviceWorker.register(
        'serviceWorker.js');
      console.log(registration);
      await navigator.serviceWorker.ready;
      console.log("service worker ready");
      await sleepMs(100);
      await fetch(`serviceworker-write?secret=${key}`);
    }
read: async () => {
      console.log("trying to register the serviceworker now...");
      const registration = await Promise.race([
        navigator.serviceWorker.register('serviceWorker.js'),
        sleepMs(500)
      ]);
      if (registration === undefined) {
        // We timed out or otherwise failed.
        throw new Error("ServiceWorker registration failed");
      }
      console.log(registration);
      await navigator.serviceWorker.ready;
      console.log("service worker ready");
      await sleepMs(100);
      let response = await fetch("serviceworker-read");
      return await response.text();
    }
result, same first party: 
Error: navigator.serviceWorker is undefined,
Error: navigator.serviceWorker is undefined,
Error: navigator.serviceWorker is undefined
result, different first party: 
Error: navigator.serviceWorker is undefined,
Error: navigator.serviceWorker is undefined,
Error: navigator.serviceWorker is undefined
unsupported: true, true, true
passed: undefined
test failed: false, false, false | write: async (key) => {
      if (!navigator.serviceWorker) {
        throw new Error("Unsupported");
      }
      let registration = await navigator.serviceWorker.register(
        'serviceWorker.js');
      console.log(registration);
      await navigator.serviceWorker.ready;
      console.log("service worker ready");
      await sleepMs(100);
      await fetch(`serviceworker-write?secret=${key}`);
    }
read: async () => {
      console.log("trying to register the serviceworker now...");
      const registration = await Promise.race([
        navigator.serviceWorker.register('serviceWorker.js'),
        sleepMs(500)
      ]);
      if (registration === undefined) {
        // We timed out or otherwise failed.
        throw new Error("ServiceWorker registration failed");
      }
      console.log(registration);
      await navigator.serviceWorker.ready;
      console.log("service worker ready");
      await sleepMs(100);
      let response = await fetch("serviceworker-read");
      return await response.text();
    }
result, same first party: 
<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>nginx/1.18.0 (Ubuntu)</center>
</body>
</html>
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
,
<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>nginx/1.18.0 (Ubuntu)</center>
</body>
</html>
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
result, different first party: , 
unsupported: false, false
passed: true, true
test failed: false, false | write: async (key) => {
      if (!navigator.serviceWorker) {
        throw new Error("Unsupported");
      }
      let registration = await navigator.serviceWorker.register(
        'serviceWorker.js');
      console.log(registration);
      await navigator.serviceWorker.ready;
      console.log("service worker ready");
      await sleepMs(100);
      await fetch(`serviceworker-write?secret=${key}`);
    }
read: async () => {
      console.log("trying to register the serviceworker now...");
      const registration = await Promise.race([
        navigator.serviceWorker.register('serviceWorker.js'),
        sleepMs(500)
      ]);
      if (registration === undefined) {
        // We timed out or otherwise failed.
        throw new Error("ServiceWorker registration failed");
      }
      console.log(registration);
      await navigator.serviceWorker.ready;
      console.log("service worker ready");
      await sleepMs(100);
      let response = await fetch("serviceworker-read");
      return await response.text();
    }
result, same first party: , 
result, different first party: , 
unsupported: false, false
passed: undefined
test failed: true, true | write: async (key) => {
      if (!navigator.serviceWorker) {
        throw new Error("Unsupported");
      }
      let registration = await navigator.serviceWorker.register(
        'serviceWorker.js');
      console.log(registration);
      await navigator.serviceWorker.ready;
      console.log("service worker ready");
      await sleepMs(100);
      await fetch(`serviceworker-write?secret=${key}`);
    }
read: async () => {
      console.log("trying to register the serviceworker now...");
      const registration = await Promise.race([
        navigator.serviceWorker.register('serviceWorker.js'),
        sleepMs(500)
      ]);
      if (registration === undefined) {
        // We timed out or otherwise failed.
        throw new Error("ServiceWorker registration failed");
      }
      console.log(registration);
      await navigator.serviceWorker.ready;
      console.log("service worker ready");
      await sleepMs(100);
      let response = await fetch("serviceworker-read");
      return await response.text();
    }
result, same first party: 
Error: navigator.serviceWorker is undefined,
Error: navigator.serviceWorker is undefined,
Error: navigator.serviceWorker is undefined
result, different first party: 
Error: navigator.serviceWorker is undefined,
Error: navigator.serviceWorker is undefined,
Error: navigator.serviceWorker is undefined
unsupported: true, true, true
passed: undefined
test failed: false, false, false | write: async (key) => {
      if (!navigator.serviceWorker) {
        throw new Error("Unsupported");
      }
      let registration = await navigator.serviceWorker.register(
        'serviceWorker.js');
      console.log(registration);
      await navigator.serviceWorker.ready;
      console.log("service worker ready");
      await sleepMs(100);
      await fetch(`serviceworker-write?secret=${key}`);
    }
read: async () => {
      console.log("trying to register the serviceworker now...");
      const registration = await Promise.race([
        navigator.serviceWorker.register('serviceWorker.js'),
        sleepMs(500)
      ]);
      if (registration === undefined) {
        // We timed out or otherwise failed.
        throw new Error("ServiceWorker registration failed");
      }
      console.log(registration);
      await navigator.serviceWorker.ready;
      console.log("service worker ready");
      await sleepMs(100);
      let response = await fetch("serviceworker-read");
      return await response.text();
    }
result, same first party: 
<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>nginx/1.18.0 (Ubuntu)</center>
</body>
</html>
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
,
<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>nginx/1.18.0 (Ubuntu)</center>
</body>
</html>
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
result, different first party: , 
unsupported: false, false
passed: true, true
test failed: false, false | write: async (key) => {
      if (!navigator.serviceWorker) {
        throw new Error("Unsupported");
      }
      let registration = await navigator.serviceWorker.register(
        'serviceWorker.js');
      console.log(registration);
      await navigator.serviceWorker.ready;
      console.log("service worker ready");
      await sleepMs(100);
      await fetch(`serviceworker-write?secret=${key}`);
    }
read: async () => {
      console.log("trying to register the serviceworker now...");
      const registration = await Promise.race([
        navigator.serviceWorker.register('serviceWorker.js'),
        sleepMs(500)
      ]);
      if (registration === undefined) {
        // We timed out or otherwise failed.
        throw new Error("ServiceWorker registration failed");
      }
      console.log(registration);
      await navigator.serviceWorker.ready;
      console.log("service worker ready");
      await sleepMs(100);
      let response = await fetch("serviceworker-read");
      return await response.text();
    }
result, same first party: 
<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>nginx/1.18.0 (Ubuntu)</center>
</body>
</html>
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
,
<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>nginx/1.18.0 (Ubuntu)</center>
</body>
</html>
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
result, different first party: , 
unsupported: false, false
passed: true, true
test failed: false, false | 
| SharedWorker The SharedWorker API allows scripts from multiple tabs to share a background thread of computation. If SharedWorker is not partitioned, then it can be abused to shared data between websites in your browser.
               | write: async (secret) => {
      try {
        let worker = new SharedWorker("supercookies_sharedworker.js");
        worker.port.start();
//        console.log("worker", worker);
        const messagePromise = new Promise((resolve) => {
          worker.port.onmessage = (e) => resolve(e.data);
        });
        worker.port.postMessage(secret);
        await messagePromise;
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      let worker = new SharedWorker("supercookies_sharedworker.js");
      worker.port.start();
      const messagePromise = new Promise((resolve, reject) => {
        worker.port.onmessage = (e) => resolve(e.data);
        setTimeout(() => reject(new Error("no SharedWorker message received")), 200);
      });
      worker.port.postMessage("request");
      const message = await messagePromise;
      if (message === "none") {
        throw new Error("Unsupported");
      }
      return message;
    }
result, same first party: 
3133a4ca-686b-4047-91c3-9a7e181c53f7,
49872d4a-b01d-4af8-8cbe-85c379a609e2,
58bb2b32-4f83-47bb-91c8-96dce39cd97a
result, different first party: 
Error: no SharedWorker message received,
Error: no SharedWorker message received,
Error: no SharedWorker message received
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (secret) => {
      try {
        let worker = new SharedWorker("supercookies_sharedworker.js");
        worker.port.start();
//        console.log("worker", worker);
        const messagePromise = new Promise((resolve) => {
          worker.port.onmessage = (e) => resolve(e.data);
        });
        worker.port.postMessage(secret);
        await messagePromise;
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      let worker = new SharedWorker("supercookies_sharedworker.js");
      worker.port.start();
      const messagePromise = new Promise((resolve, reject) => {
        worker.port.onmessage = (e) => resolve(e.data);
        setTimeout(() => reject(new Error("no SharedWorker message received")), 200);
      });
      worker.port.postMessage("request");
      const message = await messagePromise;
      if (message === "none") {
        throw new Error("Unsupported");
      }
      return message;
    }
result, same first party: 
e8485c10-5a92-474a-bd16-de23f46ee0e2,
cd5b479f-1529-4f43-a586-810ed83aeb8b
result, different first party: 
Error: Unsupported,
Error: Unsupported
unsupported: false, false
passed: true, true
test failed: false, false | write: async (secret) => {
      try {
        let worker = new SharedWorker("supercookies_sharedworker.js");
        worker.port.start();
//        console.log("worker", worker);
        const messagePromise = new Promise((resolve) => {
          worker.port.onmessage = (e) => resolve(e.data);
        });
        worker.port.postMessage(secret);
        await messagePromise;
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      let worker = new SharedWorker("supercookies_sharedworker.js");
      worker.port.start();
      const messagePromise = new Promise((resolve, reject) => {
        worker.port.onmessage = (e) => resolve(e.data);
        setTimeout(() => reject(new Error("no SharedWorker message received")), 200);
      });
      worker.port.postMessage("request");
      const message = await messagePromise;
      if (message === "none") {
        throw new Error("Unsupported");
      }
      return message;
    }
result, same first party: 
e96973da-bf72-4eb1-b6ab-a4e6c6775fb4,
67fb6941-7c51-4727-b485-317211e65ae0
result, different first party: 
Error: Unsupported,
Error: Unsupported
unsupported: false, false
passed: true, true
test failed: false, false | write: async (secret) => {
      try {
        let worker = new SharedWorker("supercookies_sharedworker.js");
        worker.port.start();
//        console.log("worker", worker);
        const messagePromise = new Promise((resolve) => {
          worker.port.onmessage = (e) => resolve(e.data);
        });
        worker.port.postMessage(secret);
        await messagePromise;
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      let worker = new SharedWorker("supercookies_sharedworker.js");
      worker.port.start();
      const messagePromise = new Promise((resolve, reject) => {
        worker.port.onmessage = (e) => resolve(e.data);
        setTimeout(() => reject(new Error("no SharedWorker message received")), 200);
      });
      worker.port.postMessage("request");
      const message = await messagePromise;
      if (message === "none") {
        throw new Error("Unsupported");
      }
      return message;
    }
result, same first party: 
94f1fd6d-f642-403e-8ef8-11180b18b1f6,
2bf6571e-4385-4b68-a076-f6868cbe4954
result, different first party: 
Error: Unsupported,
Error: Unsupported
unsupported: false, false
passed: true, true
test failed: false, false | write: async (secret) => {
      try {
        let worker = new SharedWorker("supercookies_sharedworker.js");
        worker.port.start();
//        console.log("worker", worker);
        const messagePromise = new Promise((resolve) => {
          worker.port.onmessage = (e) => resolve(e.data);
        });
        worker.port.postMessage(secret);
        await messagePromise;
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      let worker = new SharedWorker("supercookies_sharedworker.js");
      worker.port.start();
      const messagePromise = new Promise((resolve, reject) => {
        worker.port.onmessage = (e) => resolve(e.data);
        setTimeout(() => reject(new Error("no SharedWorker message received")), 200);
      });
      worker.port.postMessage("request");
      const message = await messagePromise;
      if (message === "none") {
        throw new Error("Unsupported");
      }
      return message;
    }
result, same first party: 
da67bebb-5652-441a-816e-1206b25c0225,
0bad71fe-de93-4434-bfa0-bb7f0db62eea,
6aba94ed-010e-45af-892c-dd670cd8d134
result, different first party: 
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (secret) => {
      try {
        let worker = new SharedWorker("supercookies_sharedworker.js");
        worker.port.start();
//        console.log("worker", worker);
        const messagePromise = new Promise((resolve) => {
          worker.port.onmessage = (e) => resolve(e.data);
        });
        worker.port.postMessage(secret);
        await messagePromise;
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      let worker = new SharedWorker("supercookies_sharedworker.js");
      worker.port.start();
      const messagePromise = new Promise((resolve, reject) => {
        worker.port.onmessage = (e) => resolve(e.data);
        setTimeout(() => reject(new Error("no SharedWorker message received")), 200);
      });
      worker.port.postMessage("request");
      const message = await messagePromise;
      if (message === "none") {
        throw new Error("Unsupported");
      }
      return message;
    }
result, same first party: 
9dd63e63-3256-4352-99f6-805b6f48eb8c,
b7cb77db-aca9-4570-923e-b81da9175421,
736f05a1-05e6-491e-8dad-a6647bd52dda
result, different first party: 
Error: The operation is insecure.,
Error: The operation is insecure.,
Error: The operation is insecure.
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (secret) => {
      try {
        let worker = new SharedWorker("supercookies_sharedworker.js");
        worker.port.start();
//        console.log("worker", worker);
        const messagePromise = new Promise((resolve) => {
          worker.port.onmessage = (e) => resolve(e.data);
        });
        worker.port.postMessage(secret);
        await messagePromise;
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      let worker = new SharedWorker("supercookies_sharedworker.js");
      worker.port.start();
      const messagePromise = new Promise((resolve, reject) => {
        worker.port.onmessage = (e) => resolve(e.data);
        setTimeout(() => reject(new Error("no SharedWorker message received")), 200);
      });
      worker.port.postMessage("request");
      const message = await messagePromise;
      if (message === "none") {
        throw new Error("Unsupported");
      }
      return message;
    }
result, same first party: 
50c6a369-0b5c-4de5-a70b-a0516825119e,
49e0d8b1-c126-4b6b-be66-6ba7acfd85a9
result, different first party: 
Error: Unsupported,
Error: Unsupported
unsupported: false, false
passed: true, true
test failed: false, false | write: async (secret) => {
      try {
        let worker = new SharedWorker("supercookies_sharedworker.js");
        worker.port.start();
//        console.log("worker", worker);
        const messagePromise = new Promise((resolve) => {
          worker.port.onmessage = (e) => resolve(e.data);
        });
        worker.port.postMessage(secret);
        await messagePromise;
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      let worker = new SharedWorker("supercookies_sharedworker.js");
      worker.port.start();
      const messagePromise = new Promise((resolve, reject) => {
        worker.port.onmessage = (e) => resolve(e.data);
        setTimeout(() => reject(new Error("no SharedWorker message received")), 200);
      });
      worker.port.postMessage("request");
      const message = await messagePromise;
      if (message === "none") {
        throw new Error("Unsupported");
      }
      return message;
    }
result, same first party: 
6b07cf4a-a332-4a7c-90df-fbc3dc862f86,
2d40813a-a6c5-4a6b-bb1c-f4d01a399217
result, different first party: 
Error: Unsupported,
Error: Unsupported
unsupported: false, false
passed: true, true
test failed: false, false | write: async (secret) => {
      try {
        let worker = new SharedWorker("supercookies_sharedworker.js");
        worker.port.start();
//        console.log("worker", worker);
        const messagePromise = new Promise((resolve) => {
          worker.port.onmessage = (e) => resolve(e.data);
        });
        worker.port.postMessage(secret);
        await messagePromise;
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      let worker = new SharedWorker("supercookies_sharedworker.js");
      worker.port.start();
      const messagePromise = new Promise((resolve, reject) => {
        worker.port.onmessage = (e) => resolve(e.data);
        setTimeout(() => reject(new Error("no SharedWorker message received")), 200);
      });
      worker.port.postMessage("request");
      const message = await messagePromise;
      if (message === "none") {
        throw new Error("Unsupported");
      }
      return message;
    }
result, same first party: 
3dc5c317-3ea7-42b4-be71-7902bcc4eb27,
4932f81b-b746-4e4b-b97d-c0b827047888,
3e016f09-5749-4084-a136-e9b829c1a824
result, different first party: 
Error: The operation is insecure.,
Error: The operation is insecure.,
Error: The operation is insecure.
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (secret) => {
      try {
        let worker = new SharedWorker("supercookies_sharedworker.js");
        worker.port.start();
//        console.log("worker", worker);
        const messagePromise = new Promise((resolve) => {
          worker.port.onmessage = (e) => resolve(e.data);
        });
        worker.port.postMessage(secret);
        await messagePromise;
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      let worker = new SharedWorker("supercookies_sharedworker.js");
      worker.port.start();
      const messagePromise = new Promise((resolve, reject) => {
        worker.port.onmessage = (e) => resolve(e.data);
        setTimeout(() => reject(new Error("no SharedWorker message received")), 200);
      });
      worker.port.postMessage("request");
      const message = await messagePromise;
      if (message === "none") {
        throw new Error("Unsupported");
      }
      return message;
    }
result, same first party: 
3242a94e-3344-4cb9-ba7c-ebedaf5d6e01,
87918123-fc52-4196-9d0c-a2cfc3f826d4
result, different first party: 
Error: Unsupported,
Error: Unsupported
unsupported: false, false
passed: true, true
test failed: false, false | write: async (secret) => {
      try {
        let worker = new SharedWorker("supercookies_sharedworker.js");
        worker.port.start();
//        console.log("worker", worker);
        const messagePromise = new Promise((resolve) => {
          worker.port.onmessage = (e) => resolve(e.data);
        });
        worker.port.postMessage(secret);
        await messagePromise;
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      let worker = new SharedWorker("supercookies_sharedworker.js");
      worker.port.start();
      const messagePromise = new Promise((resolve, reject) => {
        worker.port.onmessage = (e) => resolve(e.data);
        setTimeout(() => reject(new Error("no SharedWorker message received")), 200);
      });
      worker.port.postMessage("request");
      const message = await messagePromise;
      if (message === "none") {
        throw new Error("Unsupported");
      }
      return message;
    }
result, same first party: 
5c052838-a55e-4690-83c6-b7d572146713,
49042d99-5b0e-4f37-82f3-78de245eea34
result, different first party: 
Error: Unsupported,
Error: Unsupported
unsupported: false, false
passed: true, true
test failed: false, false | 
| TLS Session ID The TLS protocol is used by HTTPS to make connections secure. If the browser were to re-use a TLS session, then the session ID could be used to track users across websites.
               | write: async () => {
      let results = await fetch("https://tls.privacytests2.org:8900/");
      return (await results.json()).sessionId;
    }
read: async () => {
      let results = await fetch("https://tls.privacytests2.org:8900/");
      return (await results.json()).sessionId;
    }
result, same first party: 
7b6d69747e15faddab2da2d3ffccfdb7ea466a6280cc4b9061772a510865133d,
6d5720f4f16475f76a650363d763894ede12f258dde5d09936eefac1a20a1a56,
bd06f7cfec5f27046846c06222d2f7402ca7c178fe949131ea5e8e9687337c4c
result, different first party: 
b7b8f94da59904c9f394b3e1f042c758733ec8005bb116df061a1c143145149f,
98114d31206ce9d67ccbccfa393bb08e4f7375160dd66ecdce4a2c6dbc9f9d14,
9379558fedaab2b8743e696e702141a3f23968e9b35b6be6bb7dbec0df4f4404
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async () => {
      let results = await fetch("https://tls.privacytests2.org:8900/");
      return (await results.json()).sessionId;
    }
read: async () => {
      let results = await fetch("https://tls.privacytests2.org:8900/");
      return (await results.json()).sessionId;
    }
result, same first party: 
129a07ce753c8b526f56e683689ce17e33ac2810ed73a64eab27f501fbbb3ab5,
cd1a2f9fb26adda0bebe9d889c8477e01a7ae9d638ca00fbe4cc7313db60e1fb
result, different first party: 
6c166a0426e16689657399a1b23c4e00cc4ccf5c8e303a7fd5604fadbe6c9601,
9af64da8cc4b41ac77f7fea031e4e1c414c87af241bfe104fa70fad045299d0b
unsupported: false, false
passed: true, true
test failed: false, false | write: async () => {
      let results = await fetch("https://tls.privacytests2.org:8900/");
      return (await results.json()).sessionId;
    }
read: async () => {
      let results = await fetch("https://tls.privacytests2.org:8900/");
      return (await results.json()).sessionId;
    }
result, same first party: 
bdd6394d5c608149cd0bfed53de5e056738af568020ad30a4b2d3d2cc5522932,
3e1f83e1fc036c44b2274626f53f44574069c57f901ff2089ccfcfc240be0de9
result, different first party: 
323a2395fc447f8291b1f4b8dfd97b9427014a6d6c0a2e4e9cc9a71aab22d7f6,
44e2e48697b1963e65bff5d2ec3bf14554051c3be45289bff6098ab553c31743
unsupported: false, false
passed: true, true
test failed: false, false | write: async () => {
      let results = await fetch("https://tls.privacytests2.org:8900/");
      return (await results.json()).sessionId;
    }
read: async () => {
      let results = await fetch("https://tls.privacytests2.org:8900/");
      return (await results.json()).sessionId;
    }
result, same first party: 
7f20d2f683e1e393bca0b53beab9448e74ead2b1ee5c0b2a2484a4dc3d21a11a,
ac38a65639a96a4b44274750db28504e8a13e01c6dfa92e6df6f5c0ba717da53
result, different first party: 
21c1b50bb0d0518c3aa606d3c1d2ed22f9bf13ad6151faf6ba7001f3873b5b57,
e7cb1e2fcd8376e7f10a97bd95f959db0b834327cf4e61cb5273cad09810e279
unsupported: false, false
passed: true, true
test failed: false, false | write: async () => {
      let results = await fetch("https://tls.privacytests2.org:8900/");
      return (await results.json()).sessionId;
    }
read: async () => {
      let results = await fetch("https://tls.privacytests2.org:8900/");
      return (await results.json()).sessionId;
    }
result, same first party: 
154286f673f27aa03c7675a5f90984df2eaff811e7ebdcdf9bd64a0c0ad52852,
57eeb851ecf480be9b8dc25a1faf31899d9b7b80264aa0c9234ef33c2631e041,
9f4b0abd096518eeb93c37bbc192966ea8658f54909a9b7fa1278ba6904cadcd
result, different first party: 
dc02336b0ff8b74185ac365cd0cc9d1d478d4b110475c6570dc8a6a598466439,
07bd6ff4e803d805c6cfc8a488ebfa13101f39110d25530cd5979f732ca7d305,
ce961b6103f5670e890069f0194477d59e3c9fd8a00e867036c235fc7fa722b7
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async () => {
      let results = await fetch("https://tls.privacytests2.org:8900/");
      return (await results.json()).sessionId;
    }
read: async () => {
      let results = await fetch("https://tls.privacytests2.org:8900/");
      return (await results.json()).sessionId;
    }
result, same first party: 
4d652790765cc6b79fd06e4f0425288009c966db75d58be4aa4ebdf54454a04f,
53d633e7b3ab22a13b96ef1cc29a367eecf039fec767a22805b0329f938af72b,
72b66e955fb2fa07c2636bcbbb6d7bbad659ed2e7c5907c05c3d92888dbaabef
result, different first party: 
fc78ba45afb56cbfe3e6580569585462f613e37c5b28e2e2a037b3e2b2a06056,
d4bd82526dea64b3038568bf2c1735b0054b9ef32c202708419eb257b06c09db,
cb10c4e85865afc46db83e4cce25e93bccf5d4b8846e427fbb757deca33d8997
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async () => {
      let results = await fetch("https://tls.privacytests2.org:8900/");
      return (await results.json()).sessionId;
    }
read: async () => {
      let results = await fetch("https://tls.privacytests2.org:8900/");
      return (await results.json()).sessionId;
    }
result, same first party: 
0bcb31e66e76240198521f19a5c15c0fda8d2024a08a58aa907f8dda3d3ddbaa,
2c9dfb2cd407acf879cc94dd940de6532deeb2a7b743fb8e570fcf31fda3896d
result, different first party: 
9d6f73da218eecb1381784edb290b3642356c3cf3d8815c8e38cd0d5a99e8116,
e36e6ba6002a0fa41e92a93729bcb5f2123d4652ba0551ff83818bf7e7bd237f
unsupported: false, false
passed: true, true
test failed: false, false | write: async () => {
      let results = await fetch("https://tls.privacytests2.org:8900/");
      return (await results.json()).sessionId;
    }
read: async () => {
      let results = await fetch("https://tls.privacytests2.org:8900/");
      return (await results.json()).sessionId;
    }
result, same first party: 
f869c0199365d28b83e5926dea981569def0c45e64a3e1a85ba59cdc99d994c9,
6b44dcb9bda08a861575d80b2137ed22a033988f60dd55baa3fb9485ae97fe71
result, different first party: 
3faaec89d0965b8baec1e4110d422ce3cf6b0980496e52b6685450b19c04f95c,
2265d7a714e5975f9efc63d5d657926d6e3053326493d4153698a58d35109c1c
unsupported: false, false
passed: true, true
test failed: false, false | write: async () => {
      let results = await fetch("https://tls.privacytests2.org:8900/");
      return (await results.json()).sessionId;
    }
read: async () => {
      let results = await fetch("https://tls.privacytests2.org:8900/");
      return (await results.json()).sessionId;
    }
result, same first party: 
ee2d33e0c01df4bd50ace6d7f6ed04a1113f1fd4ffd82f63a45293d4a215ed83,
2c582ba52699fb3e64ace728850a8e19d9b61176bb7f97dbf990ed2353c03b89,
cb95bf38efa6ccb85ad55f968bb9f0aa7d2a3b2d0905fd2b7b75f0abef0a5a8b
result, different first party: 
789b2f65b5b0e59876290ecc8071854a7c3147cebd247fd5ba06cd9440656cfb,
23fbeb5918620d7f5fa5dc2a253179c8163addeec47b847f74447768992a8664,
14a15e5a65e3bf4c98128c421ef39f6a25484113ef2325543b51bf205a34b000
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async () => {
      let results = await fetch("https://tls.privacytests2.org:8900/");
      return (await results.json()).sessionId;
    }
read: async () => {
      let results = await fetch("https://tls.privacytests2.org:8900/");
      return (await results.json()).sessionId;
    }
result, same first party: 
2581c43112a33ef76996050f91c0f32fb0ba0bed68ee49fa54703aca1e234f90,
c9779ba60850b4fcac273a5b2bcf3ae6cf367b0774b551d76b100c16e4541655
result, different first party: 
90f88362f5bbc911fd450a7ec9b191088036a9204522ecafe49b9aefe488f3ce,
1c0ae48fe52866e5b3de893244e219b3bbce5437d19d1619464019c6fe46d174
unsupported: false, false
passed: true, true
test failed: false, false | write: async () => {
      let results = await fetch("https://tls.privacytests2.org:8900/");
      return (await results.json()).sessionId;
    }
read: async () => {
      let results = await fetch("https://tls.privacytests2.org:8900/");
      return (await results.json()).sessionId;
    }
result, same first party: 
d259dcc1295fef464a12016b0430a725f8bd74f803bf0ac9695f172ae66f7473,
22ec995c26741e4df659e312cbb60e2c19f687bd78415fe8f767d48ea9ae79c3
result, different first party: 
17ccb6055c43635c696b3dfc56d35bbd6fa60e740eaa31f4bdeaec431a9a6a34,
775586634c77d34dda30b6c815c06770b75723b462c6a3776d047029e4965b80
unsupported: false, false
passed: true, true
test failed: false, false | 
| XMLHttpRequest cache Similar to the newer Fetch API, any resource received may be cached by the browser. The cache is potentially vulnerable to cross-site tracking attack.
               | write: async (key) => {
      const req = new XMLHttpRequest();
      const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
      req.open("GET", testURI("resource", "xhr", key));
      req.send();
      await loadPromise;
      return key;
    }
read: async (key) => {
      const req = new XMLHttpRequest();
      const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
      req.open("GET", testURI("resource", "xhr", key));
      req.send();
      await loadPromise;
      let countResponse = await fetch(testURI("ctr", "xhr", key),
                                      {cache: "reload"});
      return (await countResponse.text()).trim();
    }
result, same first party: 1, 1, 1
result, different first party: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (key) => {
      const req = new XMLHttpRequest();
      const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
      req.open("GET", testURI("resource", "xhr", key));
      req.send();
      await loadPromise;
      return key;
    }
read: async (key) => {
      const req = new XMLHttpRequest();
      const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
      req.open("GET", testURI("resource", "xhr", key));
      req.send();
      await loadPromise;
      let countResponse = await fetch(testURI("ctr", "xhr", key),
                                      {cache: "reload"});
      return (await countResponse.text()).trim();
    }
result, same first party: 1, 1
result, different first party: 2, 2
unsupported: false, false
passed: true, true
test failed: false, false | write: async (key) => {
      const req = new XMLHttpRequest();
      const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
      req.open("GET", testURI("resource", "xhr", key));
      req.send();
      await loadPromise;
      return key;
    }
read: async (key) => {
      const req = new XMLHttpRequest();
      const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
      req.open("GET", testURI("resource", "xhr", key));
      req.send();
      await loadPromise;
      let countResponse = await fetch(testURI("ctr", "xhr", key),
                                      {cache: "reload"});
      return (await countResponse.text()).trim();
    }
result, same first party: 1, 1
result, different first party: 2, 2
unsupported: false, false
passed: true, true
test failed: false, false | write: async (key) => {
      const req = new XMLHttpRequest();
      const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
      req.open("GET", testURI("resource", "xhr", key));
      req.send();
      await loadPromise;
      return key;
    }
read: async (key) => {
      const req = new XMLHttpRequest();
      const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
      req.open("GET", testURI("resource", "xhr", key));
      req.send();
      await loadPromise;
      let countResponse = await fetch(testURI("ctr", "xhr", key),
                                      {cache: "reload"});
      return (await countResponse.text()).trim();
    }
result, same first party: 1, 1
result, different first party: 2, 2
unsupported: false, false
passed: true, true
test failed: false, false | write: async (key) => {
      const req = new XMLHttpRequest();
      const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
      req.open("GET", testURI("resource", "xhr", key));
      req.send();
      await loadPromise;
      return key;
    }
read: async (key) => {
      const req = new XMLHttpRequest();
      const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
      req.open("GET", testURI("resource", "xhr", key));
      req.send();
      await loadPromise;
      let countResponse = await fetch(testURI("ctr", "xhr", key),
                                      {cache: "reload"});
      return (await countResponse.text()).trim();
    }
result, same first party: 1, 1, 1
result, different first party: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (key) => {
      const req = new XMLHttpRequest();
      const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
      req.open("GET", testURI("resource", "xhr", key));
      req.send();
      await loadPromise;
      return key;
    }
read: async (key) => {
      const req = new XMLHttpRequest();
      const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
      req.open("GET", testURI("resource", "xhr", key));
      req.send();
      await loadPromise;
      let countResponse = await fetch(testURI("ctr", "xhr", key),
                                      {cache: "reload"});
      return (await countResponse.text()).trim();
    }
result, same first party: 1, 1, 1
result, different first party: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (key) => {
      const req = new XMLHttpRequest();
      const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
      req.open("GET", testURI("resource", "xhr", key));
      req.send();
      await loadPromise;
      return key;
    }
read: async (key) => {
      const req = new XMLHttpRequest();
      const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
      req.open("GET", testURI("resource", "xhr", key));
      req.send();
      await loadPromise;
      let countResponse = await fetch(testURI("ctr", "xhr", key),
                                      {cache: "reload"});
      return (await countResponse.text()).trim();
    }
result, same first party: 1, 1
result, different first party: 2, 2
unsupported: false, false
passed: true, true
test failed: false, false | write: async (key) => {
      const req = new XMLHttpRequest();
      const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
      req.open("GET", testURI("resource", "xhr", key));
      req.send();
      await loadPromise;
      return key;
    }
read: async (key) => {
      const req = new XMLHttpRequest();
      const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
      req.open("GET", testURI("resource", "xhr", key));
      req.send();
      await loadPromise;
      let countResponse = await fetch(testURI("ctr", "xhr", key),
                                      {cache: "reload"});
      return (await countResponse.text()).trim();
    }
result, same first party: 1, 1
result, different first party: 2, 2
unsupported: false, false
passed: true, true
test failed: false, false | write: async (key) => {
      const req = new XMLHttpRequest();
      const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
      req.open("GET", testURI("resource", "xhr", key));
      req.send();
      await loadPromise;
      return key;
    }
read: async (key) => {
      const req = new XMLHttpRequest();
      const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
      req.open("GET", testURI("resource", "xhr", key));
      req.send();
      await loadPromise;
      let countResponse = await fetch(testURI("ctr", "xhr", key),
                                      {cache: "reload"});
      return (await countResponse.text()).trim();
    }
result, same first party: 1, 1, 1
result, different first party: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (key) => {
      const req = new XMLHttpRequest();
      const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
      req.open("GET", testURI("resource", "xhr", key));
      req.send();
      await loadPromise;
      return key;
    }
read: async (key) => {
      const req = new XMLHttpRequest();
      const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
      req.open("GET", testURI("resource", "xhr", key));
      req.send();
      await loadPromise;
      let countResponse = await fetch(testURI("ctr", "xhr", key),
                                      {cache: "reload"});
      return (await countResponse.text()).trim();
    }
result, same first party: 1, 1
result, different first party: 2, 2
unsupported: false, false
passed: true, true
test failed: false, false | write: async (key) => {
      const req = new XMLHttpRequest();
      const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
      req.open("GET", testURI("resource", "xhr", key));
      req.send();
      await loadPromise;
      return key;
    }
read: async (key) => {
      const req = new XMLHttpRequest();
      const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
      req.open("GET", testURI("resource", "xhr", key));
      req.send();
      await loadPromise;
      let countResponse = await fetch(testURI("ctr", "xhr", key),
                                      {cache: "reload"});
      return (await countResponse.text()).trim();
    }
result, same first party: 1, 1
result, different first party: 2, 2
unsupported: false, false
passed: true, true
test failed: false, false | 
| Navigation testsWhich browsers prevent websites from sharing tracking data when you click on a link? When you click a hyperlink to navigate your browser from one site to another, certain browser APIs allow the first site to communicate to the second site. These privacy vulnerabilities can be fixed by introducing new limits on how much data is transfered between sites.',
         | |||||||||||
| document.referrer The Referer [sic] request header is a mechanism used by browsers to let a website know where the user is visiting from. This header is inherently tracking users across websites. In recent times, browsers have switched to a policy of trimming a referrer to convey less tracking information, but Referer continues to convey cross-site tracking data by default.
               | write: (secret) => { /* do nothing */ }
read: () => document.referrer
result, same first party: 
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/
result, different first party: 
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/
unsupported: false, false, false
passed: false, false, false
test failed: false, false, false | write: (secret) => { /* do nothing */ }
read: () => document.referrer
result, same first party: 
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/
result, different first party: 
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/
unsupported: false, false
passed: false, false
test failed: false, false | write: (secret) => { /* do nothing */ }
read: () => document.referrer
result, same first party: 
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/
result, different first party: 
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/
unsupported: false, false
passed: false, false
test failed: false, false | write: (secret) => { /* do nothing */ }
read: () => document.referrer
result, same first party: 
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/
result, different first party: 
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/
unsupported: false, false
passed: false, false
test failed: false, false | write: (secret) => { /* do nothing */ }
read: () => document.referrer
result, same first party: 
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/
result, different first party: 
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/
unsupported: false, false, false
passed: false, false, false
test failed: false, false, false | write: (secret) => { /* do nothing */ }
read: () => document.referrer
result, same first party: 
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/
result, different first party: 
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/
unsupported: false, false, false
passed: false, false, false
test failed: false, false, false | write: (secret) => { /* do nothing */ }
read: () => document.referrer
result, same first party: 
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/
result, different first party: 
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/
unsupported: false, false
passed: false, false
test failed: false, false | write: (secret) => { /* do nothing */ }
read: () => document.referrer
result, same first party: 
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/
result, different first party: 
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/
unsupported: false, false
passed: false, false
test failed: false, false | write: (secret) => { /* do nothing */ }
read: () => document.referrer
result, same first party: 
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/
result, different first party: 
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/
unsupported: false, false, false
passed: false, false, false
test failed: false, false, false | write: (secret) => { /* do nothing */ }
read: () => document.referrer
result, same first party: 
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/
result, different first party: 
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/
unsupported: false, false
passed: false, false
test failed: false, false | write: (secret) => { /* do nothing */ }
read: () => document.referrer
result, same first party: 
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/
result, different first party: 
https://test-pages.privacytests2.org/,
https://test-pages.privacytests2.org/
unsupported: false, false
passed: false, false
test failed: false, false | 
| sessionStorage The sessionStorage API is similar to the localStorage API, but it does not persist across tabs or across browser sessions. Nonetheless, it can be used to track users if they navigate from one website to another. This tracking can be thwarted by partitioning sessionStorage between websites.
               | write: (secret) => sessionStorage.setItem("secret", secret)
read: () => sessionStorage.getItem("secret")
result, same first party: 
3133a4ca-686b-4047-91c3-9a7e181c53f7,
49872d4a-b01d-4af8-8cbe-85c379a609e2,
58bb2b32-4f83-47bb-91c8-96dce39cd97a
result, different first party: , , 
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (secret) => sessionStorage.setItem("secret", secret)
read: () => sessionStorage.getItem("secret")
result, same first party: 
e8485c10-5a92-474a-bd16-de23f46ee0e2,
cd5b479f-1529-4f43-a586-810ed83aeb8b
result, different first party: , 
unsupported: false, false
passed: true, true
test failed: false, false | write: (secret) => sessionStorage.setItem("secret", secret)
read: () => sessionStorage.getItem("secret")
result, same first party: 
e96973da-bf72-4eb1-b6ab-a4e6c6775fb4,
67fb6941-7c51-4727-b485-317211e65ae0
result, different first party: , 
unsupported: false, false
passed: true, true
test failed: false, false | write: (secret) => sessionStorage.setItem("secret", secret)
read: () => sessionStorage.getItem("secret")
result, same first party: 
94f1fd6d-f642-403e-8ef8-11180b18b1f6,
2bf6571e-4385-4b68-a076-f6868cbe4954
result, different first party: , 
unsupported: false, false
passed: true, true
test failed: false, false | write: (secret) => sessionStorage.setItem("secret", secret)
read: () => sessionStorage.getItem("secret")
result, same first party: 
da67bebb-5652-441a-816e-1206b25c0225,
0bad71fe-de93-4434-bfa0-bb7f0db62eea,
6aba94ed-010e-45af-892c-dd670cd8d134
result, different first party: , , 
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (secret) => sessionStorage.setItem("secret", secret)
read: () => sessionStorage.getItem("secret")
result, same first party: 
9dd63e63-3256-4352-99f6-805b6f48eb8c,
b7cb77db-aca9-4570-923e-b81da9175421,
736f05a1-05e6-491e-8dad-a6647bd52dda
result, different first party: , , 
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (secret) => sessionStorage.setItem("secret", secret)
read: () => sessionStorage.getItem("secret")
result, same first party: 
50c6a369-0b5c-4de5-a70b-a0516825119e,
49e0d8b1-c126-4b6b-be66-6ba7acfd85a9
result, different first party: , 
unsupported: false, false
passed: true, true
test failed: false, false | write: (secret) => sessionStorage.setItem("secret", secret)
read: () => sessionStorage.getItem("secret")
result, same first party: 
6b07cf4a-a332-4a7c-90df-fbc3dc862f86,
2d40813a-a6c5-4a6b-bb1c-f4d01a399217
result, different first party: , 
unsupported: false, false
passed: true, true
test failed: false, false | write: (secret) => sessionStorage.setItem("secret", secret)
read: () => sessionStorage.getItem("secret")
result, same first party: 
3dc5c317-3ea7-42b4-be71-7902bcc4eb27,
4932f81b-b746-4e4b-b97d-c0b827047888,
3e016f09-5749-4084-a136-e9b829c1a824
result, different first party: , , 
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (secret) => sessionStorage.setItem("secret", secret)
read: () => sessionStorage.getItem("secret")
result, same first party: 
3242a94e-3344-4cb9-ba7c-ebedaf5d6e01,
87918123-fc52-4196-9d0c-a2cfc3f826d4
result, different first party: , 
unsupported: false, false
passed: true, true
test failed: false, false | write: (secret) => sessionStorage.setItem("secret", secret)
read: () => sessionStorage.getItem("secret")
result, same first party: 
5c052838-a55e-4690-83c6-b7d572146713,
49042d99-5b0e-4f37-82f3-78de245eea34
result, different first party: , 
unsupported: false, false
passed: true, true
test failed: false, false | 
| window.name The window.name API allows websites to store data that will persist after the user has navigated the tab to a different website. This mechanism could be partitioned so that data is not allowed to persist between websites.
               | write: (secret) => window.name = "name_" + secret read: () => window.name result, same first party: name_3133a4ca-686b-4047-91c3-9a7e181c53f7, name_49872d4a-b01d-4af8-8cbe-85c379a609e2, name_58bb2b32-4f83-47bb-91c8-96dce39cd97a result, different first party: , , unsupported: false, false, false passed: true, true, true test failed: false, false, false | write: (secret) => window.name = "name_" + secret read: () => window.name result, same first party: name_e8485c10-5a92-474a-bd16-de23f46ee0e2, name_cd5b479f-1529-4f43-a586-810ed83aeb8b result, different first party: name_e8485c10-5a92-474a-bd16-de23f46ee0e2, name_cd5b479f-1529-4f43-a586-810ed83aeb8b unsupported: false, false passed: false, false test failed: false, false | write: (secret) => window.name = "name_" + secret read: () => window.name result, same first party: name_e96973da-bf72-4eb1-b6ab-a4e6c6775fb4, name_67fb6941-7c51-4727-b485-317211e65ae0 result, different first party: name_e96973da-bf72-4eb1-b6ab-a4e6c6775fb4, name_67fb6941-7c51-4727-b485-317211e65ae0 unsupported: false, false passed: false, false test failed: false, false | write: (secret) => window.name = "name_" + secret read: () => window.name result, same first party: name_94f1fd6d-f642-403e-8ef8-11180b18b1f6, name_2bf6571e-4385-4b68-a076-f6868cbe4954 result, different first party: , unsupported: false, false passed: true, true test failed: false, false | write: (secret) => window.name = "name_" + secret read: () => window.name result, same first party: name_da67bebb-5652-441a-816e-1206b25c0225, name_0bad71fe-de93-4434-bfa0-bb7f0db62eea, name_6aba94ed-010e-45af-892c-dd670cd8d134 result, different first party: , , unsupported: false, false, false passed: true, true, true test failed: false, false, false | write: (secret) => window.name = "name_" + secret read: () => window.name result, same first party: name_9dd63e63-3256-4352-99f6-805b6f48eb8c, name_b7cb77db-aca9-4570-923e-b81da9175421, name_736f05a1-05e6-491e-8dad-a6647bd52dda result, different first party: , , unsupported: false, false, false passed: true, true, true test failed: false, false, false | write: (secret) => window.name = "name_" + secret read: () => window.name result, same first party: name_50c6a369-0b5c-4de5-a70b-a0516825119e, name_49e0d8b1-c126-4b6b-be66-6ba7acfd85a9 result, different first party: name_50c6a369-0b5c-4de5-a70b-a0516825119e, name_49e0d8b1-c126-4b6b-be66-6ba7acfd85a9 unsupported: false, false passed: false, false test failed: false, false | write: (secret) => window.name = "name_" + secret read: () => window.name result, same first party: name_6b07cf4a-a332-4a7c-90df-fbc3dc862f86, name_2d40813a-a6c5-4a6b-bb1c-f4d01a399217 result, different first party: , unsupported: false, false passed: true, true test failed: false, false | write: (secret) => window.name = "name_" + secret read: () => window.name result, same first party: name_3dc5c317-3ea7-42b4-be71-7902bcc4eb27, name_4932f81b-b746-4e4b-b97d-c0b827047888, name_3e016f09-5749-4084-a136-e9b829c1a824 result, different first party: , , unsupported: false, false, false passed: true, true, true test failed: false, false, false | write: (secret) => window.name = "name_" + secret read: () => window.name result, same first party: name_3242a94e-3344-4cb9-ba7c-ebedaf5d6e01, name_87918123-fc52-4196-9d0c-a2cfc3f826d4 result, different first party: name_3242a94e-3344-4cb9-ba7c-ebedaf5d6e01, name_87918123-fc52-4196-9d0c-a2cfc3f826d4 unsupported: false, false passed: false, false test failed: false, false | write: (secret) => window.name = "name_" + secret read: () => window.name result, same first party: name_5c052838-a55e-4690-83c6-b7d572146713, name_49042d99-5b0e-4f37-82f3-78de245eea34 result, different first party: name_5c052838-a55e-4690-83c6-b7d572146713, name_49042d99-5b0e-4f37-82f3-78de245eea34 unsupported: false, false passed: false, false test failed: false, false | 
| HTTPS testsWhich browsers prevent unencrypted network connections? HTTPS is the protocol that web browsers use to connect securely to websites. When HTTPS is being used, the connection is encrypted so that third parties on the network cannot read content being sent between the server and your browser. In the past, insecure connections were the default and websites would need to actively request that a browser use HTTPS. Now the status quo is shifting, and browser makers are moving toward a world where HTTPS is the default protocol.`
         | |||||||||||
| Insecure website warning Checks to see if the browser stops loading an insecure website and warns the user before giving them the option to continue. Known as HTTPS-Only Mode in some browsers.
               | passed: false, false, false result: Insecure website loaded, Insecure website loaded, Insecure website loaded | passed: false, false result: Insecure website loaded, Insecure website loaded | passed: false, false result: Insecure website loaded, Insecure website loaded | passed: false, false result: Insecure website loaded, Insecure website loaded | passed: true, true, true result: Insecure website never loaded, Insecure website never loaded, Insecure website never loaded | passed: true, true, true result: Insecure website never loaded, Insecure website never loaded, Insecure website never loaded | passed: false, false result: Insecure website loaded, Insecure website loaded | passed: false, false result: Insecure website loaded, Insecure website loaded | passed: true, true, true result: Insecure website never loaded, Insecure website never loaded, Insecure website never loaded | passed: false, false result: Insecure website loaded, Insecure website loaded | passed: false, false result: Insecure website loaded, Insecure website loaded | 
| Upgradable address Checks to see if an insecure address entered into the browser's address bar is upgraded to HTTPS whenever possible.
               | upgraded: true, true, true passed: true, true, true | upgraded: true, true passed: true, true | upgraded: true, true passed: true, true | upgraded: false, false passed: false, false | upgraded: true, true, true passed: true, true, true | upgraded: true, true, true passed: true, true, true | upgraded: true, true passed: true, true | upgraded: false, false passed: false, false | upgraded: true, true, true passed: true, true, true | upgraded: true, true passed: true, true | upgraded: true, true passed: true, true | 
| Upgradable hyperlink Checks to see if the user has clicked on a hyperlink to an insecure address, if the browser upgrades that address to HTTPS whenever possible.
               | upgraded: true, true, true passed: true, true, true | upgraded: true, true passed: true, true | upgraded: true, true passed: true, true | upgraded: false, false passed: false, false | upgraded: true, true, true passed: true, true, true | upgraded: true, true, true passed: true, true, true | upgraded: true, true passed: true, true | upgraded: false, false passed: false, false | upgraded: true, true, true passed: true, true, true | upgraded: true, true passed: true, true | upgraded: true, true passed: true, true | 
| Upgradable image Checks to see if the browser attempts to upgrade an insecure address for an image to HTTPS whenever possible.
               | passed: true, true, true result: upgraded, upgraded, upgraded | passed: true, true result: upgraded, upgraded | passed: true, true result: upgraded, upgraded | passed: true, false result: upgraded, loaded insecurely | passed: true, true, true result: upgraded, upgraded, upgraded | passed: true, true, true result: upgraded, upgraded, upgraded | passed: true, true result: upgraded, upgraded | passed: false, false result: loaded insecurely, loaded insecurely | passed: true, true, true result: upgraded, upgraded, upgraded | passed: true, true result: upgraded, upgraded | passed: true, true result: upgraded, upgraded | 
| Upgradable script Checks to see if the browser attempts to upgrade an insecure address for an script to HTTPS whenever possible.
               | passed: true, true, true result: blocked, blocked, blocked | passed: true, true result: blocked, blocked | passed: true, true result: blocked, blocked | passed: true, true result: blocked, blocked | passed: true, true, true result: upgraded, upgraded, upgraded | passed: true, true, true result: upgraded, upgraded, upgraded | passed: true, true result: blocked, blocked | passed: true, true result: blocked, blocked | passed: true, true, true result: upgraded, upgraded, upgraded | passed: true, true result: blocked, blocked | passed: true, true result: blocked, blocked | 
| Misc testsWhich browsers provide additional assorted privacy protections? This category includes tests for the presence of miscellaneous privacy features
         | |||||||||||
| ECH enabled Encrypted Client Hello (ECH) is a new protocol that hides the website you are visiting from third-party network eavesdroppers.
               | SNI_status: encrypted passed: true, true, true | SNI_status: encrypted passed: true, true | SNI_status: encrypted passed: true, true | SNI_status: plaintext passed: false, false | SNI_status: plaintext passed: false, false, false | SNI_status: plaintext passed: false, false, false | SNI_status: encrypted passed: true, true | SNI_status: plaintext passed: false, false | SNI_status: plaintext passed: false, false, false | SNI_status: encrypted passed: true, true | SNI_status: encrypted passed: true, true | 
| GPC enabled first-party The Global Privacy Control is an HTTP header that can be sent by a browser to instruct a website not to sell the user's personal data to third parties. This test checks to see if the GPC header is sent by default to the top-level website.
               | header value: 1 passed: true, true, true | header value: undefined passed: false, false | header value: undefined passed: false, false | header value: undefined passed: false, false | header value: undefined passed: false, false, false | header value: undefined passed: false, false, false | header value: undefined passed: false, false | header value: undefined passed: false, false | header value: undefined passed: false, false, false | header value: undefined passed: false, false | header value: undefined passed: false, false | 
| GPC enabled third-party The Global Privacy Control is an HTTP header that can be sent by a browser to instruct a visited website not to sell the user's personal data to other parties. This test checks to see if the GPC header is sent to third-party elements on the web page.
               | sec-gpc: 1 passed: true, true, true | passed: false, false | passed: false, false | passed: false, false | passed: false, false, false | passed: false, false, false | passed: false, false | passed: false, false | passed: false, false, false | passed: false, false | passed: false, false | 
| IP address leak IP addresses can be used to uniquely identify a large percentage of users. A proxy, VPN, or Tor can mask a user's IP address.
               | passed: false, false, false | passed: false, false | passed: false, false | passed: false, false | passed: false, false, false | passed: false, false, false | passed: false, false | passed: false, false | passed: true, true, true | passed: false, false | passed: false, false | 
| Stream isolation Browsers that use Tor can use a different Tor circuit per top-level website.
               | write: () => {
      if (!usingTor) {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      if (usingTor) {
        return ipAddress;
      } else {
        throw new Error("Unsupported");
      }
    }
unsupported: true, true, true
readSameFirstParty: 
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
readDifferentFirstParty: 
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
testFailed: false, false, false | write: () => {
      if (!usingTor) {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      if (usingTor) {
        return ipAddress;
      } else {
        throw new Error("Unsupported");
      }
    }
unsupported: true, true
readSameFirstParty: 
Error: Unsupported,
Error: Unsupported
readDifferentFirstParty: 
Error: Unsupported,
Error: Unsupported
testFailed: false, false | write: () => {
      if (!usingTor) {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      if (usingTor) {
        return ipAddress;
      } else {
        throw new Error("Unsupported");
      }
    }
unsupported: true, true
readSameFirstParty: 
Error: Unsupported,
Error: Unsupported
readDifferentFirstParty: 
Error: Unsupported,
Error: Unsupported
testFailed: false, false | write: () => {
      if (!usingTor) {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      if (usingTor) {
        return ipAddress;
      } else {
        throw new Error("Unsupported");
      }
    }
unsupported: true, true
readSameFirstParty: 
Error: Unsupported,
Error: Unsupported
readDifferentFirstParty: 
Error: Unsupported,
Error: Unsupported
testFailed: false, false | write: () => {
      if (!usingTor) {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      if (usingTor) {
        return ipAddress;
      } else {
        throw new Error("Unsupported");
      }
    }
unsupported: true, true, true
readSameFirstParty: 
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
readDifferentFirstParty: 
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
testFailed: false, false, false | write: () => {
      if (!usingTor) {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      if (usingTor) {
        return ipAddress;
      } else {
        throw new Error("Unsupported");
      }
    }
unsupported: true, true, true
readSameFirstParty: 
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
readDifferentFirstParty: 
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
testFailed: false, false, false | write: () => {
      if (!usingTor) {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      if (usingTor) {
        return ipAddress;
      } else {
        throw new Error("Unsupported");
      }
    }
unsupported: true, true
readSameFirstParty: 
Error: Unsupported,
Error: Unsupported
readDifferentFirstParty: 
Error: Unsupported,
Error: Unsupported
testFailed: false, false | write: () => {
      if (!usingTor) {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      if (usingTor) {
        return ipAddress;
      } else {
        throw new Error("Unsupported");
      }
    }
unsupported: true, true
readSameFirstParty: 
Error: Unsupported,
Error: Unsupported
readDifferentFirstParty: 
Error: Unsupported,
Error: Unsupported
testFailed: false, false | write: () => {
      if (!usingTor) {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      if (usingTor) {
        return ipAddress;
      } else {
        throw new Error("Unsupported");
      }
    }
unsupported: false, false, false
readSameFirstParty: 
2a00:1169:110:2990::,
2605:6400:10:984:41e7:4ad6:a11e:cdcc,
2001:620:20d0::20
readDifferentFirstParty: 
2a04:52c0:108:4b9e::1,
2605:6400:30:ea58:51a1:e174:d185:d6f1,
2001:67c:6ec:203:192:42:116:208
passed: true, true, true
testFailed: false, false, false | write: () => {
      if (!usingTor) {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      if (usingTor) {
        return ipAddress;
      } else {
        throw new Error("Unsupported");
      }
    }
unsupported: true, true
readSameFirstParty: 
Error: Unsupported,
Error: Unsupported
readDifferentFirstParty: 
Error: Unsupported,
Error: Unsupported
testFailed: false, false | write: () => {
      if (!usingTor) {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      if (usingTor) {
        return ipAddress;
      } else {
        throw new Error("Unsupported");
      }
    }
unsupported: true, true
readSameFirstParty: 
Error: Unsupported,
Error: Unsupported
readDifferentFirstParty: 
Error: Unsupported,
Error: Unsupported
testFailed: false, false | 
| Tor enabled The Tor network sends the browser's web requests through a series of relays to hide a user's IP address, thereby helping to mask their identity and location. This test checks to see if the Tor network is being used by default.
               | IsTorExit: false, false, false passed: false, false, false | IsTorExit: false, false passed: false, false | IsTorExit: false, false passed: false, false | IsTorExit: false, false passed: false, false | IsTorExit: false, false, false passed: false, false, false | IsTorExit: false, false, false passed: false, false, false | IsTorExit: false, false passed: false, false | IsTorExit: false, false passed: false, false | IsTorExit: true, true, true passed: true, true, true | IsTorExit: false, false passed: false, false | IsTorExit: false, false passed: false, false | 
| Fingerprinting resistance testsWhich browsers hide what's unique about your device? Fingerprinting is a technique trackers use to uniquely identify you as you browse the web. A fingerprinting script will measure several characteristics of your browser and, combining this data, will build a fingerprint that may uniquely identify you among web users. Browsers can introduce countermeasures, such as minimizing the distinguishing information disclosed by certain web APIs so your browser is harder to pick out from the crowd (so-called 'fingerprinting resistance').`,
         | |||||||||||
| Media query screen height Height of the user's screen in pixels.
               | expression: undefined desired expression: undefined actual value: 1291,1291,1293 desired value: undefined passed: true,true,true | expression: undefined desired expression: undefined actual value: 1440,1440 desired value: undefined passed: false,false | expression: undefined desired expression: undefined actual value: 1440,1440 desired value: undefined passed: false,false | expression: undefined desired expression: undefined actual value: 1440,1440 desired value: undefined passed: false,false | expression: undefined desired expression: undefined actual value: 900,900,900 desired value: undefined passed: true,true,true | expression: undefined desired expression: undefined actual value: 900,900,900 desired value: undefined passed: true,true,true | expression: undefined desired expression: undefined actual value: 1440,1440 desired value: undefined passed: false,false | expression: undefined desired expression: undefined actual value: 1440,1440 desired value: undefined passed: false,false | expression: undefined desired expression: undefined actual value: 900,900,900 desired value: undefined passed: true,true,true | expression: undefined desired expression: undefined actual value: 1440,1440 desired value: undefined passed: false,false | expression: undefined desired expression: undefined actual value: 1440,1440 desired value: undefined passed: false,false | 
| Media query screen width Width of the user's screen in pixels.
               | expression: undefined desired expression: undefined actual value: 1200,1206,1202 desired value: undefined passed: true,true,true | expression: undefined desired expression: undefined actual value: 2560,2560 desired value: undefined passed: false,false | expression: undefined desired expression: undefined actual value: 2560,2560 desired value: undefined passed: false,false | expression: undefined desired expression: undefined actual value: 2560,2560 desired value: undefined passed: false,false | expression: undefined desired expression: undefined actual value: 1600,1600,1600 desired value: undefined passed: true,true,true | expression: undefined desired expression: undefined actual value: 1400,1400,1400 desired value: undefined passed: true,true,true | expression: undefined desired expression: undefined actual value: 2560,2560 desired value: undefined passed: false,false | expression: undefined desired expression: undefined actual value: 2560,2560 desired value: undefined passed: false,false | expression: undefined desired expression: undefined actual value: 1400,1400,1400 desired value: undefined passed: true,true,true | expression: undefined desired expression: undefined actual value: 2560,2560 desired value: undefined passed: false,false | expression: undefined desired expression: undefined actual value: 2560,2560 desired value: undefined passed: false,false | 
| outerHeight Height of the browser window in pixels, including browser chrome.
               | expression: outerHeight desired expression: undefined actual value: 1291,1291,1293 desired value: undefined passed: true,true,true | expression: outerHeight desired expression: undefined actual value: 1371,1371 desired value: undefined passed: false,false | expression: outerHeight desired expression: undefined actual value: 1371,1371 desired value: undefined passed: false,false | expression: outerHeight desired expression: undefined actual value: 1040,1040 desired value: undefined passed: false,false | expression: outerHeight desired expression: undefined actual value: 900,900,900 desired value: undefined passed: true,true,true | expression: outerHeight desired expression: undefined actual value: 900,900,900 desired value: undefined passed: true,true,true | expression: outerHeight desired expression: undefined actual value: 0,1061 desired value: undefined passed: false,false | expression: outerHeight desired expression: undefined actual value: 1335,1335 desired value: undefined passed: false,false | expression: outerHeight desired expression: undefined actual value: 900,900,900 desired value: undefined passed: true,true,true | expression: outerHeight desired expression: undefined actual value: 1371,1371 desired value: undefined passed: false,false | expression: outerHeight desired expression: undefined actual value: 1371,1371 desired value: undefined passed: false,false | 
| screen.height Height of the user's screen, in pixels.
               | expression: screen.height desired expression: undefined actual value: 1291,1291,1293 desired value: undefined passed: true,true,true | expression: screen.height desired expression: undefined actual value: 1440,1440 desired value: undefined passed: false,false | expression: screen.height desired expression: undefined actual value: 1440,1440 desired value: undefined passed: false,false | expression: screen.height desired expression: undefined actual value: 1440,1440 desired value: undefined passed: false,false | expression: screen.height desired expression: undefined actual value: 900,900,900 desired value: undefined passed: true,true,true | expression: screen.height desired expression: undefined actual value: 900,900,900 desired value: undefined passed: true,true,true | expression: screen.height desired expression: undefined actual value: 1440,1440 desired value: undefined passed: false,false | expression: screen.height desired expression: undefined actual value: 1440,1440 desired value: undefined passed: false,false | expression: screen.height desired expression: undefined actual value: 900,900,900 desired value: undefined passed: true,true,true | expression: screen.height desired expression: undefined actual value: 1440,1440 desired value: undefined passed: false,false | expression: screen.height desired expression: undefined actual value: 1440,1440 desired value: undefined passed: false,false | 
| screen.width Width of the user's screen, in pixels.
               | expression: screen.width desired expression: undefined actual value: 1200,1206,1202 desired value: undefined passed: true,true,true | expression: screen.width desired expression: undefined actual value: 2560,2560 desired value: undefined passed: false,false | expression: screen.width desired expression: undefined actual value: 2560,2560 desired value: undefined passed: false,false | expression: screen.width desired expression: undefined actual value: 2560,2560 desired value: undefined passed: false,false | expression: screen.width desired expression: undefined actual value: 1600,1600,1600 desired value: undefined passed: true,true,true | expression: screen.width desired expression: undefined actual value: 1400,1400,1400 desired value: undefined passed: true,true,true | expression: screen.width desired expression: undefined actual value: 2560,2560 desired value: undefined passed: false,false | expression: screen.width desired expression: undefined actual value: 2560,2560 desired value: undefined passed: false,false | expression: screen.width desired expression: undefined actual value: 1400,1400,1400 desired value: undefined passed: true,true,true | expression: screen.width desired expression: undefined actual value: 2560,2560 desired value: undefined passed: false,false | expression: screen.width desired expression: undefined actual value: 2560,2560 desired value: undefined passed: false,false | 
| screenX Position, in pixels, of the left edge of the browser window on screen.
               | expression: screenX desired expression: undefined actual value: 1,7,4 desired value: undefined passed: true,true,true | expression: screenX desired expression: undefined actual value: 22,22 desired value: undefined passed: false,false | expression: screenX desired expression: undefined actual value: 22,22 desired value: undefined passed: false,false | expression: screenX desired expression: undefined actual value: 4,4 desired value: undefined passed: true,true | expression: screenX desired expression: undefined actual value: 0,0,0 desired value: undefined passed: true,true,true | expression: screenX desired expression: undefined actual value: 0,0,0 desired value: undefined passed: true,true,true | expression: screenX desired expression: undefined actual value: 0,320 desired value: undefined passed: true,false | expression: screenX desired expression: undefined actual value: 1358,1358 desired value: undefined passed: false,false | expression: screenX desired expression: undefined actual value: 0,0,0 desired value: undefined passed: true,true,true | expression: screenX desired expression: undefined actual value: 22,22 desired value: undefined passed: false,false | expression: screenX desired expression: undefined actual value: 22,22 desired value: undefined passed: false,false | 
| screenY Position, in pixels, of the top edge of the browser window on screen.
               | expression: screenY desired expression: undefined actual value: 1,8,2 desired value: undefined passed: true,true,true | expression: screenY desired expression: undefined actual value: 47,47 desired value: undefined passed: false,false | expression: screenY desired expression: undefined actual value: 47,47 desired value: undefined passed: false,false | expression: screenY desired expression: undefined actual value: 25,25 desired value: undefined passed: false,false | expression: screenY desired expression: undefined actual value: 0,0,0 desired value: undefined passed: true,true,true | expression: screenY desired expression: undefined actual value: 0,0,0 desired value: undefined passed: true,true,true | expression: screenY desired expression: undefined actual value: 0,202 desired value: undefined passed: true,false | expression: screenY desired expression: undefined actual value: 54,54 desired value: undefined passed: false,false | expression: screenY desired expression: undefined actual value: 0,0,0 desired value: undefined passed: true,true,true | expression: screenY desired expression: undefined actual value: 47,47 desired value: undefined passed: false,false | expression: screenY desired expression: undefined actual value: 47,47 desired value: undefined passed: false,false | 
| System font detection Web pages can detect the presence of a font installed on the user's system. The presence or absence of various fonts is commonly used to fingerprint users.
               | expression: undefined desired expression: undefined actual value: undefined desired value: undefined passed: true,true,true | expression: undefined desired expression: undefined actual value: undefined desired value: undefined passed: false,false | expression: undefined desired expression: undefined actual value: undefined desired value: undefined passed: false,false | expression: undefined desired expression: undefined actual value: undefined desired value: undefined passed: false,false | expression: undefined desired expression: undefined actual value: undefined desired value: undefined passed: true,true,true | expression: undefined desired expression: undefined actual value: undefined desired value: undefined passed: true,true,true | expression: undefined desired expression: undefined actual value: undefined desired value: undefined passed: false,false | expression: undefined desired expression: undefined actual value: undefined desired value: undefined passed: true,true | expression: undefined desired expression: undefined actual value: undefined desired value: undefined passed: true,true,true | expression: undefined desired expression: undefined actual value: undefined desired value: undefined passed: false,false | expression: undefined desired expression: undefined actual value: undefined desired value: undefined passed: false,false | 
| Tracking query parameter testsWhich browsers remove URL parameters that can track you? When you browse from one web page to another, tracking companies will frequently attach a 'tracking query parameter' to the address of the second web page. That query parameter may contain a unique identifier that tracks you individually as you browse the web. And these query parameters are frequently synchronized with cookies, making them a powerful tracking vector. Web browsers can protect you from known tracking query parameters by stripping them from web addresses before your browser sends them. (The set of tracking query parameters tested here was largely borrowed from Brave.)`
         | |||||||||||
| __hsfp HubSpot tracking parameter
               | passed: true, true, true | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | passed: true, true, true | passed: true, true, true | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | passed: true, true, true | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | 
| __hssc HubSpot tracking parameter
               | passed: true, true, true | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | passed: true, true, true | passed: true, true, true | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | passed: true, true, true | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | 
| __hstc HubSpot tracking parameter
               | passed: true, true, true | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | passed: true, true, true | passed: true, true, true | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | passed: true, true, true | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | 
| __s Drip.com email address tracking parameter
               | passed: true, true, true | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | passed: true, true, true | passed: true, true, true | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | passed: true, true, true | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | 
| _hsenc HubSpot tracking parameter
               | passed: true, true, true | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | passed: true, true, true | passed: true, true, true | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | passed: true, true, true | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | 
| _openstat Yandex tracking parameter
               | passed: true, true, true | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | passed: true, true, true | passed: true, true, true | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | passed: true, true, true | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | 
| dclid DoubleClick Click ID (Google)
               | passed: true, true, true | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | passed: true, true, true | passed: true, true, true | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | passed: true, true, true | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | 
| fbclid Facebook Click Identifier
               | passed: true, true, true | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | passed: true, true, true | passed: true, true, true | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | passed: true, true, true | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | 
| gclid Google Click Identifier
               | passed: true, true, true | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | passed: true, true, true | passed: true, true, true | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | passed: true, true, true | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | 
| hsCtaTracking HubSpot tracking parameter
               | passed: true, true, true | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | passed: true, true, true | passed: true, true, true | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | passed: true, true, true | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | 
| mc_eid Mailchimp Email ID (email recipient's address)
               | passed: true, true, true | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | passed: true, true, true | passed: true, true, true | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | passed: true, true, true | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | 
| mkt_tok Adobe Marketo tracking parameter
               | passed: true, true, true | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | passed: true, true, true | passed: true, true, true | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | passed: true, true, true | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | 
| ml_subscriber MailerLite email tracking
               | passed: true, true, true | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | passed: true, true, true | passed: true, true, true | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false, false | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | 
| ml_subscriber_hash MailerLite email tracking
               | passed: true, true, true | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | passed: true, true, true | passed: true, true, true | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false, false | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | 
| msclkid Microsoft Click ID
               | passed: true, true, true | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | passed: true, true, true | passed: true, true, true | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | passed: true, true, true | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | 
| oly_anon_id Omeda marketing 'anonymous' customer id
               | passed: true, true, true | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | passed: true, true, true | passed: true, true, true | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | passed: true, true, true | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | 
| oly_enc_id Omeda marketing 'known' customer id
               | passed: true, true, true | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | passed: true, true, true | passed: true, true, true | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | passed: true, true, true | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | 
| rb_clickid Unknown high-entropy tracking parameter
               | passed: true, true, true | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | passed: true, true, true | passed: true, true, true | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false, false | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | 
| s_cid Adobe Site Catalyst tracking parameter
               | passed: true, true, true | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | passed: true, true, true | passed: true, true, true | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false, false | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | 
| vero_conv Vero tracking parameter
               | passed: true, true, true | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | passed: true, true, true | passed: true, true, true | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false, false | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | 
| vero_id Vero tracking parameter
               | passed: true, true, true | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | passed: true, true, true | passed: true, true, true | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | passed: true, true, true | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | 
| wickedid Wicked Reports e-commerce tracking
               | passed: true, true, true | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | passed: true, true, true | passed: true, true, true | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | passed: true, true, true | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | 
| yclid Yandex Click ID
               | passed: true, true, true | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | passed: true, true, true | passed: true, true, true | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | passed: true, true, true | value: 3928911665442163 passed: false, false | value: 3928911665442163 passed: false, false | 
| Tracker content blocking testsWhich browsers block important known tracking scripts and pixels? When you visit a web page, it frequently has third-party embedded tracking content, such as scripts and tracking pixels. These embedded components spy on you. Some browsers and browser extensions maintain list of tracking companies and block their content from being loaded. This section checks to see if a browser blocks 20 of the largest trackers listed by https://whotracks.me.`
         | |||||||||||
| Adobe Tests whether the browser blocks the page from loading the tracker at https://munchkin.marketo.net/munchkin.js
               | url: https://munchkin.marketo.net/munchkin.js passed: true, true, true | url: https://munchkin.marketo.net/munchkin.js passed: false, false | url: https://munchkin.marketo.net/munchkin.js passed: false, false | url: https://munchkin.marketo.net/munchkin.js passed: false, false | url: https://munchkin.marketo.net/munchkin.js passed: true, true, true | url: https://munchkin.marketo.net/munchkin.js passed: true, true, true | url: https://munchkin.marketo.net/munchkin.js passed: false, false | url: https://munchkin.marketo.net/munchkin.js passed: false, false | url: https://munchkin.marketo.net/munchkin.js passed: false, false, false | url: https://munchkin.marketo.net/munchkin.js passed: false, false | url: https://munchkin.marketo.net/munchkin.js passed: false, false | 
| Adobe Audience Manager Tests whether the browser blocks the page from loading the tracker at https://dpm.demdex.net/ibs
               | url: https://dpm.demdex.net/ibs passed: true, true, true | url: https://dpm.demdex.net/ibs passed: false, false | url: https://dpm.demdex.net/ibs passed: false, false | url: https://dpm.demdex.net/ibs passed: false, false | url: https://dpm.demdex.net/ibs passed: true, true, true | url: https://dpm.demdex.net/ibs passed: true, true, true | url: https://dpm.demdex.net/ibs passed: false, false | url: https://dpm.demdex.net/ibs passed: false, false | url: https://dpm.demdex.net/ibs passed: false, false, false | url: https://dpm.demdex.net/ibs passed: false, false | url: https://dpm.demdex.net/ibs passed: false, false | 
| Amazon adsystem Tests whether the browser blocks the page from loading the tracker at https://s.amazon-adsystem.com/dcm
               | url: https://s.amazon-adsystem.com/dcm passed: true, true, true | url: https://s.amazon-adsystem.com/dcm passed: false, false | url: https://s.amazon-adsystem.com/dcm passed: false, false | url: https://s.amazon-adsystem.com/dcm passed: false, false | url: https://s.amazon-adsystem.com/dcm passed: true, true, true | url: https://s.amazon-adsystem.com/dcm passed: true, true, true | url: https://s.amazon-adsystem.com/dcm passed: false, false | url: https://s.amazon-adsystem.com/dcm passed: false, false | url: https://s.amazon-adsystem.com/dcm passed: false, false, false | url: https://s.amazon-adsystem.com/dcm passed: false, false | url: https://s.amazon-adsystem.com/dcm passed: false, false | 
| AppNexus Tests whether the browser blocks the page from loading the tracker at https://ib.adnxs.com/px?id=178248&t=1
               | url: https://ib.adnxs.com/px?id=178248&t=1 passed: true, true, true | url: https://ib.adnxs.com/px?id=178248&t=1 passed: false, false | url: https://ib.adnxs.com/px?id=178248&t=1 passed: false, false | url: https://ib.adnxs.com/px?id=178248&t=1 passed: false, false | url: https://ib.adnxs.com/px?id=178248&t=1 passed: true, true, true | url: https://ib.adnxs.com/px?id=178248&t=1 passed: true, true, true | url: https://ib.adnxs.com/px?id=178248&t=1 passed: false, false | url: https://ib.adnxs.com/px?id=178248&t=1 passed: false, false | url: https://ib.adnxs.com/px?id=178248&t=1 passed: false, false, false | url: https://ib.adnxs.com/px?id=178248&t=1 passed: false, false | url: https://ib.adnxs.com/px?id=178248&t=1 passed: false, false | 
| Bing Ads Tests whether the browser blocks the page from loading the tracker at https://bat.bing.com/bat.js
               | url: https://bat.bing.com/bat.js passed: true, true, true | url: https://bat.bing.com/bat.js passed: false, false | url: https://bat.bing.com/bat.js passed: false, false | url: https://bat.bing.com/bat.js passed: false, false | url: https://bat.bing.com/bat.js passed: true, true, true | url: https://bat.bing.com/bat.js passed: true, true, true | url: https://bat.bing.com/bat.js passed: false, false | url: https://bat.bing.com/bat.js passed: false, false | url: https://bat.bing.com/bat.js passed: false, false, false | url: https://bat.bing.com/bat.js passed: false, false | url: https://bat.bing.com/bat.js passed: false, false | 
| Chartbeat Tests whether the browser blocks the page from loading the tracker at https://static.chartbeat.com/js/chartbeat.js
               | url: https://static.chartbeat.com/js/chartbeat.js passed: false, false, false | url: https://static.chartbeat.com/js/chartbeat.js passed: false, false | url: https://static.chartbeat.com/js/chartbeat.js passed: false, false | url: https://static.chartbeat.com/js/chartbeat.js passed: false, false | url: https://static.chartbeat.com/js/chartbeat.js passed: false, false, false | url: https://static.chartbeat.com/js/chartbeat.js passed: false, false, false | url: https://static.chartbeat.com/js/chartbeat.js passed: false, false | url: https://static.chartbeat.com/js/chartbeat.js passed: false, false | url: https://static.chartbeat.com/js/chartbeat.js passed: false, false, false | url: https://static.chartbeat.com/js/chartbeat.js passed: false, false | url: https://static.chartbeat.com/js/chartbeat.js passed: false, false | 
| Criteo Tests whether the browser blocks the page from loading the tracker at https://dis.criteo.com/dis/rtb/appnexus/cookiematch.aspx
               | url: https://dis.criteo.com/dis/rtb/appnexus/cookiematch.aspx passed: true, true, true | url: https://dis.criteo.com/dis/rtb/appnexus/cookiematch.aspx passed: false, false | url: https://dis.criteo.com/dis/rtb/appnexus/cookiematch.aspx passed: false, false | url: https://dis.criteo.com/dis/rtb/appnexus/cookiematch.aspx passed: false, false | url: https://dis.criteo.com/dis/rtb/appnexus/cookiematch.aspx passed: true, true, true | url: https://dis.criteo.com/dis/rtb/appnexus/cookiematch.aspx passed: true, true, true | url: https://dis.criteo.com/dis/rtb/appnexus/cookiematch.aspx passed: false, false | url: https://dis.criteo.com/dis/rtb/appnexus/cookiematch.aspx passed: false, false | url: https://dis.criteo.com/dis/rtb/appnexus/cookiematch.aspx passed: false, false, false | url: https://dis.criteo.com/dis/rtb/appnexus/cookiematch.aspx passed: false, false | url: https://dis.criteo.com/dis/rtb/appnexus/cookiematch.aspx passed: false, false | 
| DoubleClick (Google) Tests whether the browser blocks the page from loading the tracker at https://securepubads.g.doubleclick.net/static/glade.js
               | url: https://securepubads.g.doubleclick.net/static/glade.js passed: true, true, true | url: https://securepubads.g.doubleclick.net/static/glade.js passed: false, false | url: https://securepubads.g.doubleclick.net/static/glade.js passed: false, false | url: https://securepubads.g.doubleclick.net/static/glade.js passed: false, false | url: https://securepubads.g.doubleclick.net/static/glade.js passed: true, true, true | url: https://securepubads.g.doubleclick.net/static/glade.js passed: true, true, true | url: https://securepubads.g.doubleclick.net/static/glade.js passed: false, false | url: https://securepubads.g.doubleclick.net/static/glade.js passed: false, false | url: https://securepubads.g.doubleclick.net/static/glade.js passed: false, false, false | url: https://securepubads.g.doubleclick.net/static/glade.js passed: false, false | url: https://securepubads.g.doubleclick.net/static/glade.js passed: false, false | 
| Facebook tracking Tests whether the browser blocks the page from loading the tracker at https://connect.facebook.net/en_US/fbevents.js
               | url: https://connect.facebook.net/en_US/fbevents.js passed: true, true, true | url: https://connect.facebook.net/en_US/fbevents.js passed: false, false | url: https://connect.facebook.net/en_US/fbevents.js passed: false, false | url: https://connect.facebook.net/en_US/fbevents.js passed: false, false | url: https://connect.facebook.net/en_US/fbevents.js passed: true, true, true | url: https://connect.facebook.net/en_US/fbevents.js passed: true, true, true | url: https://connect.facebook.net/en_US/fbevents.js passed: false, false | url: https://connect.facebook.net/en_US/fbevents.js passed: false, false | url: https://connect.facebook.net/en_US/fbevents.js passed: false, false, false | url: https://connect.facebook.net/en_US/fbevents.js passed: false, false | url: https://connect.facebook.net/en_US/fbevents.js passed: false, false | 
| Google (third-party ad pixel) Tests whether the browser blocks the page from loading the tracker at https://www.google.com/pagead/1p-user-list/
               | url: https://www.google.com/pagead/1p-user-list/ passed: true, true, true | url: https://www.google.com/pagead/1p-user-list/ passed: false, false | url: https://www.google.com/pagead/1p-user-list/ passed: false, false | url: https://www.google.com/pagead/1p-user-list/ passed: false, false | url: https://www.google.com/pagead/1p-user-list/ passed: true, true, true | url: https://www.google.com/pagead/1p-user-list/ passed: true, true, true | url: https://www.google.com/pagead/1p-user-list/ passed: false, false | url: https://www.google.com/pagead/1p-user-list/ passed: false, false | url: https://www.google.com/pagead/1p-user-list/ passed: false, false, false | url: https://www.google.com/pagead/1p-user-list/ passed: false, false | url: https://www.google.com/pagead/1p-user-list/ passed: false, false | 
| Google Analytics Tests whether the browser blocks the page from loading the tracker at https://google-analytics.com/urchin.js
               | url: https://google-analytics.com/urchin.js passed: true, true, true | url: https://google-analytics.com/urchin.js passed: false, false | url: https://google-analytics.com/urchin.js passed: false, false | url: https://google-analytics.com/urchin.js passed: false, false | url: https://google-analytics.com/urchin.js passed: true, true, true | url: https://google-analytics.com/urchin.js passed: true, true, true | url: https://google-analytics.com/urchin.js passed: false, false | url: https://google-analytics.com/urchin.js passed: false, false | url: https://google-analytics.com/urchin.js passed: false, false, false | url: https://google-analytics.com/urchin.js passed: false, false | url: https://google-analytics.com/urchin.js passed: false, false | 
| Google Tag Manager Tests whether the browser blocks the page from loading the tracker at https://www.googletagmanager.com/gtag.js?id=GTM-NX4SMZL
               | url: https://www.googletagmanager.com/gtag.js?id=GTM-NX4SMZL passed: true, true, true | url: https://www.googletagmanager.com/gtag.js?id=GTM-NX4SMZL passed: false, false | url: https://www.googletagmanager.com/gtag.js?id=GTM-NX4SMZL passed: false, false | url: https://www.googletagmanager.com/gtag.js?id=GTM-NX4SMZL passed: false, false | url: https://www.googletagmanager.com/gtag.js?id=GTM-NX4SMZL passed: true, true, true | url: https://www.googletagmanager.com/gtag.js?id=GTM-NX4SMZL passed: true, true, true | url: https://www.googletagmanager.com/gtag.js?id=GTM-NX4SMZL passed: false, false | url: https://www.googletagmanager.com/gtag.js?id=GTM-NX4SMZL passed: false, false | url: https://www.googletagmanager.com/gtag.js?id=GTM-NX4SMZL passed: false, false, false | url: https://www.googletagmanager.com/gtag.js?id=GTM-NX4SMZL passed: false, false | url: https://www.googletagmanager.com/gtag.js?id=GTM-NX4SMZL passed: false, false | 
| Index Exchange Tests whether the browser blocks the page from loading the tracker at https://dsum-sec.casalemedia.com/crum?cm_dsp_id=10&external_user_id=629685505537&C=1
               | url: https://dsum-sec.casalemedia.com/crum?cm_dsp_id=10&external_user_id=629685505537&C=1 passed: true, true, true | url: https://dsum-sec.casalemedia.com/crum?cm_dsp_id=10&external_user_id=629685505537&C=1 passed: false, false | url: https://dsum-sec.casalemedia.com/crum?cm_dsp_id=10&external_user_id=629685505537&C=1 passed: false, false | url: https://dsum-sec.casalemedia.com/crum?cm_dsp_id=10&external_user_id=629685505537&C=1 passed: false, false | url: https://dsum-sec.casalemedia.com/crum?cm_dsp_id=10&external_user_id=629685505537&C=1 passed: true, true, true | url: https://dsum-sec.casalemedia.com/crum?cm_dsp_id=10&external_user_id=629685505537&C=1 passed: true, true, true | url: https://dsum-sec.casalemedia.com/crum?cm_dsp_id=10&external_user_id=629685505537&C=1 passed: false, false | url: https://dsum-sec.casalemedia.com/crum?cm_dsp_id=10&external_user_id=629685505537&C=1 passed: false, false | url: https://dsum-sec.casalemedia.com/crum?cm_dsp_id=10&external_user_id=629685505537&C=1 passed: false, false, false | url: https://dsum-sec.casalemedia.com/crum?cm_dsp_id=10&external_user_id=629685505537&C=1 passed: false, false | url: https://dsum-sec.casalemedia.com/crum?cm_dsp_id=10&external_user_id=629685505537&C=1 passed: false, false | 
| New Relic Tests whether the browser blocks the page from loading the tracker at https://js-agent.newrelic.com/nr-1212.min.js
               | url: https://js-agent.newrelic.com/nr-1212.min.js passed: true, true, true | url: https://js-agent.newrelic.com/nr-1212.min.js passed: false, false | url: https://js-agent.newrelic.com/nr-1212.min.js passed: false, false | url: https://js-agent.newrelic.com/nr-1212.min.js passed: false, false | url: https://js-agent.newrelic.com/nr-1212.min.js passed: true, true, true | url: https://js-agent.newrelic.com/nr-1212.min.js passed: true, true, true | url: https://js-agent.newrelic.com/nr-1212.min.js passed: false, false | url: https://js-agent.newrelic.com/nr-1212.min.js passed: false, false | url: https://js-agent.newrelic.com/nr-1212.min.js passed: false, false, false | url: https://js-agent.newrelic.com/nr-1212.min.js passed: false, false | url: https://js-agent.newrelic.com/nr-1212.min.js passed: false, false | 
| Quantcast Tests whether the browser blocks the page from loading the tracker at https://pixel.quantserve.com/pixel
               | url: https://pixel.quantserve.com/pixel passed: true, true, true | url: https://pixel.quantserve.com/pixel passed: false, false | url: https://pixel.quantserve.com/pixel passed: false, false | url: https://pixel.quantserve.com/pixel passed: false, false | url: https://pixel.quantserve.com/pixel passed: true, true, true | url: https://pixel.quantserve.com/pixel passed: true, true, true | url: https://pixel.quantserve.com/pixel passed: false, false | url: https://pixel.quantserve.com/pixel passed: false, false | url: https://pixel.quantserve.com/pixel passed: false, false, false | url: https://pixel.quantserve.com/pixel passed: false, false | url: https://pixel.quantserve.com/pixel passed: false, false | 
| Scorecard Research Beacon Tests whether the browser blocks the page from loading the tracker at https://sb.scorecardresearch.com/internal-c2/default/cs.js
               | url: https://sb.scorecardresearch.com/internal-c2/default/cs.js passed: true, true, true | url: https://sb.scorecardresearch.com/internal-c2/default/cs.js passed: false, false | url: https://sb.scorecardresearch.com/internal-c2/default/cs.js passed: false, false | url: https://sb.scorecardresearch.com/internal-c2/default/cs.js passed: false, false | url: https://sb.scorecardresearch.com/internal-c2/default/cs.js passed: true, true, true | url: https://sb.scorecardresearch.com/internal-c2/default/cs.js passed: true, true, true | url: https://sb.scorecardresearch.com/internal-c2/default/cs.js passed: false, false | url: https://sb.scorecardresearch.com/internal-c2/default/cs.js passed: false, false | url: https://sb.scorecardresearch.com/internal-c2/default/cs.js passed: false, false, false | url: https://sb.scorecardresearch.com/internal-c2/default/cs.js passed: false, false | url: https://sb.scorecardresearch.com/internal-c2/default/cs.js passed: false, false | 
| Taboola Tests whether the browser blocks the page from loading the tracker at https://trc.taboola.com/futureplc-tomsguide/trc/3/json
               | url: https://trc.taboola.com/futureplc-tomsguide/trc/3/json passed: true, true, true | url: https://trc.taboola.com/futureplc-tomsguide/trc/3/json passed: false, false | url: https://trc.taboola.com/futureplc-tomsguide/trc/3/json passed: false, false | url: https://trc.taboola.com/futureplc-tomsguide/trc/3/json passed: false, false | url: https://trc.taboola.com/futureplc-tomsguide/trc/3/json passed: true, true, true | url: https://trc.taboola.com/futureplc-tomsguide/trc/3/json passed: true, true, true | url: https://trc.taboola.com/futureplc-tomsguide/trc/3/json passed: false, false | url: https://trc.taboola.com/futureplc-tomsguide/trc/3/json passed: false, false | url: https://trc.taboola.com/futureplc-tomsguide/trc/3/json passed: false, false, false | url: https://trc.taboola.com/futureplc-tomsguide/trc/3/json passed: false, false | url: https://trc.taboola.com/futureplc-tomsguide/trc/3/json passed: false, false | 
| Twitter pixel Tests whether the browser blocks the page from loading the tracker at https://t.co/i/adsct
               | url: https://t.co/i/adsct passed: true, true, true | url: https://t.co/i/adsct passed: false, false | url: https://t.co/i/adsct passed: false, false | url: https://t.co/i/adsct passed: false, false | url: https://t.co/i/adsct passed: true, true, true | url: https://t.co/i/adsct passed: true, true, true | url: https://t.co/i/adsct passed: false, false | url: https://t.co/i/adsct passed: false, false | url: https://t.co/i/adsct passed: false, false, false | url: https://t.co/i/adsct passed: false, false | url: https://t.co/i/adsct passed: false, false | 
| Yandex Ads Tests whether the browser blocks the page from loading the tracker at https://yandex.ru/ads/system/header-bidding.js
               | url: https://yandex.ru/ads/system/header-bidding.js passed: true, true, true | url: https://yandex.ru/ads/system/header-bidding.js passed: false, false | url: https://yandex.ru/ads/system/header-bidding.js passed: false, false | url: https://yandex.ru/ads/system/header-bidding.js passed: false, false | url: https://yandex.ru/ads/system/header-bidding.js passed: true, true, true | url: https://yandex.ru/ads/system/header-bidding.js passed: true, true, true | url: https://yandex.ru/ads/system/header-bidding.js passed: false, false | url: https://yandex.ru/ads/system/header-bidding.js passed: false, false | url: https://yandex.ru/ads/system/header-bidding.js passed: false, false, false | url: https://yandex.ru/ads/system/header-bidding.js passed: false, false | url: https://yandex.ru/ads/system/header-bidding.js passed: false, false | 
| Tracking cookie protection testsWhich browsers block important known tracking cookies? A large fraction of web pages on the web have hidden third-party trackers that read and write cookies in your browser. These cookies can be used to track your browsing across websites. This section checks to see if a browser stops cross-site tracking by cookies from 20 of the largest trackers listed by https://whotracks.me.`,
         | |||||||||||
| Adobe Tests whether the browser stops cookies from munchkin.marketo.net from tracking users across websites.
               | passed: true, true, true url: https://munchkin.marketo.net/munchkin.js cookieFound: false, false, false | passed: false, false url: https://munchkin.marketo.net/munchkin.js cookieFound: true, true | passed: true, true url: https://munchkin.marketo.net/munchkin.js cookieFound: false, false | passed: true, true url: https://munchkin.marketo.net/munchkin.js cookieFound: false, false | passed: true, true, true url: https://munchkin.marketo.net/munchkin.js cookieFound: false, false, false | passed: true, true, true url: https://munchkin.marketo.net/munchkin.js cookieFound: false, false, false | passed: false, false url: https://munchkin.marketo.net/munchkin.js cookieFound: true, true | passed: true, true url: https://munchkin.marketo.net/munchkin.js cookieFound: false, false | passed: true, true, true url: https://munchkin.marketo.net/munchkin.js cookieFound: false, false, false | passed: true, true url: https://munchkin.marketo.net/munchkin.js cookieFound: false, false | passed: false, false url: https://munchkin.marketo.net/munchkin.js cookieFound: true, true | 
| Adobe Audience Manager Tests whether the browser stops cookies from dpm.demdex.net from tracking users across websites.
               | passed: true, true, true url: https://dpm.demdex.net/ibs cookieFound: false, false, false | passed: false, false url: https://dpm.demdex.net/ibs cookieFound: true, true | passed: true, true url: https://dpm.demdex.net/ibs cookieFound: false, false | passed: true, true url: https://dpm.demdex.net/ibs cookieFound: false, false | passed: true, true, true url: https://dpm.demdex.net/ibs cookieFound: false, false, false | passed: true, true, true url: https://dpm.demdex.net/ibs cookieFound: false, false, false | passed: false, false url: https://dpm.demdex.net/ibs cookieFound: true, true | passed: true, true url: https://dpm.demdex.net/ibs cookieFound: false, false | passed: true, true, true url: https://dpm.demdex.net/ibs cookieFound: false, false, false | passed: true, true url: https://dpm.demdex.net/ibs cookieFound: false, false | passed: false, false url: https://dpm.demdex.net/ibs cookieFound: true, true | 
| Amazon adsystem Tests whether the browser stops cookies from s.amazon-adsystem.com from tracking users across websites.
               | passed: true, true, true url: https://s.amazon-adsystem.com/dcm cookieFound: false, false, false | passed: false, false url: https://s.amazon-adsystem.com/dcm cookieFound: true, true | passed: true, true url: https://s.amazon-adsystem.com/dcm cookieFound: false, false | passed: true, true url: https://s.amazon-adsystem.com/dcm cookieFound: false, false | passed: true, true, true url: https://s.amazon-adsystem.com/dcm cookieFound: false, false, false | passed: true, true, true url: https://s.amazon-adsystem.com/dcm cookieFound: false, false, false | passed: false, false url: https://s.amazon-adsystem.com/dcm cookieFound: true, true | passed: true, true url: https://s.amazon-adsystem.com/dcm cookieFound: false, false | passed: true, true, true url: https://s.amazon-adsystem.com/dcm cookieFound: false, false, false | passed: true, true url: https://s.amazon-adsystem.com/dcm cookieFound: false, false | passed: false, false url: https://s.amazon-adsystem.com/dcm cookieFound: true, true | 
| AppNexus Tests whether the browser stops cookies from ib.adnxs.com from tracking users across websites.
               | passed: true, true, true url: https://ib.adnxs.com/px?id=178248&t=1 cookieFound: false, false, false | passed: false, false url: https://ib.adnxs.com/px?id=178248&t=1 cookieFound: true, true | passed: false, false url: https://ib.adnxs.com/px?id=178248&t=1 cookieFound: true, true | passed: true, true url: https://ib.adnxs.com/px?id=178248&t=1 cookieFound: false, false | passed: true, true, true url: https://ib.adnxs.com/px?id=178248&t=1 cookieFound: false, false, false | passed: true, true, true url: https://ib.adnxs.com/px?id=178248&t=1 cookieFound: false, false, false | passed: false, false url: https://ib.adnxs.com/px?id=178248&t=1 cookieFound: true, true | passed: true, true url: https://ib.adnxs.com/px?id=178248&t=1 cookieFound: false, false | passed: true, true, true url: https://ib.adnxs.com/px?id=178248&t=1 cookieFound: false, false, false | passed: true, true url: https://ib.adnxs.com/px?id=178248&t=1 cookieFound: false, false | passed: false, false url: https://ib.adnxs.com/px?id=178248&t=1 cookieFound: true, true | 
| Bing Ads Tests whether the browser stops cookies from bat.bing.com from tracking users across websites.
               | passed: true, true, true url: https://bat.bing.com/bat.js cookieFound: false, false, false | passed: false, false url: https://bat.bing.com/bat.js cookieFound: true, true | passed: false, false url: https://bat.bing.com/bat.js cookieFound: true, true | passed: true, true url: https://bat.bing.com/bat.js cookieFound: false, false | passed: true, true, true url: https://bat.bing.com/bat.js cookieFound: false, false, false | passed: true, true, true url: https://bat.bing.com/bat.js cookieFound: false, false, false | passed: false, false url: https://bat.bing.com/bat.js cookieFound: true, true | passed: true, true url: https://bat.bing.com/bat.js cookieFound: false, false | passed: true, true, true url: https://bat.bing.com/bat.js cookieFound: false, false, false | passed: true, true url: https://bat.bing.com/bat.js cookieFound: false, false | passed: false, false url: https://bat.bing.com/bat.js cookieFound: true, true | 
| Chartbeat Tests whether the browser stops cookies from static.chartbeat.com from tracking users across websites.
               | passed: true, true, true url: https://static.chartbeat.com/js/chartbeat.js cookieFound: false, false, false | passed: false, false url: https://static.chartbeat.com/js/chartbeat.js cookieFound: true, true | passed: false, false url: https://static.chartbeat.com/js/chartbeat.js cookieFound: true, true | passed: true, true url: https://static.chartbeat.com/js/chartbeat.js cookieFound: false, false | passed: true, true, true url: https://static.chartbeat.com/js/chartbeat.js cookieFound: false, false, false | passed: true, true, true url: https://static.chartbeat.com/js/chartbeat.js cookieFound: false, false, false | passed: false, false url: https://static.chartbeat.com/js/chartbeat.js cookieFound: true, true | passed: true, true url: https://static.chartbeat.com/js/chartbeat.js cookieFound: false, false | passed: true, true, true url: https://static.chartbeat.com/js/chartbeat.js cookieFound: false, false, false | passed: true, true url: https://static.chartbeat.com/js/chartbeat.js cookieFound: false, false | passed: false, false url: https://static.chartbeat.com/js/chartbeat.js cookieFound: true, true | 
| Criteo Tests whether the browser stops cookies from dis.criteo.com from tracking users across websites.
               | passed: true, true, true url: https://dis.criteo.com/dis/rtb/appnexus/cookiematch.aspx cookieFound: false, false, false | passed: false, false url: https://dis.criteo.com/dis/rtb/appnexus/cookiematch.aspx cookieFound: true, true | passed: true, true url: https://dis.criteo.com/dis/rtb/appnexus/cookiematch.aspx cookieFound: false, false | passed: true, true url: https://dis.criteo.com/dis/rtb/appnexus/cookiematch.aspx cookieFound: false, false | passed: true, true, true url: https://dis.criteo.com/dis/rtb/appnexus/cookiematch.aspx cookieFound: false, false, false | passed: true, true, true url: https://dis.criteo.com/dis/rtb/appnexus/cookiematch.aspx cookieFound: false, false, false | passed: false, false url: https://dis.criteo.com/dis/rtb/appnexus/cookiematch.aspx cookieFound: true, true | passed: true, true url: https://dis.criteo.com/dis/rtb/appnexus/cookiematch.aspx cookieFound: false, false | passed: true, true, true url: https://dis.criteo.com/dis/rtb/appnexus/cookiematch.aspx cookieFound: false, false, false | passed: true, true url: https://dis.criteo.com/dis/rtb/appnexus/cookiematch.aspx cookieFound: false, false | passed: false, false url: https://dis.criteo.com/dis/rtb/appnexus/cookiematch.aspx cookieFound: true, true | 
| DoubleClick (Google) Tests whether the browser stops cookies from securepubads.g.doubleclick.net from tracking users across websites.
               | passed: true, true, true url: https://securepubads.g.doubleclick.net/static/glade.js cookieFound: false, false, false | passed: false, false url: https://securepubads.g.doubleclick.net/static/glade.js cookieFound: true, true | passed: true, true url: https://securepubads.g.doubleclick.net/static/glade.js cookieFound: false, false | passed: true, true url: https://securepubads.g.doubleclick.net/static/glade.js cookieFound: false, false | passed: true, true, true url: https://securepubads.g.doubleclick.net/static/glade.js cookieFound: false, false, false | passed: true, true, true url: https://securepubads.g.doubleclick.net/static/glade.js cookieFound: false, false, false | passed: false, false url: https://securepubads.g.doubleclick.net/static/glade.js cookieFound: true, true | passed: true, true url: https://securepubads.g.doubleclick.net/static/glade.js cookieFound: false, false | passed: true, true, true url: https://securepubads.g.doubleclick.net/static/glade.js cookieFound: false, false, false | passed: true, true url: https://securepubads.g.doubleclick.net/static/glade.js cookieFound: false, false | passed: false, false url: https://securepubads.g.doubleclick.net/static/glade.js cookieFound: true, true | 
| Facebook tracking Tests whether the browser stops cookies from connect.facebook.net from tracking users across websites.
               | passed: true, true, true url: https://connect.facebook.net/en_US/fbevents.js cookieFound: false, false, false | passed: false, false url: https://connect.facebook.net/en_US/fbevents.js cookieFound: true, true | passed: true, true url: https://connect.facebook.net/en_US/fbevents.js cookieFound: false, false | passed: true, true url: https://connect.facebook.net/en_US/fbevents.js cookieFound: false, false | passed: true, true, true url: https://connect.facebook.net/en_US/fbevents.js cookieFound: false, false, false | passed: true, true, true url: https://connect.facebook.net/en_US/fbevents.js cookieFound: false, false, false | passed: false, false url: https://connect.facebook.net/en_US/fbevents.js cookieFound: true, true | passed: true, true url: https://connect.facebook.net/en_US/fbevents.js cookieFound: false, false | passed: true, true, true url: https://connect.facebook.net/en_US/fbevents.js cookieFound: false, false, false | passed: true, true url: https://connect.facebook.net/en_US/fbevents.js cookieFound: false, false | passed: false, false url: https://connect.facebook.net/en_US/fbevents.js cookieFound: true, true | 
| Google (third-party ad pixel) Tests whether the browser stops cookies from www.google.com from tracking users across websites.
               | passed: true, true, true url: https://www.google.com/pagead/1p-user-list/ cookieFound: false, false, false | passed: false, false url: https://www.google.com/pagead/1p-user-list/ cookieFound: true, true | passed: true, true url: https://www.google.com/pagead/1p-user-list/ cookieFound: false, false | passed: true, true url: https://www.google.com/pagead/1p-user-list/ cookieFound: false, false | passed: true, true, true url: https://www.google.com/pagead/1p-user-list/ cookieFound: false, false, false | passed: true, true, true url: https://www.google.com/pagead/1p-user-list/ cookieFound: false, false, false | passed: false, false url: https://www.google.com/pagead/1p-user-list/ cookieFound: true, true | passed: true, true url: https://www.google.com/pagead/1p-user-list/ cookieFound: false, false | passed: true, true, true url: https://www.google.com/pagead/1p-user-list/ cookieFound: false, false, false | passed: true, true url: https://www.google.com/pagead/1p-user-list/ cookieFound: false, false | passed: false, false url: https://www.google.com/pagead/1p-user-list/ cookieFound: true, true | 
| Google Analytics Tests whether the browser stops cookies from google-analytics.com from tracking users across websites.
               | passed: true, true, true url: https://google-analytics.com/urchin.js cookieFound: false, false, false | passed: false, false url: https://google-analytics.com/urchin.js cookieFound: true, true | passed: false, false url: https://google-analytics.com/urchin.js cookieFound: true, true | passed: true, true url: https://google-analytics.com/urchin.js cookieFound: false, false | passed: true, true, true url: https://google-analytics.com/urchin.js cookieFound: false, false, false | passed: true, true, true url: https://google-analytics.com/urchin.js cookieFound: false, false, false | passed: false, false url: https://google-analytics.com/urchin.js cookieFound: true, true | passed: true, true url: https://google-analytics.com/urchin.js cookieFound: false, false | passed: true, true, true url: https://google-analytics.com/urchin.js cookieFound: false, false, false | passed: true, true url: https://google-analytics.com/urchin.js cookieFound: false, false | passed: false, false url: https://google-analytics.com/urchin.js cookieFound: true, true | 
| Google Tag Manager Tests whether the browser stops cookies from www.googletagmanager.com from tracking users across websites.
               | passed: true, true, true url: https://www.googletagmanager.com/gtag.js?id=GTM-NX4SMZL cookieFound: false, false, false | passed: false, false url: https://www.googletagmanager.com/gtag.js?id=GTM-NX4SMZL cookieFound: true, true | passed: false, false url: https://www.googletagmanager.com/gtag.js?id=GTM-NX4SMZL cookieFound: true, true | passed: true, true url: https://www.googletagmanager.com/gtag.js?id=GTM-NX4SMZL cookieFound: false, false | passed: true, true, true url: https://www.googletagmanager.com/gtag.js?id=GTM-NX4SMZL cookieFound: false, false, false | passed: true, true, true url: https://www.googletagmanager.com/gtag.js?id=GTM-NX4SMZL cookieFound: false, false, false | passed: false, false url: https://www.googletagmanager.com/gtag.js?id=GTM-NX4SMZL cookieFound: true, true | passed: true, true url: https://www.googletagmanager.com/gtag.js?id=GTM-NX4SMZL cookieFound: false, false | passed: true, true, true url: https://www.googletagmanager.com/gtag.js?id=GTM-NX4SMZL cookieFound: false, false, false | passed: true, true url: https://www.googletagmanager.com/gtag.js?id=GTM-NX4SMZL cookieFound: false, false | passed: false, false url: https://www.googletagmanager.com/gtag.js?id=GTM-NX4SMZL cookieFound: true, true | 
| Index Exchange Tests whether the browser stops cookies from dsum-sec.casalemedia.com from tracking users across websites.
               | passed: true, true, true url: https://dsum-sec.casalemedia.com/crum?cm_dsp_id=10&external_user_id=629685505537&C=1 cookieFound: false, false, false | passed: false, false url: https://dsum-sec.casalemedia.com/crum?cm_dsp_id=10&external_user_id=629685505537&C=1 cookieFound: true, true | passed: true, true url: https://dsum-sec.casalemedia.com/crum?cm_dsp_id=10&external_user_id=629685505537&C=1 cookieFound: false, false | passed: true, true url: https://dsum-sec.casalemedia.com/crum?cm_dsp_id=10&external_user_id=629685505537&C=1 cookieFound: false, false | passed: true, true, true url: https://dsum-sec.casalemedia.com/crum?cm_dsp_id=10&external_user_id=629685505537&C=1 cookieFound: false, false, false | passed: true, true, true url: https://dsum-sec.casalemedia.com/crum?cm_dsp_id=10&external_user_id=629685505537&C=1 cookieFound: false, false, false | passed: false, false url: https://dsum-sec.casalemedia.com/crum?cm_dsp_id=10&external_user_id=629685505537&C=1 cookieFound: true, true | passed: true, true url: https://dsum-sec.casalemedia.com/crum?cm_dsp_id=10&external_user_id=629685505537&C=1 cookieFound: false, false | passed: true, true, true url: https://dsum-sec.casalemedia.com/crum?cm_dsp_id=10&external_user_id=629685505537&C=1 cookieFound: false, false, false | passed: true, true url: https://dsum-sec.casalemedia.com/crum?cm_dsp_id=10&external_user_id=629685505537&C=1 cookieFound: false, false | passed: false, false url: https://dsum-sec.casalemedia.com/crum?cm_dsp_id=10&external_user_id=629685505537&C=1 cookieFound: true, true | 
| New Relic Tests whether the browser stops cookies from js-agent.newrelic.com from tracking users across websites.
               | passed: true, true, true url: https://js-agent.newrelic.com/nr-1212.min.js cookieFound: false, false, false | passed: false, false url: https://js-agent.newrelic.com/nr-1212.min.js cookieFound: true, true | passed: false, false url: https://js-agent.newrelic.com/nr-1212.min.js cookieFound: true, true | passed: true, true url: https://js-agent.newrelic.com/nr-1212.min.js cookieFound: false, false | passed: true, true, true url: https://js-agent.newrelic.com/nr-1212.min.js cookieFound: false, false, false | passed: true, true, true url: https://js-agent.newrelic.com/nr-1212.min.js cookieFound: false, false, false | passed: false, false url: https://js-agent.newrelic.com/nr-1212.min.js cookieFound: true, true | passed: true, true url: https://js-agent.newrelic.com/nr-1212.min.js cookieFound: false, false | passed: true, true, true url: https://js-agent.newrelic.com/nr-1212.min.js cookieFound: false, false, false | passed: true, true url: https://js-agent.newrelic.com/nr-1212.min.js cookieFound: false, false | passed: false, false url: https://js-agent.newrelic.com/nr-1212.min.js cookieFound: true, true | 
| Quantcast Tests whether the browser stops cookies from pixel.quantserve.com from tracking users across websites.
               | passed: true, true, true url: https://pixel.quantserve.com/pixel cookieFound: false, false, false | passed: false, false url: https://pixel.quantserve.com/pixel cookieFound: true, true | passed: true, true url: https://pixel.quantserve.com/pixel cookieFound: false, false | passed: true, true url: https://pixel.quantserve.com/pixel cookieFound: false, false | passed: true, true, true url: https://pixel.quantserve.com/pixel cookieFound: false, false, false | passed: true, true, true url: https://pixel.quantserve.com/pixel cookieFound: false, false, false | passed: false, false url: https://pixel.quantserve.com/pixel cookieFound: true, true | passed: true, true url: https://pixel.quantserve.com/pixel cookieFound: false, false | passed: true, true, true url: https://pixel.quantserve.com/pixel cookieFound: false, false, false | passed: true, true url: https://pixel.quantserve.com/pixel cookieFound: false, false | passed: false, false url: https://pixel.quantserve.com/pixel cookieFound: true, true | 
| Scorecard Research Beacon Tests whether the browser stops cookies from sb.scorecardresearch.com from tracking users across websites.
               | passed: true, true, true url: https://sb.scorecardresearch.com/internal-c2/default/cs.js cookieFound: false, false, false | passed: false, false url: https://sb.scorecardresearch.com/internal-c2/default/cs.js cookieFound: true, true | passed: false, false url: https://sb.scorecardresearch.com/internal-c2/default/cs.js cookieFound: true, true | passed: true, true url: https://sb.scorecardresearch.com/internal-c2/default/cs.js cookieFound: false, false | passed: true, true, true url: https://sb.scorecardresearch.com/internal-c2/default/cs.js cookieFound: false, false, false | passed: true, true, true url: https://sb.scorecardresearch.com/internal-c2/default/cs.js cookieFound: false, false, false | passed: false, false url: https://sb.scorecardresearch.com/internal-c2/default/cs.js cookieFound: true, true | passed: true, true url: https://sb.scorecardresearch.com/internal-c2/default/cs.js cookieFound: false, false | passed: true, true, true url: https://sb.scorecardresearch.com/internal-c2/default/cs.js cookieFound: false, false, false | passed: true, true url: https://sb.scorecardresearch.com/internal-c2/default/cs.js cookieFound: false, false | passed: false, false url: https://sb.scorecardresearch.com/internal-c2/default/cs.js cookieFound: true, true | 
| Taboola Tests whether the browser stops cookies from trc.taboola.com from tracking users across websites.
               | passed: true, true, true url: https://trc.taboola.com/futureplc-tomsguide/trc/3/json cookieFound: false, false, false | passed: false, false url: https://trc.taboola.com/futureplc-tomsguide/trc/3/json cookieFound: true, true | passed: true, true url: https://trc.taboola.com/futureplc-tomsguide/trc/3/json cookieFound: false, false | passed: true, true url: https://trc.taboola.com/futureplc-tomsguide/trc/3/json cookieFound: false, false | passed: true, true, true url: https://trc.taboola.com/futureplc-tomsguide/trc/3/json cookieFound: false, false, false | passed: true, true, true url: https://trc.taboola.com/futureplc-tomsguide/trc/3/json cookieFound: false, false, false | passed: false, false url: https://trc.taboola.com/futureplc-tomsguide/trc/3/json cookieFound: true, true | passed: true, true url: https://trc.taboola.com/futureplc-tomsguide/trc/3/json cookieFound: false, false | passed: true, true, true url: https://trc.taboola.com/futureplc-tomsguide/trc/3/json cookieFound: false, false, false | passed: true, true url: https://trc.taboola.com/futureplc-tomsguide/trc/3/json cookieFound: false, false | passed: false, false url: https://trc.taboola.com/futureplc-tomsguide/trc/3/json cookieFound: true, true | 
| Twitter pixel Tests whether the browser stops cookies from t.co from tracking users across websites.
               | passed: true, true, true url: https://t.co/i/adsct cookieFound: false, false, false | passed: false, false url: https://t.co/i/adsct cookieFound: true, true | passed: false, false url: https://t.co/i/adsct cookieFound: true, true | passed: true, true url: https://t.co/i/adsct cookieFound: false, false | passed: true, true, true url: https://t.co/i/adsct cookieFound: false, false, false | passed: true, true, true url: https://t.co/i/adsct cookieFound: false, false, false | passed: false, false url: https://t.co/i/adsct cookieFound: true, true | passed: true, true url: https://t.co/i/adsct cookieFound: false, false | passed: true, true, true url: https://t.co/i/adsct cookieFound: false, false, false | passed: true, true url: https://t.co/i/adsct cookieFound: false, false | passed: false, false url: https://t.co/i/adsct cookieFound: true, true | 
| Yandex Ads Tests whether the browser stops cookies from yandex.ru from tracking users across websites.
               | passed: true, true, true url: https://yandex.ru/ads/system/header-bidding.js cookieFound: false, false, false | passed: false, false url: https://yandex.ru/ads/system/header-bidding.js cookieFound: true, true | passed: true, true url: https://yandex.ru/ads/system/header-bidding.js cookieFound: false, false | passed: true, true url: https://yandex.ru/ads/system/header-bidding.js cookieFound: false, false | passed: true, true, true url: https://yandex.ru/ads/system/header-bidding.js cookieFound: false, false, false | passed: true, true, true url: https://yandex.ru/ads/system/header-bidding.js cookieFound: false, false, false | passed: false, false url: https://yandex.ru/ads/system/header-bidding.js cookieFound: true, true | passed: true, true url: https://yandex.ru/ads/system/header-bidding.js cookieFound: false, false | passed: true, true, true url: https://yandex.ru/ads/system/header-bidding.js cookieFound: false, false, false | passed: true, true url: https://yandex.ru/ads/system/header-bidding.js cookieFound: false, false | passed: false, false url: https://yandex.ru/ads/system/header-bidding.js cookieFound: true, true | 
| Cross-session first-party tracking testsWhich browsers prevent websites from tracking you across browser sessions? A common vulnerability of web browsers is that they allow websites ("first parties") to 'tag' your browser with some tracking data. This tag can be used to re-identify you when you return to a website you visited before. This category of leaks can be prevented by browser if they clean or isolate data between browser sessions. (In cases where a user has logged into a website or entered detailed information, it may be justifiable for a browser to retain information across sessions. These tests check when no such justification exists: when you have entered no significant information into a website, will the browser still retain data that allows you to be tracked across sessions?)
         | |||||||||||
| Alt-Svc Alt-Svc allows the server to indicate to the web browser that a resource should be loaded on a different server. Because this is a persistent setting, it could be used to track users across websites if it is not correctly partitioned.
               | write: async () => {
      // Clear Alt-Svc caching first.
      let responseText = "";
      for (let i = 0; i < 3; ++i) {
        await fetch(altSvcOrigin + "/clear");
        await sleepMs(100);
      }
      responseText = await fetchText(altSvcOrigin + "/protocol");
      console.log("after clear:", responseText);
      // Store "h3" state in Alt-Svc cache
      for (let i = 0; i < 3; ++i) {
        await fetch(altSvcOrigin + "/set");
        await sleepMs(100);
      }
      responseText = await fetchText(altSvcOrigin + "/protocol");
      console.log("after set:", responseText);
    }
read: async () => {
      const protocol = await fetchText(altSvcOrigin + "/protocol");
      if ((new URL(location)).searchParams.get("thirdparty") === "same") {
        if (protocol !== "h3") {
          throw new Error("Unsupported");
        }
      }
      return protocol;
    }
result, same session: h3, h3, h3
result, different session: h3, h3, h3
unsupported: false, false, false
passed: false, false, false
test failed: false, false, false | write: async () => {
      // Clear Alt-Svc caching first.
      let responseText = "";
      for (let i = 0; i < 3; ++i) {
        await fetch(altSvcOrigin + "/clear");
        await sleepMs(100);
      }
      responseText = await fetchText(altSvcOrigin + "/protocol");
      console.log("after clear:", responseText);
      // Store "h3" state in Alt-Svc cache
      for (let i = 0; i < 3; ++i) {
        await fetch(altSvcOrigin + "/set");
        await sleepMs(100);
      }
      responseText = await fetchText(altSvcOrigin + "/protocol");
      console.log("after set:", responseText);
    }
read: async () => {
      const protocol = await fetchText(altSvcOrigin + "/protocol");
      if ((new URL(location)).searchParams.get("thirdparty") === "same") {
        if (protocol !== "h3") {
          throw new Error("Unsupported");
        }
      }
      return protocol;
    }
result, same session: h3, h3
result, different session: h3, h3
unsupported: false, false
passed: false, false
test failed: false, false | write: async () => {
      // Clear Alt-Svc caching first.
      let responseText = "";
      for (let i = 0; i < 3; ++i) {
        await fetch(altSvcOrigin + "/clear");
        await sleepMs(100);
      }
      responseText = await fetchText(altSvcOrigin + "/protocol");
      console.log("after clear:", responseText);
      // Store "h3" state in Alt-Svc cache
      for (let i = 0; i < 3; ++i) {
        await fetch(altSvcOrigin + "/set");
        await sleepMs(100);
      }
      responseText = await fetchText(altSvcOrigin + "/protocol");
      console.log("after set:", responseText);
    }
read: async () => {
      const protocol = await fetchText(altSvcOrigin + "/protocol");
      if ((new URL(location)).searchParams.get("thirdparty") === "same") {
        if (protocol !== "h3") {
          throw new Error("Unsupported");
        }
      }
      return protocol;
    }
result, same session: h3, h3
result, different session: h3, h3
unsupported: false, false
passed: false, false
test failed: false, false | write: async () => {
      // Clear Alt-Svc caching first.
      let responseText = "";
      for (let i = 0; i < 3; ++i) {
        await fetch(altSvcOrigin + "/clear");
        await sleepMs(100);
      }
      responseText = await fetchText(altSvcOrigin + "/protocol");
      console.log("after clear:", responseText);
      // Store "h3" state in Alt-Svc cache
      for (let i = 0; i < 3; ++i) {
        await fetch(altSvcOrigin + "/set");
        await sleepMs(100);
      }
      responseText = await fetchText(altSvcOrigin + "/protocol");
      console.log("after set:", responseText);
    }
read: async () => {
      const protocol = await fetchText(altSvcOrigin + "/protocol");
      if ((new URL(location)).searchParams.get("thirdparty") === "same") {
        if (protocol !== "h3") {
          throw new Error("Unsupported");
        }
      }
      return protocol;
    }
result, same session: h3, h3
result, different session: h3, h3
unsupported: false, false
passed: false, false
test failed: false, false | write: async () => {
      // Clear Alt-Svc caching first.
      let responseText = "";
      for (let i = 0; i < 3; ++i) {
        await fetch(altSvcOrigin + "/clear");
        await sleepMs(100);
      }
      responseText = await fetchText(altSvcOrigin + "/protocol");
      console.log("after clear:", responseText);
      // Store "h3" state in Alt-Svc cache
      for (let i = 0; i < 3; ++i) {
        await fetch(altSvcOrigin + "/set");
        await sleepMs(100);
      }
      responseText = await fetchText(altSvcOrigin + "/protocol");
      console.log("after set:", responseText);
    }
read: async () => {
      const protocol = await fetchText(altSvcOrigin + "/protocol");
      if ((new URL(location)).searchParams.get("thirdparty") === "same") {
        if (protocol !== "h3") {
          throw new Error("Unsupported");
        }
      }
      return protocol;
    }
result, same session: h3, h3, h3
result, different session: h2, h2, h2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async () => {
      // Clear Alt-Svc caching first.
      let responseText = "";
      for (let i = 0; i < 3; ++i) {
        await fetch(altSvcOrigin + "/clear");
        await sleepMs(100);
      }
      responseText = await fetchText(altSvcOrigin + "/protocol");
      console.log("after clear:", responseText);
      // Store "h3" state in Alt-Svc cache
      for (let i = 0; i < 3; ++i) {
        await fetch(altSvcOrigin + "/set");
        await sleepMs(100);
      }
      responseText = await fetchText(altSvcOrigin + "/protocol");
      console.log("after set:", responseText);
    }
read: async () => {
      const protocol = await fetchText(altSvcOrigin + "/protocol");
      if ((new URL(location)).searchParams.get("thirdparty") === "same") {
        if (protocol !== "h3") {
          throw new Error("Unsupported");
        }
      }
      return protocol;
    }
result, same session: h3, h3, h3
result, different session: h2, h2, h2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async () => {
      // Clear Alt-Svc caching first.
      let responseText = "";
      for (let i = 0; i < 3; ++i) {
        await fetch(altSvcOrigin + "/clear");
        await sleepMs(100);
      }
      responseText = await fetchText(altSvcOrigin + "/protocol");
      console.log("after clear:", responseText);
      // Store "h3" state in Alt-Svc cache
      for (let i = 0; i < 3; ++i) {
        await fetch(altSvcOrigin + "/set");
        await sleepMs(100);
      }
      responseText = await fetchText(altSvcOrigin + "/protocol");
      console.log("after set:", responseText);
    }
read: async () => {
      const protocol = await fetchText(altSvcOrigin + "/protocol");
      if ((new URL(location)).searchParams.get("thirdparty") === "same") {
        if (protocol !== "h3") {
          throw new Error("Unsupported");
        }
      }
      return protocol;
    }
result, same session: h3, h3
result, different session: h3, h3
unsupported: false, false
passed: false, false
test failed: false, false | write: async () => {
      // Clear Alt-Svc caching first.
      let responseText = "";
      for (let i = 0; i < 3; ++i) {
        await fetch(altSvcOrigin + "/clear");
        await sleepMs(100);
      }
      responseText = await fetchText(altSvcOrigin + "/protocol");
      console.log("after clear:", responseText);
      // Store "h3" state in Alt-Svc cache
      for (let i = 0; i < 3; ++i) {
        await fetch(altSvcOrigin + "/set");
        await sleepMs(100);
      }
      responseText = await fetchText(altSvcOrigin + "/protocol");
      console.log("after set:", responseText);
    }
read: async () => {
      const protocol = await fetchText(altSvcOrigin + "/protocol");
      if ((new URL(location)).searchParams.get("thirdparty") === "same") {
        if (protocol !== "h3") {
          throw new Error("Unsupported");
        }
      }
      return protocol;
    }
result, same session: h3, h2
result, different session: h2, h3
unsupported: false, true
passed: true
test failed: false, false | write: async () => {
      // Clear Alt-Svc caching first.
      let responseText = "";
      for (let i = 0; i < 3; ++i) {
        await fetch(altSvcOrigin + "/clear");
        await sleepMs(100);
      }
      responseText = await fetchText(altSvcOrigin + "/protocol");
      console.log("after clear:", responseText);
      // Store "h3" state in Alt-Svc cache
      for (let i = 0; i < 3; ++i) {
        await fetch(altSvcOrigin + "/set");
        await sleepMs(100);
      }
      responseText = await fetchText(altSvcOrigin + "/protocol");
      console.log("after set:", responseText);
    }
read: async () => {
      const protocol = await fetchText(altSvcOrigin + "/protocol");
      if ((new URL(location)).searchParams.get("thirdparty") === "same") {
        if (protocol !== "h3") {
          throw new Error("Unsupported");
        }
      }
      return protocol;
    }
result, same session: h2, h2, h2
result, different session: h2, h2, h2
unsupported: true, true, true
passed: undefined
test failed: false, false, false | write: async () => {
      // Clear Alt-Svc caching first.
      let responseText = "";
      for (let i = 0; i < 3; ++i) {
        await fetch(altSvcOrigin + "/clear");
        await sleepMs(100);
      }
      responseText = await fetchText(altSvcOrigin + "/protocol");
      console.log("after clear:", responseText);
      // Store "h3" state in Alt-Svc cache
      for (let i = 0; i < 3; ++i) {
        await fetch(altSvcOrigin + "/set");
        await sleepMs(100);
      }
      responseText = await fetchText(altSvcOrigin + "/protocol");
      console.log("after set:", responseText);
    }
read: async () => {
      const protocol = await fetchText(altSvcOrigin + "/protocol");
      if ((new URL(location)).searchParams.get("thirdparty") === "same") {
        if (protocol !== "h3") {
          throw new Error("Unsupported");
        }
      }
      return protocol;
    }
result, same session: h3, h3
result, different session: h3, h3
unsupported: false, false
passed: false, false
test failed: false, false | write: async () => {
      // Clear Alt-Svc caching first.
      let responseText = "";
      for (let i = 0; i < 3; ++i) {
        await fetch(altSvcOrigin + "/clear");
        await sleepMs(100);
      }
      responseText = await fetchText(altSvcOrigin + "/protocol");
      console.log("after clear:", responseText);
      // Store "h3" state in Alt-Svc cache
      for (let i = 0; i < 3; ++i) {
        await fetch(altSvcOrigin + "/set");
        await sleepMs(100);
      }
      responseText = await fetchText(altSvcOrigin + "/protocol");
      console.log("after set:", responseText);
    }
read: async () => {
      const protocol = await fetchText(altSvcOrigin + "/protocol");
      if ((new URL(location)).searchParams.get("thirdparty") === "same") {
        if (protocol !== "h3") {
          throw new Error("Unsupported");
        }
      }
      return protocol;
    }
result, same session: h3, h3
result, different session: h3, h3
unsupported: false, false
passed: false, false
test failed: false, false | 
| CacheStorage The Cache API is a content storage mechanism originally introduced to support ServiceWorkers. If the same Cache object is accessible to multiple websites, it can be abused to track users.
               | write: async (key) => {
      try {
        let cache = await caches.open("supercookies");
        cache.addAll([`test.css?key=${key}`]);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      let cache = await caches.open("supercookies");
      let cacheKeys = await cache.keys();
      let url = cacheKeys[0].url;
      return (new URL(url)).searchParams.get("key");
    }
result, same session: 
3133a4ca-686b-4047-91c3-9a7e181c53f7_1p,
49872d4a-b01d-4af8-8cbe-85c379a609e2_1p,
58bb2b32-4f83-47bb-91c8-96dce39cd97a_1p
result, different session: 
3133a4ca-686b-4047-91c3-9a7e181c53f7_1p,
49872d4a-b01d-4af8-8cbe-85c379a609e2_1p,
58bb2b32-4f83-47bb-91c8-96dce39cd97a_1p
unsupported: false, false, false
passed: false, false, false
test failed: false, false, false | write: async (key) => {
      try {
        let cache = await caches.open("supercookies");
        cache.addAll([`test.css?key=${key}`]);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      let cache = await caches.open("supercookies");
      let cacheKeys = await cache.keys();
      let url = cacheKeys[0].url;
      return (new URL(url)).searchParams.get("key");
    }
result, same session: 
e8485c10-5a92-474a-bd16-de23f46ee0e2_1p,
cd5b479f-1529-4f43-a586-810ed83aeb8b_1p
result, different session: 
e8485c10-5a92-474a-bd16-de23f46ee0e2_1p,
cd5b479f-1529-4f43-a586-810ed83aeb8b_1p
unsupported: false, false
passed: false, false
test failed: false, false | write: async (key) => {
      try {
        let cache = await caches.open("supercookies");
        cache.addAll([`test.css?key=${key}`]);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      let cache = await caches.open("supercookies");
      let cacheKeys = await cache.keys();
      let url = cacheKeys[0].url;
      return (new URL(url)).searchParams.get("key");
    }
result, same session: 
e96973da-bf72-4eb1-b6ab-a4e6c6775fb4_1p,
67fb6941-7c51-4727-b485-317211e65ae0_1p
result, different session: 
e96973da-bf72-4eb1-b6ab-a4e6c6775fb4_1p,
67fb6941-7c51-4727-b485-317211e65ae0_1p
unsupported: false, false
passed: false, false
test failed: false, false | write: async (key) => {
      try {
        let cache = await caches.open("supercookies");
        cache.addAll([`test.css?key=${key}`]);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      let cache = await caches.open("supercookies");
      let cacheKeys = await cache.keys();
      let url = cacheKeys[0].url;
      return (new URL(url)).searchParams.get("key");
    }
result, same session: 
94f1fd6d-f642-403e-8ef8-11180b18b1f6_1p,
2bf6571e-4385-4b68-a076-f6868cbe4954_1p
result, different session: 
94f1fd6d-f642-403e-8ef8-11180b18b1f6_1p,
2bf6571e-4385-4b68-a076-f6868cbe4954_1p
unsupported: false, false
passed: false, false
test failed: false, false | write: async (key) => {
      try {
        let cache = await caches.open("supercookies");
        cache.addAll([`test.css?key=${key}`]);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      let cache = await caches.open("supercookies");
      let cacheKeys = await cache.keys();
      let url = cacheKeys[0].url;
      return (new URL(url)).searchParams.get("key");
    }
result, same session: 
da67bebb-5652-441a-816e-1206b25c0225_1p,
0bad71fe-de93-4434-bfa0-bb7f0db62eea_1p,
6aba94ed-010e-45af-892c-dd670cd8d134_1p
result, different session: 
Error: cacheKeys[0] is undefined,
Error: cacheKeys[0] is undefined,
Error: cacheKeys[0] is undefined
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (key) => {
      try {
        let cache = await caches.open("supercookies");
        cache.addAll([`test.css?key=${key}`]);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      let cache = await caches.open("supercookies");
      let cacheKeys = await cache.keys();
      let url = cacheKeys[0].url;
      return (new URL(url)).searchParams.get("key");
    }
result, same session: 
Error: The operation is insecure.,
Error: The operation is insecure.,
Error: The operation is insecure.
result, different session: 
Error: The operation is insecure.,
Error: The operation is insecure.,
Error: The operation is insecure.
unsupported: true, true, true
passed: undefined
test failed: false, false, false | write: async (key) => {
      try {
        let cache = await caches.open("supercookies");
        cache.addAll([`test.css?key=${key}`]);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      let cache = await caches.open("supercookies");
      let cacheKeys = await cache.keys();
      let url = cacheKeys[0].url;
      return (new URL(url)).searchParams.get("key");
    }
result, same session: 
50c6a369-0b5c-4de5-a70b-a0516825119e_1p,
49e0d8b1-c126-4b6b-be66-6ba7acfd85a9_1p
result, different session: 
50c6a369-0b5c-4de5-a70b-a0516825119e_1p,
49e0d8b1-c126-4b6b-be66-6ba7acfd85a9_1p
unsupported: false, false
passed: false, false
test failed: false, false | write: async (key) => {
      try {
        let cache = await caches.open("supercookies");
        cache.addAll([`test.css?key=${key}`]);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      let cache = await caches.open("supercookies");
      let cacheKeys = await cache.keys();
      let url = cacheKeys[0].url;
      return (new URL(url)).searchParams.get("key");
    }
result, same session: 
6b07cf4a-a332-4a7c-90df-fbc3dc862f86_1p,
2d40813a-a6c5-4a6b-bb1c-f4d01a399217_1p
result, different session: 
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url')
unsupported: false, false
passed: true, true
test failed: false, false | write: async (key) => {
      try {
        let cache = await caches.open("supercookies");
        cache.addAll([`test.css?key=${key}`]);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      let cache = await caches.open("supercookies");
      let cacheKeys = await cache.keys();
      let url = cacheKeys[0].url;
      return (new URL(url)).searchParams.get("key");
    }
result, same session: 
Error: The operation is insecure.,
Error: The operation is insecure.,
Error: The operation is insecure.
result, different session: 
Error: The operation is insecure.,
Error: The operation is insecure.,
Error: The operation is insecure.
unsupported: true, true, true
passed: undefined
test failed: false, false, false | write: async (key) => {
      try {
        let cache = await caches.open("supercookies");
        cache.addAll([`test.css?key=${key}`]);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      let cache = await caches.open("supercookies");
      let cacheKeys = await cache.keys();
      let url = cacheKeys[0].url;
      return (new URL(url)).searchParams.get("key");
    }
result, same session: 
3242a94e-3344-4cb9-ba7c-ebedaf5d6e01_1p,
87918123-fc52-4196-9d0c-a2cfc3f826d4_1p
result, different session: 
3242a94e-3344-4cb9-ba7c-ebedaf5d6e01_1p,
87918123-fc52-4196-9d0c-a2cfc3f826d4_1p
unsupported: false, false
passed: false, false
test failed: false, false | write: async (key) => {
      try {
        let cache = await caches.open("supercookies");
        cache.addAll([`test.css?key=${key}`]);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      let cache = await caches.open("supercookies");
      let cacheKeys = await cache.keys();
      let url = cacheKeys[0].url;
      return (new URL(url)).searchParams.get("key");
    }
result, same session: 
5c052838-a55e-4690-83c6-b7d572146713_1p,
49042d99-5b0e-4f37-82f3-78de245eea34_1p
result, different session: 
5c052838-a55e-4690-83c6-b7d572146713_1p,
49042d99-5b0e-4f37-82f3-78de245eea34_1p
unsupported: false, false
passed: false, false
test failed: false, false | 
| cookie (HTTP) The cookie, first introduced by Netscape in 1994, is a small amount of data stored by your browser on a website's behalf. It has legitimate uses, but it is also the classic cross-site tracking mechanism, and today still the most popular method of tracking users across websites. Browsers can stop cookies from being used for cross-site tracking by either blocking or partitioning them.
               | write: async (secret) => {
      // Request a page that will send an HTTPOnly 'set-cookie' response header with secret value.
      await fetch(`${baseURI}cookie?secret=${secret}_http`);
    }
read: async () => {
      // Test if we now send a requests with a 'cookie' header containing the secret.
      let response = await fetch(`${baseURI}headers`);
      let cookie = (await response.json())["cookie"];
      return cookie ? cookie.match(/secret=([\w-]+)/)[1]: null;
    }
result, same session: 
3133a4ca-686b-4047-91c3-9a7e181c53f7_1p_http,
49872d4a-b01d-4af8-8cbe-85c379a609e2_1p_http,
58bb2b32-4f83-47bb-91c8-96dce39cd97a_1p_http
result, different session: 
3133a4ca-686b-4047-91c3-9a7e181c53f7_1p_http,
49872d4a-b01d-4af8-8cbe-85c379a609e2_1p_http,
58bb2b32-4f83-47bb-91c8-96dce39cd97a_1p_http
unsupported: false, false, false
passed: false, false, false
test failed: false, false, false | write: async (secret) => {
      // Request a page that will send an HTTPOnly 'set-cookie' response header with secret value.
      await fetch(`${baseURI}cookie?secret=${secret}_http`);
    }
read: async () => {
      // Test if we now send a requests with a 'cookie' header containing the secret.
      let response = await fetch(`${baseURI}headers`);
      let cookie = (await response.json())["cookie"];
      return cookie ? cookie.match(/secret=([\w-]+)/)[1]: null;
    }
result, same session: 
e8485c10-5a92-474a-bd16-de23f46ee0e2_1p_http,
cd5b479f-1529-4f43-a586-810ed83aeb8b_1p_http
result, different session: 
e8485c10-5a92-474a-bd16-de23f46ee0e2_1p_http,
cd5b479f-1529-4f43-a586-810ed83aeb8b_1p_http
unsupported: false, false
passed: false, false
test failed: false, false | write: async (secret) => {
      // Request a page that will send an HTTPOnly 'set-cookie' response header with secret value.
      await fetch(`${baseURI}cookie?secret=${secret}_http`);
    }
read: async () => {
      // Test if we now send a requests with a 'cookie' header containing the secret.
      let response = await fetch(`${baseURI}headers`);
      let cookie = (await response.json())["cookie"];
      return cookie ? cookie.match(/secret=([\w-]+)/)[1]: null;
    }
result, same session: 
e96973da-bf72-4eb1-b6ab-a4e6c6775fb4_1p_http,
67fb6941-7c51-4727-b485-317211e65ae0_1p_http
result, different session: 
e96973da-bf72-4eb1-b6ab-a4e6c6775fb4_1p_http,
67fb6941-7c51-4727-b485-317211e65ae0_1p_http
unsupported: false, false
passed: false, false
test failed: false, false | write: async (secret) => {
      // Request a page that will send an HTTPOnly 'set-cookie' response header with secret value.
      await fetch(`${baseURI}cookie?secret=${secret}_http`);
    }
read: async () => {
      // Test if we now send a requests with a 'cookie' header containing the secret.
      let response = await fetch(`${baseURI}headers`);
      let cookie = (await response.json())["cookie"];
      return cookie ? cookie.match(/secret=([\w-]+)/)[1]: null;
    }
result, same session: 
94f1fd6d-f642-403e-8ef8-11180b18b1f6_1p_http,
2bf6571e-4385-4b68-a076-f6868cbe4954_1p_http
result, different session: 
94f1fd6d-f642-403e-8ef8-11180b18b1f6_1p_http,
2bf6571e-4385-4b68-a076-f6868cbe4954_1p_http
unsupported: false, false
passed: false, false
test failed: false, false | write: async (secret) => {
      // Request a page that will send an HTTPOnly 'set-cookie' response header with secret value.
      await fetch(`${baseURI}cookie?secret=${secret}_http`);
    }
read: async () => {
      // Test if we now send a requests with a 'cookie' header containing the secret.
      let response = await fetch(`${baseURI}headers`);
      let cookie = (await response.json())["cookie"];
      return cookie ? cookie.match(/secret=([\w-]+)/)[1]: null;
    }
result, same session: 
da67bebb-5652-441a-816e-1206b25c0225_1p_http,
0bad71fe-de93-4434-bfa0-bb7f0db62eea_1p_http,
6aba94ed-010e-45af-892c-dd670cd8d134_1p_http
result, different session: , , 
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (secret) => {
      // Request a page that will send an HTTPOnly 'set-cookie' response header with secret value.
      await fetch(`${baseURI}cookie?secret=${secret}_http`);
    }
read: async () => {
      // Test if we now send a requests with a 'cookie' header containing the secret.
      let response = await fetch(`${baseURI}headers`);
      let cookie = (await response.json())["cookie"];
      return cookie ? cookie.match(/secret=([\w-]+)/)[1]: null;
    }
result, same session: 
9dd63e63-3256-4352-99f6-805b6f48eb8c_1p_http,
b7cb77db-aca9-4570-923e-b81da9175421_1p_http,
736f05a1-05e6-491e-8dad-a6647bd52dda_1p_http
result, different session: , , 
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (secret) => {
      // Request a page that will send an HTTPOnly 'set-cookie' response header with secret value.
      await fetch(`${baseURI}cookie?secret=${secret}_http`);
    }
read: async () => {
      // Test if we now send a requests with a 'cookie' header containing the secret.
      let response = await fetch(`${baseURI}headers`);
      let cookie = (await response.json())["cookie"];
      return cookie ? cookie.match(/secret=([\w-]+)/)[1]: null;
    }
result, same session: 
50c6a369-0b5c-4de5-a70b-a0516825119e_1p_http,
49e0d8b1-c126-4b6b-be66-6ba7acfd85a9_1p_http
result, different session: 
50c6a369-0b5c-4de5-a70b-a0516825119e_1p_http,
49e0d8b1-c126-4b6b-be66-6ba7acfd85a9_1p_http
unsupported: false, false
passed: false, false
test failed: false, false | write: async (secret) => {
      // Request a page that will send an HTTPOnly 'set-cookie' response header with secret value.
      await fetch(`${baseURI}cookie?secret=${secret}_http`);
    }
read: async () => {
      // Test if we now send a requests with a 'cookie' header containing the secret.
      let response = await fetch(`${baseURI}headers`);
      let cookie = (await response.json())["cookie"];
      return cookie ? cookie.match(/secret=([\w-]+)/)[1]: null;
    }
result, same session: 
6b07cf4a-a332-4a7c-90df-fbc3dc862f86_1p_http,
2d40813a-a6c5-4a6b-bb1c-f4d01a399217_1p_http
result, different session: 
6b07cf4a-a332-4a7c-90df-fbc3dc862f86_1p_http,
2d40813a-a6c5-4a6b-bb1c-f4d01a399217_1p_http
unsupported: false, false
passed: false, false
test failed: false, false | write: async (secret) => {
      // Request a page that will send an HTTPOnly 'set-cookie' response header with secret value.
      await fetch(`${baseURI}cookie?secret=${secret}_http`);
    }
read: async () => {
      // Test if we now send a requests with a 'cookie' header containing the secret.
      let response = await fetch(`${baseURI}headers`);
      let cookie = (await response.json())["cookie"];
      return cookie ? cookie.match(/secret=([\w-]+)/)[1]: null;
    }
result, same session: 
3dc5c317-3ea7-42b4-be71-7902bcc4eb27_1p_http,
4932f81b-b746-4e4b-b97d-c0b827047888_1p_http,
3e016f09-5749-4084-a136-e9b829c1a824_1p_http
result, different session: , , 
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (secret) => {
      // Request a page that will send an HTTPOnly 'set-cookie' response header with secret value.
      await fetch(`${baseURI}cookie?secret=${secret}_http`);
    }
read: async () => {
      // Test if we now send a requests with a 'cookie' header containing the secret.
      let response = await fetch(`${baseURI}headers`);
      let cookie = (await response.json())["cookie"];
      return cookie ? cookie.match(/secret=([\w-]+)/)[1]: null;
    }
result, same session: 
3242a94e-3344-4cb9-ba7c-ebedaf5d6e01_1p_http,
87918123-fc52-4196-9d0c-a2cfc3f826d4_1p_http
result, different session: 
3242a94e-3344-4cb9-ba7c-ebedaf5d6e01_1p_http,
87918123-fc52-4196-9d0c-a2cfc3f826d4_1p_http
unsupported: false, false
passed: false, false
test failed: false, false | write: async (secret) => {
      // Request a page that will send an HTTPOnly 'set-cookie' response header with secret value.
      await fetch(`${baseURI}cookie?secret=${secret}_http`);
    }
read: async () => {
      // Test if we now send a requests with a 'cookie' header containing the secret.
      let response = await fetch(`${baseURI}headers`);
      let cookie = (await response.json())["cookie"];
      return cookie ? cookie.match(/secret=([\w-]+)/)[1]: null;
    }
result, same session: 
5c052838-a55e-4690-83c6-b7d572146713_1p_http,
49042d99-5b0e-4f37-82f3-78de245eea34_1p_http
result, different session: 
5c052838-a55e-4690-83c6-b7d572146713_1p_http,
49042d99-5b0e-4f37-82f3-78de245eea34_1p_http
unsupported: false, false
passed: false, false
test failed: false, false | 
| cookie (JS) The cookie, first introduced by Netscape in 1994, is a small amount of data stored by your browser on a website's behalf. It has legitimate uses, but it is also the classic cross-site tracking mechanism, and today still the most popular method of tracking users across websites. Browsers can stop cookies from being used for cross-site tracking by either blocking or partitioning them.
               | write: (secret) => {
      document.cookie = `secret=${secret}_js; max-age=3600; SameSite=None; Secure`;
    }
read: () => document.cookie ? document.cookie.match(/secret=([\w-]+)/)[1] : null
result, same session: 
3133a4ca-686b-4047-91c3-9a7e181c53f7_1p_js,
49872d4a-b01d-4af8-8cbe-85c379a609e2_1p_js,
58bb2b32-4f83-47bb-91c8-96dce39cd97a_1p_js
result, different session: 
3133a4ca-686b-4047-91c3-9a7e181c53f7_1p_js,
49872d4a-b01d-4af8-8cbe-85c379a609e2_1p_js,
58bb2b32-4f83-47bb-91c8-96dce39cd97a_1p_js
unsupported: false, false, false
passed: false, false, false
test failed: false, false, false | write: (secret) => {
      document.cookie = `secret=${secret}_js; max-age=3600; SameSite=None; Secure`;
    }
read: () => document.cookie ? document.cookie.match(/secret=([\w-]+)/)[1] : null
result, same session: 
e8485c10-5a92-474a-bd16-de23f46ee0e2_1p_js,
cd5b479f-1529-4f43-a586-810ed83aeb8b_1p_js
result, different session: 
e8485c10-5a92-474a-bd16-de23f46ee0e2_1p_js,
cd5b479f-1529-4f43-a586-810ed83aeb8b_1p_js
unsupported: false, false
passed: false, false
test failed: false, false | write: (secret) => {
      document.cookie = `secret=${secret}_js; max-age=3600; SameSite=None; Secure`;
    }
read: () => document.cookie ? document.cookie.match(/secret=([\w-]+)/)[1] : null
result, same session: 
e96973da-bf72-4eb1-b6ab-a4e6c6775fb4_1p_js,
67fb6941-7c51-4727-b485-317211e65ae0_1p_js
result, different session: 
e96973da-bf72-4eb1-b6ab-a4e6c6775fb4_1p_js,
67fb6941-7c51-4727-b485-317211e65ae0_1p_js
unsupported: false, false
passed: false, false
test failed: false, false | write: (secret) => {
      document.cookie = `secret=${secret}_js; max-age=3600; SameSite=None; Secure`;
    }
read: () => document.cookie ? document.cookie.match(/secret=([\w-]+)/)[1] : null
result, same session: 
94f1fd6d-f642-403e-8ef8-11180b18b1f6_1p_js,
2bf6571e-4385-4b68-a076-f6868cbe4954_1p_js
result, different session: 
94f1fd6d-f642-403e-8ef8-11180b18b1f6_1p_js,
2bf6571e-4385-4b68-a076-f6868cbe4954_1p_js
unsupported: false, false
passed: false, false
test failed: false, false | write: (secret) => {
      document.cookie = `secret=${secret}_js; max-age=3600; SameSite=None; Secure`;
    }
read: () => document.cookie ? document.cookie.match(/secret=([\w-]+)/)[1] : null
result, same session: 
da67bebb-5652-441a-816e-1206b25c0225_1p_js,
0bad71fe-de93-4434-bfa0-bb7f0db62eea_1p_js,
6aba94ed-010e-45af-892c-dd670cd8d134_1p_js
result, different session: , , 
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (secret) => {
      document.cookie = `secret=${secret}_js; max-age=3600; SameSite=None; Secure`;
    }
read: () => document.cookie ? document.cookie.match(/secret=([\w-]+)/)[1] : null
result, same session: 
9dd63e63-3256-4352-99f6-805b6f48eb8c_1p_js,
b7cb77db-aca9-4570-923e-b81da9175421_1p_js,
736f05a1-05e6-491e-8dad-a6647bd52dda_1p_js
result, different session: , , 
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (secret) => {
      document.cookie = `secret=${secret}_js; max-age=3600; SameSite=None; Secure`;
    }
read: () => document.cookie ? document.cookie.match(/secret=([\w-]+)/)[1] : null
result, same session: 
50c6a369-0b5c-4de5-a70b-a0516825119e_1p_js,
49e0d8b1-c126-4b6b-be66-6ba7acfd85a9_1p_js
result, different session: 
50c6a369-0b5c-4de5-a70b-a0516825119e_1p_js,
49e0d8b1-c126-4b6b-be66-6ba7acfd85a9_1p_js
unsupported: false, false
passed: false, false
test failed: false, false | write: (secret) => {
      document.cookie = `secret=${secret}_js; max-age=3600; SameSite=None; Secure`;
    }
read: () => document.cookie ? document.cookie.match(/secret=([\w-]+)/)[1] : null
result, same session: 
6b07cf4a-a332-4a7c-90df-fbc3dc862f86_1p_js,
2d40813a-a6c5-4a6b-bb1c-f4d01a399217_1p_js
result, different session: , 
unsupported: false, false
passed: true, true
test failed: false, false | write: (secret) => {
      document.cookie = `secret=${secret}_js; max-age=3600; SameSite=None; Secure`;
    }
read: () => document.cookie ? document.cookie.match(/secret=([\w-]+)/)[1] : null
result, same session: 
3dc5c317-3ea7-42b4-be71-7902bcc4eb27_1p_js,
4932f81b-b746-4e4b-b97d-c0b827047888_1p_js,
3e016f09-5749-4084-a136-e9b829c1a824_1p_js
result, different session: , , 
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (secret) => {
      document.cookie = `secret=${secret}_js; max-age=3600; SameSite=None; Secure`;
    }
read: () => document.cookie ? document.cookie.match(/secret=([\w-]+)/)[1] : null
result, same session: 
3242a94e-3344-4cb9-ba7c-ebedaf5d6e01_1p_js,
87918123-fc52-4196-9d0c-a2cfc3f826d4_1p_js
result, different session: 
3242a94e-3344-4cb9-ba7c-ebedaf5d6e01_1p_js,
87918123-fc52-4196-9d0c-a2cfc3f826d4_1p_js
unsupported: false, false
passed: false, false
test failed: false, false | write: (secret) => {
      document.cookie = `secret=${secret}_js; max-age=3600; SameSite=None; Secure`;
    }
read: () => document.cookie ? document.cookie.match(/secret=([\w-]+)/)[1] : null
result, same session: 
5c052838-a55e-4690-83c6-b7d572146713_1p_js,
49042d99-5b0e-4f37-82f3-78de245eea34_1p_js
result, different session: 
5c052838-a55e-4690-83c6-b7d572146713_1p_js,
49042d99-5b0e-4f37-82f3-78de245eea34_1p_js
unsupported: false, false
passed: false, false
test failed: false, false | 
| CookieStore The Cookie Store API is an alternative asynchronous API for managing cookies, supported by some browsers.
               | write: (data) => {
      const msPerHour = 60 * 60 * 1000;
      if (!window.cookieStore) {
        throw new Error("Unsupported");
      }
      window.cookieStore.set({
        name: "partition_test",
        value: data,
        expires: Date.now() + msPerHour,
        sameSite: "none"
      });
    }
read: async () => {
      if (!window.cookieStore) {
        throw new Error("Unsupported");
      }
      const cookie = await window.cookieStore.get("partition_test");
      if (!cookie) {
        return null;
      }
      return cookie.value;
    }
result, same session: 
3133a4ca-686b-4047-91c3-9a7e181c53f7_1p,
49872d4a-b01d-4af8-8cbe-85c379a609e2_1p,
58bb2b32-4f83-47bb-91c8-96dce39cd97a_1p
result, different session: 
3133a4ca-686b-4047-91c3-9a7e181c53f7_1p,
49872d4a-b01d-4af8-8cbe-85c379a609e2_1p,
58bb2b32-4f83-47bb-91c8-96dce39cd97a_1p
unsupported: false, false, false
passed: false, false, false
test failed: false, false, false | write: (data) => {
      const msPerHour = 60 * 60 * 1000;
      if (!window.cookieStore) {
        throw new Error("Unsupported");
      }
      window.cookieStore.set({
        name: "partition_test",
        value: data,
        expires: Date.now() + msPerHour,
        sameSite: "none"
      });
    }
read: async () => {
      if (!window.cookieStore) {
        throw new Error("Unsupported");
      }
      const cookie = await window.cookieStore.get("partition_test");
      if (!cookie) {
        return null;
      }
      return cookie.value;
    }
result, same session: 
e8485c10-5a92-474a-bd16-de23f46ee0e2_1p,
cd5b479f-1529-4f43-a586-810ed83aeb8b_1p
result, different session: 
e8485c10-5a92-474a-bd16-de23f46ee0e2_1p,
cd5b479f-1529-4f43-a586-810ed83aeb8b_1p
unsupported: false, false
passed: false, false
test failed: false, false | write: (data) => {
      const msPerHour = 60 * 60 * 1000;
      if (!window.cookieStore) {
        throw new Error("Unsupported");
      }
      window.cookieStore.set({
        name: "partition_test",
        value: data,
        expires: Date.now() + msPerHour,
        sameSite: "none"
      });
    }
read: async () => {
      if (!window.cookieStore) {
        throw new Error("Unsupported");
      }
      const cookie = await window.cookieStore.get("partition_test");
      if (!cookie) {
        return null;
      }
      return cookie.value;
    }
result, same session: 
e96973da-bf72-4eb1-b6ab-a4e6c6775fb4_1p,
67fb6941-7c51-4727-b485-317211e65ae0_1p
result, different session: 
e96973da-bf72-4eb1-b6ab-a4e6c6775fb4_1p,
67fb6941-7c51-4727-b485-317211e65ae0_1p
unsupported: false, false
passed: false, false
test failed: false, false | write: (data) => {
      const msPerHour = 60 * 60 * 1000;
      if (!window.cookieStore) {
        throw new Error("Unsupported");
      }
      window.cookieStore.set({
        name: "partition_test",
        value: data,
        expires: Date.now() + msPerHour,
        sameSite: "none"
      });
    }
read: async () => {
      if (!window.cookieStore) {
        throw new Error("Unsupported");
      }
      const cookie = await window.cookieStore.get("partition_test");
      if (!cookie) {
        return null;
      }
      return cookie.value;
    }
result, same session: 
Error: Unsupported,
Error: Unsupported
result, different session: 
Error: Unsupported,
Error: Unsupported
unsupported: true, true
passed: undefined
test failed: false, false | write: (data) => {
      const msPerHour = 60 * 60 * 1000;
      if (!window.cookieStore) {
        throw new Error("Unsupported");
      }
      window.cookieStore.set({
        name: "partition_test",
        value: data,
        expires: Date.now() + msPerHour,
        sameSite: "none"
      });
    }
read: async () => {
      if (!window.cookieStore) {
        throw new Error("Unsupported");
      }
      const cookie = await window.cookieStore.get("partition_test");
      if (!cookie) {
        return null;
      }
      return cookie.value;
    }
result, same session: 
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
result, different session: 
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
unsupported: true, true, true
passed: undefined
test failed: false, false, false | write: (data) => {
      const msPerHour = 60 * 60 * 1000;
      if (!window.cookieStore) {
        throw new Error("Unsupported");
      }
      window.cookieStore.set({
        name: "partition_test",
        value: data,
        expires: Date.now() + msPerHour,
        sameSite: "none"
      });
    }
read: async () => {
      if (!window.cookieStore) {
        throw new Error("Unsupported");
      }
      const cookie = await window.cookieStore.get("partition_test");
      if (!cookie) {
        return null;
      }
      return cookie.value;
    }
result, same session: 
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
result, different session: 
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
unsupported: true, true, true
passed: undefined
test failed: false, false, false | write: (data) => {
      const msPerHour = 60 * 60 * 1000;
      if (!window.cookieStore) {
        throw new Error("Unsupported");
      }
      window.cookieStore.set({
        name: "partition_test",
        value: data,
        expires: Date.now() + msPerHour,
        sameSite: "none"
      });
    }
read: async () => {
      if (!window.cookieStore) {
        throw new Error("Unsupported");
      }
      const cookie = await window.cookieStore.get("partition_test");
      if (!cookie) {
        return null;
      }
      return cookie.value;
    }
result, same session: 
50c6a369-0b5c-4de5-a70b-a0516825119e_1p,
49e0d8b1-c126-4b6b-be66-6ba7acfd85a9_1p
result, different session: 
50c6a369-0b5c-4de5-a70b-a0516825119e_1p,
49e0d8b1-c126-4b6b-be66-6ba7acfd85a9_1p
unsupported: false, false
passed: false, false
test failed: false, false | write: (data) => {
      const msPerHour = 60 * 60 * 1000;
      if (!window.cookieStore) {
        throw new Error("Unsupported");
      }
      window.cookieStore.set({
        name: "partition_test",
        value: data,
        expires: Date.now() + msPerHour,
        sameSite: "none"
      });
    }
read: async () => {
      if (!window.cookieStore) {
        throw new Error("Unsupported");
      }
      const cookie = await window.cookieStore.get("partition_test");
      if (!cookie) {
        return null;
      }
      return cookie.value;
    }
result, same session: 
Error: Unsupported,
Error: Unsupported
result, different session: 
Error: Unsupported,
Error: Unsupported
unsupported: true, true
passed: undefined
test failed: false, false | write: (data) => {
      const msPerHour = 60 * 60 * 1000;
      if (!window.cookieStore) {
        throw new Error("Unsupported");
      }
      window.cookieStore.set({
        name: "partition_test",
        value: data,
        expires: Date.now() + msPerHour,
        sameSite: "none"
      });
    }
read: async () => {
      if (!window.cookieStore) {
        throw new Error("Unsupported");
      }
      const cookie = await window.cookieStore.get("partition_test");
      if (!cookie) {
        return null;
      }
      return cookie.value;
    }
result, same session: 
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
result, different session: 
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
unsupported: true, true, true
passed: undefined
test failed: false, false, false | write: (data) => {
      const msPerHour = 60 * 60 * 1000;
      if (!window.cookieStore) {
        throw new Error("Unsupported");
      }
      window.cookieStore.set({
        name: "partition_test",
        value: data,
        expires: Date.now() + msPerHour,
        sameSite: "none"
      });
    }
read: async () => {
      if (!window.cookieStore) {
        throw new Error("Unsupported");
      }
      const cookie = await window.cookieStore.get("partition_test");
      if (!cookie) {
        return null;
      }
      return cookie.value;
    }
result, same session: 
3242a94e-3344-4cb9-ba7c-ebedaf5d6e01_1p,
87918123-fc52-4196-9d0c-a2cfc3f826d4_1p
result, different session: 
3242a94e-3344-4cb9-ba7c-ebedaf5d6e01_1p,
87918123-fc52-4196-9d0c-a2cfc3f826d4_1p
unsupported: false, false
passed: false, false
test failed: false, false | write: (data) => {
      const msPerHour = 60 * 60 * 1000;
      if (!window.cookieStore) {
        throw new Error("Unsupported");
      }
      window.cookieStore.set({
        name: "partition_test",
        value: data,
        expires: Date.now() + msPerHour,
        sameSite: "none"
      });
    }
read: async () => {
      if (!window.cookieStore) {
        throw new Error("Unsupported");
      }
      const cookie = await window.cookieStore.get("partition_test");
      if (!cookie) {
        return null;
      }
      return cookie.value;
    }
result, same session: 
5c052838-a55e-4690-83c6-b7d572146713_1p,
49042d99-5b0e-4f37-82f3-78de245eea34_1p
result, different session: 
5c052838-a55e-4690-83c6-b7d572146713_1p,
49042d99-5b0e-4f37-82f3-78de245eea34_1p
unsupported: false, false
passed: false, false
test failed: false, false | 
| CSS cache CSS stylesheets are cached, and if that cache is shared between websites, it can be used to track users across sites.
               | write: async (key) => {
      const href = testURI("resource", "css", key);
      const head = document.getElementsByTagName("head")[0];
      head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
      const testElement = document.querySelector("#css");
      let fontFamily;
      while (true) {
        await sleepMs(100);
        fontFamily = getComputedStyle(testElement).fontFamily;
        if (fontFamily.startsWith("fake")) {
          break;
        }
      }
      console.log(fontFamily);
      return key;
    }
read: async (key) => {
      const href = testURI("resource", "css", key);
      const head = document.getElementsByTagName("head")[0];
      head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
      const testElement = document.querySelector("#css");
      let fontFamily;
      while (true) {
        await sleepMs(100);
        fontFamily = getComputedStyle(testElement).fontFamily;
        if (fontFamily.startsWith("fake")) {
          break;
        }
      }
      console.log(fontFamily);
      return fontFamily;
    }
result, same session: 
fake_508783845145308,
fake_17008740192218608,
fake_4779195720306413
result, different session: 
fake_508783845145308,
fake_17008740192218608,
fake_4779195720306413
unsupported: false, false, false
passed: false, false, false
test failed: false, false, false | write: async (key) => {
      const href = testURI("resource", "css", key);
      const head = document.getElementsByTagName("head")[0];
      head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
      const testElement = document.querySelector("#css");
      let fontFamily;
      while (true) {
        await sleepMs(100);
        fontFamily = getComputedStyle(testElement).fontFamily;
        if (fontFamily.startsWith("fake")) {
          break;
        }
      }
      console.log(fontFamily);
      return key;
    }
read: async (key) => {
      const href = testURI("resource", "css", key);
      const head = document.getElementsByTagName("head")[0];
      head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
      const testElement = document.querySelector("#css");
      let fontFamily;
      while (true) {
        await sleepMs(100);
        fontFamily = getComputedStyle(testElement).fontFamily;
        if (fontFamily.startsWith("fake")) {
          break;
        }
      }
      console.log(fontFamily);
      return fontFamily;
    }
result, same session: 
fake_48920180145673986,
fake_42288328513959517
result, different session: 
fake_48920180145673986,
fake_42288328513959517
unsupported: false, false
passed: false, false
test failed: false, false | write: async (key) => {
      const href = testURI("resource", "css", key);
      const head = document.getElementsByTagName("head")[0];
      head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
      const testElement = document.querySelector("#css");
      let fontFamily;
      while (true) {
        await sleepMs(100);
        fontFamily = getComputedStyle(testElement).fontFamily;
        if (fontFamily.startsWith("fake")) {
          break;
        }
      }
      console.log(fontFamily);
      return key;
    }
read: async (key) => {
      const href = testURI("resource", "css", key);
      const head = document.getElementsByTagName("head")[0];
      head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
      const testElement = document.querySelector("#css");
      let fontFamily;
      while (true) {
        await sleepMs(100);
        fontFamily = getComputedStyle(testElement).fontFamily;
        if (fontFamily.startsWith("fake")) {
          break;
        }
      }
      console.log(fontFamily);
      return fontFamily;
    }
result, same session: 
fake_9310738708578981,
fake_27725712451792317
result, different session: 
fake_9310738708578981,
fake_27725712451792317
unsupported: false, false
passed: false, false
test failed: false, false | write: async (key) => {
      const href = testURI("resource", "css", key);
      const head = document.getElementsByTagName("head")[0];
      head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
      const testElement = document.querySelector("#css");
      let fontFamily;
      while (true) {
        await sleepMs(100);
        fontFamily = getComputedStyle(testElement).fontFamily;
        if (fontFamily.startsWith("fake")) {
          break;
        }
      }
      console.log(fontFamily);
      return key;
    }
read: async (key) => {
      const href = testURI("resource", "css", key);
      const head = document.getElementsByTagName("head")[0];
      head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
      const testElement = document.querySelector("#css");
      let fontFamily;
      while (true) {
        await sleepMs(100);
        fontFamily = getComputedStyle(testElement).fontFamily;
        if (fontFamily.startsWith("fake")) {
          break;
        }
      }
      console.log(fontFamily);
      return fontFamily;
    }
result, same session: 
fake_7293871207316731,
fake_8718563693993413
result, different session: 
fake_7293871207316731,
fake_8718563693993413
unsupported: false, false
passed: false, false
test failed: false, false | write: async (key) => {
      const href = testURI("resource", "css", key);
      const head = document.getElementsByTagName("head")[0];
      head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
      const testElement = document.querySelector("#css");
      let fontFamily;
      while (true) {
        await sleepMs(100);
        fontFamily = getComputedStyle(testElement).fontFamily;
        if (fontFamily.startsWith("fake")) {
          break;
        }
      }
      console.log(fontFamily);
      return key;
    }
read: async (key) => {
      const href = testURI("resource", "css", key);
      const head = document.getElementsByTagName("head")[0];
      head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
      const testElement = document.querySelector("#css");
      let fontFamily;
      while (true) {
        await sleepMs(100);
        fontFamily = getComputedStyle(testElement).fontFamily;
        if (fontFamily.startsWith("fake")) {
          break;
        }
      }
      console.log(fontFamily);
      return fontFamily;
    }
result, same session: 
fake_31895042359787085,
fake_7122488308372175,
fake_1929007704092851
result, different session: 
fake_4175980763439966,
fake_5849587075679779,
fake_6065218142700934
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (key) => {
      const href = testURI("resource", "css", key);
      const head = document.getElementsByTagName("head")[0];
      head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
      const testElement = document.querySelector("#css");
      let fontFamily;
      while (true) {
        await sleepMs(100);
        fontFamily = getComputedStyle(testElement).fontFamily;
        if (fontFamily.startsWith("fake")) {
          break;
        }
      }
      console.log(fontFamily);
      return key;
    }
read: async (key) => {
      const href = testURI("resource", "css", key);
      const head = document.getElementsByTagName("head")[0];
      head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
      const testElement = document.querySelector("#css");
      let fontFamily;
      while (true) {
        await sleepMs(100);
        fontFamily = getComputedStyle(testElement).fontFamily;
        if (fontFamily.startsWith("fake")) {
          break;
        }
      }
      console.log(fontFamily);
      return fontFamily;
    }
result, same session: 
fake_672093538674055,
fake_42050226898555065,
fake_9841746138059619
result, different session: 
fake_4413151026660034,
fake_871369240010899,
fake_48514688162886443
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (key) => {
      const href = testURI("resource", "css", key);
      const head = document.getElementsByTagName("head")[0];
      head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
      const testElement = document.querySelector("#css");
      let fontFamily;
      while (true) {
        await sleepMs(100);
        fontFamily = getComputedStyle(testElement).fontFamily;
        if (fontFamily.startsWith("fake")) {
          break;
        }
      }
      console.log(fontFamily);
      return key;
    }
read: async (key) => {
      const href = testURI("resource", "css", key);
      const head = document.getElementsByTagName("head")[0];
      head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
      const testElement = document.querySelector("#css");
      let fontFamily;
      while (true) {
        await sleepMs(100);
        fontFamily = getComputedStyle(testElement).fontFamily;
        if (fontFamily.startsWith("fake")) {
          break;
        }
      }
      console.log(fontFamily);
      return fontFamily;
    }
result, same session: 
fake_3888033870146348,
fake_9472267445037261
result, different session: 
fake_3888033870146348,
fake_9472267445037261
unsupported: false, false
passed: false, false
test failed: false, false | write: async (key) => {
      const href = testURI("resource", "css", key);
      const head = document.getElementsByTagName("head")[0];
      head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
      const testElement = document.querySelector("#css");
      let fontFamily;
      while (true) {
        await sleepMs(100);
        fontFamily = getComputedStyle(testElement).fontFamily;
        if (fontFamily.startsWith("fake")) {
          break;
        }
      }
      console.log(fontFamily);
      return key;
    }
read: async (key) => {
      const href = testURI("resource", "css", key);
      const head = document.getElementsByTagName("head")[0];
      head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
      const testElement = document.querySelector("#css");
      let fontFamily;
      while (true) {
        await sleepMs(100);
        fontFamily = getComputedStyle(testElement).fontFamily;
        if (fontFamily.startsWith("fake")) {
          break;
        }
      }
      console.log(fontFamily);
      return fontFamily;
    }
result, same session: 
fake_20088933378188734,
fake_9125769474870549
result, different session: 
fake_20088933378188734,
fake_9125769474870549
unsupported: false, false
passed: false, false
test failed: false, false | write: async (key) => {
      const href = testURI("resource", "css", key);
      const head = document.getElementsByTagName("head")[0];
      head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
      const testElement = document.querySelector("#css");
      let fontFamily;
      while (true) {
        await sleepMs(100);
        fontFamily = getComputedStyle(testElement).fontFamily;
        if (fontFamily.startsWith("fake")) {
          break;
        }
      }
      console.log(fontFamily);
      return key;
    }
read: async (key) => {
      const href = testURI("resource", "css", key);
      const head = document.getElementsByTagName("head")[0];
      head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
      const testElement = document.querySelector("#css");
      let fontFamily;
      while (true) {
        await sleepMs(100);
        fontFamily = getComputedStyle(testElement).fontFamily;
        if (fontFamily.startsWith("fake")) {
          break;
        }
      }
      console.log(fontFamily);
      return fontFamily;
    }
result, same session: 
fake_5215145645012496,
fake_5568928771361725,
fake_9565348716725948
result, different session: 
fake_591964330241892,
fake_37837809966157443,
fake_6406785456215565
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (key) => {
      const href = testURI("resource", "css", key);
      const head = document.getElementsByTagName("head")[0];
      head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
      const testElement = document.querySelector("#css");
      let fontFamily;
      while (true) {
        await sleepMs(100);
        fontFamily = getComputedStyle(testElement).fontFamily;
        if (fontFamily.startsWith("fake")) {
          break;
        }
      }
      console.log(fontFamily);
      return key;
    }
read: async (key) => {
      const href = testURI("resource", "css", key);
      const head = document.getElementsByTagName("head")[0];
      head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
      const testElement = document.querySelector("#css");
      let fontFamily;
      while (true) {
        await sleepMs(100);
        fontFamily = getComputedStyle(testElement).fontFamily;
        if (fontFamily.startsWith("fake")) {
          break;
        }
      }
      console.log(fontFamily);
      return fontFamily;
    }
result, same session: 
fake_48395985947609854,
fake_5251097733449621
result, different session: 
fake_48395985947609854,
fake_5251097733449621
unsupported: false, false
passed: false, false
test failed: false, false | write: async (key) => {
      const href = testURI("resource", "css", key);
      const head = document.getElementsByTagName("head")[0];
      head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
      const testElement = document.querySelector("#css");
      let fontFamily;
      while (true) {
        await sleepMs(100);
        fontFamily = getComputedStyle(testElement).fontFamily;
        if (fontFamily.startsWith("fake")) {
          break;
        }
      }
      console.log(fontFamily);
      return key;
    }
read: async (key) => {
      const href = testURI("resource", "css", key);
      const head = document.getElementsByTagName("head")[0];
      head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
      const testElement = document.querySelector("#css");
      let fontFamily;
      while (true) {
        await sleepMs(100);
        fontFamily = getComputedStyle(testElement).fontFamily;
        if (fontFamily.startsWith("fake")) {
          break;
        }
      }
      console.log(fontFamily);
      return fontFamily;
    }
result, same session: 
fake_8629921784246313,
fake_45489412931596607
result, different session: 
fake_8629921784246313,
fake_45489412931596607
unsupported: false, false
passed: false, false
test failed: false, false | 
| favicon cache A favicon is an icon that represents a website, typically shown in browser tab and bookmarks menu. If the favicon cache is not partitioned, it can be used to track users across websites.
               | write: (key) => key
read: async (key) => {
      // Wait for the favicon to load (defined in supercookies.html)
      await sleepMs(2000);
      let response = await fetch(
        testURI("ctr", "favicon", key), {"cache": "reload"});
      let count = (await response.text()).trim();
      if (count === "0") {
        throw new Error("No requests received");
      }
      return count;
    }
result, same session: 1, 1, 1
result, different session: 1, 1, 1
unsupported: false, false, false
passed: false, false, false
test failed: false, false, false | write: (key) => key
read: async (key) => {
      // Wait for the favicon to load (defined in supercookies.html)
      await sleepMs(2000);
      let response = await fetch(
        testURI("ctr", "favicon", key), {"cache": "reload"});
      let count = (await response.text()).trim();
      if (count === "0") {
        throw new Error("No requests received");
      }
      return count;
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: (key) => key
read: async (key) => {
      // Wait for the favicon to load (defined in supercookies.html)
      await sleepMs(2000);
      let response = await fetch(
        testURI("ctr", "favicon", key), {"cache": "reload"});
      let count = (await response.text()).trim();
      if (count === "0") {
        throw new Error("No requests received");
      }
      return count;
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: (key) => key
read: async (key) => {
      // Wait for the favicon to load (defined in supercookies.html)
      await sleepMs(2000);
      let response = await fetch(
        testURI("ctr", "favicon", key), {"cache": "reload"});
      let count = (await response.text()).trim();
      if (count === "0") {
        throw new Error("No requests received");
      }
      return count;
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: (key) => key
read: async (key) => {
      // Wait for the favicon to load (defined in supercookies.html)
      await sleepMs(2000);
      let response = await fetch(
        testURI("ctr", "favicon", key), {"cache": "reload"});
      let count = (await response.text()).trim();
      if (count === "0") {
        throw new Error("No requests received");
      }
      return count;
    }
result, same session: 1, 1, 1
result, different session: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (key) => key
read: async (key) => {
      // Wait for the favicon to load (defined in supercookies.html)
      await sleepMs(2000);
      let response = await fetch(
        testURI("ctr", "favicon", key), {"cache": "reload"});
      let count = (await response.text()).trim();
      if (count === "0") {
        throw new Error("No requests received");
      }
      return count;
    }
result, same session: 1, 1, 1
result, different session: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (key) => key
read: async (key) => {
      // Wait for the favicon to load (defined in supercookies.html)
      await sleepMs(2000);
      let response = await fetch(
        testURI("ctr", "favicon", key), {"cache": "reload"});
      let count = (await response.text()).trim();
      if (count === "0") {
        throw new Error("No requests received");
      }
      return count;
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: (key) => key
read: async (key) => {
      // Wait for the favicon to load (defined in supercookies.html)
      await sleepMs(2000);
      let response = await fetch(
        testURI("ctr", "favicon", key), {"cache": "reload"});
      let count = (await response.text()).trim();
      if (count === "0") {
        throw new Error("No requests received");
      }
      return count;
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: (key) => key
read: async (key) => {
      // Wait for the favicon to load (defined in supercookies.html)
      await sleepMs(2000);
      let response = await fetch(
        testURI("ctr", "favicon", key), {"cache": "reload"});
      let count = (await response.text()).trim();
      if (count === "0") {
        throw new Error("No requests received");
      }
      return count;
    }
result, same session: 1, 1, 1
result, different session: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (key) => key
read: async (key) => {
      // Wait for the favicon to load (defined in supercookies.html)
      await sleepMs(2000);
      let response = await fetch(
        testURI("ctr", "favicon", key), {"cache": "reload"});
      let count = (await response.text()).trim();
      if (count === "0") {
        throw new Error("No requests received");
      }
      return count;
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: (key) => key
read: async (key) => {
      // Wait for the favicon to load (defined in supercookies.html)
      await sleepMs(2000);
      let response = await fetch(
        testURI("ctr", "favicon", key), {"cache": "reload"});
      let count = (await response.text()).trim();
      if (count === "0") {
        throw new Error("No requests received");
      }
      return count;
    }
result, same session: 2, 2
result, different session: 2, 2
unsupported: false, false
passed: false, false
test failed: false, false | 
| fetch cache When a resource is received via the Fetch API, it is frequently cached. That cache can potentially be abused for cross-site tracking.
               | write: async (key) => {
      let response = await fetch(testURI("resource", "fetch", key),
                                 {cache: "force-cache"});
      return key;
    }
read: async (key) => {
      let response = await fetch(testURI("resource", "fetch", key),
                                 {cache: "force-cache"});
      let countResponse = await fetch(testURI("ctr", "fetch", key),
                                      {cache: "reload"});
      return (await countResponse.text()).trim();
    }
result, same session: 1, 1, 1
result, different session: 1, 1, 1
unsupported: false, false, false
passed: false, false, false
test failed: false, false, false | write: async (key) => {
      let response = await fetch(testURI("resource", "fetch", key),
                                 {cache: "force-cache"});
      return key;
    }
read: async (key) => {
      let response = await fetch(testURI("resource", "fetch", key),
                                 {cache: "force-cache"});
      let countResponse = await fetch(testURI("ctr", "fetch", key),
                                      {cache: "reload"});
      return (await countResponse.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: async (key) => {
      let response = await fetch(testURI("resource", "fetch", key),
                                 {cache: "force-cache"});
      return key;
    }
read: async (key) => {
      let response = await fetch(testURI("resource", "fetch", key),
                                 {cache: "force-cache"});
      let countResponse = await fetch(testURI("ctr", "fetch", key),
                                      {cache: "reload"});
      return (await countResponse.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: async (key) => {
      let response = await fetch(testURI("resource", "fetch", key),
                                 {cache: "force-cache"});
      return key;
    }
read: async (key) => {
      let response = await fetch(testURI("resource", "fetch", key),
                                 {cache: "force-cache"});
      let countResponse = await fetch(testURI("ctr", "fetch", key),
                                      {cache: "reload"});
      return (await countResponse.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: async (key) => {
      let response = await fetch(testURI("resource", "fetch", key),
                                 {cache: "force-cache"});
      return key;
    }
read: async (key) => {
      let response = await fetch(testURI("resource", "fetch", key),
                                 {cache: "force-cache"});
      let countResponse = await fetch(testURI("ctr", "fetch", key),
                                      {cache: "reload"});
      return (await countResponse.text()).trim();
    }
result, same session: 1, 1, 1
result, different session: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (key) => {
      let response = await fetch(testURI("resource", "fetch", key),
                                 {cache: "force-cache"});
      return key;
    }
read: async (key) => {
      let response = await fetch(testURI("resource", "fetch", key),
                                 {cache: "force-cache"});
      let countResponse = await fetch(testURI("ctr", "fetch", key),
                                      {cache: "reload"});
      return (await countResponse.text()).trim();
    }
result, same session: 1, 1, 1
result, different session: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (key) => {
      let response = await fetch(testURI("resource", "fetch", key),
                                 {cache: "force-cache"});
      return key;
    }
read: async (key) => {
      let response = await fetch(testURI("resource", "fetch", key),
                                 {cache: "force-cache"});
      let countResponse = await fetch(testURI("ctr", "fetch", key),
                                      {cache: "reload"});
      return (await countResponse.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: async (key) => {
      let response = await fetch(testURI("resource", "fetch", key),
                                 {cache: "force-cache"});
      return key;
    }
read: async (key) => {
      let response = await fetch(testURI("resource", "fetch", key),
                                 {cache: "force-cache"});
      let countResponse = await fetch(testURI("ctr", "fetch", key),
                                      {cache: "reload"});
      return (await countResponse.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: async (key) => {
      let response = await fetch(testURI("resource", "fetch", key),
                                 {cache: "force-cache"});
      return key;
    }
read: async (key) => {
      let response = await fetch(testURI("resource", "fetch", key),
                                 {cache: "force-cache"});
      let countResponse = await fetch(testURI("ctr", "fetch", key),
                                      {cache: "reload"});
      return (await countResponse.text()).trim();
    }
result, same session: 1, 1, 1
result, different session: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (key) => {
      let response = await fetch(testURI("resource", "fetch", key),
                                 {cache: "force-cache"});
      return key;
    }
read: async (key) => {
      let response = await fetch(testURI("resource", "fetch", key),
                                 {cache: "force-cache"});
      let countResponse = await fetch(testURI("ctr", "fetch", key),
                                      {cache: "reload"});
      return (await countResponse.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: async (key) => {
      let response = await fetch(testURI("resource", "fetch", key),
                                 {cache: "force-cache"});
      return key;
    }
read: async (key) => {
      let response = await fetch(testURI("resource", "fetch", key),
                                 {cache: "force-cache"});
      let countResponse = await fetch(testURI("ctr", "fetch", key),
                                      {cache: "reload"});
      return (await countResponse.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | 
| font cache Web fonts are sometimes stored in their own cache, which is vulnerable to being abused for cross-site tracking.
               | write: async (key) => {
      let style = document.createElement("style");
      style.type='text/css';
      let fontURI = testURI("resource", "font", key);
      style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } body { font-family: "myFont" }`;
      document.getElementsByTagName("head")[0].appendChild(style);
      return key;
    }
read: async (key) => {
      const text = document.createElement("span");
      text.id = "text";
      text.innerText = "test";
      document.body.appendChild(text);
      const originalWidth = text.getBoundingClientRect().width;
      let style = document.createElement("style");
      style.type='text/css';
      let fontURI = testURI("resource", "font", key);
      style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } #text { font-family: "myFont" }`;
      document.getElementsByTagName("head")[0].appendChild(style);
      let newWidth;
      do {
        await sleepMs(100);
        newWidth = text.getBoundingClientRect().width;
      } while (newWidth < 0 || newWidth === originalWidth)
      let response = await fetch(
        testURI("ctr", "font", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1, 1
result, different session: 1, 1, 1
unsupported: false, false, false
passed: false, false, false
test failed: false, false, false | write: async (key) => {
      let style = document.createElement("style");
      style.type='text/css';
      let fontURI = testURI("resource", "font", key);
      style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } body { font-family: "myFont" }`;
      document.getElementsByTagName("head")[0].appendChild(style);
      return key;
    }
read: async (key) => {
      const text = document.createElement("span");
      text.id = "text";
      text.innerText = "test";
      document.body.appendChild(text);
      const originalWidth = text.getBoundingClientRect().width;
      let style = document.createElement("style");
      style.type='text/css';
      let fontURI = testURI("resource", "font", key);
      style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } #text { font-family: "myFont" }`;
      document.getElementsByTagName("head")[0].appendChild(style);
      let newWidth;
      do {
        await sleepMs(100);
        newWidth = text.getBoundingClientRect().width;
      } while (newWidth < 0 || newWidth === originalWidth)
      let response = await fetch(
        testURI("ctr", "font", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: async (key) => {
      let style = document.createElement("style");
      style.type='text/css';
      let fontURI = testURI("resource", "font", key);
      style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } body { font-family: "myFont" }`;
      document.getElementsByTagName("head")[0].appendChild(style);
      return key;
    }
read: async (key) => {
      const text = document.createElement("span");
      text.id = "text";
      text.innerText = "test";
      document.body.appendChild(text);
      const originalWidth = text.getBoundingClientRect().width;
      let style = document.createElement("style");
      style.type='text/css';
      let fontURI = testURI("resource", "font", key);
      style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } #text { font-family: "myFont" }`;
      document.getElementsByTagName("head")[0].appendChild(style);
      let newWidth;
      do {
        await sleepMs(100);
        newWidth = text.getBoundingClientRect().width;
      } while (newWidth < 0 || newWidth === originalWidth)
      let response = await fetch(
        testURI("ctr", "font", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: async (key) => {
      let style = document.createElement("style");
      style.type='text/css';
      let fontURI = testURI("resource", "font", key);
      style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } body { font-family: "myFont" }`;
      document.getElementsByTagName("head")[0].appendChild(style);
      return key;
    }
read: async (key) => {
      const text = document.createElement("span");
      text.id = "text";
      text.innerText = "test";
      document.body.appendChild(text);
      const originalWidth = text.getBoundingClientRect().width;
      let style = document.createElement("style");
      style.type='text/css';
      let fontURI = testURI("resource", "font", key);
      style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } #text { font-family: "myFont" }`;
      document.getElementsByTagName("head")[0].appendChild(style);
      let newWidth;
      do {
        await sleepMs(100);
        newWidth = text.getBoundingClientRect().width;
      } while (newWidth < 0 || newWidth === originalWidth)
      let response = await fetch(
        testURI("ctr", "font", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: async (key) => {
      let style = document.createElement("style");
      style.type='text/css';
      let fontURI = testURI("resource", "font", key);
      style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } body { font-family: "myFont" }`;
      document.getElementsByTagName("head")[0].appendChild(style);
      return key;
    }
read: async (key) => {
      const text = document.createElement("span");
      text.id = "text";
      text.innerText = "test";
      document.body.appendChild(text);
      const originalWidth = text.getBoundingClientRect().width;
      let style = document.createElement("style");
      style.type='text/css';
      let fontURI = testURI("resource", "font", key);
      style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } #text { font-family: "myFont" }`;
      document.getElementsByTagName("head")[0].appendChild(style);
      let newWidth;
      do {
        await sleepMs(100);
        newWidth = text.getBoundingClientRect().width;
      } while (newWidth < 0 || newWidth === originalWidth)
      let response = await fetch(
        testURI("ctr", "font", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1, 1
result, different session: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (key) => {
      let style = document.createElement("style");
      style.type='text/css';
      let fontURI = testURI("resource", "font", key);
      style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } body { font-family: "myFont" }`;
      document.getElementsByTagName("head")[0].appendChild(style);
      return key;
    }
read: async (key) => {
      const text = document.createElement("span");
      text.id = "text";
      text.innerText = "test";
      document.body.appendChild(text);
      const originalWidth = text.getBoundingClientRect().width;
      let style = document.createElement("style");
      style.type='text/css';
      let fontURI = testURI("resource", "font", key);
      style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } #text { font-family: "myFont" }`;
      document.getElementsByTagName("head")[0].appendChild(style);
      let newWidth;
      do {
        await sleepMs(100);
        newWidth = text.getBoundingClientRect().width;
      } while (newWidth < 0 || newWidth === originalWidth)
      let response = await fetch(
        testURI("ctr", "font", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1, 1
result, different session: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (key) => {
      let style = document.createElement("style");
      style.type='text/css';
      let fontURI = testURI("resource", "font", key);
      style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } body { font-family: "myFont" }`;
      document.getElementsByTagName("head")[0].appendChild(style);
      return key;
    }
read: async (key) => {
      const text = document.createElement("span");
      text.id = "text";
      text.innerText = "test";
      document.body.appendChild(text);
      const originalWidth = text.getBoundingClientRect().width;
      let style = document.createElement("style");
      style.type='text/css';
      let fontURI = testURI("resource", "font", key);
      style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } #text { font-family: "myFont" }`;
      document.getElementsByTagName("head")[0].appendChild(style);
      let newWidth;
      do {
        await sleepMs(100);
        newWidth = text.getBoundingClientRect().width;
      } while (newWidth < 0 || newWidth === originalWidth)
      let response = await fetch(
        testURI("ctr", "font", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: async (key) => {
      let style = document.createElement("style");
      style.type='text/css';
      let fontURI = testURI("resource", "font", key);
      style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } body { font-family: "myFont" }`;
      document.getElementsByTagName("head")[0].appendChild(style);
      return key;
    }
read: async (key) => {
      const text = document.createElement("span");
      text.id = "text";
      text.innerText = "test";
      document.body.appendChild(text);
      const originalWidth = text.getBoundingClientRect().width;
      let style = document.createElement("style");
      style.type='text/css';
      let fontURI = testURI("resource", "font", key);
      style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } #text { font-family: "myFont" }`;
      document.getElementsByTagName("head")[0].appendChild(style);
      let newWidth;
      do {
        await sleepMs(100);
        newWidth = text.getBoundingClientRect().width;
      } while (newWidth < 0 || newWidth === originalWidth)
      let response = await fetch(
        testURI("ctr", "font", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: async (key) => {
      let style = document.createElement("style");
      style.type='text/css';
      let fontURI = testURI("resource", "font", key);
      style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } body { font-family: "myFont" }`;
      document.getElementsByTagName("head")[0].appendChild(style);
      return key;
    }
read: async (key) => {
      const text = document.createElement("span");
      text.id = "text";
      text.innerText = "test";
      document.body.appendChild(text);
      const originalWidth = text.getBoundingClientRect().width;
      let style = document.createElement("style");
      style.type='text/css';
      let fontURI = testURI("resource", "font", key);
      style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } #text { font-family: "myFont" }`;
      document.getElementsByTagName("head")[0].appendChild(style);
      let newWidth;
      do {
        await sleepMs(100);
        newWidth = text.getBoundingClientRect().width;
      } while (newWidth < 0 || newWidth === originalWidth)
      let response = await fetch(
        testURI("ctr", "font", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1, 1
result, different session: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (key) => {
      let style = document.createElement("style");
      style.type='text/css';
      let fontURI = testURI("resource", "font", key);
      style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } body { font-family: "myFont" }`;
      document.getElementsByTagName("head")[0].appendChild(style);
      return key;
    }
read: async (key) => {
      const text = document.createElement("span");
      text.id = "text";
      text.innerText = "test";
      document.body.appendChild(text);
      const originalWidth = text.getBoundingClientRect().width;
      let style = document.createElement("style");
      style.type='text/css';
      let fontURI = testURI("resource", "font", key);
      style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } #text { font-family: "myFont" }`;
      document.getElementsByTagName("head")[0].appendChild(style);
      let newWidth;
      do {
        await sleepMs(100);
        newWidth = text.getBoundingClientRect().width;
      } while (newWidth < 0 || newWidth === originalWidth)
      let response = await fetch(
        testURI("ctr", "font", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: async (key) => {
      let style = document.createElement("style");
      style.type='text/css';
      let fontURI = testURI("resource", "font", key);
      style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } body { font-family: "myFont" }`;
      document.getElementsByTagName("head")[0].appendChild(style);
      return key;
    }
read: async (key) => {
      const text = document.createElement("span");
      text.id = "text";
      text.innerText = "test";
      document.body.appendChild(text);
      const originalWidth = text.getBoundingClientRect().width;
      let style = document.createElement("style");
      style.type='text/css';
      let fontURI = testURI("resource", "font", key);
      style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } #text { font-family: "myFont" }`;
      document.getElementsByTagName("head")[0].appendChild(style);
      let newWidth;
      do {
        await sleepMs(100);
        newWidth = text.getBoundingClientRect().width;
      } while (newWidth < 0 || newWidth === originalWidth)
      let response = await fetch(
        testURI("ctr", "font", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | 
| iframe cache An iframe is an element in a web page than allows websites to embed a second web page. Caching of this web page could be abused for cross-site tracking.
               | write: (key) => new Promise((resolve, reject) => {
      let iframe = document.createElement("iframe");
      document.body.appendChild(iframe);
      iframe.addEventListener("load", () => resolve(key), {once: true});
      iframe.src = testURI("resource", "page", key);
    })
read: async (key) => {
      let iframe = document.createElement("iframe");
      document.body.appendChild(iframe);
      let iframeLoadPromise = new Promise((resolve, reject) => {
        iframe.addEventListener("load", resolve, {once: true});
      });
      let address = testURI("resource", "page", key);
      iframe.src = address;
      await iframeLoadPromise;
      let response = await fetch(
        testURI("ctr", "page", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1, 1
result, different session: 1, 1, 1
unsupported: false, false, false
passed: false, false, false
test failed: false, false, false | write: (key) => new Promise((resolve, reject) => {
      let iframe = document.createElement("iframe");
      document.body.appendChild(iframe);
      iframe.addEventListener("load", () => resolve(key), {once: true});
      iframe.src = testURI("resource", "page", key);
    })
read: async (key) => {
      let iframe = document.createElement("iframe");
      document.body.appendChild(iframe);
      let iframeLoadPromise = new Promise((resolve, reject) => {
        iframe.addEventListener("load", resolve, {once: true});
      });
      let address = testURI("resource", "page", key);
      iframe.src = address;
      await iframeLoadPromise;
      let response = await fetch(
        testURI("ctr", "page", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: (key) => new Promise((resolve, reject) => {
      let iframe = document.createElement("iframe");
      document.body.appendChild(iframe);
      iframe.addEventListener("load", () => resolve(key), {once: true});
      iframe.src = testURI("resource", "page", key);
    })
read: async (key) => {
      let iframe = document.createElement("iframe");
      document.body.appendChild(iframe);
      let iframeLoadPromise = new Promise((resolve, reject) => {
        iframe.addEventListener("load", resolve, {once: true});
      });
      let address = testURI("resource", "page", key);
      iframe.src = address;
      await iframeLoadPromise;
      let response = await fetch(
        testURI("ctr", "page", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: (key) => new Promise((resolve, reject) => {
      let iframe = document.createElement("iframe");
      document.body.appendChild(iframe);
      iframe.addEventListener("load", () => resolve(key), {once: true});
      iframe.src = testURI("resource", "page", key);
    })
read: async (key) => {
      let iframe = document.createElement("iframe");
      document.body.appendChild(iframe);
      let iframeLoadPromise = new Promise((resolve, reject) => {
        iframe.addEventListener("load", resolve, {once: true});
      });
      let address = testURI("resource", "page", key);
      iframe.src = address;
      await iframeLoadPromise;
      let response = await fetch(
        testURI("ctr", "page", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: (key) => new Promise((resolve, reject) => {
      let iframe = document.createElement("iframe");
      document.body.appendChild(iframe);
      iframe.addEventListener("load", () => resolve(key), {once: true});
      iframe.src = testURI("resource", "page", key);
    })
read: async (key) => {
      let iframe = document.createElement("iframe");
      document.body.appendChild(iframe);
      let iframeLoadPromise = new Promise((resolve, reject) => {
        iframe.addEventListener("load", resolve, {once: true});
      });
      let address = testURI("resource", "page", key);
      iframe.src = address;
      await iframeLoadPromise;
      let response = await fetch(
        testURI("ctr", "page", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1, 1
result, different session: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (key) => new Promise((resolve, reject) => {
      let iframe = document.createElement("iframe");
      document.body.appendChild(iframe);
      iframe.addEventListener("load", () => resolve(key), {once: true});
      iframe.src = testURI("resource", "page", key);
    })
read: async (key) => {
      let iframe = document.createElement("iframe");
      document.body.appendChild(iframe);
      let iframeLoadPromise = new Promise((resolve, reject) => {
        iframe.addEventListener("load", resolve, {once: true});
      });
      let address = testURI("resource", "page", key);
      iframe.src = address;
      await iframeLoadPromise;
      let response = await fetch(
        testURI("ctr", "page", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1, 1
result, different session: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (key) => new Promise((resolve, reject) => {
      let iframe = document.createElement("iframe");
      document.body.appendChild(iframe);
      iframe.addEventListener("load", () => resolve(key), {once: true});
      iframe.src = testURI("resource", "page", key);
    })
read: async (key) => {
      let iframe = document.createElement("iframe");
      document.body.appendChild(iframe);
      let iframeLoadPromise = new Promise((resolve, reject) => {
        iframe.addEventListener("load", resolve, {once: true});
      });
      let address = testURI("resource", "page", key);
      iframe.src = address;
      await iframeLoadPromise;
      let response = await fetch(
        testURI("ctr", "page", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: (key) => new Promise((resolve, reject) => {
      let iframe = document.createElement("iframe");
      document.body.appendChild(iframe);
      iframe.addEventListener("load", () => resolve(key), {once: true});
      iframe.src = testURI("resource", "page", key);
    })
read: async (key) => {
      let iframe = document.createElement("iframe");
      document.body.appendChild(iframe);
      let iframeLoadPromise = new Promise((resolve, reject) => {
        iframe.addEventListener("load", resolve, {once: true});
      });
      let address = testURI("resource", "page", key);
      iframe.src = address;
      await iframeLoadPromise;
      let response = await fetch(
        testURI("ctr", "page", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: (key) => new Promise((resolve, reject) => {
      let iframe = document.createElement("iframe");
      document.body.appendChild(iframe);
      iframe.addEventListener("load", () => resolve(key), {once: true});
      iframe.src = testURI("resource", "page", key);
    })
read: async (key) => {
      let iframe = document.createElement("iframe");
      document.body.appendChild(iframe);
      let iframeLoadPromise = new Promise((resolve, reject) => {
        iframe.addEventListener("load", resolve, {once: true});
      });
      let address = testURI("resource", "page", key);
      iframe.src = address;
      await iframeLoadPromise;
      let response = await fetch(
        testURI("ctr", "page", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1, 1
result, different session: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (key) => new Promise((resolve, reject) => {
      let iframe = document.createElement("iframe");
      document.body.appendChild(iframe);
      iframe.addEventListener("load", () => resolve(key), {once: true});
      iframe.src = testURI("resource", "page", key);
    })
read: async (key) => {
      let iframe = document.createElement("iframe");
      document.body.appendChild(iframe);
      let iframeLoadPromise = new Promise((resolve, reject) => {
        iframe.addEventListener("load", resolve, {once: true});
      });
      let address = testURI("resource", "page", key);
      iframe.src = address;
      await iframeLoadPromise;
      let response = await fetch(
        testURI("ctr", "page", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: (key) => new Promise((resolve, reject) => {
      let iframe = document.createElement("iframe");
      document.body.appendChild(iframe);
      iframe.addEventListener("load", () => resolve(key), {once: true});
      iframe.src = testURI("resource", "page", key);
    })
read: async (key) => {
      let iframe = document.createElement("iframe");
      document.body.appendChild(iframe);
      let iframeLoadPromise = new Promise((resolve, reject) => {
        iframe.addEventListener("load", resolve, {once: true});
      });
      let address = testURI("resource", "page", key);
      iframe.src = address;
      await iframeLoadPromise;
      let response = await fetch(
        testURI("ctr", "page", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | 
| image cache Caching of images in web browsers is a standard behavior. But if that cache leaks between websites, it can be abused for cross-site tracking.
               | write: (key) => new Promise((resolve, reject) => {
      let img = document.createElement("img");
      document.body.appendChild(img);
      img.addEventListener("load", () => resolve(key), {once: true});
      img.src = testURI("resource", "image", key);
    })
read: async (key) => {
      let img = document.createElement("img");
      document.body.appendChild(img);
      let imgLoadPromise = new Promise((resolve, reject) => {
        img.addEventListener("load", resolve, {once: true});
      });
      img.src = testURI("resource", "image", key);
      await imgLoadPromise;
      let response = await fetch(
        testURI("ctr", "image", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1, 1
result, different session: 1, 1, 1
unsupported: false, false, false
passed: false, false, false
test failed: false, false, false | write: (key) => new Promise((resolve, reject) => {
      let img = document.createElement("img");
      document.body.appendChild(img);
      img.addEventListener("load", () => resolve(key), {once: true});
      img.src = testURI("resource", "image", key);
    })
read: async (key) => {
      let img = document.createElement("img");
      document.body.appendChild(img);
      let imgLoadPromise = new Promise((resolve, reject) => {
        img.addEventListener("load", resolve, {once: true});
      });
      img.src = testURI("resource", "image", key);
      await imgLoadPromise;
      let response = await fetch(
        testURI("ctr", "image", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: (key) => new Promise((resolve, reject) => {
      let img = document.createElement("img");
      document.body.appendChild(img);
      img.addEventListener("load", () => resolve(key), {once: true});
      img.src = testURI("resource", "image", key);
    })
read: async (key) => {
      let img = document.createElement("img");
      document.body.appendChild(img);
      let imgLoadPromise = new Promise((resolve, reject) => {
        img.addEventListener("load", resolve, {once: true});
      });
      img.src = testURI("resource", "image", key);
      await imgLoadPromise;
      let response = await fetch(
        testURI("ctr", "image", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: (key) => new Promise((resolve, reject) => {
      let img = document.createElement("img");
      document.body.appendChild(img);
      img.addEventListener("load", () => resolve(key), {once: true});
      img.src = testURI("resource", "image", key);
    })
read: async (key) => {
      let img = document.createElement("img");
      document.body.appendChild(img);
      let imgLoadPromise = new Promise((resolve, reject) => {
        img.addEventListener("load", resolve, {once: true});
      });
      img.src = testURI("resource", "image", key);
      await imgLoadPromise;
      let response = await fetch(
        testURI("ctr", "image", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: (key) => new Promise((resolve, reject) => {
      let img = document.createElement("img");
      document.body.appendChild(img);
      img.addEventListener("load", () => resolve(key), {once: true});
      img.src = testURI("resource", "image", key);
    })
read: async (key) => {
      let img = document.createElement("img");
      document.body.appendChild(img);
      let imgLoadPromise = new Promise((resolve, reject) => {
        img.addEventListener("load", resolve, {once: true});
      });
      img.src = testURI("resource", "image", key);
      await imgLoadPromise;
      let response = await fetch(
        testURI("ctr", "image", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1, 1
result, different session: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (key) => new Promise((resolve, reject) => {
      let img = document.createElement("img");
      document.body.appendChild(img);
      img.addEventListener("load", () => resolve(key), {once: true});
      img.src = testURI("resource", "image", key);
    })
read: async (key) => {
      let img = document.createElement("img");
      document.body.appendChild(img);
      let imgLoadPromise = new Promise((resolve, reject) => {
        img.addEventListener("load", resolve, {once: true});
      });
      img.src = testURI("resource", "image", key);
      await imgLoadPromise;
      let response = await fetch(
        testURI("ctr", "image", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1, 1
result, different session: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (key) => new Promise((resolve, reject) => {
      let img = document.createElement("img");
      document.body.appendChild(img);
      img.addEventListener("load", () => resolve(key), {once: true});
      img.src = testURI("resource", "image", key);
    })
read: async (key) => {
      let img = document.createElement("img");
      document.body.appendChild(img);
      let imgLoadPromise = new Promise((resolve, reject) => {
        img.addEventListener("load", resolve, {once: true});
      });
      img.src = testURI("resource", "image", key);
      await imgLoadPromise;
      let response = await fetch(
        testURI("ctr", "image", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: (key) => new Promise((resolve, reject) => {
      let img = document.createElement("img");
      document.body.appendChild(img);
      img.addEventListener("load", () => resolve(key), {once: true});
      img.src = testURI("resource", "image", key);
    })
read: async (key) => {
      let img = document.createElement("img");
      document.body.appendChild(img);
      let imgLoadPromise = new Promise((resolve, reject) => {
        img.addEventListener("load", resolve, {once: true});
      });
      img.src = testURI("resource", "image", key);
      await imgLoadPromise;
      let response = await fetch(
        testURI("ctr", "image", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: (key) => new Promise((resolve, reject) => {
      let img = document.createElement("img");
      document.body.appendChild(img);
      img.addEventListener("load", () => resolve(key), {once: true});
      img.src = testURI("resource", "image", key);
    })
read: async (key) => {
      let img = document.createElement("img");
      document.body.appendChild(img);
      let imgLoadPromise = new Promise((resolve, reject) => {
        img.addEventListener("load", resolve, {once: true});
      });
      img.src = testURI("resource", "image", key);
      await imgLoadPromise;
      let response = await fetch(
        testURI("ctr", "image", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1, 1
result, different session: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (key) => new Promise((resolve, reject) => {
      let img = document.createElement("img");
      document.body.appendChild(img);
      img.addEventListener("load", () => resolve(key), {once: true});
      img.src = testURI("resource", "image", key);
    })
read: async (key) => {
      let img = document.createElement("img");
      document.body.appendChild(img);
      let imgLoadPromise = new Promise((resolve, reject) => {
        img.addEventListener("load", resolve, {once: true});
      });
      img.src = testURI("resource", "image", key);
      await imgLoadPromise;
      let response = await fetch(
        testURI("ctr", "image", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: (key) => new Promise((resolve, reject) => {
      let img = document.createElement("img");
      document.body.appendChild(img);
      img.addEventListener("load", () => resolve(key), {once: true});
      img.src = testURI("resource", "image", key);
    })
read: async (key) => {
      let img = document.createElement("img");
      document.body.appendChild(img);
      let imgLoadPromise = new Promise((resolve, reject) => {
        img.addEventListener("load", resolve, {once: true});
      });
      img.src = testURI("resource", "image", key);
      await imgLoadPromise;
      let response = await fetch(
        testURI("ctr", "image", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | 
| indexedDB The IndexedDB API exposes a transactional database to web pages. That database can be used to track users across websites, unless it is partitioned.
               | write: async (secret) => {
      try {
        return await IdbKeyVal.set("secret", secret);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: () => IdbKeyVal.get("secret")
result, same session: 
3133a4ca-686b-4047-91c3-9a7e181c53f7_1p,
49872d4a-b01d-4af8-8cbe-85c379a609e2_1p,
58bb2b32-4f83-47bb-91c8-96dce39cd97a_1p
result, different session: 
3133a4ca-686b-4047-91c3-9a7e181c53f7_1p,
49872d4a-b01d-4af8-8cbe-85c379a609e2_1p,
58bb2b32-4f83-47bb-91c8-96dce39cd97a_1p
unsupported: false, false, false
passed: false, false, false
test failed: false, false, false | write: async (secret) => {
      try {
        return await IdbKeyVal.set("secret", secret);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: () => IdbKeyVal.get("secret")
result, same session: 
e8485c10-5a92-474a-bd16-de23f46ee0e2_1p,
cd5b479f-1529-4f43-a586-810ed83aeb8b_1p
result, different session: 
e8485c10-5a92-474a-bd16-de23f46ee0e2_1p,
cd5b479f-1529-4f43-a586-810ed83aeb8b_1p
unsupported: false, false
passed: false, false
test failed: false, false | write: async (secret) => {
      try {
        return await IdbKeyVal.set("secret", secret);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: () => IdbKeyVal.get("secret")
result, same session: 
e96973da-bf72-4eb1-b6ab-a4e6c6775fb4_1p,
67fb6941-7c51-4727-b485-317211e65ae0_1p
result, different session: 
e96973da-bf72-4eb1-b6ab-a4e6c6775fb4_1p,
67fb6941-7c51-4727-b485-317211e65ae0_1p
unsupported: false, false
passed: false, false
test failed: false, false | write: async (secret) => {
      try {
        return await IdbKeyVal.set("secret", secret);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: () => IdbKeyVal.get("secret")
result, same session: 
94f1fd6d-f642-403e-8ef8-11180b18b1f6_1p,
2bf6571e-4385-4b68-a076-f6868cbe4954_1p
result, different session: 
94f1fd6d-f642-403e-8ef8-11180b18b1f6_1p,
2bf6571e-4385-4b68-a076-f6868cbe4954_1p
unsupported: false, false
passed: false, false
test failed: false, false | write: async (secret) => {
      try {
        return await IdbKeyVal.set("secret", secret);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: () => IdbKeyVal.get("secret")
result, same session: 
da67bebb-5652-441a-816e-1206b25c0225_1p,
0bad71fe-de93-4434-bfa0-bb7f0db62eea_1p,
6aba94ed-010e-45af-892c-dd670cd8d134_1p
result, different session: undefined
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (secret) => {
      try {
        return await IdbKeyVal.set("secret", secret);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: () => IdbKeyVal.get("secret")
result, same session: 
9dd63e63-3256-4352-99f6-805b6f48eb8c_1p,
b7cb77db-aca9-4570-923e-b81da9175421_1p,
736f05a1-05e6-491e-8dad-a6647bd52dda_1p
result, different session: undefined
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (secret) => {
      try {
        return await IdbKeyVal.set("secret", secret);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: () => IdbKeyVal.get("secret")
result, same session: 
50c6a369-0b5c-4de5-a70b-a0516825119e_1p,
49e0d8b1-c126-4b6b-be66-6ba7acfd85a9_1p
result, different session: 
50c6a369-0b5c-4de5-a70b-a0516825119e_1p,
49e0d8b1-c126-4b6b-be66-6ba7acfd85a9_1p
unsupported: false, false
passed: false, false
test failed: false, false | write: async (secret) => {
      try {
        return await IdbKeyVal.set("secret", secret);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: () => IdbKeyVal.get("secret")
result, same session: 
6b07cf4a-a332-4a7c-90df-fbc3dc862f86_1p,
2d40813a-a6c5-4a6b-bb1c-f4d01a399217_1p
result, different session: undefined
unsupported: false, false
passed: true, true
test failed: false, false | write: async (secret) => {
      try {
        return await IdbKeyVal.set("secret", secret);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: () => IdbKeyVal.get("secret")
result, same session: 
3dc5c317-3ea7-42b4-be71-7902bcc4eb27_1p,
4932f81b-b746-4e4b-b97d-c0b827047888_1p,
3e016f09-5749-4084-a136-e9b829c1a824_1p
result, different session: undefined
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (secret) => {
      try {
        return await IdbKeyVal.set("secret", secret);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: () => IdbKeyVal.get("secret")
result, same session: 
3242a94e-3344-4cb9-ba7c-ebedaf5d6e01_1p,
87918123-fc52-4196-9d0c-a2cfc3f826d4_1p
result, different session: 
3242a94e-3344-4cb9-ba7c-ebedaf5d6e01_1p,
87918123-fc52-4196-9d0c-a2cfc3f826d4_1p
unsupported: false, false
passed: false, false
test failed: false, false | write: async (secret) => {
      try {
        return await IdbKeyVal.set("secret", secret);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: () => IdbKeyVal.get("secret")
result, same session: 
5c052838-a55e-4690-83c6-b7d572146713_1p,
49042d99-5b0e-4f37-82f3-78de245eea34_1p
result, different session: 
5c052838-a55e-4690-83c6-b7d572146713_1p,
49042d99-5b0e-4f37-82f3-78de245eea34_1p
unsupported: false, false
passed: false, false
test failed: false, false | 
| localStorage The localStorage API gives websites access to a key-value database that will remain available across visits. If the localStorage API is not partitioned or blocked, it can also be used to track users across websites.
               | write: (secret) => localStorage.setItem("secret", secret)
read: () => localStorage.getItem("secret")
result, same session: 
3133a4ca-686b-4047-91c3-9a7e181c53f7_1p,
49872d4a-b01d-4af8-8cbe-85c379a609e2_1p,
58bb2b32-4f83-47bb-91c8-96dce39cd97a_1p
result, different session: 
3133a4ca-686b-4047-91c3-9a7e181c53f7_1p,
49872d4a-b01d-4af8-8cbe-85c379a609e2_1p,
58bb2b32-4f83-47bb-91c8-96dce39cd97a_1p
unsupported: false, false, false
passed: false, false, false
test failed: false, false, false | write: (secret) => localStorage.setItem("secret", secret)
read: () => localStorage.getItem("secret")
result, same session: 
e8485c10-5a92-474a-bd16-de23f46ee0e2_1p,
cd5b479f-1529-4f43-a586-810ed83aeb8b_1p
result, different session: 
e8485c10-5a92-474a-bd16-de23f46ee0e2_1p,
cd5b479f-1529-4f43-a586-810ed83aeb8b_1p
unsupported: false, false
passed: false, false
test failed: false, false | write: (secret) => localStorage.setItem("secret", secret)
read: () => localStorage.getItem("secret")
result, same session: 
e96973da-bf72-4eb1-b6ab-a4e6c6775fb4_1p,
67fb6941-7c51-4727-b485-317211e65ae0_1p
result, different session: 
e96973da-bf72-4eb1-b6ab-a4e6c6775fb4_1p,
67fb6941-7c51-4727-b485-317211e65ae0_1p
unsupported: false, false
passed: false, false
test failed: false, false | write: (secret) => localStorage.setItem("secret", secret)
read: () => localStorage.getItem("secret")
result, same session: 
94f1fd6d-f642-403e-8ef8-11180b18b1f6_1p,
2bf6571e-4385-4b68-a076-f6868cbe4954_1p
result, different session: 
94f1fd6d-f642-403e-8ef8-11180b18b1f6_1p,
2bf6571e-4385-4b68-a076-f6868cbe4954_1p
unsupported: false, false
passed: false, false
test failed: false, false | write: (secret) => localStorage.setItem("secret", secret)
read: () => localStorage.getItem("secret")
result, same session: 
da67bebb-5652-441a-816e-1206b25c0225_1p,
0bad71fe-de93-4434-bfa0-bb7f0db62eea_1p,
6aba94ed-010e-45af-892c-dd670cd8d134_1p
result, different session: , , 
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (secret) => localStorage.setItem("secret", secret)
read: () => localStorage.getItem("secret")
result, same session: 
9dd63e63-3256-4352-99f6-805b6f48eb8c_1p,
b7cb77db-aca9-4570-923e-b81da9175421_1p,
736f05a1-05e6-491e-8dad-a6647bd52dda_1p
result, different session: , , 
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (secret) => localStorage.setItem("secret", secret)
read: () => localStorage.getItem("secret")
result, same session: 
50c6a369-0b5c-4de5-a70b-a0516825119e_1p,
49e0d8b1-c126-4b6b-be66-6ba7acfd85a9_1p
result, different session: 
50c6a369-0b5c-4de5-a70b-a0516825119e_1p,
49e0d8b1-c126-4b6b-be66-6ba7acfd85a9_1p
unsupported: false, false
passed: false, false
test failed: false, false | write: (secret) => localStorage.setItem("secret", secret)
read: () => localStorage.getItem("secret")
result, same session: 
6b07cf4a-a332-4a7c-90df-fbc3dc862f86_1p,
2d40813a-a6c5-4a6b-bb1c-f4d01a399217_1p
result, different session: , 
unsupported: false, false
passed: true, true
test failed: false, false | write: (secret) => localStorage.setItem("secret", secret)
read: () => localStorage.getItem("secret")
result, same session: 
3dc5c317-3ea7-42b4-be71-7902bcc4eb27_1p,
4932f81b-b746-4e4b-b97d-c0b827047888_1p,
3e016f09-5749-4084-a136-e9b829c1a824_1p
result, different session: , , 
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (secret) => localStorage.setItem("secret", secret)
read: () => localStorage.getItem("secret")
result, same session: 
3242a94e-3344-4cb9-ba7c-ebedaf5d6e01_1p,
87918123-fc52-4196-9d0c-a2cfc3f826d4_1p
result, different session: 
3242a94e-3344-4cb9-ba7c-ebedaf5d6e01_1p,
87918123-fc52-4196-9d0c-a2cfc3f826d4_1p
unsupported: false, false
passed: false, false
test failed: false, false | write: (secret) => localStorage.setItem("secret", secret)
read: () => localStorage.getItem("secret")
result, same session: 
5c052838-a55e-4690-83c6-b7d572146713_1p,
49042d99-5b0e-4f37-82f3-78de245eea34_1p
result, different session: 
5c052838-a55e-4690-83c6-b7d572146713_1p,
49042d99-5b0e-4f37-82f3-78de245eea34_1p
unsupported: false, false
passed: false, false
test failed: false, false | 
| prefetch cache A <link rel='prefetch'...> suggests to browsers they should fetch a resource ahead of time and cache it. But if browsers don't partition this cache, it can be used to track users across websites.
               | write: async (key) => {
      let link = document.createElement("link");
      link.rel = "prefetch";
      link.href = testURI("resource", "prefetch", key);
      document.getElementsByTagName("head")[0].appendChild(link);
      return key;
    }
read: async (key) => {
      let link = document.createElement("link");
      link.rel = "prefetch";
      link.href = testURI("resource", "prefetch", key);
      document.getElementsByTagName("head")[0].appendChild(link);
      await sleepMs(500);
      let response = await fetch(
        testURI("ctr", "prefetch", key), {"cache": "reload"});
      let countString = (await response.text()).trim();
      if (parseInt(countString) === 0) {
        throw new Error("No requests received");
      }
      return countString;
    }
result, same session: 1, 1, 1
result, different session: 1, 1, 1
unsupported: false, false, false
passed: false, false, false
test failed: false, false, false | write: async (key) => {
      let link = document.createElement("link");
      link.rel = "prefetch";
      link.href = testURI("resource", "prefetch", key);
      document.getElementsByTagName("head")[0].appendChild(link);
      return key;
    }
read: async (key) => {
      let link = document.createElement("link");
      link.rel = "prefetch";
      link.href = testURI("resource", "prefetch", key);
      document.getElementsByTagName("head")[0].appendChild(link);
      await sleepMs(500);
      let response = await fetch(
        testURI("ctr", "prefetch", key), {"cache": "reload"});
      let countString = (await response.text()).trim();
      if (parseInt(countString) === 0) {
        throw new Error("No requests received");
      }
      return countString;
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: async (key) => {
      let link = document.createElement("link");
      link.rel = "prefetch";
      link.href = testURI("resource", "prefetch", key);
      document.getElementsByTagName("head")[0].appendChild(link);
      return key;
    }
read: async (key) => {
      let link = document.createElement("link");
      link.rel = "prefetch";
      link.href = testURI("resource", "prefetch", key);
      document.getElementsByTagName("head")[0].appendChild(link);
      await sleepMs(500);
      let response = await fetch(
        testURI("ctr", "prefetch", key), {"cache": "reload"});
      let countString = (await response.text()).trim();
      if (parseInt(countString) === 0) {
        throw new Error("No requests received");
      }
      return countString;
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: async (key) => {
      let link = document.createElement("link");
      link.rel = "prefetch";
      link.href = testURI("resource", "prefetch", key);
      document.getElementsByTagName("head")[0].appendChild(link);
      return key;
    }
read: async (key) => {
      let link = document.createElement("link");
      link.rel = "prefetch";
      link.href = testURI("resource", "prefetch", key);
      document.getElementsByTagName("head")[0].appendChild(link);
      await sleepMs(500);
      let response = await fetch(
        testURI("ctr", "prefetch", key), {"cache": "reload"});
      let countString = (await response.text()).trim();
      if (parseInt(countString) === 0) {
        throw new Error("No requests received");
      }
      return countString;
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: async (key) => {
      let link = document.createElement("link");
      link.rel = "prefetch";
      link.href = testURI("resource", "prefetch", key);
      document.getElementsByTagName("head")[0].appendChild(link);
      return key;
    }
read: async (key) => {
      let link = document.createElement("link");
      link.rel = "prefetch";
      link.href = testURI("resource", "prefetch", key);
      document.getElementsByTagName("head")[0].appendChild(link);
      await sleepMs(500);
      let response = await fetch(
        testURI("ctr", "prefetch", key), {"cache": "reload"});
      let countString = (await response.text()).trim();
      if (parseInt(countString) === 0) {
        throw new Error("No requests received");
      }
      return countString;
    }
result, same session: 
Error: No requests received,
Error: No requests received,
Error: No requests received
result, different session: 
Error: No requests received,
Error: No requests received,
Error: No requests received
unsupported: true, true, true
passed: undefined
test failed: false, false, false | write: async (key) => {
      let link = document.createElement("link");
      link.rel = "prefetch";
      link.href = testURI("resource", "prefetch", key);
      document.getElementsByTagName("head")[0].appendChild(link);
      return key;
    }
read: async (key) => {
      let link = document.createElement("link");
      link.rel = "prefetch";
      link.href = testURI("resource", "prefetch", key);
      document.getElementsByTagName("head")[0].appendChild(link);
      await sleepMs(500);
      let response = await fetch(
        testURI("ctr", "prefetch", key), {"cache": "reload"});
      let countString = (await response.text()).trim();
      if (parseInt(countString) === 0) {
        throw new Error("No requests received");
      }
      return countString;
    }
result, same session: 
Error: No requests received,
Error: No requests received,
Error: No requests received
result, different session: 
Error: No requests received,
Error: No requests received,
Error: No requests received
unsupported: true, true, true
passed: undefined
test failed: false, false, false | write: async (key) => {
      let link = document.createElement("link");
      link.rel = "prefetch";
      link.href = testURI("resource", "prefetch", key);
      document.getElementsByTagName("head")[0].appendChild(link);
      return key;
    }
read: async (key) => {
      let link = document.createElement("link");
      link.rel = "prefetch";
      link.href = testURI("resource", "prefetch", key);
      document.getElementsByTagName("head")[0].appendChild(link);
      await sleepMs(500);
      let response = await fetch(
        testURI("ctr", "prefetch", key), {"cache": "reload"});
      let countString = (await response.text()).trim();
      if (parseInt(countString) === 0) {
        throw new Error("No requests received");
      }
      return countString;
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: async (key) => {
      let link = document.createElement("link");
      link.rel = "prefetch";
      link.href = testURI("resource", "prefetch", key);
      document.getElementsByTagName("head")[0].appendChild(link);
      return key;
    }
read: async (key) => {
      let link = document.createElement("link");
      link.rel = "prefetch";
      link.href = testURI("resource", "prefetch", key);
      document.getElementsByTagName("head")[0].appendChild(link);
      await sleepMs(500);
      let response = await fetch(
        testURI("ctr", "prefetch", key), {"cache": "reload"});
      let countString = (await response.text()).trim();
      if (parseInt(countString) === 0) {
        throw new Error("No requests received");
      }
      return countString;
    }
result, same session: 
Error: No requests received,
Error: No requests received
result, different session: 
Error: No requests received,
Error: No requests received
unsupported: true, true
passed: undefined
test failed: false, false | write: async (key) => {
      let link = document.createElement("link");
      link.rel = "prefetch";
      link.href = testURI("resource", "prefetch", key);
      document.getElementsByTagName("head")[0].appendChild(link);
      return key;
    }
read: async (key) => {
      let link = document.createElement("link");
      link.rel = "prefetch";
      link.href = testURI("resource", "prefetch", key);
      document.getElementsByTagName("head")[0].appendChild(link);
      await sleepMs(500);
      let response = await fetch(
        testURI("ctr", "prefetch", key), {"cache": "reload"});
      let countString = (await response.text()).trim();
      if (parseInt(countString) === 0) {
        throw new Error("No requests received");
      }
      return countString;
    }
result, same session: 
Error: No requests received,
Error: No requests received,
Error: No requests received
result, different session: 
Error: No requests received,
Error: No requests received,
Error: No requests received
unsupported: true, true, true
passed: undefined
test failed: false, false, false | write: async (key) => {
      let link = document.createElement("link");
      link.rel = "prefetch";
      link.href = testURI("resource", "prefetch", key);
      document.getElementsByTagName("head")[0].appendChild(link);
      return key;
    }
read: async (key) => {
      let link = document.createElement("link");
      link.rel = "prefetch";
      link.href = testURI("resource", "prefetch", key);
      document.getElementsByTagName("head")[0].appendChild(link);
      await sleepMs(500);
      let response = await fetch(
        testURI("ctr", "prefetch", key), {"cache": "reload"});
      let countString = (await response.text()).trim();
      if (parseInt(countString) === 0) {
        throw new Error("No requests received");
      }
      return countString;
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: async (key) => {
      let link = document.createElement("link");
      link.rel = "prefetch";
      link.href = testURI("resource", "prefetch", key);
      document.getElementsByTagName("head")[0].appendChild(link);
      return key;
    }
read: async (key) => {
      let link = document.createElement("link");
      link.rel = "prefetch";
      link.href = testURI("resource", "prefetch", key);
      document.getElementsByTagName("head")[0].appendChild(link);
      await sleepMs(500);
      let response = await fetch(
        testURI("ctr", "prefetch", key), {"cache": "reload"});
      let countString = (await response.text()).trim();
      if (parseInt(countString) === 0) {
        throw new Error("No requests received");
      }
      return countString;
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | 
| script cache Caching of scripts in web browsers is a standard behavior. But if that cache leaks between websites, it can be abused for cross-site tracking.
               | write: (key) => new Promise((resolve, reject) => {
      let script = document.createElement("script");
      document.body.appendChild(script);
      script.addEventListener("load", () => resolve(key), {once: true});
      script.src = testURI("resource", "script", key);
    })
read: async (key) => {
      let script = document.createElement("script");
      document.body.appendChild(script);
      let scriptLoadPromise = new Promise((resolve, reject) => {
        script.addEventListener("load", resolve, {once: true});
      });
      script.src = testURI("resource", "script", key);
      await scriptLoadPromise;
      let response = await fetch(
        testURI("ctr", "script", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1, 1
result, different session: 1, 1, 1
unsupported: false, false, false
passed: false, false, false
test failed: false, false, false | write: (key) => new Promise((resolve, reject) => {
      let script = document.createElement("script");
      document.body.appendChild(script);
      script.addEventListener("load", () => resolve(key), {once: true});
      script.src = testURI("resource", "script", key);
    })
read: async (key) => {
      let script = document.createElement("script");
      document.body.appendChild(script);
      let scriptLoadPromise = new Promise((resolve, reject) => {
        script.addEventListener("load", resolve, {once: true});
      });
      script.src = testURI("resource", "script", key);
      await scriptLoadPromise;
      let response = await fetch(
        testURI("ctr", "script", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: (key) => new Promise((resolve, reject) => {
      let script = document.createElement("script");
      document.body.appendChild(script);
      script.addEventListener("load", () => resolve(key), {once: true});
      script.src = testURI("resource", "script", key);
    })
read: async (key) => {
      let script = document.createElement("script");
      document.body.appendChild(script);
      let scriptLoadPromise = new Promise((resolve, reject) => {
        script.addEventListener("load", resolve, {once: true});
      });
      script.src = testURI("resource", "script", key);
      await scriptLoadPromise;
      let response = await fetch(
        testURI("ctr", "script", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: (key) => new Promise((resolve, reject) => {
      let script = document.createElement("script");
      document.body.appendChild(script);
      script.addEventListener("load", () => resolve(key), {once: true});
      script.src = testURI("resource", "script", key);
    })
read: async (key) => {
      let script = document.createElement("script");
      document.body.appendChild(script);
      let scriptLoadPromise = new Promise((resolve, reject) => {
        script.addEventListener("load", resolve, {once: true});
      });
      script.src = testURI("resource", "script", key);
      await scriptLoadPromise;
      let response = await fetch(
        testURI("ctr", "script", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: (key) => new Promise((resolve, reject) => {
      let script = document.createElement("script");
      document.body.appendChild(script);
      script.addEventListener("load", () => resolve(key), {once: true});
      script.src = testURI("resource", "script", key);
    })
read: async (key) => {
      let script = document.createElement("script");
      document.body.appendChild(script);
      let scriptLoadPromise = new Promise((resolve, reject) => {
        script.addEventListener("load", resolve, {once: true});
      });
      script.src = testURI("resource", "script", key);
      await scriptLoadPromise;
      let response = await fetch(
        testURI("ctr", "script", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1, 1
result, different session: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (key) => new Promise((resolve, reject) => {
      let script = document.createElement("script");
      document.body.appendChild(script);
      script.addEventListener("load", () => resolve(key), {once: true});
      script.src = testURI("resource", "script", key);
    })
read: async (key) => {
      let script = document.createElement("script");
      document.body.appendChild(script);
      let scriptLoadPromise = new Promise((resolve, reject) => {
        script.addEventListener("load", resolve, {once: true});
      });
      script.src = testURI("resource", "script", key);
      await scriptLoadPromise;
      let response = await fetch(
        testURI("ctr", "script", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1, 1
result, different session: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (key) => new Promise((resolve, reject) => {
      let script = document.createElement("script");
      document.body.appendChild(script);
      script.addEventListener("load", () => resolve(key), {once: true});
      script.src = testURI("resource", "script", key);
    })
read: async (key) => {
      let script = document.createElement("script");
      document.body.appendChild(script);
      let scriptLoadPromise = new Promise((resolve, reject) => {
        script.addEventListener("load", resolve, {once: true});
      });
      script.src = testURI("resource", "script", key);
      await scriptLoadPromise;
      let response = await fetch(
        testURI("ctr", "script", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: (key) => new Promise((resolve, reject) => {
      let script = document.createElement("script");
      document.body.appendChild(script);
      script.addEventListener("load", () => resolve(key), {once: true});
      script.src = testURI("resource", "script", key);
    })
read: async (key) => {
      let script = document.createElement("script");
      document.body.appendChild(script);
      let scriptLoadPromise = new Promise((resolve, reject) => {
        script.addEventListener("load", resolve, {once: true});
      });
      script.src = testURI("resource", "script", key);
      await scriptLoadPromise;
      let response = await fetch(
        testURI("ctr", "script", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: (key) => new Promise((resolve, reject) => {
      let script = document.createElement("script");
      document.body.appendChild(script);
      script.addEventListener("load", () => resolve(key), {once: true});
      script.src = testURI("resource", "script", key);
    })
read: async (key) => {
      let script = document.createElement("script");
      document.body.appendChild(script);
      let scriptLoadPromise = new Promise((resolve, reject) => {
        script.addEventListener("load", resolve, {once: true});
      });
      script.src = testURI("resource", "script", key);
      await scriptLoadPromise;
      let response = await fetch(
        testURI("ctr", "script", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1, 1
result, different session: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (key) => new Promise((resolve, reject) => {
      let script = document.createElement("script");
      document.body.appendChild(script);
      script.addEventListener("load", () => resolve(key), {once: true});
      script.src = testURI("resource", "script", key);
    })
read: async (key) => {
      let script = document.createElement("script");
      document.body.appendChild(script);
      let scriptLoadPromise = new Promise((resolve, reject) => {
        script.addEventListener("load", resolve, {once: true});
      });
      script.src = testURI("resource", "script", key);
      await scriptLoadPromise;
      let response = await fetch(
        testURI("ctr", "script", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: (key) => new Promise((resolve, reject) => {
      let script = document.createElement("script");
      document.body.appendChild(script);
      script.addEventListener("load", () => resolve(key), {once: true});
      script.src = testURI("resource", "script", key);
    })
read: async (key) => {
      let script = document.createElement("script");
      document.body.appendChild(script);
      let scriptLoadPromise = new Promise((resolve, reject) => {
        script.addEventListener("load", resolve, {once: true});
      });
      script.src = testURI("resource", "script", key);
      await scriptLoadPromise;
      let response = await fetch(
        testURI("ctr", "script", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | 
| XMLHttpRequest cache Similar to the newer Fetch API, any resource received may be cached by the browser. The cache is potentially vulnerable to cross-site tracking attack.
               | write: async (key) => {
      const req = new XMLHttpRequest();
      const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
      req.open("GET", testURI("resource", "xhr", key));
      req.send();
      await loadPromise;
      return key;
    }
read: async (key) => {
      const req = new XMLHttpRequest();
      const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
      req.open("GET", testURI("resource", "xhr", key));
      req.send();
      await loadPromise;
      let countResponse = await fetch(testURI("ctr", "xhr", key),
                                      {cache: "reload"});
      return (await countResponse.text()).trim();
    }
result, same session: 1, 1, 1
result, different session: 1, 1, 1
unsupported: false, false, false
passed: false, false, false
test failed: false, false, false | write: async (key) => {
      const req = new XMLHttpRequest();
      const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
      req.open("GET", testURI("resource", "xhr", key));
      req.send();
      await loadPromise;
      return key;
    }
read: async (key) => {
      const req = new XMLHttpRequest();
      const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
      req.open("GET", testURI("resource", "xhr", key));
      req.send();
      await loadPromise;
      let countResponse = await fetch(testURI("ctr", "xhr", key),
                                      {cache: "reload"});
      return (await countResponse.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: async (key) => {
      const req = new XMLHttpRequest();
      const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
      req.open("GET", testURI("resource", "xhr", key));
      req.send();
      await loadPromise;
      return key;
    }
read: async (key) => {
      const req = new XMLHttpRequest();
      const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
      req.open("GET", testURI("resource", "xhr", key));
      req.send();
      await loadPromise;
      let countResponse = await fetch(testURI("ctr", "xhr", key),
                                      {cache: "reload"});
      return (await countResponse.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: async (key) => {
      const req = new XMLHttpRequest();
      const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
      req.open("GET", testURI("resource", "xhr", key));
      req.send();
      await loadPromise;
      return key;
    }
read: async (key) => {
      const req = new XMLHttpRequest();
      const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
      req.open("GET", testURI("resource", "xhr", key));
      req.send();
      await loadPromise;
      let countResponse = await fetch(testURI("ctr", "xhr", key),
                                      {cache: "reload"});
      return (await countResponse.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: async (key) => {
      const req = new XMLHttpRequest();
      const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
      req.open("GET", testURI("resource", "xhr", key));
      req.send();
      await loadPromise;
      return key;
    }
read: async (key) => {
      const req = new XMLHttpRequest();
      const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
      req.open("GET", testURI("resource", "xhr", key));
      req.send();
      await loadPromise;
      let countResponse = await fetch(testURI("ctr", "xhr", key),
                                      {cache: "reload"});
      return (await countResponse.text()).trim();
    }
result, same session: 1, 1, 1
result, different session: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (key) => {
      const req = new XMLHttpRequest();
      const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
      req.open("GET", testURI("resource", "xhr", key));
      req.send();
      await loadPromise;
      return key;
    }
read: async (key) => {
      const req = new XMLHttpRequest();
      const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
      req.open("GET", testURI("resource", "xhr", key));
      req.send();
      await loadPromise;
      let countResponse = await fetch(testURI("ctr", "xhr", key),
                                      {cache: "reload"});
      return (await countResponse.text()).trim();
    }
result, same session: 1, 1, 1
result, different session: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (key) => {
      const req = new XMLHttpRequest();
      const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
      req.open("GET", testURI("resource", "xhr", key));
      req.send();
      await loadPromise;
      return key;
    }
read: async (key) => {
      const req = new XMLHttpRequest();
      const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
      req.open("GET", testURI("resource", "xhr", key));
      req.send();
      await loadPromise;
      let countResponse = await fetch(testURI("ctr", "xhr", key),
                                      {cache: "reload"});
      return (await countResponse.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: async (key) => {
      const req = new XMLHttpRequest();
      const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
      req.open("GET", testURI("resource", "xhr", key));
      req.send();
      await loadPromise;
      return key;
    }
read: async (key) => {
      const req = new XMLHttpRequest();
      const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
      req.open("GET", testURI("resource", "xhr", key));
      req.send();
      await loadPromise;
      let countResponse = await fetch(testURI("ctr", "xhr", key),
                                      {cache: "reload"});
      return (await countResponse.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: async (key) => {
      const req = new XMLHttpRequest();
      const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
      req.open("GET", testURI("resource", "xhr", key));
      req.send();
      await loadPromise;
      return key;
    }
read: async (key) => {
      const req = new XMLHttpRequest();
      const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
      req.open("GET", testURI("resource", "xhr", key));
      req.send();
      await loadPromise;
      let countResponse = await fetch(testURI("ctr", "xhr", key),
                                      {cache: "reload"});
      return (await countResponse.text()).trim();
    }
result, same session: 1, 1, 1
result, different session: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (key) => {
      const req = new XMLHttpRequest();
      const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
      req.open("GET", testURI("resource", "xhr", key));
      req.send();
      await loadPromise;
      return key;
    }
read: async (key) => {
      const req = new XMLHttpRequest();
      const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
      req.open("GET", testURI("resource", "xhr", key));
      req.send();
      await loadPromise;
      let countResponse = await fetch(testURI("ctr", "xhr", key),
                                      {cache: "reload"});
      return (await countResponse.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: async (key) => {
      const req = new XMLHttpRequest();
      const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
      req.open("GET", testURI("resource", "xhr", key));
      req.send();
      await loadPromise;
      return key;
    }
read: async (key) => {
      const req = new XMLHttpRequest();
      const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
      req.open("GET", testURI("resource", "xhr", key));
      req.send();
      await loadPromise;
      let countResponse = await fetch(testURI("ctr", "xhr", key),
                                      {cache: "reload"});
      return (await countResponse.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | 
| Cross-session third-party tracking testsWhich browsers prevent third-party trackers from tracking you across browser sessions? A common vulnerability of web browsers is that they allow third-party trackers to 'tag' your browser with some tracking data. This tag can be used to re-identify you when you return to a website you visited before. This category of leaks can be prevented by browser if they clean or isolate data between browser sessions. (In cases where a user has logged into a website or entered detailed information, it may be justifiable for a browser to retain information across sessions. These tests check when no such justification exists: when you have entered no significant information into a website, will the browser still retain data that allows you to be tracked across sessions?)
         | |||||||||||
| Alt-Svc Alt-Svc allows the server to indicate to the web browser that a resource should be loaded on a different server. Because this is a persistent setting, it could be used to track users across websites if it is not correctly partitioned.
               | write: async () => {
      // Clear Alt-Svc caching first.
      let responseText = "";
      for (let i = 0; i < 3; ++i) {
        await fetch(altSvcOrigin + "/clear");
        await sleepMs(100);
      }
      responseText = await fetchText(altSvcOrigin + "/protocol");
      console.log("after clear:", responseText);
      // Store "h3" state in Alt-Svc cache
      for (let i = 0; i < 3; ++i) {
        await fetch(altSvcOrigin + "/set");
        await sleepMs(100);
      }
      responseText = await fetchText(altSvcOrigin + "/protocol");
      console.log("after set:", responseText);
    }
read: async () => {
      const protocol = await fetchText(altSvcOrigin + "/protocol");
      if ((new URL(location)).searchParams.get("thirdparty") === "same") {
        if (protocol !== "h3") {
          throw new Error("Unsupported");
        }
      }
      return protocol;
    }
result, same session: h3, h3, h3
result, different session: h3, h3, h3
unsupported: false, false, false
passed: false, false, false
test failed: false, false, false | write: async () => {
      // Clear Alt-Svc caching first.
      let responseText = "";
      for (let i = 0; i < 3; ++i) {
        await fetch(altSvcOrigin + "/clear");
        await sleepMs(100);
      }
      responseText = await fetchText(altSvcOrigin + "/protocol");
      console.log("after clear:", responseText);
      // Store "h3" state in Alt-Svc cache
      for (let i = 0; i < 3; ++i) {
        await fetch(altSvcOrigin + "/set");
        await sleepMs(100);
      }
      responseText = await fetchText(altSvcOrigin + "/protocol");
      console.log("after set:", responseText);
    }
read: async () => {
      const protocol = await fetchText(altSvcOrigin + "/protocol");
      if ((new URL(location)).searchParams.get("thirdparty") === "same") {
        if (protocol !== "h3") {
          throw new Error("Unsupported");
        }
      }
      return protocol;
    }
result, same session: h3, h3
result, different session: h3, h3
unsupported: false, false
passed: false, false
test failed: false, false | write: async () => {
      // Clear Alt-Svc caching first.
      let responseText = "";
      for (let i = 0; i < 3; ++i) {
        await fetch(altSvcOrigin + "/clear");
        await sleepMs(100);
      }
      responseText = await fetchText(altSvcOrigin + "/protocol");
      console.log("after clear:", responseText);
      // Store "h3" state in Alt-Svc cache
      for (let i = 0; i < 3; ++i) {
        await fetch(altSvcOrigin + "/set");
        await sleepMs(100);
      }
      responseText = await fetchText(altSvcOrigin + "/protocol");
      console.log("after set:", responseText);
    }
read: async () => {
      const protocol = await fetchText(altSvcOrigin + "/protocol");
      if ((new URL(location)).searchParams.get("thirdparty") === "same") {
        if (protocol !== "h3") {
          throw new Error("Unsupported");
        }
      }
      return protocol;
    }
result, same session: h3, h3
result, different session: h3, h3
unsupported: false, false
passed: false, false
test failed: false, false | write: async () => {
      // Clear Alt-Svc caching first.
      let responseText = "";
      for (let i = 0; i < 3; ++i) {
        await fetch(altSvcOrigin + "/clear");
        await sleepMs(100);
      }
      responseText = await fetchText(altSvcOrigin + "/protocol");
      console.log("after clear:", responseText);
      // Store "h3" state in Alt-Svc cache
      for (let i = 0; i < 3; ++i) {
        await fetch(altSvcOrigin + "/set");
        await sleepMs(100);
      }
      responseText = await fetchText(altSvcOrigin + "/protocol");
      console.log("after set:", responseText);
    }
read: async () => {
      const protocol = await fetchText(altSvcOrigin + "/protocol");
      if ((new URL(location)).searchParams.get("thirdparty") === "same") {
        if (protocol !== "h3") {
          throw new Error("Unsupported");
        }
      }
      return protocol;
    }
result, same session: h3, h3
result, different session: h3, h3
unsupported: false, false
passed: false, false
test failed: false, false | write: async () => {
      // Clear Alt-Svc caching first.
      let responseText = "";
      for (let i = 0; i < 3; ++i) {
        await fetch(altSvcOrigin + "/clear");
        await sleepMs(100);
      }
      responseText = await fetchText(altSvcOrigin + "/protocol");
      console.log("after clear:", responseText);
      // Store "h3" state in Alt-Svc cache
      for (let i = 0; i < 3; ++i) {
        await fetch(altSvcOrigin + "/set");
        await sleepMs(100);
      }
      responseText = await fetchText(altSvcOrigin + "/protocol");
      console.log("after set:", responseText);
    }
read: async () => {
      const protocol = await fetchText(altSvcOrigin + "/protocol");
      if ((new URL(location)).searchParams.get("thirdparty") === "same") {
        if (protocol !== "h3") {
          throw new Error("Unsupported");
        }
      }
      return protocol;
    }
result, same session: h3, h3, h3
result, different session: h2, h2, h2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async () => {
      // Clear Alt-Svc caching first.
      let responseText = "";
      for (let i = 0; i < 3; ++i) {
        await fetch(altSvcOrigin + "/clear");
        await sleepMs(100);
      }
      responseText = await fetchText(altSvcOrigin + "/protocol");
      console.log("after clear:", responseText);
      // Store "h3" state in Alt-Svc cache
      for (let i = 0; i < 3; ++i) {
        await fetch(altSvcOrigin + "/set");
        await sleepMs(100);
      }
      responseText = await fetchText(altSvcOrigin + "/protocol");
      console.log("after set:", responseText);
    }
read: async () => {
      const protocol = await fetchText(altSvcOrigin + "/protocol");
      if ((new URL(location)).searchParams.get("thirdparty") === "same") {
        if (protocol !== "h3") {
          throw new Error("Unsupported");
        }
      }
      return protocol;
    }
result, same session: h3, h3, h3
result, different session: h2, h2, h2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async () => {
      // Clear Alt-Svc caching first.
      let responseText = "";
      for (let i = 0; i < 3; ++i) {
        await fetch(altSvcOrigin + "/clear");
        await sleepMs(100);
      }
      responseText = await fetchText(altSvcOrigin + "/protocol");
      console.log("after clear:", responseText);
      // Store "h3" state in Alt-Svc cache
      for (let i = 0; i < 3; ++i) {
        await fetch(altSvcOrigin + "/set");
        await sleepMs(100);
      }
      responseText = await fetchText(altSvcOrigin + "/protocol");
      console.log("after set:", responseText);
    }
read: async () => {
      const protocol = await fetchText(altSvcOrigin + "/protocol");
      if ((new URL(location)).searchParams.get("thirdparty") === "same") {
        if (protocol !== "h3") {
          throw new Error("Unsupported");
        }
      }
      return protocol;
    }
result, same session: h3, h3
result, different session: h3, h3
unsupported: false, false
passed: false, false
test failed: false, false | write: async () => {
      // Clear Alt-Svc caching first.
      let responseText = "";
      for (let i = 0; i < 3; ++i) {
        await fetch(altSvcOrigin + "/clear");
        await sleepMs(100);
      }
      responseText = await fetchText(altSvcOrigin + "/protocol");
      console.log("after clear:", responseText);
      // Store "h3" state in Alt-Svc cache
      for (let i = 0; i < 3; ++i) {
        await fetch(altSvcOrigin + "/set");
        await sleepMs(100);
      }
      responseText = await fetchText(altSvcOrigin + "/protocol");
      console.log("after set:", responseText);
    }
read: async () => {
      const protocol = await fetchText(altSvcOrigin + "/protocol");
      if ((new URL(location)).searchParams.get("thirdparty") === "same") {
        if (protocol !== "h3") {
          throw new Error("Unsupported");
        }
      }
      return protocol;
    }
result, same session: h2, h2
result, different session: h3, h3
unsupported: true, true
passed: undefined
test failed: false, false | write: async () => {
      // Clear Alt-Svc caching first.
      let responseText = "";
      for (let i = 0; i < 3; ++i) {
        await fetch(altSvcOrigin + "/clear");
        await sleepMs(100);
      }
      responseText = await fetchText(altSvcOrigin + "/protocol");
      console.log("after clear:", responseText);
      // Store "h3" state in Alt-Svc cache
      for (let i = 0; i < 3; ++i) {
        await fetch(altSvcOrigin + "/set");
        await sleepMs(100);
      }
      responseText = await fetchText(altSvcOrigin + "/protocol");
      console.log("after set:", responseText);
    }
read: async () => {
      const protocol = await fetchText(altSvcOrigin + "/protocol");
      if ((new URL(location)).searchParams.get("thirdparty") === "same") {
        if (protocol !== "h3") {
          throw new Error("Unsupported");
        }
      }
      return protocol;
    }
result, same session: h2, h2, h2
result, different session: h2, h2, h2
unsupported: true, true, true
passed: undefined
test failed: false, false, false | write: async () => {
      // Clear Alt-Svc caching first.
      let responseText = "";
      for (let i = 0; i < 3; ++i) {
        await fetch(altSvcOrigin + "/clear");
        await sleepMs(100);
      }
      responseText = await fetchText(altSvcOrigin + "/protocol");
      console.log("after clear:", responseText);
      // Store "h3" state in Alt-Svc cache
      for (let i = 0; i < 3; ++i) {
        await fetch(altSvcOrigin + "/set");
        await sleepMs(100);
      }
      responseText = await fetchText(altSvcOrigin + "/protocol");
      console.log("after set:", responseText);
    }
read: async () => {
      const protocol = await fetchText(altSvcOrigin + "/protocol");
      if ((new URL(location)).searchParams.get("thirdparty") === "same") {
        if (protocol !== "h3") {
          throw new Error("Unsupported");
        }
      }
      return protocol;
    }
result, same session: h3, h3
result, different session: h3, h3
unsupported: false, false
passed: false, false
test failed: false, false | write: async () => {
      // Clear Alt-Svc caching first.
      let responseText = "";
      for (let i = 0; i < 3; ++i) {
        await fetch(altSvcOrigin + "/clear");
        await sleepMs(100);
      }
      responseText = await fetchText(altSvcOrigin + "/protocol");
      console.log("after clear:", responseText);
      // Store "h3" state in Alt-Svc cache
      for (let i = 0; i < 3; ++i) {
        await fetch(altSvcOrigin + "/set");
        await sleepMs(100);
      }
      responseText = await fetchText(altSvcOrigin + "/protocol");
      console.log("after set:", responseText);
    }
read: async () => {
      const protocol = await fetchText(altSvcOrigin + "/protocol");
      if ((new URL(location)).searchParams.get("thirdparty") === "same") {
        if (protocol !== "h3") {
          throw new Error("Unsupported");
        }
      }
      return protocol;
    }
result, same session: h3, h3
result, different session: h3, h3
unsupported: false, false
passed: false, false
test failed: false, false | 
| CacheStorage The Cache API is a content storage mechanism originally introduced to support ServiceWorkers. If the same Cache object is accessible to multiple websites, it can be abused to track users.
               | write: async (key) => {
      try {
        let cache = await caches.open("supercookies");
        cache.addAll([`test.css?key=${key}`]);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      let cache = await caches.open("supercookies");
      let cacheKeys = await cache.keys();
      let url = cacheKeys[0].url;
      return (new URL(url)).searchParams.get("key");
    }
result, same session: 
Error: Failed to execute 'open' on 'CacheStorage': An attempt was made to break through the security policy of the user agent.,
Error: Failed to execute 'open' on 'CacheStorage': An attempt was made to break through the security policy of the user agent.,
Error: Failed to execute 'open' on 'CacheStorage': An attempt was made to break through the security policy of the user agent.
result, different session: 
Error: Failed to execute 'open' on 'CacheStorage': An attempt was made to break through the security policy of the user agent.,
Error: Failed to execute 'open' on 'CacheStorage': An attempt was made to break through the security policy of the user agent.,
Error: Failed to execute 'open' on 'CacheStorage': An attempt was made to break through the security policy of the user agent.
unsupported: true, true, true
passed: undefined
test failed: false, false, false | write: async (key) => {
      try {
        let cache = await caches.open("supercookies");
        cache.addAll([`test.css?key=${key}`]);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      let cache = await caches.open("supercookies");
      let cacheKeys = await cache.keys();
      let url = cacheKeys[0].url;
      return (new URL(url)).searchParams.get("key");
    }
result, same session: 
e8485c10-5a92-474a-bd16-de23f46ee0e2_3p,
cd5b479f-1529-4f43-a586-810ed83aeb8b_3p
result, different session: 
e8485c10-5a92-474a-bd16-de23f46ee0e2_3p,
cd5b479f-1529-4f43-a586-810ed83aeb8b_3p
unsupported: false, false
passed: false, false
test failed: false, false | write: async (key) => {
      try {
        let cache = await caches.open("supercookies");
        cache.addAll([`test.css?key=${key}`]);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      let cache = await caches.open("supercookies");
      let cacheKeys = await cache.keys();
      let url = cacheKeys[0].url;
      return (new URL(url)).searchParams.get("key");
    }
result, same session: 
e96973da-bf72-4eb1-b6ab-a4e6c6775fb4_3p,
67fb6941-7c51-4727-b485-317211e65ae0_3p
result, different session: 
e96973da-bf72-4eb1-b6ab-a4e6c6775fb4_3p,
67fb6941-7c51-4727-b485-317211e65ae0_3p
unsupported: false, false
passed: false, false
test failed: false, false | write: async (key) => {
      try {
        let cache = await caches.open("supercookies");
        cache.addAll([`test.css?key=${key}`]);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      let cache = await caches.open("supercookies");
      let cacheKeys = await cache.keys();
      let url = cacheKeys[0].url;
      return (new URL(url)).searchParams.get("key");
    }
result, same session: 
94f1fd6d-f642-403e-8ef8-11180b18b1f6_3p,
2bf6571e-4385-4b68-a076-f6868cbe4954_3p
result, different session: 
94f1fd6d-f642-403e-8ef8-11180b18b1f6_3p,
2bf6571e-4385-4b68-a076-f6868cbe4954_3p
unsupported: false, false
passed: false, false
test failed: false, false | write: async (key) => {
      try {
        let cache = await caches.open("supercookies");
        cache.addAll([`test.css?key=${key}`]);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      let cache = await caches.open("supercookies");
      let cacheKeys = await cache.keys();
      let url = cacheKeys[0].url;
      return (new URL(url)).searchParams.get("key");
    }
result, same session: 
da67bebb-5652-441a-816e-1206b25c0225_3p,
0bad71fe-de93-4434-bfa0-bb7f0db62eea_3p,
6aba94ed-010e-45af-892c-dd670cd8d134_3p
result, different session: 
Error: cacheKeys[0] is undefined,
Error: cacheKeys[0] is undefined,
Error: cacheKeys[0] is undefined
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (key) => {
      try {
        let cache = await caches.open("supercookies");
        cache.addAll([`test.css?key=${key}`]);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      let cache = await caches.open("supercookies");
      let cacheKeys = await cache.keys();
      let url = cacheKeys[0].url;
      return (new URL(url)).searchParams.get("key");
    }
result, same session: 
Error: The operation is insecure.,
Error: The operation is insecure.,
Error: The operation is insecure.
result, different session: 
Error: The operation is insecure.,
Error: The operation is insecure.,
Error: The operation is insecure.
unsupported: true, true, true
passed: undefined
test failed: false, false, false | write: async (key) => {
      try {
        let cache = await caches.open("supercookies");
        cache.addAll([`test.css?key=${key}`]);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      let cache = await caches.open("supercookies");
      let cacheKeys = await cache.keys();
      let url = cacheKeys[0].url;
      return (new URL(url)).searchParams.get("key");
    }
result, same session: 
50c6a369-0b5c-4de5-a70b-a0516825119e_3p,
49e0d8b1-c126-4b6b-be66-6ba7acfd85a9_3p
result, different session: 
50c6a369-0b5c-4de5-a70b-a0516825119e_3p,
49e0d8b1-c126-4b6b-be66-6ba7acfd85a9_3p
unsupported: false, false
passed: false, false
test failed: false, false | write: async (key) => {
      try {
        let cache = await caches.open("supercookies");
        cache.addAll([`test.css?key=${key}`]);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      let cache = await caches.open("supercookies");
      let cacheKeys = await cache.keys();
      let url = cacheKeys[0].url;
      return (new URL(url)).searchParams.get("key");
    }
result, same session: 
6b07cf4a-a332-4a7c-90df-fbc3dc862f86_3p,
2d40813a-a6c5-4a6b-bb1c-f4d01a399217_3p
result, different session: 
Error: undefined is not an object (evaluating 'cacheKeys[0].url'),
Error: undefined is not an object (evaluating 'cacheKeys[0].url')
unsupported: false, false
passed: true, true
test failed: false, false | write: async (key) => {
      try {
        let cache = await caches.open("supercookies");
        cache.addAll([`test.css?key=${key}`]);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      let cache = await caches.open("supercookies");
      let cacheKeys = await cache.keys();
      let url = cacheKeys[0].url;
      return (new URL(url)).searchParams.get("key");
    }
result, same session: 
Error: The operation is insecure.,
Error: The operation is insecure.,
Error: The operation is insecure.
result, different session: 
Error: The operation is insecure.,
Error: The operation is insecure.,
Error: The operation is insecure.
unsupported: true, true, true
passed: undefined
test failed: false, false, false | write: async (key) => {
      try {
        let cache = await caches.open("supercookies");
        cache.addAll([`test.css?key=${key}`]);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      let cache = await caches.open("supercookies");
      let cacheKeys = await cache.keys();
      let url = cacheKeys[0].url;
      return (new URL(url)).searchParams.get("key");
    }
result, same session: 
3242a94e-3344-4cb9-ba7c-ebedaf5d6e01_3p,
87918123-fc52-4196-9d0c-a2cfc3f826d4_3p
result, different session: 
3242a94e-3344-4cb9-ba7c-ebedaf5d6e01_3p,
87918123-fc52-4196-9d0c-a2cfc3f826d4_3p
unsupported: false, false
passed: false, false
test failed: false, false | write: async (key) => {
      try {
        let cache = await caches.open("supercookies");
        cache.addAll([`test.css?key=${key}`]);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: async () => {
      let cache = await caches.open("supercookies");
      let cacheKeys = await cache.keys();
      let url = cacheKeys[0].url;
      return (new URL(url)).searchParams.get("key");
    }
result, same session: 
5c052838-a55e-4690-83c6-b7d572146713_3p,
49042d99-5b0e-4f37-82f3-78de245eea34_3p
result, different session: 
5c052838-a55e-4690-83c6-b7d572146713_3p,
49042d99-5b0e-4f37-82f3-78de245eea34_3p
unsupported: false, false
passed: false, false
test failed: false, false | 
| cookie (HTTP) The cookie, first introduced by Netscape in 1994, is a small amount of data stored by your browser on a website's behalf. It has legitimate uses, but it is also the classic cross-site tracking mechanism, and today still the most popular method of tracking users across websites. Browsers can stop cookies from being used for cross-site tracking by either blocking or partitioning them.
               | write: async (secret) => {
      // Request a page that will send an HTTPOnly 'set-cookie' response header with secret value.
      await fetch(`${baseURI}cookie?secret=${secret}_http`);
    }
read: async () => {
      // Test if we now send a requests with a 'cookie' header containing the secret.
      let response = await fetch(`${baseURI}headers`);
      let cookie = (await response.json())["cookie"];
      return cookie ? cookie.match(/secret=([\w-]+)/)[1]: null;
    }
result, same session: 
3133a4ca-686b-4047-91c3-9a7e181c53f7_3p_http,
49872d4a-b01d-4af8-8cbe-85c379a609e2_3p_http,
58bb2b32-4f83-47bb-91c8-96dce39cd97a_3p_http
result, different session: , , 
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (secret) => {
      // Request a page that will send an HTTPOnly 'set-cookie' response header with secret value.
      await fetch(`${baseURI}cookie?secret=${secret}_http`);
    }
read: async () => {
      // Test if we now send a requests with a 'cookie' header containing the secret.
      let response = await fetch(`${baseURI}headers`);
      let cookie = (await response.json())["cookie"];
      return cookie ? cookie.match(/secret=([\w-]+)/)[1]: null;
    }
result, same session: 
e8485c10-5a92-474a-bd16-de23f46ee0e2_3p_http,
cd5b479f-1529-4f43-a586-810ed83aeb8b_3p_http
result, different session: 
e8485c10-5a92-474a-bd16-de23f46ee0e2_3p_http,
cd5b479f-1529-4f43-a586-810ed83aeb8b_3p_http
unsupported: false, false
passed: false, false
test failed: false, false | write: async (secret) => {
      // Request a page that will send an HTTPOnly 'set-cookie' response header with secret value.
      await fetch(`${baseURI}cookie?secret=${secret}_http`);
    }
read: async () => {
      // Test if we now send a requests with a 'cookie' header containing the secret.
      let response = await fetch(`${baseURI}headers`);
      let cookie = (await response.json())["cookie"];
      return cookie ? cookie.match(/secret=([\w-]+)/)[1]: null;
    }
result, same session: 
e96973da-bf72-4eb1-b6ab-a4e6c6775fb4_3p_http,
67fb6941-7c51-4727-b485-317211e65ae0_3p_http
result, different session: 
e96973da-bf72-4eb1-b6ab-a4e6c6775fb4_3p_http,
67fb6941-7c51-4727-b485-317211e65ae0_3p_http
unsupported: false, false
passed: false, false
test failed: false, false | write: async (secret) => {
      // Request a page that will send an HTTPOnly 'set-cookie' response header with secret value.
      await fetch(`${baseURI}cookie?secret=${secret}_http`);
    }
read: async () => {
      // Test if we now send a requests with a 'cookie' header containing the secret.
      let response = await fetch(`${baseURI}headers`);
      let cookie = (await response.json())["cookie"];
      return cookie ? cookie.match(/secret=([\w-]+)/)[1]: null;
    }
result, same session: 
94f1fd6d-f642-403e-8ef8-11180b18b1f6_3p_http,
2bf6571e-4385-4b68-a076-f6868cbe4954_3p_http
result, different session: 
94f1fd6d-f642-403e-8ef8-11180b18b1f6_3p_http,
2bf6571e-4385-4b68-a076-f6868cbe4954_3p_http
unsupported: false, false
passed: false, false
test failed: false, false | write: async (secret) => {
      // Request a page that will send an HTTPOnly 'set-cookie' response header with secret value.
      await fetch(`${baseURI}cookie?secret=${secret}_http`);
    }
read: async () => {
      // Test if we now send a requests with a 'cookie' header containing the secret.
      let response = await fetch(`${baseURI}headers`);
      let cookie = (await response.json())["cookie"];
      return cookie ? cookie.match(/secret=([\w-]+)/)[1]: null;
    }
result, same session: 
da67bebb-5652-441a-816e-1206b25c0225_3p_http,
0bad71fe-de93-4434-bfa0-bb7f0db62eea_3p_http,
6aba94ed-010e-45af-892c-dd670cd8d134_3p_http
result, different session: , , 
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (secret) => {
      // Request a page that will send an HTTPOnly 'set-cookie' response header with secret value.
      await fetch(`${baseURI}cookie?secret=${secret}_http`);
    }
read: async () => {
      // Test if we now send a requests with a 'cookie' header containing the secret.
      let response = await fetch(`${baseURI}headers`);
      let cookie = (await response.json())["cookie"];
      return cookie ? cookie.match(/secret=([\w-]+)/)[1]: null;
    }
result, same session: , , 
result, different session: , , 
unsupported: true, true, true
passed: undefined
test failed: false, false, false | write: async (secret) => {
      // Request a page that will send an HTTPOnly 'set-cookie' response header with secret value.
      await fetch(`${baseURI}cookie?secret=${secret}_http`);
    }
read: async () => {
      // Test if we now send a requests with a 'cookie' header containing the secret.
      let response = await fetch(`${baseURI}headers`);
      let cookie = (await response.json())["cookie"];
      return cookie ? cookie.match(/secret=([\w-]+)/)[1]: null;
    }
result, same session: 
50c6a369-0b5c-4de5-a70b-a0516825119e_3p_http,
49e0d8b1-c126-4b6b-be66-6ba7acfd85a9_3p_http
result, different session: 
50c6a369-0b5c-4de5-a70b-a0516825119e_3p_http,
49e0d8b1-c126-4b6b-be66-6ba7acfd85a9_3p_http
unsupported: false, false
passed: false, false
test failed: false, false | write: async (secret) => {
      // Request a page that will send an HTTPOnly 'set-cookie' response header with secret value.
      await fetch(`${baseURI}cookie?secret=${secret}_http`);
    }
read: async () => {
      // Test if we now send a requests with a 'cookie' header containing the secret.
      let response = await fetch(`${baseURI}headers`);
      let cookie = (await response.json())["cookie"];
      return cookie ? cookie.match(/secret=([\w-]+)/)[1]: null;
    }
result, same session: , 
result, different session: , 
unsupported: true, true
passed: undefined
test failed: false, false | write: async (secret) => {
      // Request a page that will send an HTTPOnly 'set-cookie' response header with secret value.
      await fetch(`${baseURI}cookie?secret=${secret}_http`);
    }
read: async () => {
      // Test if we now send a requests with a 'cookie' header containing the secret.
      let response = await fetch(`${baseURI}headers`);
      let cookie = (await response.json())["cookie"];
      return cookie ? cookie.match(/secret=([\w-]+)/)[1]: null;
    }
result, same session: , , 
result, different session: , , 
unsupported: true, true, true
passed: undefined
test failed: false, false, false | write: async (secret) => {
      // Request a page that will send an HTTPOnly 'set-cookie' response header with secret value.
      await fetch(`${baseURI}cookie?secret=${secret}_http`);
    }
read: async () => {
      // Test if we now send a requests with a 'cookie' header containing the secret.
      let response = await fetch(`${baseURI}headers`);
      let cookie = (await response.json())["cookie"];
      return cookie ? cookie.match(/secret=([\w-]+)/)[1]: null;
    }
result, same session: , 
result, different session: , 
unsupported: true, true
passed: undefined
test failed: false, false | write: async (secret) => {
      // Request a page that will send an HTTPOnly 'set-cookie' response header with secret value.
      await fetch(`${baseURI}cookie?secret=${secret}_http`);
    }
read: async () => {
      // Test if we now send a requests with a 'cookie' header containing the secret.
      let response = await fetch(`${baseURI}headers`);
      let cookie = (await response.json())["cookie"];
      return cookie ? cookie.match(/secret=([\w-]+)/)[1]: null;
    }
result, same session: 
5c052838-a55e-4690-83c6-b7d572146713_3p_http,
49042d99-5b0e-4f37-82f3-78de245eea34_3p_http
result, different session: 
5c052838-a55e-4690-83c6-b7d572146713_3p_http,
49042d99-5b0e-4f37-82f3-78de245eea34_3p_http
unsupported: false, false
passed: false, false
test failed: false, false | 
| cookie (JS) The cookie, first introduced by Netscape in 1994, is a small amount of data stored by your browser on a website's behalf. It has legitimate uses, but it is also the classic cross-site tracking mechanism, and today still the most popular method of tracking users across websites. Browsers can stop cookies from being used for cross-site tracking by either blocking or partitioning them.
               | write: (secret) => {
      document.cookie = `secret=${secret}_js; max-age=3600; SameSite=None; Secure`;
    }
read: () => document.cookie ? document.cookie.match(/secret=([\w-]+)/)[1] : null
result, same session: 
3133a4ca-686b-4047-91c3-9a7e181c53f7_3p_js,
49872d4a-b01d-4af8-8cbe-85c379a609e2_3p_js,
58bb2b32-4f83-47bb-91c8-96dce39cd97a_3p_js
result, different session: , , 
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (secret) => {
      document.cookie = `secret=${secret}_js; max-age=3600; SameSite=None; Secure`;
    }
read: () => document.cookie ? document.cookie.match(/secret=([\w-]+)/)[1] : null
result, same session: 
e8485c10-5a92-474a-bd16-de23f46ee0e2_3p_js,
cd5b479f-1529-4f43-a586-810ed83aeb8b_3p_js
result, different session: 
e8485c10-5a92-474a-bd16-de23f46ee0e2_3p_js,
cd5b479f-1529-4f43-a586-810ed83aeb8b_3p_js
unsupported: false, false
passed: false, false
test failed: false, false | write: (secret) => {
      document.cookie = `secret=${secret}_js; max-age=3600; SameSite=None; Secure`;
    }
read: () => document.cookie ? document.cookie.match(/secret=([\w-]+)/)[1] : null
result, same session: 
e96973da-bf72-4eb1-b6ab-a4e6c6775fb4_3p_js,
67fb6941-7c51-4727-b485-317211e65ae0_3p_js
result, different session: 
e96973da-bf72-4eb1-b6ab-a4e6c6775fb4_3p_js,
67fb6941-7c51-4727-b485-317211e65ae0_3p_js
unsupported: false, false
passed: false, false
test failed: false, false | write: (secret) => {
      document.cookie = `secret=${secret}_js; max-age=3600; SameSite=None; Secure`;
    }
read: () => document.cookie ? document.cookie.match(/secret=([\w-]+)/)[1] : null
result, same session: 
94f1fd6d-f642-403e-8ef8-11180b18b1f6_3p_js,
2bf6571e-4385-4b68-a076-f6868cbe4954_3p_js
result, different session: 
94f1fd6d-f642-403e-8ef8-11180b18b1f6_3p_js,
2bf6571e-4385-4b68-a076-f6868cbe4954_3p_js
unsupported: false, false
passed: false, false
test failed: false, false | write: (secret) => {
      document.cookie = `secret=${secret}_js; max-age=3600; SameSite=None; Secure`;
    }
read: () => document.cookie ? document.cookie.match(/secret=([\w-]+)/)[1] : null
result, same session: 
da67bebb-5652-441a-816e-1206b25c0225_3p_js,
0bad71fe-de93-4434-bfa0-bb7f0db62eea_3p_js,
6aba94ed-010e-45af-892c-dd670cd8d134_3p_js
result, different session: , , 
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (secret) => {
      document.cookie = `secret=${secret}_js; max-age=3600; SameSite=None; Secure`;
    }
read: () => document.cookie ? document.cookie.match(/secret=([\w-]+)/)[1] : null
result, same session: , , 
result, different session: , , 
unsupported: true, true, true
passed: undefined
test failed: false, false, false | write: (secret) => {
      document.cookie = `secret=${secret}_js; max-age=3600; SameSite=None; Secure`;
    }
read: () => document.cookie ? document.cookie.match(/secret=([\w-]+)/)[1] : null
result, same session: 
50c6a369-0b5c-4de5-a70b-a0516825119e_3p_js,
49e0d8b1-c126-4b6b-be66-6ba7acfd85a9_3p_js
result, different session: 
50c6a369-0b5c-4de5-a70b-a0516825119e_3p_js,
49e0d8b1-c126-4b6b-be66-6ba7acfd85a9_3p_js
unsupported: false, false
passed: false, false
test failed: false, false | write: (secret) => {
      document.cookie = `secret=${secret}_js; max-age=3600; SameSite=None; Secure`;
    }
read: () => document.cookie ? document.cookie.match(/secret=([\w-]+)/)[1] : null
result, same session: , 
result, different session: , 
unsupported: true, true
passed: undefined
test failed: false, false | write: (secret) => {
      document.cookie = `secret=${secret}_js; max-age=3600; SameSite=None; Secure`;
    }
read: () => document.cookie ? document.cookie.match(/secret=([\w-]+)/)[1] : null
result, same session: , , 
result, different session: , , 
unsupported: true, true, true
passed: undefined
test failed: false, false, false | write: (secret) => {
      document.cookie = `secret=${secret}_js; max-age=3600; SameSite=None; Secure`;
    }
read: () => document.cookie ? document.cookie.match(/secret=([\w-]+)/)[1] : null
result, same session: , 
result, different session: , 
unsupported: true, true
passed: undefined
test failed: false, false | write: (secret) => {
      document.cookie = `secret=${secret}_js; max-age=3600; SameSite=None; Secure`;
    }
read: () => document.cookie ? document.cookie.match(/secret=([\w-]+)/)[1] : null
result, same session: 
5c052838-a55e-4690-83c6-b7d572146713_3p_js,
49042d99-5b0e-4f37-82f3-78de245eea34_3p_js
result, different session: 
5c052838-a55e-4690-83c6-b7d572146713_3p_js,
49042d99-5b0e-4f37-82f3-78de245eea34_3p_js
unsupported: false, false
passed: false, false
test failed: false, false | 
| CookieStore The Cookie Store API is an alternative asynchronous API for managing cookies, supported by some browsers.
               | write: (data) => {
      const msPerHour = 60 * 60 * 1000;
      if (!window.cookieStore) {
        throw new Error("Unsupported");
      }
      window.cookieStore.set({
        name: "partition_test",
        value: data,
        expires: Date.now() + msPerHour,
        sameSite: "none"
      });
    }
read: async () => {
      if (!window.cookieStore) {
        throw new Error("Unsupported");
      }
      const cookie = await window.cookieStore.get("partition_test");
      if (!cookie) {
        return null;
      }
      return cookie.value;
    }
result, same session: 
3133a4ca-686b-4047-91c3-9a7e181c53f7_3p,
49872d4a-b01d-4af8-8cbe-85c379a609e2_3p,
58bb2b32-4f83-47bb-91c8-96dce39cd97a_3p
result, different session: , , 
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (data) => {
      const msPerHour = 60 * 60 * 1000;
      if (!window.cookieStore) {
        throw new Error("Unsupported");
      }
      window.cookieStore.set({
        name: "partition_test",
        value: data,
        expires: Date.now() + msPerHour,
        sameSite: "none"
      });
    }
read: async () => {
      if (!window.cookieStore) {
        throw new Error("Unsupported");
      }
      const cookie = await window.cookieStore.get("partition_test");
      if (!cookie) {
        return null;
      }
      return cookie.value;
    }
result, same session: 
e8485c10-5a92-474a-bd16-de23f46ee0e2_3p,
cd5b479f-1529-4f43-a586-810ed83aeb8b_3p
result, different session: 
e8485c10-5a92-474a-bd16-de23f46ee0e2_3p,
cd5b479f-1529-4f43-a586-810ed83aeb8b_3p
unsupported: false, false
passed: false, false
test failed: false, false | write: (data) => {
      const msPerHour = 60 * 60 * 1000;
      if (!window.cookieStore) {
        throw new Error("Unsupported");
      }
      window.cookieStore.set({
        name: "partition_test",
        value: data,
        expires: Date.now() + msPerHour,
        sameSite: "none"
      });
    }
read: async () => {
      if (!window.cookieStore) {
        throw new Error("Unsupported");
      }
      const cookie = await window.cookieStore.get("partition_test");
      if (!cookie) {
        return null;
      }
      return cookie.value;
    }
result, same session: 
e96973da-bf72-4eb1-b6ab-a4e6c6775fb4_3p,
67fb6941-7c51-4727-b485-317211e65ae0_3p
result, different session: 
e96973da-bf72-4eb1-b6ab-a4e6c6775fb4_3p,
67fb6941-7c51-4727-b485-317211e65ae0_3p
unsupported: false, false
passed: false, false
test failed: false, false | write: (data) => {
      const msPerHour = 60 * 60 * 1000;
      if (!window.cookieStore) {
        throw new Error("Unsupported");
      }
      window.cookieStore.set({
        name: "partition_test",
        value: data,
        expires: Date.now() + msPerHour,
        sameSite: "none"
      });
    }
read: async () => {
      if (!window.cookieStore) {
        throw new Error("Unsupported");
      }
      const cookie = await window.cookieStore.get("partition_test");
      if (!cookie) {
        return null;
      }
      return cookie.value;
    }
result, same session: 
Error: Unsupported,
Error: Unsupported
result, different session: 
Error: Unsupported,
Error: Unsupported
unsupported: true, true
passed: undefined
test failed: false, false | write: (data) => {
      const msPerHour = 60 * 60 * 1000;
      if (!window.cookieStore) {
        throw new Error("Unsupported");
      }
      window.cookieStore.set({
        name: "partition_test",
        value: data,
        expires: Date.now() + msPerHour,
        sameSite: "none"
      });
    }
read: async () => {
      if (!window.cookieStore) {
        throw new Error("Unsupported");
      }
      const cookie = await window.cookieStore.get("partition_test");
      if (!cookie) {
        return null;
      }
      return cookie.value;
    }
result, same session: 
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
result, different session: 
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
unsupported: true, true, true
passed: undefined
test failed: false, false, false | write: (data) => {
      const msPerHour = 60 * 60 * 1000;
      if (!window.cookieStore) {
        throw new Error("Unsupported");
      }
      window.cookieStore.set({
        name: "partition_test",
        value: data,
        expires: Date.now() + msPerHour,
        sameSite: "none"
      });
    }
read: async () => {
      if (!window.cookieStore) {
        throw new Error("Unsupported");
      }
      const cookie = await window.cookieStore.get("partition_test");
      if (!cookie) {
        return null;
      }
      return cookie.value;
    }
result, same session: 
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
result, different session: 
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
unsupported: true, true, true
passed: undefined
test failed: false, false, false | write: (data) => {
      const msPerHour = 60 * 60 * 1000;
      if (!window.cookieStore) {
        throw new Error("Unsupported");
      }
      window.cookieStore.set({
        name: "partition_test",
        value: data,
        expires: Date.now() + msPerHour,
        sameSite: "none"
      });
    }
read: async () => {
      if (!window.cookieStore) {
        throw new Error("Unsupported");
      }
      const cookie = await window.cookieStore.get("partition_test");
      if (!cookie) {
        return null;
      }
      return cookie.value;
    }
result, same session: 
50c6a369-0b5c-4de5-a70b-a0516825119e_3p,
49e0d8b1-c126-4b6b-be66-6ba7acfd85a9_3p
result, different session: 
50c6a369-0b5c-4de5-a70b-a0516825119e_3p,
49e0d8b1-c126-4b6b-be66-6ba7acfd85a9_3p
unsupported: false, false
passed: false, false
test failed: false, false | write: (data) => {
      const msPerHour = 60 * 60 * 1000;
      if (!window.cookieStore) {
        throw new Error("Unsupported");
      }
      window.cookieStore.set({
        name: "partition_test",
        value: data,
        expires: Date.now() + msPerHour,
        sameSite: "none"
      });
    }
read: async () => {
      if (!window.cookieStore) {
        throw new Error("Unsupported");
      }
      const cookie = await window.cookieStore.get("partition_test");
      if (!cookie) {
        return null;
      }
      return cookie.value;
    }
result, same session: 
Error: Unsupported,
Error: Unsupported
result, different session: 
Error: Unsupported,
Error: Unsupported
unsupported: true, true
passed: undefined
test failed: false, false | write: (data) => {
      const msPerHour = 60 * 60 * 1000;
      if (!window.cookieStore) {
        throw new Error("Unsupported");
      }
      window.cookieStore.set({
        name: "partition_test",
        value: data,
        expires: Date.now() + msPerHour,
        sameSite: "none"
      });
    }
read: async () => {
      if (!window.cookieStore) {
        throw new Error("Unsupported");
      }
      const cookie = await window.cookieStore.get("partition_test");
      if (!cookie) {
        return null;
      }
      return cookie.value;
    }
result, same session: 
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
result, different session: 
Error: Unsupported,
Error: Unsupported,
Error: Unsupported
unsupported: true, true, true
passed: undefined
test failed: false, false, false | write: (data) => {
      const msPerHour = 60 * 60 * 1000;
      if (!window.cookieStore) {
        throw new Error("Unsupported");
      }
      window.cookieStore.set({
        name: "partition_test",
        value: data,
        expires: Date.now() + msPerHour,
        sameSite: "none"
      });
    }
read: async () => {
      if (!window.cookieStore) {
        throw new Error("Unsupported");
      }
      const cookie = await window.cookieStore.get("partition_test");
      if (!cookie) {
        return null;
      }
      return cookie.value;
    }
result, same session: , 
result, different session: , 
unsupported: true, true
passed: undefined
test failed: false, false | write: (data) => {
      const msPerHour = 60 * 60 * 1000;
      if (!window.cookieStore) {
        throw new Error("Unsupported");
      }
      window.cookieStore.set({
        name: "partition_test",
        value: data,
        expires: Date.now() + msPerHour,
        sameSite: "none"
      });
    }
read: async () => {
      if (!window.cookieStore) {
        throw new Error("Unsupported");
      }
      const cookie = await window.cookieStore.get("partition_test");
      if (!cookie) {
        return null;
      }
      return cookie.value;
    }
result, same session: 
5c052838-a55e-4690-83c6-b7d572146713_3p,
49042d99-5b0e-4f37-82f3-78de245eea34_3p
result, different session: 
5c052838-a55e-4690-83c6-b7d572146713_3p,
49042d99-5b0e-4f37-82f3-78de245eea34_3p
unsupported: false, false
passed: false, false
test failed: false, false | 
| CSS cache CSS stylesheets are cached, and if that cache is shared between websites, it can be used to track users across sites.
               | write: async (key) => {
      const href = testURI("resource", "css", key);
      const head = document.getElementsByTagName("head")[0];
      head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
      const testElement = document.querySelector("#css");
      let fontFamily;
      while (true) {
        await sleepMs(100);
        fontFamily = getComputedStyle(testElement).fontFamily;
        if (fontFamily.startsWith("fake")) {
          break;
        }
      }
      console.log(fontFamily);
      return key;
    }
read: async (key) => {
      const href = testURI("resource", "css", key);
      const head = document.getElementsByTagName("head")[0];
      head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
      const testElement = document.querySelector("#css");
      let fontFamily;
      while (true) {
        await sleepMs(100);
        fontFamily = getComputedStyle(testElement).fontFamily;
        if (fontFamily.startsWith("fake")) {
          break;
        }
      }
      console.log(fontFamily);
      return fontFamily;
    }
result, same session: 
fake_10024969083872226,
fake_645311468876544,
fake_9297235619084356
result, different session: 
fake_10024969083872226,
fake_645311468876544,
fake_9297235619084356
unsupported: false, false, false
passed: false, false, false
test failed: false, false, false | write: async (key) => {
      const href = testURI("resource", "css", key);
      const head = document.getElementsByTagName("head")[0];
      head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
      const testElement = document.querySelector("#css");
      let fontFamily;
      while (true) {
        await sleepMs(100);
        fontFamily = getComputedStyle(testElement).fontFamily;
        if (fontFamily.startsWith("fake")) {
          break;
        }
      }
      console.log(fontFamily);
      return key;
    }
read: async (key) => {
      const href = testURI("resource", "css", key);
      const head = document.getElementsByTagName("head")[0];
      head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
      const testElement = document.querySelector("#css");
      let fontFamily;
      while (true) {
        await sleepMs(100);
        fontFamily = getComputedStyle(testElement).fontFamily;
        if (fontFamily.startsWith("fake")) {
          break;
        }
      }
      console.log(fontFamily);
      return fontFamily;
    }
result, same session: 
fake_9865249303298964,
fake_8201418796953572
result, different session: 
fake_9865249303298964,
fake_8201418796953572
unsupported: false, false
passed: false, false
test failed: false, false | write: async (key) => {
      const href = testURI("resource", "css", key);
      const head = document.getElementsByTagName("head")[0];
      head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
      const testElement = document.querySelector("#css");
      let fontFamily;
      while (true) {
        await sleepMs(100);
        fontFamily = getComputedStyle(testElement).fontFamily;
        if (fontFamily.startsWith("fake")) {
          break;
        }
      }
      console.log(fontFamily);
      return key;
    }
read: async (key) => {
      const href = testURI("resource", "css", key);
      const head = document.getElementsByTagName("head")[0];
      head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
      const testElement = document.querySelector("#css");
      let fontFamily;
      while (true) {
        await sleepMs(100);
        fontFamily = getComputedStyle(testElement).fontFamily;
        if (fontFamily.startsWith("fake")) {
          break;
        }
      }
      console.log(fontFamily);
      return fontFamily;
    }
result, same session: 
fake_13981137005710487,
fake_09199919997102302
result, different session: 
fake_13981137005710487,
fake_09199919997102302
unsupported: false, false
passed: false, false
test failed: false, false | write: async (key) => {
      const href = testURI("resource", "css", key);
      const head = document.getElementsByTagName("head")[0];
      head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
      const testElement = document.querySelector("#css");
      let fontFamily;
      while (true) {
        await sleepMs(100);
        fontFamily = getComputedStyle(testElement).fontFamily;
        if (fontFamily.startsWith("fake")) {
          break;
        }
      }
      console.log(fontFamily);
      return key;
    }
read: async (key) => {
      const href = testURI("resource", "css", key);
      const head = document.getElementsByTagName("head")[0];
      head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
      const testElement = document.querySelector("#css");
      let fontFamily;
      while (true) {
        await sleepMs(100);
        fontFamily = getComputedStyle(testElement).fontFamily;
        if (fontFamily.startsWith("fake")) {
          break;
        }
      }
      console.log(fontFamily);
      return fontFamily;
    }
result, same session: 
fake_3027421884063177,
fake_4294466135566877
result, different session: 
fake_3027421884063177,
fake_4294466135566877
unsupported: false, false
passed: false, false
test failed: false, false | write: async (key) => {
      const href = testURI("resource", "css", key);
      const head = document.getElementsByTagName("head")[0];
      head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
      const testElement = document.querySelector("#css");
      let fontFamily;
      while (true) {
        await sleepMs(100);
        fontFamily = getComputedStyle(testElement).fontFamily;
        if (fontFamily.startsWith("fake")) {
          break;
        }
      }
      console.log(fontFamily);
      return key;
    }
read: async (key) => {
      const href = testURI("resource", "css", key);
      const head = document.getElementsByTagName("head")[0];
      head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
      const testElement = document.querySelector("#css");
      let fontFamily;
      while (true) {
        await sleepMs(100);
        fontFamily = getComputedStyle(testElement).fontFamily;
        if (fontFamily.startsWith("fake")) {
          break;
        }
      }
      console.log(fontFamily);
      return fontFamily;
    }
result, same session: 
fake_9407883047969985,
fake_43327761872220316,
fake_006493521883932596
result, different session: 
fake_8128017590121881,
fake_3141912139852927,
fake_7551765823770851
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (key) => {
      const href = testURI("resource", "css", key);
      const head = document.getElementsByTagName("head")[0];
      head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
      const testElement = document.querySelector("#css");
      let fontFamily;
      while (true) {
        await sleepMs(100);
        fontFamily = getComputedStyle(testElement).fontFamily;
        if (fontFamily.startsWith("fake")) {
          break;
        }
      }
      console.log(fontFamily);
      return key;
    }
read: async (key) => {
      const href = testURI("resource", "css", key);
      const head = document.getElementsByTagName("head")[0];
      head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
      const testElement = document.querySelector("#css");
      let fontFamily;
      while (true) {
        await sleepMs(100);
        fontFamily = getComputedStyle(testElement).fontFamily;
        if (fontFamily.startsWith("fake")) {
          break;
        }
      }
      console.log(fontFamily);
      return fontFamily;
    }
result, same session: 
fake_3878237488851295,
fake_39661969238859074,
fake_1354094943322528
result, different session: 
fake_6319798597530046,
fake_38610413769864094,
fake_892008830521448
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (key) => {
      const href = testURI("resource", "css", key);
      const head = document.getElementsByTagName("head")[0];
      head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
      const testElement = document.querySelector("#css");
      let fontFamily;
      while (true) {
        await sleepMs(100);
        fontFamily = getComputedStyle(testElement).fontFamily;
        if (fontFamily.startsWith("fake")) {
          break;
        }
      }
      console.log(fontFamily);
      return key;
    }
read: async (key) => {
      const href = testURI("resource", "css", key);
      const head = document.getElementsByTagName("head")[0];
      head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
      const testElement = document.querySelector("#css");
      let fontFamily;
      while (true) {
        await sleepMs(100);
        fontFamily = getComputedStyle(testElement).fontFamily;
        if (fontFamily.startsWith("fake")) {
          break;
        }
      }
      console.log(fontFamily);
      return fontFamily;
    }
result, same session: 
fake_22117989341444555,
fake_9386250032990402
result, different session: 
fake_22117989341444555,
fake_9386250032990402
unsupported: false, false
passed: false, false
test failed: false, false | write: async (key) => {
      const href = testURI("resource", "css", key);
      const head = document.getElementsByTagName("head")[0];
      head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
      const testElement = document.querySelector("#css");
      let fontFamily;
      while (true) {
        await sleepMs(100);
        fontFamily = getComputedStyle(testElement).fontFamily;
        if (fontFamily.startsWith("fake")) {
          break;
        }
      }
      console.log(fontFamily);
      return key;
    }
read: async (key) => {
      const href = testURI("resource", "css", key);
      const head = document.getElementsByTagName("head")[0];
      head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
      const testElement = document.querySelector("#css");
      let fontFamily;
      while (true) {
        await sleepMs(100);
        fontFamily = getComputedStyle(testElement).fontFamily;
        if (fontFamily.startsWith("fake")) {
          break;
        }
      }
      console.log(fontFamily);
      return fontFamily;
    }
result, same session: 
fake_11379715226408527,
fake_49154901601409695
result, different session: 
fake_11379715226408527,
fake_49154901601409695
unsupported: false, false
passed: false, false
test failed: false, false | write: async (key) => {
      const href = testURI("resource", "css", key);
      const head = document.getElementsByTagName("head")[0];
      head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
      const testElement = document.querySelector("#css");
      let fontFamily;
      while (true) {
        await sleepMs(100);
        fontFamily = getComputedStyle(testElement).fontFamily;
        if (fontFamily.startsWith("fake")) {
          break;
        }
      }
      console.log(fontFamily);
      return key;
    }
read: async (key) => {
      const href = testURI("resource", "css", key);
      const head = document.getElementsByTagName("head")[0];
      head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
      const testElement = document.querySelector("#css");
      let fontFamily;
      while (true) {
        await sleepMs(100);
        fontFamily = getComputedStyle(testElement).fontFamily;
        if (fontFamily.startsWith("fake")) {
          break;
        }
      }
      console.log(fontFamily);
      return fontFamily;
    }
result, same session: 
fake_1663589707223987,
fake_8346015236876292,
fake_2983882914415559
result, different session: 
fake_024357676555958552,
fake_863224165600124,
fake_7457602342489178
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (key) => {
      const href = testURI("resource", "css", key);
      const head = document.getElementsByTagName("head")[0];
      head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
      const testElement = document.querySelector("#css");
      let fontFamily;
      while (true) {
        await sleepMs(100);
        fontFamily = getComputedStyle(testElement).fontFamily;
        if (fontFamily.startsWith("fake")) {
          break;
        }
      }
      console.log(fontFamily);
      return key;
    }
read: async (key) => {
      const href = testURI("resource", "css", key);
      const head = document.getElementsByTagName("head")[0];
      head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
      const testElement = document.querySelector("#css");
      let fontFamily;
      while (true) {
        await sleepMs(100);
        fontFamily = getComputedStyle(testElement).fontFamily;
        if (fontFamily.startsWith("fake")) {
          break;
        }
      }
      console.log(fontFamily);
      return fontFamily;
    }
result, same session: 
fake_2852250135780452,
fake_311912849753075
result, different session: 
fake_2852250135780452,
fake_311912849753075
unsupported: false, false
passed: false, false
test failed: false, false | write: async (key) => {
      const href = testURI("resource", "css", key);
      const head = document.getElementsByTagName("head")[0];
      head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
      const testElement = document.querySelector("#css");
      let fontFamily;
      while (true) {
        await sleepMs(100);
        fontFamily = getComputedStyle(testElement).fontFamily;
        if (fontFamily.startsWith("fake")) {
          break;
        }
      }
      console.log(fontFamily);
      return key;
    }
read: async (key) => {
      const href = testURI("resource", "css", key);
      const head = document.getElementsByTagName("head")[0];
      head.innerHTML += `<link type="text/css" rel="stylesheet" href="${href}">`;
      const testElement = document.querySelector("#css");
      let fontFamily;
      while (true) {
        await sleepMs(100);
        fontFamily = getComputedStyle(testElement).fontFamily;
        if (fontFamily.startsWith("fake")) {
          break;
        }
      }
      console.log(fontFamily);
      return fontFamily;
    }
result, same session: 
fake_22723225708248407,
fake_882451567651815
result, different session: 
fake_22723225708248407,
fake_882451567651815
unsupported: false, false
passed: false, false
test failed: false, false | 
| favicon cache A favicon is an icon that represents a website, typically shown in browser tab and bookmarks menu. If the favicon cache is not partitioned, it can be used to track users across websites.
               | write: (key) => key
read: async (key) => {
      // Wait for the favicon to load (defined in supercookies.html)
      await sleepMs(2000);
      let response = await fetch(
        testURI("ctr", "favicon", key), {"cache": "reload"});
      let count = (await response.text()).trim();
      if (count === "0") {
        throw new Error("No requests received");
      }
      return count;
    }
result, same session: 1, 1, 1
result, different session: 1, 1, 1
unsupported: false, false, false
passed: false, false, false
test failed: false, false, false | write: (key) => key
read: async (key) => {
      // Wait for the favicon to load (defined in supercookies.html)
      await sleepMs(2000);
      let response = await fetch(
        testURI("ctr", "favicon", key), {"cache": "reload"});
      let count = (await response.text()).trim();
      if (count === "0") {
        throw new Error("No requests received");
      }
      return count;
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: (key) => key
read: async (key) => {
      // Wait for the favicon to load (defined in supercookies.html)
      await sleepMs(2000);
      let response = await fetch(
        testURI("ctr", "favicon", key), {"cache": "reload"});
      let count = (await response.text()).trim();
      if (count === "0") {
        throw new Error("No requests received");
      }
      return count;
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: (key) => key
read: async (key) => {
      // Wait for the favicon to load (defined in supercookies.html)
      await sleepMs(2000);
      let response = await fetch(
        testURI("ctr", "favicon", key), {"cache": "reload"});
      let count = (await response.text()).trim();
      if (count === "0") {
        throw new Error("No requests received");
      }
      return count;
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: (key) => key
read: async (key) => {
      // Wait for the favicon to load (defined in supercookies.html)
      await sleepMs(2000);
      let response = await fetch(
        testURI("ctr", "favicon", key), {"cache": "reload"});
      let count = (await response.text()).trim();
      if (count === "0") {
        throw new Error("No requests received");
      }
      return count;
    }
result, same session: 1, 1, 1
result, different session: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (key) => key
read: async (key) => {
      // Wait for the favicon to load (defined in supercookies.html)
      await sleepMs(2000);
      let response = await fetch(
        testURI("ctr", "favicon", key), {"cache": "reload"});
      let count = (await response.text()).trim();
      if (count === "0") {
        throw new Error("No requests received");
      }
      return count;
    }
result, same session: 1, 1, 1
result, different session: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (key) => key
read: async (key) => {
      // Wait for the favicon to load (defined in supercookies.html)
      await sleepMs(2000);
      let response = await fetch(
        testURI("ctr", "favicon", key), {"cache": "reload"});
      let count = (await response.text()).trim();
      if (count === "0") {
        throw new Error("No requests received");
      }
      return count;
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: (key) => key
read: async (key) => {
      // Wait for the favicon to load (defined in supercookies.html)
      await sleepMs(2000);
      let response = await fetch(
        testURI("ctr", "favicon", key), {"cache": "reload"});
      let count = (await response.text()).trim();
      if (count === "0") {
        throw new Error("No requests received");
      }
      return count;
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: (key) => key
read: async (key) => {
      // Wait for the favicon to load (defined in supercookies.html)
      await sleepMs(2000);
      let response = await fetch(
        testURI("ctr", "favicon", key), {"cache": "reload"});
      let count = (await response.text()).trim();
      if (count === "0") {
        throw new Error("No requests received");
      }
      return count;
    }
result, same session: 1, 1, 1
result, different session: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (key) => key
read: async (key) => {
      // Wait for the favicon to load (defined in supercookies.html)
      await sleepMs(2000);
      let response = await fetch(
        testURI("ctr", "favicon", key), {"cache": "reload"});
      let count = (await response.text()).trim();
      if (count === "0") {
        throw new Error("No requests received");
      }
      return count;
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: (key) => key
read: async (key) => {
      // Wait for the favicon to load (defined in supercookies.html)
      await sleepMs(2000);
      let response = await fetch(
        testURI("ctr", "favicon", key), {"cache": "reload"});
      let count = (await response.text()).trim();
      if (count === "0") {
        throw new Error("No requests received");
      }
      return count;
    }
result, same session: 2, 2
result, different session: 2, 2
unsupported: false, false
passed: false, false
test failed: false, false | 
| fetch cache When a resource is received via the Fetch API, it is frequently cached. That cache can potentially be abused for cross-site tracking.
               | write: async (key) => {
      let response = await fetch(testURI("resource", "fetch", key),
                                 {cache: "force-cache"});
      return key;
    }
read: async (key) => {
      let response = await fetch(testURI("resource", "fetch", key),
                                 {cache: "force-cache"});
      let countResponse = await fetch(testURI("ctr", "fetch", key),
                                      {cache: "reload"});
      return (await countResponse.text()).trim();
    }
result, same session: 1, 1, 1
result, different session: 1, 1, 1
unsupported: false, false, false
passed: false, false, false
test failed: false, false, false | write: async (key) => {
      let response = await fetch(testURI("resource", "fetch", key),
                                 {cache: "force-cache"});
      return key;
    }
read: async (key) => {
      let response = await fetch(testURI("resource", "fetch", key),
                                 {cache: "force-cache"});
      let countResponse = await fetch(testURI("ctr", "fetch", key),
                                      {cache: "reload"});
      return (await countResponse.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: async (key) => {
      let response = await fetch(testURI("resource", "fetch", key),
                                 {cache: "force-cache"});
      return key;
    }
read: async (key) => {
      let response = await fetch(testURI("resource", "fetch", key),
                                 {cache: "force-cache"});
      let countResponse = await fetch(testURI("ctr", "fetch", key),
                                      {cache: "reload"});
      return (await countResponse.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: async (key) => {
      let response = await fetch(testURI("resource", "fetch", key),
                                 {cache: "force-cache"});
      return key;
    }
read: async (key) => {
      let response = await fetch(testURI("resource", "fetch", key),
                                 {cache: "force-cache"});
      let countResponse = await fetch(testURI("ctr", "fetch", key),
                                      {cache: "reload"});
      return (await countResponse.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: async (key) => {
      let response = await fetch(testURI("resource", "fetch", key),
                                 {cache: "force-cache"});
      return key;
    }
read: async (key) => {
      let response = await fetch(testURI("resource", "fetch", key),
                                 {cache: "force-cache"});
      let countResponse = await fetch(testURI("ctr", "fetch", key),
                                      {cache: "reload"});
      return (await countResponse.text()).trim();
    }
result, same session: 1, 1, 1
result, different session: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (key) => {
      let response = await fetch(testURI("resource", "fetch", key),
                                 {cache: "force-cache"});
      return key;
    }
read: async (key) => {
      let response = await fetch(testURI("resource", "fetch", key),
                                 {cache: "force-cache"});
      let countResponse = await fetch(testURI("ctr", "fetch", key),
                                      {cache: "reload"});
      return (await countResponse.text()).trim();
    }
result, same session: 1, 1, 1
result, different session: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (key) => {
      let response = await fetch(testURI("resource", "fetch", key),
                                 {cache: "force-cache"});
      return key;
    }
read: async (key) => {
      let response = await fetch(testURI("resource", "fetch", key),
                                 {cache: "force-cache"});
      let countResponse = await fetch(testURI("ctr", "fetch", key),
                                      {cache: "reload"});
      return (await countResponse.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: async (key) => {
      let response = await fetch(testURI("resource", "fetch", key),
                                 {cache: "force-cache"});
      return key;
    }
read: async (key) => {
      let response = await fetch(testURI("resource", "fetch", key),
                                 {cache: "force-cache"});
      let countResponse = await fetch(testURI("ctr", "fetch", key),
                                      {cache: "reload"});
      return (await countResponse.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: async (key) => {
      let response = await fetch(testURI("resource", "fetch", key),
                                 {cache: "force-cache"});
      return key;
    }
read: async (key) => {
      let response = await fetch(testURI("resource", "fetch", key),
                                 {cache: "force-cache"});
      let countResponse = await fetch(testURI("ctr", "fetch", key),
                                      {cache: "reload"});
      return (await countResponse.text()).trim();
    }
result, same session: 1, 1, 1
result, different session: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (key) => {
      let response = await fetch(testURI("resource", "fetch", key),
                                 {cache: "force-cache"});
      return key;
    }
read: async (key) => {
      let response = await fetch(testURI("resource", "fetch", key),
                                 {cache: "force-cache"});
      let countResponse = await fetch(testURI("ctr", "fetch", key),
                                      {cache: "reload"});
      return (await countResponse.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: async (key) => {
      let response = await fetch(testURI("resource", "fetch", key),
                                 {cache: "force-cache"});
      return key;
    }
read: async (key) => {
      let response = await fetch(testURI("resource", "fetch", key),
                                 {cache: "force-cache"});
      let countResponse = await fetch(testURI("ctr", "fetch", key),
                                      {cache: "reload"});
      return (await countResponse.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | 
| font cache Web fonts are sometimes stored in their own cache, which is vulnerable to being abused for cross-site tracking.
               | write: async (key) => {
      let style = document.createElement("style");
      style.type='text/css';
      let fontURI = testURI("resource", "font", key);
      style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } body { font-family: "myFont" }`;
      document.getElementsByTagName("head")[0].appendChild(style);
      return key;
    }
read: async (key) => {
      const text = document.createElement("span");
      text.id = "text";
      text.innerText = "test";
      document.body.appendChild(text);
      const originalWidth = text.getBoundingClientRect().width;
      let style = document.createElement("style");
      style.type='text/css';
      let fontURI = testURI("resource", "font", key);
      style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } #text { font-family: "myFont" }`;
      document.getElementsByTagName("head")[0].appendChild(style);
      let newWidth;
      do {
        await sleepMs(100);
        newWidth = text.getBoundingClientRect().width;
      } while (newWidth < 0 || newWidth === originalWidth)
      let response = await fetch(
        testURI("ctr", "font", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1, 1
result, different session: 1, 1, 1
unsupported: false, false, false
passed: false, false, false
test failed: false, false, false | write: async (key) => {
      let style = document.createElement("style");
      style.type='text/css';
      let fontURI = testURI("resource", "font", key);
      style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } body { font-family: "myFont" }`;
      document.getElementsByTagName("head")[0].appendChild(style);
      return key;
    }
read: async (key) => {
      const text = document.createElement("span");
      text.id = "text";
      text.innerText = "test";
      document.body.appendChild(text);
      const originalWidth = text.getBoundingClientRect().width;
      let style = document.createElement("style");
      style.type='text/css';
      let fontURI = testURI("resource", "font", key);
      style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } #text { font-family: "myFont" }`;
      document.getElementsByTagName("head")[0].appendChild(style);
      let newWidth;
      do {
        await sleepMs(100);
        newWidth = text.getBoundingClientRect().width;
      } while (newWidth < 0 || newWidth === originalWidth)
      let response = await fetch(
        testURI("ctr", "font", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: async (key) => {
      let style = document.createElement("style");
      style.type='text/css';
      let fontURI = testURI("resource", "font", key);
      style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } body { font-family: "myFont" }`;
      document.getElementsByTagName("head")[0].appendChild(style);
      return key;
    }
read: async (key) => {
      const text = document.createElement("span");
      text.id = "text";
      text.innerText = "test";
      document.body.appendChild(text);
      const originalWidth = text.getBoundingClientRect().width;
      let style = document.createElement("style");
      style.type='text/css';
      let fontURI = testURI("resource", "font", key);
      style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } #text { font-family: "myFont" }`;
      document.getElementsByTagName("head")[0].appendChild(style);
      let newWidth;
      do {
        await sleepMs(100);
        newWidth = text.getBoundingClientRect().width;
      } while (newWidth < 0 || newWidth === originalWidth)
      let response = await fetch(
        testURI("ctr", "font", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: async (key) => {
      let style = document.createElement("style");
      style.type='text/css';
      let fontURI = testURI("resource", "font", key);
      style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } body { font-family: "myFont" }`;
      document.getElementsByTagName("head")[0].appendChild(style);
      return key;
    }
read: async (key) => {
      const text = document.createElement("span");
      text.id = "text";
      text.innerText = "test";
      document.body.appendChild(text);
      const originalWidth = text.getBoundingClientRect().width;
      let style = document.createElement("style");
      style.type='text/css';
      let fontURI = testURI("resource", "font", key);
      style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } #text { font-family: "myFont" }`;
      document.getElementsByTagName("head")[0].appendChild(style);
      let newWidth;
      do {
        await sleepMs(100);
        newWidth = text.getBoundingClientRect().width;
      } while (newWidth < 0 || newWidth === originalWidth)
      let response = await fetch(
        testURI("ctr", "font", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 2, 1
result, different session: 2, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: async (key) => {
      let style = document.createElement("style");
      style.type='text/css';
      let fontURI = testURI("resource", "font", key);
      style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } body { font-family: "myFont" }`;
      document.getElementsByTagName("head")[0].appendChild(style);
      return key;
    }
read: async (key) => {
      const text = document.createElement("span");
      text.id = "text";
      text.innerText = "test";
      document.body.appendChild(text);
      const originalWidth = text.getBoundingClientRect().width;
      let style = document.createElement("style");
      style.type='text/css';
      let fontURI = testURI("resource", "font", key);
      style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } #text { font-family: "myFont" }`;
      document.getElementsByTagName("head")[0].appendChild(style);
      let newWidth;
      do {
        await sleepMs(100);
        newWidth = text.getBoundingClientRect().width;
      } while (newWidth < 0 || newWidth === originalWidth)
      let response = await fetch(
        testURI("ctr", "font", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1, 1
result, different session: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (key) => {
      let style = document.createElement("style");
      style.type='text/css';
      let fontURI = testURI("resource", "font", key);
      style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } body { font-family: "myFont" }`;
      document.getElementsByTagName("head")[0].appendChild(style);
      return key;
    }
read: async (key) => {
      const text = document.createElement("span");
      text.id = "text";
      text.innerText = "test";
      document.body.appendChild(text);
      const originalWidth = text.getBoundingClientRect().width;
      let style = document.createElement("style");
      style.type='text/css';
      let fontURI = testURI("resource", "font", key);
      style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } #text { font-family: "myFont" }`;
      document.getElementsByTagName("head")[0].appendChild(style);
      let newWidth;
      do {
        await sleepMs(100);
        newWidth = text.getBoundingClientRect().width;
      } while (newWidth < 0 || newWidth === originalWidth)
      let response = await fetch(
        testURI("ctr", "font", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1, 1
result, different session: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (key) => {
      let style = document.createElement("style");
      style.type='text/css';
      let fontURI = testURI("resource", "font", key);
      style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } body { font-family: "myFont" }`;
      document.getElementsByTagName("head")[0].appendChild(style);
      return key;
    }
read: async (key) => {
      const text = document.createElement("span");
      text.id = "text";
      text.innerText = "test";
      document.body.appendChild(text);
      const originalWidth = text.getBoundingClientRect().width;
      let style = document.createElement("style");
      style.type='text/css';
      let fontURI = testURI("resource", "font", key);
      style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } #text { font-family: "myFont" }`;
      document.getElementsByTagName("head")[0].appendChild(style);
      let newWidth;
      do {
        await sleepMs(100);
        newWidth = text.getBoundingClientRect().width;
      } while (newWidth < 0 || newWidth === originalWidth)
      let response = await fetch(
        testURI("ctr", "font", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: async (key) => {
      let style = document.createElement("style");
      style.type='text/css';
      let fontURI = testURI("resource", "font", key);
      style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } body { font-family: "myFont" }`;
      document.getElementsByTagName("head")[0].appendChild(style);
      return key;
    }
read: async (key) => {
      const text = document.createElement("span");
      text.id = "text";
      text.innerText = "test";
      document.body.appendChild(text);
      const originalWidth = text.getBoundingClientRect().width;
      let style = document.createElement("style");
      style.type='text/css';
      let fontURI = testURI("resource", "font", key);
      style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } #text { font-family: "myFont" }`;
      document.getElementsByTagName("head")[0].appendChild(style);
      let newWidth;
      do {
        await sleepMs(100);
        newWidth = text.getBoundingClientRect().width;
      } while (newWidth < 0 || newWidth === originalWidth)
      let response = await fetch(
        testURI("ctr", "font", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: async (key) => {
      let style = document.createElement("style");
      style.type='text/css';
      let fontURI = testURI("resource", "font", key);
      style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } body { font-family: "myFont" }`;
      document.getElementsByTagName("head")[0].appendChild(style);
      return key;
    }
read: async (key) => {
      const text = document.createElement("span");
      text.id = "text";
      text.innerText = "test";
      document.body.appendChild(text);
      const originalWidth = text.getBoundingClientRect().width;
      let style = document.createElement("style");
      style.type='text/css';
      let fontURI = testURI("resource", "font", key);
      style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } #text { font-family: "myFont" }`;
      document.getElementsByTagName("head")[0].appendChild(style);
      let newWidth;
      do {
        await sleepMs(100);
        newWidth = text.getBoundingClientRect().width;
      } while (newWidth < 0 || newWidth === originalWidth)
      let response = await fetch(
        testURI("ctr", "font", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1, 1
result, different session: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (key) => {
      let style = document.createElement("style");
      style.type='text/css';
      let fontURI = testURI("resource", "font", key);
      style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } body { font-family: "myFont" }`;
      document.getElementsByTagName("head")[0].appendChild(style);
      return key;
    }
read: async (key) => {
      const text = document.createElement("span");
      text.id = "text";
      text.innerText = "test";
      document.body.appendChild(text);
      const originalWidth = text.getBoundingClientRect().width;
      let style = document.createElement("style");
      style.type='text/css';
      let fontURI = testURI("resource", "font", key);
      style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } #text { font-family: "myFont" }`;
      document.getElementsByTagName("head")[0].appendChild(style);
      let newWidth;
      do {
        await sleepMs(100);
        newWidth = text.getBoundingClientRect().width;
      } while (newWidth < 0 || newWidth === originalWidth)
      let response = await fetch(
        testURI("ctr", "font", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: async (key) => {
      let style = document.createElement("style");
      style.type='text/css';
      let fontURI = testURI("resource", "font", key);
      style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } body { font-family: "myFont" }`;
      document.getElementsByTagName("head")[0].appendChild(style);
      return key;
    }
read: async (key) => {
      const text = document.createElement("span");
      text.id = "text";
      text.innerText = "test";
      document.body.appendChild(text);
      const originalWidth = text.getBoundingClientRect().width;
      let style = document.createElement("style");
      style.type='text/css';
      let fontURI = testURI("resource", "font", key);
      style.innerHTML = `@font-face {font-family: "myFont"; src: url("${fontURI}"); } #text { font-family: "myFont" }`;
      document.getElementsByTagName("head")[0].appendChild(style);
      let newWidth;
      do {
        await sleepMs(100);
        newWidth = text.getBoundingClientRect().width;
      } while (newWidth < 0 || newWidth === originalWidth)
      let response = await fetch(
        testURI("ctr", "font", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | 
| iframe cache An iframe is an element in a web page than allows websites to embed a second web page. Caching of this web page could be abused for cross-site tracking.
               | write: (key) => new Promise((resolve, reject) => {
      let iframe = document.createElement("iframe");
      document.body.appendChild(iframe);
      iframe.addEventListener("load", () => resolve(key), {once: true});
      iframe.src = testURI("resource", "page", key);
    })
read: async (key) => {
      let iframe = document.createElement("iframe");
      document.body.appendChild(iframe);
      let iframeLoadPromise = new Promise((resolve, reject) => {
        iframe.addEventListener("load", resolve, {once: true});
      });
      let address = testURI("resource", "page", key);
      iframe.src = address;
      await iframeLoadPromise;
      let response = await fetch(
        testURI("ctr", "page", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1, 1
result, different session: 1, 1, 1
unsupported: false, false, false
passed: false, false, false
test failed: false, false, false | write: (key) => new Promise((resolve, reject) => {
      let iframe = document.createElement("iframe");
      document.body.appendChild(iframe);
      iframe.addEventListener("load", () => resolve(key), {once: true});
      iframe.src = testURI("resource", "page", key);
    })
read: async (key) => {
      let iframe = document.createElement("iframe");
      document.body.appendChild(iframe);
      let iframeLoadPromise = new Promise((resolve, reject) => {
        iframe.addEventListener("load", resolve, {once: true});
      });
      let address = testURI("resource", "page", key);
      iframe.src = address;
      await iframeLoadPromise;
      let response = await fetch(
        testURI("ctr", "page", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: (key) => new Promise((resolve, reject) => {
      let iframe = document.createElement("iframe");
      document.body.appendChild(iframe);
      iframe.addEventListener("load", () => resolve(key), {once: true});
      iframe.src = testURI("resource", "page", key);
    })
read: async (key) => {
      let iframe = document.createElement("iframe");
      document.body.appendChild(iframe);
      let iframeLoadPromise = new Promise((resolve, reject) => {
        iframe.addEventListener("load", resolve, {once: true});
      });
      let address = testURI("resource", "page", key);
      iframe.src = address;
      await iframeLoadPromise;
      let response = await fetch(
        testURI("ctr", "page", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: (key) => new Promise((resolve, reject) => {
      let iframe = document.createElement("iframe");
      document.body.appendChild(iframe);
      iframe.addEventListener("load", () => resolve(key), {once: true});
      iframe.src = testURI("resource", "page", key);
    })
read: async (key) => {
      let iframe = document.createElement("iframe");
      document.body.appendChild(iframe);
      let iframeLoadPromise = new Promise((resolve, reject) => {
        iframe.addEventListener("load", resolve, {once: true});
      });
      let address = testURI("resource", "page", key);
      iframe.src = address;
      await iframeLoadPromise;
      let response = await fetch(
        testURI("ctr", "page", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: (key) => new Promise((resolve, reject) => {
      let iframe = document.createElement("iframe");
      document.body.appendChild(iframe);
      iframe.addEventListener("load", () => resolve(key), {once: true});
      iframe.src = testURI("resource", "page", key);
    })
read: async (key) => {
      let iframe = document.createElement("iframe");
      document.body.appendChild(iframe);
      let iframeLoadPromise = new Promise((resolve, reject) => {
        iframe.addEventListener("load", resolve, {once: true});
      });
      let address = testURI("resource", "page", key);
      iframe.src = address;
      await iframeLoadPromise;
      let response = await fetch(
        testURI("ctr", "page", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1, 1
result, different session: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (key) => new Promise((resolve, reject) => {
      let iframe = document.createElement("iframe");
      document.body.appendChild(iframe);
      iframe.addEventListener("load", () => resolve(key), {once: true});
      iframe.src = testURI("resource", "page", key);
    })
read: async (key) => {
      let iframe = document.createElement("iframe");
      document.body.appendChild(iframe);
      let iframeLoadPromise = new Promise((resolve, reject) => {
        iframe.addEventListener("load", resolve, {once: true});
      });
      let address = testURI("resource", "page", key);
      iframe.src = address;
      await iframeLoadPromise;
      let response = await fetch(
        testURI("ctr", "page", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1, 1
result, different session: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (key) => new Promise((resolve, reject) => {
      let iframe = document.createElement("iframe");
      document.body.appendChild(iframe);
      iframe.addEventListener("load", () => resolve(key), {once: true});
      iframe.src = testURI("resource", "page", key);
    })
read: async (key) => {
      let iframe = document.createElement("iframe");
      document.body.appendChild(iframe);
      let iframeLoadPromise = new Promise((resolve, reject) => {
        iframe.addEventListener("load", resolve, {once: true});
      });
      let address = testURI("resource", "page", key);
      iframe.src = address;
      await iframeLoadPromise;
      let response = await fetch(
        testURI("ctr", "page", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: (key) => new Promise((resolve, reject) => {
      let iframe = document.createElement("iframe");
      document.body.appendChild(iframe);
      iframe.addEventListener("load", () => resolve(key), {once: true});
      iframe.src = testURI("resource", "page", key);
    })
read: async (key) => {
      let iframe = document.createElement("iframe");
      document.body.appendChild(iframe);
      let iframeLoadPromise = new Promise((resolve, reject) => {
        iframe.addEventListener("load", resolve, {once: true});
      });
      let address = testURI("resource", "page", key);
      iframe.src = address;
      await iframeLoadPromise;
      let response = await fetch(
        testURI("ctr", "page", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: (key) => new Promise((resolve, reject) => {
      let iframe = document.createElement("iframe");
      document.body.appendChild(iframe);
      iframe.addEventListener("load", () => resolve(key), {once: true});
      iframe.src = testURI("resource", "page", key);
    })
read: async (key) => {
      let iframe = document.createElement("iframe");
      document.body.appendChild(iframe);
      let iframeLoadPromise = new Promise((resolve, reject) => {
        iframe.addEventListener("load", resolve, {once: true});
      });
      let address = testURI("resource", "page", key);
      iframe.src = address;
      await iframeLoadPromise;
      let response = await fetch(
        testURI("ctr", "page", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1, 1
result, different session: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (key) => new Promise((resolve, reject) => {
      let iframe = document.createElement("iframe");
      document.body.appendChild(iframe);
      iframe.addEventListener("load", () => resolve(key), {once: true});
      iframe.src = testURI("resource", "page", key);
    })
read: async (key) => {
      let iframe = document.createElement("iframe");
      document.body.appendChild(iframe);
      let iframeLoadPromise = new Promise((resolve, reject) => {
        iframe.addEventListener("load", resolve, {once: true});
      });
      let address = testURI("resource", "page", key);
      iframe.src = address;
      await iframeLoadPromise;
      let response = await fetch(
        testURI("ctr", "page", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: (key) => new Promise((resolve, reject) => {
      let iframe = document.createElement("iframe");
      document.body.appendChild(iframe);
      iframe.addEventListener("load", () => resolve(key), {once: true});
      iframe.src = testURI("resource", "page", key);
    })
read: async (key) => {
      let iframe = document.createElement("iframe");
      document.body.appendChild(iframe);
      let iframeLoadPromise = new Promise((resolve, reject) => {
        iframe.addEventListener("load", resolve, {once: true});
      });
      let address = testURI("resource", "page", key);
      iframe.src = address;
      await iframeLoadPromise;
      let response = await fetch(
        testURI("ctr", "page", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | 
| image cache Caching of images in web browsers is a standard behavior. But if that cache leaks between websites, it can be abused for cross-site tracking.
               | write: (key) => new Promise((resolve, reject) => {
      let img = document.createElement("img");
      document.body.appendChild(img);
      img.addEventListener("load", () => resolve(key), {once: true});
      img.src = testURI("resource", "image", key);
    })
read: async (key) => {
      let img = document.createElement("img");
      document.body.appendChild(img);
      let imgLoadPromise = new Promise((resolve, reject) => {
        img.addEventListener("load", resolve, {once: true});
      });
      img.src = testURI("resource", "image", key);
      await imgLoadPromise;
      let response = await fetch(
        testURI("ctr", "image", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1, 1
result, different session: 1, 1, 1
unsupported: false, false, false
passed: false, false, false
test failed: false, false, false | write: (key) => new Promise((resolve, reject) => {
      let img = document.createElement("img");
      document.body.appendChild(img);
      img.addEventListener("load", () => resolve(key), {once: true});
      img.src = testURI("resource", "image", key);
    })
read: async (key) => {
      let img = document.createElement("img");
      document.body.appendChild(img);
      let imgLoadPromise = new Promise((resolve, reject) => {
        img.addEventListener("load", resolve, {once: true});
      });
      img.src = testURI("resource", "image", key);
      await imgLoadPromise;
      let response = await fetch(
        testURI("ctr", "image", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: (key) => new Promise((resolve, reject) => {
      let img = document.createElement("img");
      document.body.appendChild(img);
      img.addEventListener("load", () => resolve(key), {once: true});
      img.src = testURI("resource", "image", key);
    })
read: async (key) => {
      let img = document.createElement("img");
      document.body.appendChild(img);
      let imgLoadPromise = new Promise((resolve, reject) => {
        img.addEventListener("load", resolve, {once: true});
      });
      img.src = testURI("resource", "image", key);
      await imgLoadPromise;
      let response = await fetch(
        testURI("ctr", "image", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: (key) => new Promise((resolve, reject) => {
      let img = document.createElement("img");
      document.body.appendChild(img);
      img.addEventListener("load", () => resolve(key), {once: true});
      img.src = testURI("resource", "image", key);
    })
read: async (key) => {
      let img = document.createElement("img");
      document.body.appendChild(img);
      let imgLoadPromise = new Promise((resolve, reject) => {
        img.addEventListener("load", resolve, {once: true});
      });
      img.src = testURI("resource", "image", key);
      await imgLoadPromise;
      let response = await fetch(
        testURI("ctr", "image", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 2, 2
result, different session: 2, 2
unsupported: false, false
passed: false, false
test failed: false, false | write: (key) => new Promise((resolve, reject) => {
      let img = document.createElement("img");
      document.body.appendChild(img);
      img.addEventListener("load", () => resolve(key), {once: true});
      img.src = testURI("resource", "image", key);
    })
read: async (key) => {
      let img = document.createElement("img");
      document.body.appendChild(img);
      let imgLoadPromise = new Promise((resolve, reject) => {
        img.addEventListener("load", resolve, {once: true});
      });
      img.src = testURI("resource", "image", key);
      await imgLoadPromise;
      let response = await fetch(
        testURI("ctr", "image", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1, 1
result, different session: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (key) => new Promise((resolve, reject) => {
      let img = document.createElement("img");
      document.body.appendChild(img);
      img.addEventListener("load", () => resolve(key), {once: true});
      img.src = testURI("resource", "image", key);
    })
read: async (key) => {
      let img = document.createElement("img");
      document.body.appendChild(img);
      let imgLoadPromise = new Promise((resolve, reject) => {
        img.addEventListener("load", resolve, {once: true});
      });
      img.src = testURI("resource", "image", key);
      await imgLoadPromise;
      let response = await fetch(
        testURI("ctr", "image", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1, 1
result, different session: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (key) => new Promise((resolve, reject) => {
      let img = document.createElement("img");
      document.body.appendChild(img);
      img.addEventListener("load", () => resolve(key), {once: true});
      img.src = testURI("resource", "image", key);
    })
read: async (key) => {
      let img = document.createElement("img");
      document.body.appendChild(img);
      let imgLoadPromise = new Promise((resolve, reject) => {
        img.addEventListener("load", resolve, {once: true});
      });
      img.src = testURI("resource", "image", key);
      await imgLoadPromise;
      let response = await fetch(
        testURI("ctr", "image", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: (key) => new Promise((resolve, reject) => {
      let img = document.createElement("img");
      document.body.appendChild(img);
      img.addEventListener("load", () => resolve(key), {once: true});
      img.src = testURI("resource", "image", key);
    })
read: async (key) => {
      let img = document.createElement("img");
      document.body.appendChild(img);
      let imgLoadPromise = new Promise((resolve, reject) => {
        img.addEventListener("load", resolve, {once: true});
      });
      img.src = testURI("resource", "image", key);
      await imgLoadPromise;
      let response = await fetch(
        testURI("ctr", "image", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: (key) => new Promise((resolve, reject) => {
      let img = document.createElement("img");
      document.body.appendChild(img);
      img.addEventListener("load", () => resolve(key), {once: true});
      img.src = testURI("resource", "image", key);
    })
read: async (key) => {
      let img = document.createElement("img");
      document.body.appendChild(img);
      let imgLoadPromise = new Promise((resolve, reject) => {
        img.addEventListener("load", resolve, {once: true});
      });
      img.src = testURI("resource", "image", key);
      await imgLoadPromise;
      let response = await fetch(
        testURI("ctr", "image", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1, 1
result, different session: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (key) => new Promise((resolve, reject) => {
      let img = document.createElement("img");
      document.body.appendChild(img);
      img.addEventListener("load", () => resolve(key), {once: true});
      img.src = testURI("resource", "image", key);
    })
read: async (key) => {
      let img = document.createElement("img");
      document.body.appendChild(img);
      let imgLoadPromise = new Promise((resolve, reject) => {
        img.addEventListener("load", resolve, {once: true});
      });
      img.src = testURI("resource", "image", key);
      await imgLoadPromise;
      let response = await fetch(
        testURI("ctr", "image", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: (key) => new Promise((resolve, reject) => {
      let img = document.createElement("img");
      document.body.appendChild(img);
      img.addEventListener("load", () => resolve(key), {once: true});
      img.src = testURI("resource", "image", key);
    })
read: async (key) => {
      let img = document.createElement("img");
      document.body.appendChild(img);
      let imgLoadPromise = new Promise((resolve, reject) => {
        img.addEventListener("load", resolve, {once: true});
      });
      img.src = testURI("resource", "image", key);
      await imgLoadPromise;
      let response = await fetch(
        testURI("ctr", "image", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | 
| indexedDB The IndexedDB API exposes a transactional database to web pages. That database can be used to track users across websites, unless it is partitioned.
               | write: async (secret) => {
      try {
        return await IdbKeyVal.set("secret", secret);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: () => IdbKeyVal.get("secret")
result, same session: 
Error: The user denied permission to access the database.,
Error: The user denied permission to access the database.,
Error: The user denied permission to access the database.
result, different session: 
Error: The user denied permission to access the database.,
Error: The user denied permission to access the database.,
Error: The user denied permission to access the database.
unsupported: true, true, true
passed: undefined
test failed: false, false, false | write: async (secret) => {
      try {
        return await IdbKeyVal.set("secret", secret);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: () => IdbKeyVal.get("secret")
result, same session: 
e8485c10-5a92-474a-bd16-de23f46ee0e2_3p,
cd5b479f-1529-4f43-a586-810ed83aeb8b_3p
result, different session: 
e8485c10-5a92-474a-bd16-de23f46ee0e2_3p,
cd5b479f-1529-4f43-a586-810ed83aeb8b_3p
unsupported: false, false
passed: false, false
test failed: false, false | write: async (secret) => {
      try {
        return await IdbKeyVal.set("secret", secret);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: () => IdbKeyVal.get("secret")
result, same session: 
e96973da-bf72-4eb1-b6ab-a4e6c6775fb4_3p,
67fb6941-7c51-4727-b485-317211e65ae0_3p
result, different session: 
e96973da-bf72-4eb1-b6ab-a4e6c6775fb4_3p,
67fb6941-7c51-4727-b485-317211e65ae0_3p
unsupported: false, false
passed: false, false
test failed: false, false | write: async (secret) => {
      try {
        return await IdbKeyVal.set("secret", secret);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: () => IdbKeyVal.get("secret")
result, same session: 
94f1fd6d-f642-403e-8ef8-11180b18b1f6_3p,
2bf6571e-4385-4b68-a076-f6868cbe4954_3p
result, different session: 
94f1fd6d-f642-403e-8ef8-11180b18b1f6_3p,
2bf6571e-4385-4b68-a076-f6868cbe4954_3p
unsupported: false, false
passed: false, false
test failed: false, false | write: async (secret) => {
      try {
        return await IdbKeyVal.set("secret", secret);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: () => IdbKeyVal.get("secret")
result, same session: 
da67bebb-5652-441a-816e-1206b25c0225_3p,
0bad71fe-de93-4434-bfa0-bb7f0db62eea_3p,
6aba94ed-010e-45af-892c-dd670cd8d134_3p
result, different session: undefined
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (secret) => {
      try {
        return await IdbKeyVal.set("secret", secret);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: () => IdbKeyVal.get("secret")
result, same session: 
Error: The operation is insecure.,
Error: The operation is insecure.,
Error: The operation is insecure.
result, different session: 
Error: The operation is insecure.,
Error: The operation is insecure.,
Error: The operation is insecure.
unsupported: true, true, true
passed: undefined
test failed: false, false, false | write: async (secret) => {
      try {
        return await IdbKeyVal.set("secret", secret);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: () => IdbKeyVal.get("secret")
result, same session: 
50c6a369-0b5c-4de5-a70b-a0516825119e_3p,
49e0d8b1-c126-4b6b-be66-6ba7acfd85a9_3p
result, different session: 
50c6a369-0b5c-4de5-a70b-a0516825119e_3p,
49e0d8b1-c126-4b6b-be66-6ba7acfd85a9_3p
unsupported: false, false
passed: false, false
test failed: false, false | write: async (secret) => {
      try {
        return await IdbKeyVal.set("secret", secret);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: () => IdbKeyVal.get("secret")
result, same session: 
6b07cf4a-a332-4a7c-90df-fbc3dc862f86_3p,
2d40813a-a6c5-4a6b-bb1c-f4d01a399217_3p
result, different session: undefined
unsupported: false, false
passed: true, true
test failed: false, false | write: async (secret) => {
      try {
        return await IdbKeyVal.set("secret", secret);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: () => IdbKeyVal.get("secret")
result, same session: 
Error: The operation is insecure.,
Error: The operation is insecure.,
Error: The operation is insecure.
result, different session: 
Error: The operation is insecure.,
Error: The operation is insecure.,
Error: The operation is insecure.
unsupported: true, true, true
passed: undefined
test failed: false, false, false | write: async (secret) => {
      try {
        return await IdbKeyVal.set("secret", secret);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: () => IdbKeyVal.get("secret")
result, same session: 
3242a94e-3344-4cb9-ba7c-ebedaf5d6e01_3p,
87918123-fc52-4196-9d0c-a2cfc3f826d4_3p
result, different session: 
3242a94e-3344-4cb9-ba7c-ebedaf5d6e01_3p,
87918123-fc52-4196-9d0c-a2cfc3f826d4_3p
unsupported: false, false
passed: false, false
test failed: false, false | write: async (secret) => {
      try {
        return await IdbKeyVal.set("secret", secret);
      } catch (e) {
        throw new Error("Unsupported");
      }
    }
read: () => IdbKeyVal.get("secret")
result, same session: 
5c052838-a55e-4690-83c6-b7d572146713_3p,
49042d99-5b0e-4f37-82f3-78de245eea34_3p
result, different session: 
5c052838-a55e-4690-83c6-b7d572146713_3p,
49042d99-5b0e-4f37-82f3-78de245eea34_3p
unsupported: false, false
passed: false, false
test failed: false, false | 
| localStorage The localStorage API gives websites access to a key-value database that will remain available across visits. If the localStorage API is not partitioned or blocked, it can also be used to track users across websites.
               | write: (secret) => localStorage.setItem("secret", secret)
read: () => localStorage.getItem("secret")
result, same session: 
3133a4ca-686b-4047-91c3-9a7e181c53f7_3p,
49872d4a-b01d-4af8-8cbe-85c379a609e2_3p,
58bb2b32-4f83-47bb-91c8-96dce39cd97a_3p
result, different session: , , 
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (secret) => localStorage.setItem("secret", secret)
read: () => localStorage.getItem("secret")
result, same session: 
e8485c10-5a92-474a-bd16-de23f46ee0e2_3p,
cd5b479f-1529-4f43-a586-810ed83aeb8b_3p
result, different session: 
e8485c10-5a92-474a-bd16-de23f46ee0e2_3p,
cd5b479f-1529-4f43-a586-810ed83aeb8b_3p
unsupported: false, false
passed: false, false
test failed: false, false | write: (secret) => localStorage.setItem("secret", secret)
read: () => localStorage.getItem("secret")
result, same session: 
e96973da-bf72-4eb1-b6ab-a4e6c6775fb4_3p,
67fb6941-7c51-4727-b485-317211e65ae0_3p
result, different session: 
e96973da-bf72-4eb1-b6ab-a4e6c6775fb4_3p,
67fb6941-7c51-4727-b485-317211e65ae0_3p
unsupported: false, false
passed: false, false
test failed: false, false | write: (secret) => localStorage.setItem("secret", secret)
read: () => localStorage.getItem("secret")
result, same session: 
94f1fd6d-f642-403e-8ef8-11180b18b1f6_3p,
2bf6571e-4385-4b68-a076-f6868cbe4954_3p
result, different session: 
94f1fd6d-f642-403e-8ef8-11180b18b1f6_3p,
2bf6571e-4385-4b68-a076-f6868cbe4954_3p
unsupported: false, false
passed: false, false
test failed: false, false | write: (secret) => localStorage.setItem("secret", secret)
read: () => localStorage.getItem("secret")
result, same session: 
da67bebb-5652-441a-816e-1206b25c0225_3p,
0bad71fe-de93-4434-bfa0-bb7f0db62eea_3p,
6aba94ed-010e-45af-892c-dd670cd8d134_3p
result, different session: , , 
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (secret) => localStorage.setItem("secret", secret)
read: () => localStorage.getItem("secret")
result, same session: 
Error: The operation is insecure.,
Error: The operation is insecure.,
Error: The operation is insecure.
result, different session: 
Error: The operation is insecure.,
Error: The operation is insecure.,
Error: The operation is insecure.
unsupported: true, true, true
passed: undefined
test failed: false, false, false | write: (secret) => localStorage.setItem("secret", secret)
read: () => localStorage.getItem("secret")
result, same session: 
50c6a369-0b5c-4de5-a70b-a0516825119e_3p,
49e0d8b1-c126-4b6b-be66-6ba7acfd85a9_3p
result, different session: 
50c6a369-0b5c-4de5-a70b-a0516825119e_3p,
49e0d8b1-c126-4b6b-be66-6ba7acfd85a9_3p
unsupported: false, false
passed: false, false
test failed: false, false | write: (secret) => localStorage.setItem("secret", secret)
read: () => localStorage.getItem("secret")
result, same session: 
6b07cf4a-a332-4a7c-90df-fbc3dc862f86_3p,
2d40813a-a6c5-4a6b-bb1c-f4d01a399217_3p
result, different session: , 
unsupported: false, false
passed: true, true
test failed: false, false | write: (secret) => localStorage.setItem("secret", secret)
read: () => localStorage.getItem("secret")
result, same session: 
Error: The operation is insecure.,
Error: The operation is insecure.,
Error: The operation is insecure.
result, different session: 
Error: The operation is insecure.,
Error: The operation is insecure.,
Error: The operation is insecure.
unsupported: true, true, true
passed: undefined
test failed: false, false, false | write: (secret) => localStorage.setItem("secret", secret)
read: () => localStorage.getItem("secret")
result, same session: 
3242a94e-3344-4cb9-ba7c-ebedaf5d6e01_3p,
87918123-fc52-4196-9d0c-a2cfc3f826d4_3p
result, different session: 
3242a94e-3344-4cb9-ba7c-ebedaf5d6e01_3p,
87918123-fc52-4196-9d0c-a2cfc3f826d4_3p
unsupported: false, false
passed: false, false
test failed: false, false | write: (secret) => localStorage.setItem("secret", secret)
read: () => localStorage.getItem("secret")
result, same session: 
5c052838-a55e-4690-83c6-b7d572146713_3p,
49042d99-5b0e-4f37-82f3-78de245eea34_3p
result, different session: 
5c052838-a55e-4690-83c6-b7d572146713_3p,
49042d99-5b0e-4f37-82f3-78de245eea34_3p
unsupported: false, false
passed: false, false
test failed: false, false | 
| prefetch cache A <link rel='prefetch'...> suggests to browsers they should fetch a resource ahead of time and cache it. But if browsers don't partition this cache, it can be used to track users across websites.
               | write: async (key) => {
      let link = document.createElement("link");
      link.rel = "prefetch";
      link.href = testURI("resource", "prefetch", key);
      document.getElementsByTagName("head")[0].appendChild(link);
      return key;
    }
read: async (key) => {
      let link = document.createElement("link");
      link.rel = "prefetch";
      link.href = testURI("resource", "prefetch", key);
      document.getElementsByTagName("head")[0].appendChild(link);
      await sleepMs(500);
      let response = await fetch(
        testURI("ctr", "prefetch", key), {"cache": "reload"});
      let countString = (await response.text()).trim();
      if (parseInt(countString) === 0) {
        throw new Error("No requests received");
      }
      return countString;
    }
result, same session: 1, 1, 1
result, different session: 1, 1, 1
unsupported: false, false, false
passed: false, false, false
test failed: false, false, false | write: async (key) => {
      let link = document.createElement("link");
      link.rel = "prefetch";
      link.href = testURI("resource", "prefetch", key);
      document.getElementsByTagName("head")[0].appendChild(link);
      return key;
    }
read: async (key) => {
      let link = document.createElement("link");
      link.rel = "prefetch";
      link.href = testURI("resource", "prefetch", key);
      document.getElementsByTagName("head")[0].appendChild(link);
      await sleepMs(500);
      let response = await fetch(
        testURI("ctr", "prefetch", key), {"cache": "reload"});
      let countString = (await response.text()).trim();
      if (parseInt(countString) === 0) {
        throw new Error("No requests received");
      }
      return countString;
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: async (key) => {
      let link = document.createElement("link");
      link.rel = "prefetch";
      link.href = testURI("resource", "prefetch", key);
      document.getElementsByTagName("head")[0].appendChild(link);
      return key;
    }
read: async (key) => {
      let link = document.createElement("link");
      link.rel = "prefetch";
      link.href = testURI("resource", "prefetch", key);
      document.getElementsByTagName("head")[0].appendChild(link);
      await sleepMs(500);
      let response = await fetch(
        testURI("ctr", "prefetch", key), {"cache": "reload"});
      let countString = (await response.text()).trim();
      if (parseInt(countString) === 0) {
        throw new Error("No requests received");
      }
      return countString;
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: async (key) => {
      let link = document.createElement("link");
      link.rel = "prefetch";
      link.href = testURI("resource", "prefetch", key);
      document.getElementsByTagName("head")[0].appendChild(link);
      return key;
    }
read: async (key) => {
      let link = document.createElement("link");
      link.rel = "prefetch";
      link.href = testURI("resource", "prefetch", key);
      document.getElementsByTagName("head")[0].appendChild(link);
      await sleepMs(500);
      let response = await fetch(
        testURI("ctr", "prefetch", key), {"cache": "reload"});
      let countString = (await response.text()).trim();
      if (parseInt(countString) === 0) {
        throw new Error("No requests received");
      }
      return countString;
    }
result, same session: 
Error: No requests received,
Error: No requests received
result, different session: 
Error: No requests received,
Error: No requests received
unsupported: true, true
passed: undefined
test failed: false, false | write: async (key) => {
      let link = document.createElement("link");
      link.rel = "prefetch";
      link.href = testURI("resource", "prefetch", key);
      document.getElementsByTagName("head")[0].appendChild(link);
      return key;
    }
read: async (key) => {
      let link = document.createElement("link");
      link.rel = "prefetch";
      link.href = testURI("resource", "prefetch", key);
      document.getElementsByTagName("head")[0].appendChild(link);
      await sleepMs(500);
      let response = await fetch(
        testURI("ctr", "prefetch", key), {"cache": "reload"});
      let countString = (await response.text()).trim();
      if (parseInt(countString) === 0) {
        throw new Error("No requests received");
      }
      return countString;
    }
result, same session: 
Error: No requests received,
Error: No requests received,
Error: No requests received
result, different session: 
Error: No requests received,
Error: No requests received,
Error: No requests received
unsupported: true, true, true
passed: undefined
test failed: false, false, false | write: async (key) => {
      let link = document.createElement("link");
      link.rel = "prefetch";
      link.href = testURI("resource", "prefetch", key);
      document.getElementsByTagName("head")[0].appendChild(link);
      return key;
    }
read: async (key) => {
      let link = document.createElement("link");
      link.rel = "prefetch";
      link.href = testURI("resource", "prefetch", key);
      document.getElementsByTagName("head")[0].appendChild(link);
      await sleepMs(500);
      let response = await fetch(
        testURI("ctr", "prefetch", key), {"cache": "reload"});
      let countString = (await response.text()).trim();
      if (parseInt(countString) === 0) {
        throw new Error("No requests received");
      }
      return countString;
    }
result, same session: 
Error: No requests received,
Error: No requests received,
Error: No requests received
result, different session: 
Error: No requests received,
Error: No requests received,
Error: No requests received
unsupported: true, true, true
passed: undefined
test failed: false, false, false | write: async (key) => {
      let link = document.createElement("link");
      link.rel = "prefetch";
      link.href = testURI("resource", "prefetch", key);
      document.getElementsByTagName("head")[0].appendChild(link);
      return key;
    }
read: async (key) => {
      let link = document.createElement("link");
      link.rel = "prefetch";
      link.href = testURI("resource", "prefetch", key);
      document.getElementsByTagName("head")[0].appendChild(link);
      await sleepMs(500);
      let response = await fetch(
        testURI("ctr", "prefetch", key), {"cache": "reload"});
      let countString = (await response.text()).trim();
      if (parseInt(countString) === 0) {
        throw new Error("No requests received");
      }
      return countString;
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: async (key) => {
      let link = document.createElement("link");
      link.rel = "prefetch";
      link.href = testURI("resource", "prefetch", key);
      document.getElementsByTagName("head")[0].appendChild(link);
      return key;
    }
read: async (key) => {
      let link = document.createElement("link");
      link.rel = "prefetch";
      link.href = testURI("resource", "prefetch", key);
      document.getElementsByTagName("head")[0].appendChild(link);
      await sleepMs(500);
      let response = await fetch(
        testURI("ctr", "prefetch", key), {"cache": "reload"});
      let countString = (await response.text()).trim();
      if (parseInt(countString) === 0) {
        throw new Error("No requests received");
      }
      return countString;
    }
result, same session: 
Error: No requests received,
Error: No requests received
result, different session: 
Error: No requests received,
Error: No requests received
unsupported: true, true
passed: undefined
test failed: false, false | write: async (key) => {
      let link = document.createElement("link");
      link.rel = "prefetch";
      link.href = testURI("resource", "prefetch", key);
      document.getElementsByTagName("head")[0].appendChild(link);
      return key;
    }
read: async (key) => {
      let link = document.createElement("link");
      link.rel = "prefetch";
      link.href = testURI("resource", "prefetch", key);
      document.getElementsByTagName("head")[0].appendChild(link);
      await sleepMs(500);
      let response = await fetch(
        testURI("ctr", "prefetch", key), {"cache": "reload"});
      let countString = (await response.text()).trim();
      if (parseInt(countString) === 0) {
        throw new Error("No requests received");
      }
      return countString;
    }
result, same session: 
Error: No requests received,
Error: No requests received,
Error: No requests received
result, different session: 
Error: No requests received,
Error: No requests received,
Error: No requests received
unsupported: true, true, true
passed: undefined
test failed: false, false, false | write: async (key) => {
      let link = document.createElement("link");
      link.rel = "prefetch";
      link.href = testURI("resource", "prefetch", key);
      document.getElementsByTagName("head")[0].appendChild(link);
      return key;
    }
read: async (key) => {
      let link = document.createElement("link");
      link.rel = "prefetch";
      link.href = testURI("resource", "prefetch", key);
      document.getElementsByTagName("head")[0].appendChild(link);
      await sleepMs(500);
      let response = await fetch(
        testURI("ctr", "prefetch", key), {"cache": "reload"});
      let countString = (await response.text()).trim();
      if (parseInt(countString) === 0) {
        throw new Error("No requests received");
      }
      return countString;
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: async (key) => {
      let link = document.createElement("link");
      link.rel = "prefetch";
      link.href = testURI("resource", "prefetch", key);
      document.getElementsByTagName("head")[0].appendChild(link);
      return key;
    }
read: async (key) => {
      let link = document.createElement("link");
      link.rel = "prefetch";
      link.href = testURI("resource", "prefetch", key);
      document.getElementsByTagName("head")[0].appendChild(link);
      await sleepMs(500);
      let response = await fetch(
        testURI("ctr", "prefetch", key), {"cache": "reload"});
      let countString = (await response.text()).trim();
      if (parseInt(countString) === 0) {
        throw new Error("No requests received");
      }
      return countString;
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | 
| script cache Caching of scripts in web browsers is a standard behavior. But if that cache leaks between websites, it can be abused for cross-site tracking.
               | write: (key) => new Promise((resolve, reject) => {
      let script = document.createElement("script");
      document.body.appendChild(script);
      script.addEventListener("load", () => resolve(key), {once: true});
      script.src = testURI("resource", "script", key);
    })
read: async (key) => {
      let script = document.createElement("script");
      document.body.appendChild(script);
      let scriptLoadPromise = new Promise((resolve, reject) => {
        script.addEventListener("load", resolve, {once: true});
      });
      script.src = testURI("resource", "script", key);
      await scriptLoadPromise;
      let response = await fetch(
        testURI("ctr", "script", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1, 1
result, different session: 1, 1, 1
unsupported: false, false, false
passed: false, false, false
test failed: false, false, false | write: (key) => new Promise((resolve, reject) => {
      let script = document.createElement("script");
      document.body.appendChild(script);
      script.addEventListener("load", () => resolve(key), {once: true});
      script.src = testURI("resource", "script", key);
    })
read: async (key) => {
      let script = document.createElement("script");
      document.body.appendChild(script);
      let scriptLoadPromise = new Promise((resolve, reject) => {
        script.addEventListener("load", resolve, {once: true});
      });
      script.src = testURI("resource", "script", key);
      await scriptLoadPromise;
      let response = await fetch(
        testURI("ctr", "script", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: (key) => new Promise((resolve, reject) => {
      let script = document.createElement("script");
      document.body.appendChild(script);
      script.addEventListener("load", () => resolve(key), {once: true});
      script.src = testURI("resource", "script", key);
    })
read: async (key) => {
      let script = document.createElement("script");
      document.body.appendChild(script);
      let scriptLoadPromise = new Promise((resolve, reject) => {
        script.addEventListener("load", resolve, {once: true});
      });
      script.src = testURI("resource", "script", key);
      await scriptLoadPromise;
      let response = await fetch(
        testURI("ctr", "script", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: (key) => new Promise((resolve, reject) => {
      let script = document.createElement("script");
      document.body.appendChild(script);
      script.addEventListener("load", () => resolve(key), {once: true});
      script.src = testURI("resource", "script", key);
    })
read: async (key) => {
      let script = document.createElement("script");
      document.body.appendChild(script);
      let scriptLoadPromise = new Promise((resolve, reject) => {
        script.addEventListener("load", resolve, {once: true});
      });
      script.src = testURI("resource", "script", key);
      await scriptLoadPromise;
      let response = await fetch(
        testURI("ctr", "script", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: (key) => new Promise((resolve, reject) => {
      let script = document.createElement("script");
      document.body.appendChild(script);
      script.addEventListener("load", () => resolve(key), {once: true});
      script.src = testURI("resource", "script", key);
    })
read: async (key) => {
      let script = document.createElement("script");
      document.body.appendChild(script);
      let scriptLoadPromise = new Promise((resolve, reject) => {
        script.addEventListener("load", resolve, {once: true});
      });
      script.src = testURI("resource", "script", key);
      await scriptLoadPromise;
      let response = await fetch(
        testURI("ctr", "script", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1, 1
result, different session: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (key) => new Promise((resolve, reject) => {
      let script = document.createElement("script");
      document.body.appendChild(script);
      script.addEventListener("load", () => resolve(key), {once: true});
      script.src = testURI("resource", "script", key);
    })
read: async (key) => {
      let script = document.createElement("script");
      document.body.appendChild(script);
      let scriptLoadPromise = new Promise((resolve, reject) => {
        script.addEventListener("load", resolve, {once: true});
      });
      script.src = testURI("resource", "script", key);
      await scriptLoadPromise;
      let response = await fetch(
        testURI("ctr", "script", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1, 1
result, different session: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (key) => new Promise((resolve, reject) => {
      let script = document.createElement("script");
      document.body.appendChild(script);
      script.addEventListener("load", () => resolve(key), {once: true});
      script.src = testURI("resource", "script", key);
    })
read: async (key) => {
      let script = document.createElement("script");
      document.body.appendChild(script);
      let scriptLoadPromise = new Promise((resolve, reject) => {
        script.addEventListener("load", resolve, {once: true});
      });
      script.src = testURI("resource", "script", key);
      await scriptLoadPromise;
      let response = await fetch(
        testURI("ctr", "script", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: (key) => new Promise((resolve, reject) => {
      let script = document.createElement("script");
      document.body.appendChild(script);
      script.addEventListener("load", () => resolve(key), {once: true});
      script.src = testURI("resource", "script", key);
    })
read: async (key) => {
      let script = document.createElement("script");
      document.body.appendChild(script);
      let scriptLoadPromise = new Promise((resolve, reject) => {
        script.addEventListener("load", resolve, {once: true});
      });
      script.src = testURI("resource", "script", key);
      await scriptLoadPromise;
      let response = await fetch(
        testURI("ctr", "script", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: (key) => new Promise((resolve, reject) => {
      let script = document.createElement("script");
      document.body.appendChild(script);
      script.addEventListener("load", () => resolve(key), {once: true});
      script.src = testURI("resource", "script", key);
    })
read: async (key) => {
      let script = document.createElement("script");
      document.body.appendChild(script);
      let scriptLoadPromise = new Promise((resolve, reject) => {
        script.addEventListener("load", resolve, {once: true});
      });
      script.src = testURI("resource", "script", key);
      await scriptLoadPromise;
      let response = await fetch(
        testURI("ctr", "script", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1, 1
result, different session: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: (key) => new Promise((resolve, reject) => {
      let script = document.createElement("script");
      document.body.appendChild(script);
      script.addEventListener("load", () => resolve(key), {once: true});
      script.src = testURI("resource", "script", key);
    })
read: async (key) => {
      let script = document.createElement("script");
      document.body.appendChild(script);
      let scriptLoadPromise = new Promise((resolve, reject) => {
        script.addEventListener("load", resolve, {once: true});
      });
      script.src = testURI("resource", "script", key);
      await scriptLoadPromise;
      let response = await fetch(
        testURI("ctr", "script", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: (key) => new Promise((resolve, reject) => {
      let script = document.createElement("script");
      document.body.appendChild(script);
      script.addEventListener("load", () => resolve(key), {once: true});
      script.src = testURI("resource", "script", key);
    })
read: async (key) => {
      let script = document.createElement("script");
      document.body.appendChild(script);
      let scriptLoadPromise = new Promise((resolve, reject) => {
        script.addEventListener("load", resolve, {once: true});
      });
      script.src = testURI("resource", "script", key);
      await scriptLoadPromise;
      let response = await fetch(
        testURI("ctr", "script", key), {"cache": "reload"});
      return (await response.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | 
| XMLHttpRequest cache Similar to the newer Fetch API, any resource received may be cached by the browser. The cache is potentially vulnerable to cross-site tracking attack.
               | write: async (key) => {
      const req = new XMLHttpRequest();
      const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
      req.open("GET", testURI("resource", "xhr", key));
      req.send();
      await loadPromise;
      return key;
    }
read: async (key) => {
      const req = new XMLHttpRequest();
      const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
      req.open("GET", testURI("resource", "xhr", key));
      req.send();
      await loadPromise;
      let countResponse = await fetch(testURI("ctr", "xhr", key),
                                      {cache: "reload"});
      return (await countResponse.text()).trim();
    }
result, same session: 1, 1, 1
result, different session: 1, 1, 1
unsupported: false, false, false
passed: false, false, false
test failed: false, false, false | write: async (key) => {
      const req = new XMLHttpRequest();
      const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
      req.open("GET", testURI("resource", "xhr", key));
      req.send();
      await loadPromise;
      return key;
    }
read: async (key) => {
      const req = new XMLHttpRequest();
      const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
      req.open("GET", testURI("resource", "xhr", key));
      req.send();
      await loadPromise;
      let countResponse = await fetch(testURI("ctr", "xhr", key),
                                      {cache: "reload"});
      return (await countResponse.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: async (key) => {
      const req = new XMLHttpRequest();
      const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
      req.open("GET", testURI("resource", "xhr", key));
      req.send();
      await loadPromise;
      return key;
    }
read: async (key) => {
      const req = new XMLHttpRequest();
      const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
      req.open("GET", testURI("resource", "xhr", key));
      req.send();
      await loadPromise;
      let countResponse = await fetch(testURI("ctr", "xhr", key),
                                      {cache: "reload"});
      return (await countResponse.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: async (key) => {
      const req = new XMLHttpRequest();
      const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
      req.open("GET", testURI("resource", "xhr", key));
      req.send();
      await loadPromise;
      return key;
    }
read: async (key) => {
      const req = new XMLHttpRequest();
      const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
      req.open("GET", testURI("resource", "xhr", key));
      req.send();
      await loadPromise;
      let countResponse = await fetch(testURI("ctr", "xhr", key),
                                      {cache: "reload"});
      return (await countResponse.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: async (key) => {
      const req = new XMLHttpRequest();
      const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
      req.open("GET", testURI("resource", "xhr", key));
      req.send();
      await loadPromise;
      return key;
    }
read: async (key) => {
      const req = new XMLHttpRequest();
      const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
      req.open("GET", testURI("resource", "xhr", key));
      req.send();
      await loadPromise;
      let countResponse = await fetch(testURI("ctr", "xhr", key),
                                      {cache: "reload"});
      return (await countResponse.text()).trim();
    }
result, same session: 1, 1, 1
result, different session: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (key) => {
      const req = new XMLHttpRequest();
      const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
      req.open("GET", testURI("resource", "xhr", key));
      req.send();
      await loadPromise;
      return key;
    }
read: async (key) => {
      const req = new XMLHttpRequest();
      const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
      req.open("GET", testURI("resource", "xhr", key));
      req.send();
      await loadPromise;
      let countResponse = await fetch(testURI("ctr", "xhr", key),
                                      {cache: "reload"});
      return (await countResponse.text()).trim();
    }
result, same session: 1, 1, 1
result, different session: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (key) => {
      const req = new XMLHttpRequest();
      const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
      req.open("GET", testURI("resource", "xhr", key));
      req.send();
      await loadPromise;
      return key;
    }
read: async (key) => {
      const req = new XMLHttpRequest();
      const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
      req.open("GET", testURI("resource", "xhr", key));
      req.send();
      await loadPromise;
      let countResponse = await fetch(testURI("ctr", "xhr", key),
                                      {cache: "reload"});
      return (await countResponse.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: async (key) => {
      const req = new XMLHttpRequest();
      const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
      req.open("GET", testURI("resource", "xhr", key));
      req.send();
      await loadPromise;
      return key;
    }
read: async (key) => {
      const req = new XMLHttpRequest();
      const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
      req.open("GET", testURI("resource", "xhr", key));
      req.send();
      await loadPromise;
      let countResponse = await fetch(testURI("ctr", "xhr", key),
                                      {cache: "reload"});
      return (await countResponse.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: async (key) => {
      const req = new XMLHttpRequest();
      const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
      req.open("GET", testURI("resource", "xhr", key));
      req.send();
      await loadPromise;
      return key;
    }
read: async (key) => {
      const req = new XMLHttpRequest();
      const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
      req.open("GET", testURI("resource", "xhr", key));
      req.send();
      await loadPromise;
      let countResponse = await fetch(testURI("ctr", "xhr", key),
                                      {cache: "reload"});
      return (await countResponse.text()).trim();
    }
result, same session: 1, 1, 1
result, different session: 2, 2, 2
unsupported: false, false, false
passed: true, true, true
test failed: false, false, false | write: async (key) => {
      const req = new XMLHttpRequest();
      const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
      req.open("GET", testURI("resource", "xhr", key));
      req.send();
      await loadPromise;
      return key;
    }
read: async (key) => {
      const req = new XMLHttpRequest();
      const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
      req.open("GET", testURI("resource", "xhr", key));
      req.send();
      await loadPromise;
      let countResponse = await fetch(testURI("ctr", "xhr", key),
                                      {cache: "reload"});
      return (await countResponse.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | write: async (key) => {
      const req = new XMLHttpRequest();
      const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
      req.open("GET", testURI("resource", "xhr", key));
      req.send();
      await loadPromise;
      return key;
    }
read: async (key) => {
      const req = new XMLHttpRequest();
      const loadPromise = new Promise(resolve => req.addEventListener("load", resolve));
      req.open("GET", testURI("resource", "xhr", key));
      req.send();
      await loadPromise;
      let countResponse = await fetch(testURI("ctr", "xhr", key),
                                      {cache: "reload"});
      return (await countResponse.text()).trim();
    }
result, same session: 1, 1
result, different session: 1, 1
unsupported: false, false
passed: false, false
test failed: false, false | 
| DNS privacy testsWhich browsers keep their DNS queries encrypted? The Domain Name System (DNS) is the method by which web browsers look up the IP address for each website you visit. In a DNS query, a web browser will ask a DNS resolver (somewhere on the internet) for the IP address corresponding to a domain name (such as nytimes.com) for a website you want to visit. Traditionally, most web browsers have sent their DNS queries unencrypted, which means your ISP or anyone else on the network between your computer and the DNS resolver can eavesdrop on the websites you visit. In recent years, web browsers and operating systems have begun to introduce encrypted DNS, including the DNS over HTTPS (DoH) protocol, to encrypt the DNS request from your browser and the response from the resolver to keep your browsing history from leaking. These tests check whether a browser is still protecting its DNS requests by sending them encrypted.
         | |||||||||||
| Location: Brazil 
  Checks whether the browser decides to use
  encrypted DNS if the computer is located in Brazil.
               | passed: false, false, false leak detected: true, true, true | passed: false, false leak detected: true, true | passed: false, false leak detected: true, true | passed: false, false leak detected: true, true | passed: false, false, false leak detected: true, true, true | passed: true, true, true leak detected: false, false, false | passed: false, false leak detected: true, true | passed: false, false leak detected: true, true | passed: true, true, true leak detected: false, false, false | passed: false, false leak detected: true, true | passed: false, false leak detected: true, true | 
| Location: China 
  Checks whether the browser decides to use
  encrypted DNS if the computer is located in China.
               | passed: false, false, false leak detected: true, true, true | passed: false, false leak detected: true, true | passed: false, false leak detected: true, true | passed: false, false leak detected: true, true | passed: false, false, false leak detected: true, true, true | passed: true, true, true leak detected: false, false, false | passed: false, false leak detected: true, true | passed: false, false leak detected: true, true | passed: true, true, true leak detected: false, false, false | passed: false, false leak detected: true, true | passed: false, false leak detected: true, true | 
| Location: Germany 
  Checks whether the browser decides to use
  encrypted DNS if the computer is located in Germany.
               | passed: false, false, false leak detected: true, true, true | passed: false, false leak detected: true, true | passed: false, false leak detected: true, true | passed: false, false leak detected: true, true | passed: false, false, false leak detected: true, true, true | passed: true, true, true leak detected: false, false, false | passed: false, false leak detected: true, true | passed: false, false leak detected: true, true | passed: true, true, true leak detected: false, false, false | passed: false, false leak detected: true, true | passed: false, false leak detected: true, true | 
| Location: India 
  Checks whether the browser decides to use
  encrypted DNS if the computer is located in India.
               | passed: false, false, false leak detected: true, true, true | passed: false, false leak detected: true, true | passed: false, false leak detected: true, true | passed: false, false leak detected: true, true | passed: false, false, false leak detected: true, true, true | passed: true, true, true leak detected: false, false, false | passed: false, false leak detected: true, true | passed: false, false leak detected: true, true | passed: true, true, true leak detected: false, false, false | passed: false, false leak detected: true, true | passed: false, false leak detected: true, true | 
| Location: Nigeria 
  Checks whether the browser decides to use
  encrypted DNS if the computer is located in Nigeria.
               | passed: false, false, false leak detected: true, true, true | passed: false, false leak detected: true, true | passed: false, false leak detected: true, true | passed: false, false leak detected: true, true | passed: false, false, false leak detected: true, true, true | passed: true, true, true leak detected: false, false, false | passed: false, false leak detected: true, true | passed: false, false leak detected: true, true | passed: true, true, true leak detected: false, false, false | passed: false, false leak detected: true, true | passed: false, false leak detected: true, true | 
| Location: Russia 
  Checks whether the browser decides to use
  encrypted DNS if the computer is located in Russia.
               | passed: false, false, false leak detected: true, true, true | passed: false, false leak detected: true, true | passed: false, false leak detected: true, true | passed: true, true leak detected: false, false | passed: false, false, false leak detected: true, true, true | passed: true, true, true leak detected: false, false, false | passed: false, false leak detected: true, true | passed: false, false leak detected: true, true | passed: true, true, true leak detected: false, false, false | passed: false, false leak detected: true, true | passed: false, false leak detected: true, true | 
| Location: United States 
  Checks whether the browser decides to use
  encrypted DNS if the computer is located in United States.
               | passed: false, false, false leak detected: true, true, true | passed: false, false leak detected: true, true | passed: false, false leak detected: true, true | passed: true, true leak detected: false, false | passed: false, false, false leak detected: true, true, true | passed: true, true, true leak detected: false, false, false | passed: false, false leak detected: true, true | passed: false, false leak detected: true, true | passed: true, true, true leak detected: false, false, false | passed: false, false leak detected: true, true | passed: false, false leak detected: true, true | 
| OS DNS: Cloudflare 
  Checks whether the browser decides to use
  encrypted DNS if the operating system's default DNS provider is Cloudflare.
               | passed: true, true, true leak detected: false, false, false | passed: true, true leak detected: false, false | passed: true, true leak detected: false, false | passed: true, true leak detected: false, false | passed: true, true, true leak detected: false, false, false | passed: true, true, true leak detected: false, false, false | passed: false, false leak detected: true, true | passed: true, true leak detected: false, false | passed: true, true, true leak detected: false, false, false | passed: false, false leak detected: true, true | passed: true, true leak detected: false, false | 
| OS DNS: Comodo 
  Checks whether the browser decides to use
  encrypted DNS if the operating system's default DNS provider is Comodo.
               | passed: false, false, false leak detected: true, true, true | passed: false, false leak detected: true, true | passed: false, false leak detected: true, true | passed: false, false leak detected: true, true | passed: false, false, false leak detected: true, true, true | passed: true, true, true leak detected: false, false, false | passed: false, false leak detected: true, true | passed: false, false leak detected: true, true | passed: true, true, true leak detected: false, false, false | passed: false, false leak detected: true, true | passed: false, false leak detected: true, true | 
| OS DNS: Google 
  Checks whether the browser decides to use
  encrypted DNS if the operating system's default DNS provider is Google.
               | passed: true, true, true leak detected: false, false, false | passed: true, true leak detected: false, false | passed: true, true leak detected: false, false | passed: true, true leak detected: false, false | passed: true, true, true leak detected: false, false, false | passed: true, true, true leak detected: false, false, false | passed: false, false leak detected: true, true | passed: true, true leak detected: false, false | passed: true, true, true leak detected: false, false, false | passed: false, false leak detected: true, true | passed: true, true leak detected: false, false | 
| OS DNS: Quad9 
  Checks whether the browser decides to use
  encrypted DNS if the operating system's default DNS provider is Quad9.
               | passed: false, false, false leak detected: true, true, true | passed: false, false leak detected: true, true | passed: false, false leak detected: true, true | passed: true, true leak detected: false, false | passed: true, true, true leak detected: false, false, false | passed: true, true, true leak detected: false, false, false | passed: false, false leak detected: true, true | passed: true, true leak detected: false, false | passed: true, true, true leak detected: false, false, false | passed: false, false leak detected: true, true | passed: false, false leak detected: true, true |