Bug 1480899 - Do not activate the window.open() heuristic for allowing storage access if opener access hasn't been granted; r=englehardt
authorEhsan Akhgari <ehsan@mozilla.com>
Wed, 08 Aug 2018 14:39:16 -0400
changeset 486421 77038440246afb2d878f129dffd4b7c2af6b38ec
parent 486420 ed645e2703e989bd2cb7971162d06fef54d7daf2
child 486422 975fd6febbeabdaf741f859f7f8b60ef1b297480
push id9719
push userffxbld-merge
push dateFri, 24 Aug 2018 17:49:46 +0000
treeherdermozilla-beta@719ec98fba77 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersenglehardt
bugs1480899
milestone63.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 1480899 - Do not activate the window.open() heuristic for allowing storage access if opener access hasn't been granted; r=englehardt
dom/base/nsGlobalWindowOuter.cpp
toolkit/components/antitracking/test/browser/3rdParty.html
toolkit/components/antitracking/test/browser/3rdPartyOpen.html
toolkit/components/antitracking/test/browser/3rdPartyUI.html
toolkit/components/antitracking/test/browser/3rdPartyWO.html
toolkit/components/antitracking/test/browser/browser.ini
toolkit/components/antitracking/test/browser/browser_blockingNoOpener.js
toolkit/components/antitracking/test/browser/head.js
--- a/dom/base/nsGlobalWindowOuter.cpp
+++ b/dom/base/nsGlobalWindowOuter.cpp
@@ -6999,17 +6999,17 @@ nsGlobalWindowOuter::OpenInternal(const 
 
     }
   }
 
   NS_ENSURE_SUCCESS(rv, rv);
 
   // success!
 
