Bug 1646136 - Isolate prefetch channels per first-party when privacy.partition.network_state is set to true, r=mayhemer
authorAndrea Marchesini <amarchesini@mozilla.com>
Wed, 24 Jun 2020 15:31:31 +0000
changeset 537188 baa45f675f4cb872342c5ac92bbd0411bd2701e6
parent 537187 8bde5b74dd1b69c2929294514ef9c4211c3104a8
child 537189 8d5bc317935c4ae68dcb14fab6f027d1aa247c10
push id37537
push userncsoregi@mozilla.com
push dateWed, 24 Jun 2020 21:50:10 +0000
treeherdermozilla-central@1004d422aedb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmayhemer
bugs1646136
milestone79.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1646136 - Isolate prefetch channels per first-party when privacy.partition.network_state is set to true, r=mayhemer Differential Revision: https://phabricator.services.mozilla.com/D80046
toolkit/components/antitracking/test/xpcshell/test_staticPartition_prefetch.js
toolkit/components/antitracking/test/xpcshell/xpcshell.ini
new file mode 100644
--- /dev/null
+++ b/toolkit/components/antitracking/test/xpcshell/test_staticPartition_prefetch.js
@@ -0,0 +1,170 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+"use strict";
+
+const { NetUtil } = ChromeUtils.import("resource://gre/modules/NetUtil.jsm");
+
+const { CookieXPCShellUtils } = ChromeUtils.import(
+  "resource://testing-common/CookieXPCShellUtils.jsm"
+);
+
+let gHints = 0;
+
+CookieXPCShellUtils.init(this);
+
+function countMatchingCacheEntries(cacheEntries, domain, path) {
+  return cacheEntries
+    .map(entry => entry.uri.asciiSpec)
+    .filter(spec => spec.includes(domain))
+    .filter(spec => spec.includes(path)).length;
+}
+
+async function checkCache(originAttributes) {
+  const loadContextInfo = Services.loadContextInfo.custom(
+    false,
+    originAttributes
+  );
+
+  const data = await new Promise(resolve => {
+    let cacheEntries = [];
+    let cacheVisitor = {
+      onCacheStorageInfo(num, consumption) {},
+      onCacheEntryInfo(uri, idEnhance) {
+        cacheEntries.push({ uri, idEnhance });
+      },
+      onCacheEntryVisitCompleted() {
+        resolve(cacheEntries);
+      },
+      QueryInterface: ChromeUtils.generateQI(["nsICacheStorageVisitor"]),
+    };
+    // Visiting the disk cache also visits memory storage so we do not
+    // need to use Services.cache2.memoryCacheStorage() here.
+    let storage = Services.cache2.diskCacheStorage(loadContextInfo, false);
+    storage.asyncVisitStorage(cacheVisitor, true);
+  });
+
+  let foundEntryCount = countMatchingCacheEntries(
+    data,
+    "example.org",
+    "image.png"
+  );
+  ok(
+    foundEntryCount > 0,
+    `Cache entries expected for image.png and OA=${originAttributes}`
+  );
+}
+
+add_task(async () => {
+  do_get_profile();
+
+  Services.prefs.setBoolPref("network.prefetch-next", true);
+  Services.prefs.setIntPref("network.cookie.cookieBehavior", 0);
+
+  const server = CookieXPCShellUtils.createServer({
+    hosts: ["example.org", "foo.com"],
+  });
+
+  server.registerPathHandler("/image.png", (metadata, response) => {
+    gHints++;
+    response.setHeader("Cache-Control", "max-age=10000", false);
+    response.setStatusLine(metadata.httpVersion, 200, "OK");
+    response.setHeader("Content-Type", "image/png", false);
+    var body =
+      "iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAIAAADZSiLoAAAAEUlEQVQImWP4z8AAQTAamQkAhpcI+DeMzFcAAAAASUVORK5CYII=";
+    response.bodyOutputStream.write(body, body.length);
+  });
+
+  server.registerPathHandler("/prefetch", (metadata, response) => {
+    response.setStatusLine(metadata.httpVersion, 200, "OK");
+    response.setHeader("Content-Type", "text/html", false);
+    var body = `<html><head></head><body><script>
+      const link = document.createElement("link")
+      link.setAttribute("rel", "prefetch");
+      link.setAttribute("href", "http://example.org/image.png");
+      document.head.appendChild(link);
+      link.onload = () => {
+        const img = document.createElement("IMG");
+        img.src = "http://example.org/image.png";
+        document.body.appendChild(img);
+        fetch("/done").then(() => {});
+      }
+    </script></body></html>`;
+    response.bodyOutputStream.write(body, body.length);
+  });
+
+  const tests = [
+    {
+      hints: 2,
+      originAttributes: { partitionKey: "(http,example.org)" },
+      prefValue: true,
+    },
+    {
+      hints: 1,
+      originAttributes: {},
+      prefValue: false,
+    },
+  ];
+
+  for (let test of tests) {
+    await new Promise(resolve =>
+      Services.clearData.deleteData(Ci.nsIClearDataService.CLEAR_ALL, resolve)
+    );
+
+    info("Reset the counter");
+    gHints = 0;
+
+    info("Enabling network state partitioning");
+    Services.prefs.setBoolPref(
+      "privacy.partition.network_state",
+      test.prefValue
+    );
+
+    let complete = new Promise(resolve => {
+      server.registerPathHandler("/done", (metadata, response) => {
+        response.setHeader("Cache-Control", "max-age=10000", false);
+        response.setStatusLine(metadata.httpVersion, 200, "OK");
+        response.setHeader("Content-Type", "text/html", false);
+        var body = "OK";
+        response.bodyOutputStream.write(body, body.length);
+        resolve();
+      });
+    });
+
+    info("Let's load a page with origin A");
+    let contentPage = await CookieXPCShellUtils.loadContentPage(
+      "http://example.org/prefetch"
+    );
+
+    await complete;
+    await checkCache(test.originAttributes);
+    await contentPage.close();
+
+    complete = new Promise(resolve => {
+      server.registerPathHandler("/done", (metadata, response) => {
+        response.setHeader("Cache-Control", "max-age=10000", false);
+        response.setStatusLine(metadata.httpVersion, 200, "OK");
+        response.setHeader("Content-Type", "text/html", false);
+        var body = "OK";
+        response.bodyOutputStream.write(body, body.length);
+        resolve();
+      });
+    });
+
+    info("Let's load a page with origin B");
+    contentPage = await CookieXPCShellUtils.loadContentPage(
+      "http://foo.com/prefetch"
+    );
+
+    await complete;
+    await checkCache(test.originAttributes);
+    await contentPage.close();
+
+    Assert.equal(
+      gHints,
+      test.hints,
+      "We have the current number of requests with pref " + test.prefValue
+    );
+  }
+});
--- a/toolkit/components/antitracking/test/xpcshell/xpcshell.ini
+++ b/toolkit/components/antitracking/test/xpcshell/xpcshell.ini
@@ -6,8 +6,10 @@ head = head.js ../../../../components/ur
 [test_purge_trackers_telemetry.js]
 [test_tracking_db_service.js]
 [test_rejectForeignAllowList.js]
 skip-if = toolkit == 'android' # Bug 1567341
 [test_staticPartition_image.js]
 skip-if = toolkit == 'android' # Bug 1567341
 [test_staticPartition_authhttp.js]
 skip-if = toolkit == 'android' # Bug 1567341
+[test_staticPartition_prefetch.js]
+skip-if = toolkit == 'android' # Bug 1567341