bug 1310331 - implement matchAboutBlank for tabs.executeScript draft 1310331-matchAboutBlank
authorTomislav Jovanovic <tomica@gmail.com>
Thu, 03 Nov 2016 11:41:21 +0100
changeset 437698 1d5e7afc60fcb30d11c459442ce58583784e4191
parent 437246 d38d06f85ef59c5dbb5d4a1a8d895957a78714de
child 536706 3b0432b5b33999166bba56729cae0f2066a0d674
push id35490
push userbmo:tomica@gmail.com
push dateFri, 11 Nov 2016 13:51:51 +0000
bugs1310331
milestone52.0a1
bug 1310331 - implement matchAboutBlank for tabs.executeScript MozReview-Commit-ID: ApuakyrctzD
browser/base/content/tabbrowser.xml
browser/components/extensions/ext-tabs.js
browser/components/extensions/test/browser/browser_ext_tabs_executeScript.js
browser/components/extensions/test/browser/file_iframe_document.html
toolkit/components/extensions/ExtensionContent.jsm
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -2110,38 +2110,40 @@
             var aSkipAnimation;
             var aAllowMixedContent;
             var aForceNotRemote;
             var aNoReferrer;
             var aUserContextId;
             var aEventDetail;
             var aRelatedBrowser;
             var aOriginPrincipal;
+            var aDisallowInheritPrincipal;
             var aOpener;
             if (arguments.length == 2 &&
                 typeof arguments[1] == "object" &&
                 !(arguments[1] instanceof Ci.nsIURI)) {
               let params = arguments[1];
-              aReferrerURI          = params.referrerURI;
-              aReferrerPolicy       = params.referrerPolicy;
-              aCharset              = params.charset;
-              aPostData             = params.postData;
-              aOwner                = params.ownerTab;
-              aAllowThirdPartyFixup = params.allowThirdPartyFixup;
-              aFromExternal         = params.fromExternal;
-              aRelatedToCurrent     = params.relatedToCurrent;
-              aSkipAnimation        = params.skipAnimation;
-              aAllowMixedContent    = params.allowMixedContent;
-              aForceNotRemote       = params.forceNotRemote;
-              aNoReferrer           = params.noReferrer;
-              aUserContextId        = params.userContextId;
-              aEventDetail          = params.eventDetail;
-              aRelatedBrowser       = params.relatedBrowser;
-              aOriginPrincipal      = params.originPrincipal;
-              aOpener               = params.opener;
+              aReferrerURI              = params.referrerURI;
+              aReferrerPolicy           = params.referrerPolicy;
+              aCharset                  = params.charset;
+              aPostData                 = params.postData;
+              aOwner                    = params.ownerTab;
+              aAllowThirdPartyFixup     = params.allowThirdPartyFixup;
+              aFromExternal             = params.fromExternal;
+              aRelatedToCurrent         = params.relatedToCurrent;
+              aSkipAnimation            = params.skipAnimation;
+              aAllowMixedContent        = params.allowMixedContent;
+              aForceNotRemote           = params.forceNotRemote;
+              aNoReferrer               = params.noReferrer;
+              aUserContextId            = params.userContextId;
+              aEventDetail              = params.eventDetail;
+              aRelatedBrowser           = params.relatedBrowser;
+              aOriginPrincipal          = params.originPrincipal;
+              aDisallowInheritPrincipal = params.disallowInheritPrincipal;
+              aOpener                   = params.opener;
             }
 
             // if we're adding tabs, we're past interrupt mode, ditch the owner
             if (this.mCurrentTab.owner)
               this.mCurrentTab.owner = null;
 
             var t = document.createElementNS(NS_XUL, "tab");
 
@@ -2218,31 +2220,33 @@
             t.dispatchEvent(evt);
 
             if (!usingPreloadedContent && aOriginPrincipal) {
               b.createAboutBlankContentViewer(aOriginPrincipal);
             }
 
             // If we didn't swap docShells with a preloaded browser
             // then let's just continue loading the page normally.