-  if (!aCalledNoScript && !windowExists && uri) {
+  if (!aCalledNoScript && !windowExists && uri && !forceNoOpener) {
     MaybeAllowStorageForOpenedWindow(uri);
   }
 
   NS_ENSURE_TRUE(domReturn, NS_OK);
   nsCOMPtr<nsPIDOMWindowOuter> outerReturn =
     nsPIDOMWindowOuter::From(domReturn);
   outerReturn.swap(*aReturn);
 
--- a/toolkit/components/antitracking/test/browser/3rdParty.html
+++ b/toolkit/components/antitracking/test/browser/3rdParty.html
@@ -16,16 +16,17 @@ function ok(what, msg) {
 
 function is(a, b, msg) {
   ok(a === b, msg);
 }
 
 onmessage = function(e) {
   let runnableStr = `(() => {return (${e.data});})();`;
   let runnable = eval(runnableStr); // eslint-disable-line no-eval
-  runnable.call(this).then(_ => {
+  // Phase 1: Here we want to test that a 3rd party context is not blocked if pref is off
+  runnable.call(this, /* Phase */ 1).then(_ => {
     parent.postMessage({ type: "finish" }, "*");
   });
 };
 
 </script>
 </body>
 </html>
--- a/toolkit/components/antitracking/test/browser/3rdPartyOpen.html
+++ b/toolkit/components/antitracking/test/browser/3rdPartyOpen.html
@@ -1,15 +1,17 @@
 <html>
 <head>
   <title>A popup!</title>
 </head>
 <body>
 <h1>hi!</h1>
 <script>
 
-opener.postMessage("hello!", "*");
+if (opener) {
+  opener.postMessage("hello!", "*");
+}
 window.close();
 
 </script>
 </body>
 </html>
 
--- a/toolkit/components/antitracking/test/browser/3rdPartyUI.html
+++ b/toolkit/components/antitracking/test/browser/3rdPartyUI.html
@@ -16,16 +16,18 @@ function ok(what, msg) {
 
 function is(a, b, msg) {
   ok(a === b, msg);
 }
 
 onmessage = function(e) {
   let runnableStr = `(() => {return (${e.data.callback});})();`;
   let runnable = eval(runnableStr); // eslint-disable-line no-eval
-  runnable.call(this).then(_ => {
+  // Phase 3: Here we want to test that a third-party context doesn't
+  // get blocked with user interaction present
+  runnable.call(this, /* Phase */ 3).then(_ => {
     parent.postMessage({ type: "finish" }, "*");
   });
 };
 
 </script>
 </body>
 </html>
--- a/toolkit/components/antitracking/test/browser/3rdPartyWO.html
+++ b/toolkit/components/antitracking/test/browser/3rdPartyWO.html
@@ -16,27 +16,38 @@ function ok(what, msg) {
 
 function is(a, b, msg) {
   ok(a === b, msg);
 }
 
 onmessage = function(e) {
   let runnableStr = `(() => {return (${e.data.blockingCallback});})();`;
   let runnable = eval(runnableStr); // eslint-disable-line no-eval
-  runnable.call(this).then(_ => {
+  // Phase 2: Here we want to test that a third-party context doesn't
+  // get blocked with when the same origin is opened through window.open().
+  runnable.call(this, /* Phase */ 2).then(_ => {
     info("Let's do a window.open()");
     return new Promise(resolve => {
-      onmessage = resolve;
-      window.open("3rdPartyOpen.html");
+
+      if (location.search == "?noopener") {
+        let features = "noopener";
+
+        window.open("3rdPartyOpen.html", undefined, features);
+        setTimeout(resolve, 1000);
+      } else {
+        onmessage = resolve;
+
+        window.open("3rdPartyOpen.html");
+      }
     });
   }).then(_ => {
     info("The popup has been dismissed!");
     let runnableStr = `(() => {return (${e.data.nonBlockingCallback});})();`;
     let runnable = eval(runnableStr); // eslint-disable-line no-eval
-    return runnable.call(this);
+    return runnable.call(this, /* Phase */ 2);
   }).then(_ => {
     parent.postMessage({ type: "finish" }, "*");
   });
 };
 
 </script>
 </body>
 </html>
--- a/toolkit/components/antitracking/test/browser/browser.ini
+++ b/toolkit/components/antitracking/test/browser/browser.ini
@@ -11,15 +11,16 @@ support-files =
   popup.html
 
 [browser_blockingCookies.js]
 support-files = server.sjs
 [browser_blockingIndexedDb.js]
 [browser_blockingStorage.js]
 [browser_blockingWorkers.js]
 [browser_blockingMessaging.js]
+[browser_blockingNoOpener.js]
 [browser_existingCookiesForSubresources.js]
 [browser_imageCache.js]
 support-files = image.sjs
 [browser_subResources.js]
 support-files = subResources.sjs
 [browser_script.js]
 support-files = tracker.js
new file mode 100644
--- /dev/null
+++ b/toolkit/components/antitracking/test/browser/browser_blockingNoOpener.js
@@ -0,0 +1,35 @@
+gFeatures = "noopener";
+
+AntiTracking.runTest("Blocking in the case of noopener windows",
+  async _ => {
+    try {
+      localStorage.foo = 42;
+      ok(false, "LocalStorage cannot be used!");
+    } catch (e) {
+      ok(true, "LocalStorage cannot be used!");
+      is(e.name, "SecurityError", "We want a security error message.");
+    }
+  },
+  async phase => {
+    switch (phase) {
+      case 1:
+        localStorage.foo = 42;
+        ok(true, "LocalStorage is allowed");
+        break;
+      case 2:
+        try {
+          localStorage.foo = 42;
+          ok(false, "LocalStorage cannot be used!");
+        } catch (e) {
+          ok(true, "LocalStorage cannot be used!");
+          is(e.name, "SecurityError", "We want a security error message.");
+        }
+        break;
+    }
+  },
+  async _ => {
+    await new Promise(resolve => {
+      Services.clearData.deleteData(Ci.nsIClearDataService.CLEAR_ALL, value => resolve());
+    });
+  },
+  null, true, false);
--- a/toolkit/components/antitracking/test/browser/head.js
+++ b/toolkit/components/antitracking/test/browser/head.js
@@ -4,16 +4,18 @@ const TEST_3RD_PARTY_DOMAIN = "https://t
 const TEST_PATH = "browser/toolkit/components/antitracking/test/browser/";
 
 const TEST_TOP_PAGE = TEST_DOMAIN + TEST_PATH + "page.html";
 const TEST_POPUP_PAGE = TEST_DOMAIN + TEST_PATH + "popup.html";
 const TEST_3RD_PARTY_PAGE = TEST_3RD_PARTY_DOMAIN + TEST_PATH + "3rdParty.html";
 const TEST_3RD_PARTY_PAGE_WO = TEST_3RD_PARTY_DOMAIN + TEST_PATH + "3rdPartyWO.html";
 const TEST_3RD_PARTY_PAGE_UI = TEST_3RD_PARTY_DOMAIN + TEST_PATH + "3rdPartyUI.html";
 
+var gFeatures = undefined;
+
 let {UrlClassifierTestUtils} = ChromeUtils.import("resource://testing-common/UrlClassifierTestUtils.jsm", {});
 
 this.AntiTracking = {
   runTest(name, callbackTracking, callbackNonTracking, cleanupFunction, extraPrefs, windowOpenTest = true, userInteractionTest = true) {
     // Here we want to test that a 3rd party context is simply blocked.
     this._createTask(name, true, callbackTracking, extraPrefs);
     this._createCleanupTask(cleanupFunction);
 
@@ -125,19 +127,24 @@ this.AntiTracking = {
 
       info("Creating a new tab");
       let tab = BrowserTestUtils.addTab(gBrowser, TEST_TOP_PAGE);
       gBrowser.selectedTab = tab;
 
       let browser = gBrowser.getBrowserForTab(tab);
       await BrowserTestUtils.browserLoaded(browser);
 
+      let pageURL = TEST_3RD_PARTY_PAGE_WO;
+      if (gFeatures == "noopener") {
+        pageURL += "?noopener";
+      }
+
       info("Creating a 3rd party content");
       await ContentTask.spawn(browser,
-                              { page: TEST_3RD_PARTY_PAGE_WO,
+                              { page: pageURL,
                                 blockingCallback: blockingCallback.toString(),
                                 nonBlockingCallback: nonBlockingCallback.toString(),
                               },
                               async function(obj) {
         await new content.Promise(resolve => {
           let ifr = content.document.createElement("iframe");
           ifr.onload = function() {
             info("Sending code to the 3rd party content");