Bug 1522181 - multiple external protocol URL blocker behind pref, r=smaug
authorAndrea Marchesini <amarchesini@mozilla.com>
Thu, 24 Jan 2019 06:31:41 +0100
changeset 515295 24ad7d6e7775e9ada0d86dd9068c9c99a099041a
parent 515262 5d64e252185c4521d414bd197e4159a2971c1617
child 515296 e9ffcdc09fdd83c28201e11a54c2f51e1a22e0ab
push id1953
push userffxbld-merge
push dateMon, 11 Mar 2019 12:10:20 +0000
treeherdermozilla-release@9c35dcbaa899 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1522181
milestone66.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 1522181 - multiple external protocol URL blocker behind pref, r=smaug
docshell/base/nsDocShell.cpp
dom/html/test/test_external_protocol_iframe.html
modules/libpref/init/StaticPrefList.h
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -9635,39 +9635,42 @@ nsresult nsDocShell::DoURILoad(nsDocShel
     return rv;
   }
 
   if (IsFrame()) {
     MOZ_ASSERT(aContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_IFRAME ||
                    aContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_FRAME,
                "DoURILoad thinks this is a frame and InternalLoad does not");
 
-    // Only allow URLs able to return data in iframes.
-    bool doesNotReturnData = false;
-    NS_URIChainHasFlags(aLoadState->URI(),
-                        nsIProtocolHandler::URI_DOES_NOT_RETURN_DATA,
-                        &doesNotReturnData);
-    if (doesNotReturnData) {
-      bool popupBlocked = true;
-
-      // Let's consider external protocols as popups and let's check if the page
-      // is allowed to open them without abuse regardless of allowed events
-      if (PopupBlocker::GetPopupControlState() <= PopupBlocker::openBlocked) {
-        popupBlocked = !PopupBlocker::TryUsePopupOpeningToken();
-      } else {
-        nsCOMPtr<nsINode> loadingNode =
-            mScriptGlobal->AsOuter()->GetFrameElementInternal();
-        if (loadingNode) {
-          popupBlocked = !PopupBlocker::CanShowPopupByPermission(
-              loadingNode->NodePrincipal());
+    if (StaticPrefs::dom_block_external_protocol_in_iframes()) {
+      // Only allow URLs able to return data in iframes.
+      bool doesNotReturnData = false;
+      NS_URIChainHasFlags(aLoadState->URI(),
+                          nsIProtocolHandler::URI_DOES_NOT_RETURN_DATA,
+                          &doesNotReturnData);
+      if (doesNotReturnData) {
+        bool popupBlocked = true;
+
+        // Let's consider external protocols as popups and let's check if the
+        // page is allowed to open them without abuse regardless of allowed
+        // events
+        if (PopupBlocker::GetPopupControlState() <= PopupBlocker::openBlocked) {
+          popupBlocked = !PopupBlocker::TryUsePopupOpeningToken();
+        } else {
+          nsCOMPtr<nsINode> loadingNode =
+              mScriptGlobal->AsOuter()->GetFrameElementInternal();
+          if (loadingNode) {
+            popupBlocked = !PopupBlocker::CanShowPopupByPermission(
+                loadingNode->NodePrincipal());
+          }
         }
-      }
-
-      if (popupBlocked) {
-        return NS_ERROR_UNKNOWN_PROTOCOL;
+
+        if (popupBlocked) {
+          return NS_ERROR_UNKNOWN_PROTOCOL;
+        }
       }
     }
 
     // Only allow view-source scheme in top-level docshells. view-source is
     // the only scheme to which this applies at the moment due to potential
     // timing attacks to read data from cross-origin iframes. If this widens
     // we should add a protocol flag for whether the scheme is allowed in
     // frames and use something like nsNetUtil::NS_URIChainHasFlags.
--- a/dom/html/test/test_external_protocol_iframe.html
+++ b/dom/html/test/test_external_protocol_iframe.html
@@ -5,35 +5,40 @@
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
 </head>
 <body>
   <div id='foo'><a href='#'>Click here to test this issue</a></div>
   <script>
 
-SimpleTest.waitForExplicitFinish();
+function next() {
+  let foo = document.getElementById('foo');
+  foo.addEventListener('click', _ => {
+    is(ChromeUtils.getPopupControlState(), "openAllowed", "Click events allow popups");
+    ok(!ChromeUtils.isPopupTokenUnused(), "Popup token has not been used yet");
 
-let foo = document.getElementById('foo');
-foo.addEventListener('click', _ => {
-  is(ChromeUtils.getPopupControlState(), "openAllowed", "Click events allow popups");
-  ok(!ChromeUtils.isPopupTokenUnused(), "Popup token has not been used yet");
+    for (let i = 0; i < 10; ++i) {
+      let ifr = document.createElement('iframe');
+      ifr.src = "foo+bar:all_good";
+      document.body.appendChild(ifr);
 
-  for (let i = 0; i < 10; ++i) {
-    let ifr = document.createElement('iframe');
-    ifr.src = "foo+bar:all_good";
-    document.body.appendChild(ifr);
+      is(ChromeUtils.getPopupControlState(), "openAllowed", "Click events allow popups");
+      ok(ChromeUtils.isPopupTokenUnused(), "Popup token has been used!");
+    }
 
-    is(ChromeUtils.getPopupControlState(), "openAllowed", "Click events allow popups");
-    ok(ChromeUtils.isPopupTokenUnused(), "Popup token has been used!");
-  }
+    SimpleTest.finish();
 
-  SimpleTest.finish();
+  }, {once: true});
 
-}, {once: true});
+  setTimeout(_ => {
+    sendMouseEvent({type:'click'}, 'foo');
+  }, 0);
+}
 
-setTimeout(_ => {
-  sendMouseEvent({type:'click'}, 'foo');
-}, 0);
+SpecialPowers.pushPrefEnv({'set': [
+  ['dom.block_external_protocol_in_iframes', true],
+]}, next);
 
+SimpleTest.waitForExplicitFinish();
   </script>
 </body>
 </html>
--- a/modules/libpref/init/StaticPrefList.h
+++ b/modules/libpref/init/StaticPrefList.h
@@ -451,16 +451,29 @@ VARCACHE_PREF(
 // Enable content type normalization of XHR uploads via MIME Sniffing standard
 // Disabled for now in bz1499136
 VARCACHE_PREF(
   "dom.xhr.standard_content_type_normalization",
    dom_xhr_standard_content_type_normalization,
   RelaxedAtomicBool, false
 )
 
+// Block multiple external protocol URLs in iframes per single event.
+#ifdef NIGHTLY_BUILD
+#define PREF_VALUE true
+#else
+#define PREF_VALUE false
+#endif
+VARCACHE_PREF(
+  "dom.block_external_protocol_in_iframes",
+   dom_block_external_protocol_in_iframes,
+  bool, PREF_VALUE
+)
+#undef PREF_VALUE
+
 // Block multiple window.open() per single event.
 VARCACHE_PREF(
   "dom.block_multiple_popups",
    dom_block_multiple_popups,
   bool, true
 )
 
 // For area and anchor elements with target=_blank and no rel set to