-            if (!usingPreloadedContent && !uriIsAboutBlank) {
+            if (!usingPreloadedContent && (!uriIsAboutBlank || aDisallowInheritPrincipal)) {
               // pretend the user typed this so it'll be available till
               // the document successfully loads
               if (aURI && gInitialPages.indexOf(aURI) == -1)
                 b.userTypedValue = aURI;
 
               let flags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
               if (aAllowThirdPartyFixup) {
                 flags |= Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
                 flags |= Ci.nsIWebNavigation.LOAD_FLAGS_FIXUP_SCHEME_TYPOS;
               }
               if (aFromExternal)
                 flags |= Ci.nsIWebNavigation.LOAD_FLAGS_FROM_EXTERNAL;
               if (aAllowMixedContent)
                 flags |= Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_MIXED_CONTENT;
+              if (aDisallowInheritPrincipal)
+                flags |= Ci.nsIWebNavigation.LOAD_FLAGS_DISALLOW_INHERIT_PRINCIPAL;
               try {
                 b.loadURIWithFlags(aURI, {
                   flags,
                   referrerURI: aNoReferrer ? null: aReferrerURI,
                   referrerPolicy: aReferrerPolicy,
                   charset: aCharset,
                   postData: aPostData,
                 });
--- a/browser/components/extensions/ext-tabs.js
+++ b/browser/components/extensions/ext-tabs.js
@@ -543,16 +543,20 @@ extensions.registerSchemaAPI("tabs", "ad
               if (!containerId) {
                 return Promise.reject({message: `No cookie store exists with ID ${createProperties.cookieStoreId}`});
               }
 
               options.userContextId = containerId;
             }
           }
 
+          // Make sure things like about:blank and data: URIs never inherit,
+          // and instead always get a NullPrincipal.
+          options.disallowInheritPrincipal = true;
+
           tabListener.initTabReady();
           let tab = window.gBrowser.addTab(url || window.BROWSER_NEW_TAB_URL, options);
 
           let active = true;
           if (createProperties.active !== null) {
             active = createProperties.active;
           }
           if (active) {
--- a/browser/components/extensions/test/browser/browser_ext_tabs_executeScript.js
+++ b/browser/components/extensions/test/browser/browser_ext_tabs_executeScript.js
@@ -83,16 +83,30 @@ add_task(function* testExecuteScript() {
           browser.test.assertEq(2, result.length, "Result has correct length");
 
           browser.test.assertTrue(/\/file_iframe_document\.html$/.test(result[0]), "First result is correct");
           browser.test.assertEq("http://mochi.test:8888/", result[1], "Second result is correct");
         }),
 
         browser.tabs.executeScript({
           code: "location.href;",
+          allFrames: true,
+          matchAboutBlank: true,
+        }).then(result => {
+          browser.test.assertTrue(Array.isArray(result), "Result is an array");
+
+          browser.test.assertEq(3, result.length, "Result has correct length");
+
+          browser.test.assertTrue(/\/file_iframe_document\.html$/.test(result[0]), "First result is correct");
+          browser.test.assertEq("http://mochi.test:8888/", result[1], "Second result is correct");
+          browser.test.assertEq("about:blank", result[2], "Thirds result is correct");
+        }),
+
+        browser.tabs.executeScript({
+          code: "location.href;",
           runAt: "document_end",
         }).then(result => {
           browser.test.assertEq(1, result.length, "Expected callback result");
           browser.test.assertEq("string", typeof result[0], "Result is a string");
 
           browser.test.assertTrue(/\/file_iframe_document\.html$/.test(result[0]), "Result is correct");
         }),
 
@@ -182,16 +196,22 @@ add_task(function* testExecuteScript() {
         browser.tabs.create({url: "http://example.com/"}).then(async tab => {
           let result = await browser.tabs.executeScript(tab.id, {code: "location.href"});
 
           browser.test.assertEq("http://example.com/", result[0], "Script executed correctly in new tab");
 
           await browser.tabs.remove(tab.id);
         }),
 
+        browser.tabs.create({url: "about:blank"}).then(async tab => {
+          const result = await browser.tabs.executeScript(tab.id, {code: "location.href", matchAboutBlank: true});
+          browser.test.assertEq("about:blank", result[0], "Script executed correctly in new tab");
+          await browser.tabs.remove(tab.id);
+        }),
+
         new Promise(resolve => {
           browser.runtime.onMessage.addListener(message => {
             browser.test.assertEq("script ran", message, "Expected runtime message");
             resolve();
           });
         }),
       ]);
 
--- a/browser/components/extensions/test/browser/file_iframe_document.html
+++ b/browser/components/extensions/test/browser/file_iframe_document.html
@@ -1,10 +1,11 @@
 <!DOCTYPE html>
 <html lang="en">
 <head>
   <meta charset="UTF-8">
   <title></title>
 </head>
 <body>
   <iframe src="/"></iframe>
+  <iframe src="about:blank"></iframe>
 </body>
 </html>
--- a/toolkit/components/extensions/ExtensionContent.jsm
+++ b/toolkit/components/extensions/ExtensionContent.jsm
@@ -138,19 +138,26 @@ Script.prototype = {
 
     // If mozAddonManager is present on this page, don't allow
     // content scripts.
     if (window.navigator.mozAddonManager !== undefined) {
       return false;
     }
 
     if (this.match_about_blank && ["about:blank", "about:srcdoc"].includes(uri.spec)) {
-      // When matching about:blank/srcdoc documents, the checks below
+      const principal = window.document.nodePrincipal;
+
+      // When matching top-level about:blank documents,
+      // allow loading into any with a NullPrincipal.
+      if (window === window.top && principal.isNullPrincipal) {
+        return true;
+      }
+      // When matching about:blank/srcdoc iframes, the checks below
       // need to be performed against the "owner" document's URI.
-      uri = window.document.nodePrincipal.URI;
+      uri = principal.URI;
     }
 
     if (!(this.matches_.matches(uri) || this.matches_host_.matchesIgnoringPath(uri))) {
       return false;
     }
 
     if (this.exclude_matches_.matches(uri)) {
       return false;
@@ -615,17 +622,17 @@ DocumentManager = {
       return null;
     };
 
     let promises = Array.from(this.enumerateWindows(global.docShell), executeInWin)
                         .filter(promise => promise);
 
     if (!promises.length) {
       let details = {};
-      for (let key of ["all_frames", "frame_id", "matches_about_blank", "matchesHost"]) {
+      for (let key of ["all_frames", "frame_id", "match_about_blank", "matchesHost"]) {
         if (key in options) {
           details[key] = options[key];
         }
       }
 
       return Promise.reject({message: `No window matching ${JSON.stringify(details)}`});
     }
     if (!options.all_frames && promises.length > 1) {