Bug 1485305 - browser/ Ensure loadURI always passes a triggeringPrincipal() r=Mossop
☠☠ backed out by 64c64fc5e163 ☠ ☠
authorJonathan Kingston <jkt@mozilla.com>
Wed, 29 Aug 2018 15:43:27 +0100
changeset 436911 2fee48378f73a1037e931141376c33bfc51e2f6c
parent 436910 6263695b3cb85d546f5fc46d357c94a057b71cf7
child 436912 03632075ac2c766489608126b3661ca19c4e9f22
push id107951
push userjkingston@mozilla.com
push dateTue, 18 Sep 2018 10:18:26 +0000
treeherdermozilla-inbound@63c50fd60ae4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersMossop
bugs1485305
milestone64.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 1485305 - browser/ Ensure loadURI always passes a triggeringPrincipal() r=Mossop Differential Revision: https://phabricator.services.mozilla.com/D4551
browser/actors/BlockedSiteChild.jsm
browser/base/content/browser.js
browser/base/content/tabbrowser.js
browser/components/extensions/parent/ext-browser.js
browser/components/newtab/lib/SnippetsFeed.jsm
browser/components/preferences/in-content/subdialogs.js
browser/components/preferences/in-content/sync.js
browser/components/sessionstore/SessionStore.jsm
browser/components/sessionstore/content/aboutSessionRestore.js
browser/components/shell/HeadlessShell.jsm
browser/components/uitour/UITour.jsm
browser/modules/ContentSearch.jsm
--- a/browser/actors/BlockedSiteChild.jsm
+++ b/browser/actors/BlockedSiteChild.jsm
@@ -3,16 +3,18 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 
 var EXPORTED_SYMBOLS = ["BlockedSiteChild"];
 
 ChromeUtils.import("resource://gre/modules/ActorChild.jsm");
+ChromeUtils.defineModuleGetter(this, "Utils",
+  "resource://gre/modules/sessionstore/Utils.jsm");
 
 ChromeUtils.defineModuleGetter(this, "SafeBrowsing",
                                "resource://gre/modules/SafeBrowsing.jsm");
 
 function getSiteBlockedErrorDetails(docShell) {
   let blockedInfo = {};
   if (docShell.failedChannel) {
     let classifiedChannel = docShell.failedChannel.
@@ -24,17 +26,19 @@ function getSiteBlockedErrorDetails(docS
 
       // Remove the query to avoid leaking sensitive data
       if (reportUri instanceof Ci.nsIURL) {
         reportUri = reportUri.mutate()
                              .setQuery("")
                              .finalize();
       }
 
+      let triggeringPrincipal = docShell.failedChannel.loadInfo ? Utils.serializePrincipal(docShell.failedChannel.loadInfo.triggeringPrincipal) : null;
       blockedInfo = { list: classifiedChannel.matchedList,
+                      triggeringPrincipal,
                       provider: classifiedChannel.matchedProvider,
                       uri: reportUri.asciiSpec };
     }
   }
   return blockedInfo;
 }
 
 class BlockedSiteChild extends ActorChild {
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1004,16 +1004,28 @@ function handleUriInChrome(aBrowser, aUr
     } catch (e) {
       return false;
     }
   }
 
   return false;
 }
 
+/* Creates a null principal using the userContextId
+   from the current selected tab or a passed in tab argument */
+function _createNullPrincipalFromTabUserContextId(tab = gBrowser.selectedTab) {
+  let userContextId;
+  if (tab.hasAttribute("usercontextid")) {
+    userContextId = tab.getAttribute("usercontextid");
+  }
+  return Services.scriptSecurityManager.createNullPrincipal({
+    userContextId,
+  });
+}
+
 // A shared function used by both remote and non-remote browser XBL bindings to
 // load a URI or redirect it to the correct process.
 function _loadURI(browser, uri, params = {}) {
   let tab = gBrowser.getTabForBrowser(browser);
   // Preloaded browsers don't have tabs, so we ignore those.
   if (tab) {
     maybeRecordAbandonmentTelemetry(tab, "newURI");
   }
@@ -1026,16 +1038,20 @@ function _loadURI(browser, uri, params =
     flags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE,
     referrerURI,
     referrerPolicy = Ci.nsIHttpChannel.REFERRER_POLICY_UNSET,
     triggeringPrincipal,
     postData,
     userContextId,
   } = params || {};
 
+  if (!triggeringPrincipal) {
+    throw new Error("Must load with a triggering Principal");
+  }
+
   let {
     uriObject,
     requiredRemoteType,
     mustChangeProcess,
     newFrameloader,
   } = E10SUtils.shouldLoadURIInBrowser(browser, uri, gMultiProcessBrowser,
                                        flags);
   if (uriObject && handleUriInChrome(browser, uriObject)) {
@@ -2394,16 +2410,20 @@ function BrowserCloseTabOrWindow(event) 
 function BrowserTryToCloseWindow() {
   if (WindowIsClosing())
     window.close(); // WindowIsClosing does all the necessary checks
 }
 
 function loadURI(uri, referrer, postData, allowThirdPartyFixup, referrerPolicy,
                  userContextId, originPrincipal, forceAboutBlankViewerInCurrent,
                  triggeringPrincipal, allowInheritPrincipal = false) {
+  if (!triggeringPrincipal) {
+    throw new Error("Must load with a triggering Principal");
+  }
+
   try {
     openLinkIn(uri, "current",
                { referrerURI: referrer,
                  referrerPolicy,
                  postData,
                  allowThirdPartyFixup,
                  userContextId,
                  originPrincipal,
@@ -3135,20 +3155,22 @@ var BrowserOnClick = {
           }
           this.ignoreWarningLink(reason, blockedInfo);
         }
         break;
     }
   },
 
   ignoreWarningLink(reason, blockedInfo) {
+    let triggeringPrincipal = Utils.deserializePrincipal(blockedInfo.triggeringPrincipal) || _createNullPrincipalFromTabUserContextId();
     // Allow users to override and continue through to the site,
     // but add a notify bar as a reminder, so that they don't lose
     // track after, e.g., tab switching.
     gBrowser.loadURI(gBrowser.currentURI.spec, {
+      triggeringPrincipal,
       flags: Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CLASSIFIER,
     });
 
     Services.perms.add(gBrowser.currentURI, "safe-browsing",
                        Ci.nsIPermissionManager.ALLOW_ACTION,
                        Ci.nsIPermissionManager.EXPIRE_SESSION);
 
     let buttons = [{
@@ -3203,33 +3225,37 @@ var BrowserOnClick = {
 /**
  * Re-direct the browser to a known-safe page.  This function is
  * used when, for example, the user browses to a known malware page
  * and is presented with about:blocked.  The "Get me out of here!"
  * button should take the user to the default start page so that even
  * when their own homepage is infected, we can get them somewhere safe.
  */
 function getMeOutOfHere() {
-  gBrowser.loadURI(getDefaultHomePage());
+  gBrowser.loadURI(getDefaultHomePage(), {
+    triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(), // Also needs to load homepage
+  });
 }
 
 /**
  * Re-direct the browser to the previous page or a known-safe page if no
  * previous page is found in history.  This function is used when the user
  * browses to a secure page with certificate issues and is presented with
  * about:certerror.  The "Go Back" button should take the user to the previous
  * or a default start page so that even when their own homepage is on a server
  * that has certificate errors, we can get them somewhere safe.
  */
 function goBackFromErrorPage() {
   let state = JSON.parse(SessionStore.getTabState(gBrowser.selectedTab));
   if (state.index == 1) {
     // If the unsafe page is the first or the only one in history, go to the
     // start page.
-    gBrowser.loadURI(getDefaultHomePage());
+    gBrowser.loadURI(getDefaultHomePage(), {
+      triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
+    });
   } else {
     BrowserBack();
   }
 }
 
 /**
  * Return the default start page for the cases when the user's own homepage is
  * infected, so we can get them somewhere safe.
@@ -3255,17 +3281,20 @@ function getWebNavigation() {
 }
 
 function BrowserReloadWithFlags(reloadFlags) {
   let url = gBrowser.currentURI.spec;
   if (gBrowser.updateBrowserRemotenessByURL(gBrowser.selectedBrowser, url)) {
     // If the remoteness has changed, the new browser doesn't have any
     // information of what was loaded before, so we need to load the previous
     // URL again.
-    gBrowser.loadURI(url, { flags: reloadFlags });
+    gBrowser.loadURI(url, {
+      flags: reloadFlags,
+      triggeringPrincipal: gBrowser.selectedBrowser.contentPrincipal,
+    });
     return;
   }
 
   // Do this after the above case where we might flip remoteness.
   // Unfortunately, we'll count the remoteness flip case as a
   // "newURL" load, since we're using loadURI, but hopefully
   // that's rare enough to not matter.
   maybeRecordAbandonmentTelemetry(gBrowser.selectedTab, "reload");
@@ -7671,17 +7700,19 @@ function switchToTabHavingURI(aURI, aOpe
             window.gBrowser.tabContainer.selectedIndex + 1,
             /* aSelectTab = */ true
           );
         } else {
           aWindow.focus();
         }
 
         if (ignoreFragment == "whenComparingAndReplace" || replaceQueryString) {
-          browser.loadURI(aURI.spec);
+          browser.loadURI(aURI.spec, {
+            triggeringPrincipal: aOpenParams.triggeringPrincipal || _createNullPrincipalFromTabUserContextId(),
+          });
         }
 
         if (!doAdopt) {
           aWindow.gBrowser.tabContainer.selectedIndex = i;
         }
 
         return true;
       }
--- a/browser/base/content/tabbrowser.js
+++ b/browser/base/content/tabbrowser.js
@@ -1787,17 +1787,19 @@ window._gBrowser = {
 
     if (remoteType != E10SUtils.NOT_REMOTE) {
       // For remote browsers, we need to make sure that the webProgress is
       // instantiated, otherwise the parent won't get informed about the state
       // of the preloaded browser until it gets attached to a tab.
       browser.webProgress;
     }
 
-    browser.loadURI(BROWSER_NEW_TAB_URL);
+    browser.loadURI(BROWSER_NEW_TAB_URL, {
+      triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
+    });
     browser.docShellIsActive = false;
     browser._urlbarFocused = true;
 
     // Make sure the preloaded browser is loaded with desired zoom level
     let tabURI = Services.io.newURI(BROWSER_NEW_TAB_URL);
     FullZoom.onLocationChange(tabURI, false, browser);
   },
 
--- a/browser/components/extensions/parent/ext-browser.js
+++ b/browser/components/extensions/parent/ext-browser.js
@@ -119,16 +119,17 @@ global.waitForTabLoaded = (tab, url) => 
     });
   });
 };
 
 global.replaceUrlInTab = (gBrowser, tab, url) => {
   let loaded = waitForTabLoaded(tab, url);
   gBrowser.loadURI(url, {
     flags: Ci.nsIWebNavigation.LOAD_FLAGS_REPLACE_HISTORY,
+    triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(), // This is safe from this functions usage however it would be preferred not to dot his.
   });
   return loaded;
 };
 
 /**
  * Manages tab-specific and window-specific context data, and dispatches
  * tab select events across all windows.
  */
--- a/browser/components/newtab/lib/SnippetsFeed.jsm
+++ b/browser/components/newtab/lib/SnippetsFeed.jsm
@@ -185,17 +185,19 @@ this.SnippetsFeed = class SnippetsFeed {
     Services.prefs.removeObserver(FXA_USERNAME_PREF, this._refresh);
     Services.obs.removeObserver(this, SEARCH_ENGINE_OBSERVER_TOPIC);
     this.store.dispatch(ac.BroadcastToContent({type: at.SNIPPETS_RESET}));
   }
 
   async showFirefoxAccounts(browser) {
     const url = await FxAccounts.config.promiseSignUpURI("snippets");
     // We want to replace the current tab.
-    browser.loadURI(url);
+    browser.loadURI(url, {
+      triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal({}),
+    });
   }
 
   async onAction(action) {
     switch (action.type) {
       case at.INIT:
         this.init();
         break;
       case at.UNINIT:
--- a/browser/components/preferences/in-content/subdialogs.js
+++ b/browser/components/preferences/in-content/subdialogs.js
@@ -154,17 +154,19 @@ SubDialog.prototype = {
           this._frame.removeEventListener("load", onBlankLoad);
           // We're now officially done closing, so update the state to reflect that.
           this._openedURL = null;
           this._isClosing = false;
           this._resolveClosePromise();
         }
       };
       this._frame.addEventListener("load", onBlankLoad);
-      this._frame.loadURI("about:blank");
+      this._frame.loadURI("about:blank", {
+        triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal({}),
+      });
     }, 0);
   },
 
   handleEvent(aEvent) {
     switch (aEvent.type) {
       case "click":
         // Close the dialog if the user clicked the overlay background, just
         // like when the user presses the ESC key (case "command" below).
--- a/browser/components/preferences/in-content/sync.js
+++ b/browser/components/preferences/in-content/sync.js
@@ -353,17 +353,19 @@ var gSyncPane = {
     win.switchToTabHavingURI(url, true, options);
   },
 
   // Replace the current tab with the specified URL.
   replaceTabWithUrl(url) {
     // Get the <browser> element hosting us.
     let browser = window.docShell.chromeEventHandler;
     // And tell it to load our URL.
-    browser.loadURI(url);
+    browser.loadURI(url, {
+      triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal({}),
+    });
   },
 
   async signIn() {
     const url = await FxAccounts.config.promiseSignInURI(this._getEntryPoint());
     this.replaceTabWithUrl(url);
   },
 
   async reSignIn() {
--- a/browser/components/sessionstore/SessionStore.jsm
+++ b/browser/components/sessionstore/SessionStore.jsm
@@ -2955,17 +2955,21 @@ var SessionStoreInternal = {
                       "Somehow a crashed browser is still remote.");
     }
 
     // We put the browser at about:blank in case the user is
     // restoring tabs on demand. This way, the user won't see
     // a flash of the about:tabcrashed page after selecting
     // the revived tab.
     aTab.removeAttribute("crashed");
-    browser.loadURI("about:blank");
+    browser.loadURI("about:blank", {
+      triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal({
+        userContextId: aTab.userContextId,
+      }),
+    });
 
     let data = TabState.collect(aTab, TAB_CUSTOM_VALUES.get(aTab));
     this.restoreTab(aTab, data, {
       forceOnDemand: true,
     });
   },
 
   /**
--- a/browser/components/sessionstore/content/aboutSessionRestore.js
+++ b/browser/components/sessionstore/content/aboutSessionRestore.js
@@ -175,17 +175,19 @@ function restoreSession() {
     var tabbrowser = top.gBrowser;
     var tabIndex = tabbrowser.getBrowserIndexForDocument(document);
     tabbrowser.removeTab(tabbrowser.tabs[tabIndex]);
   }, "browser-delayed-startup-finished");
 }
 
 function startNewSession() {
   if (Services.prefs.getIntPref("browser.startup.page") == 0)
-    getBrowserWindow().gBrowser.loadURI("about:blank");
+    getBrowserWindow().gBrowser.loadURI("about:blank", {
+      triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal({}),
+    });
   else
     getBrowserWindow().BrowserHome();
 }
 
 function onListClick(aEvent) {
   // don't react to right-clicks
   if (aEvent.button == 2)
     return;
--- a/browser/components/shell/HeadlessShell.jsm
+++ b/browser/components/shell/HeadlessShell.jsm
@@ -8,19 +8,19 @@ var EXPORTED_SYMBOLS = ["HeadlessShell"]
 
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/osfile.jsm");
 
 // Refrences to the progress listeners to keep them from being gc'ed
 // before they are called.
 const progressListeners = new Map();
 
-function loadContentWindow(webNavigation, uri) {
+function loadContentWindow(webNavigation, uri, principal) {
   return new Promise((resolve, reject) => {
-    webNavigation.loadURI(uri, Ci.nsIWebNavigation.LOAD_FLAGS_NONE, null, null, null);
+    webNavigation.loadURI(uri, Ci.nsIWebNavigation.LOAD_FLAGS_NONE, null, null, null, principal);
     let docShell = webNavigation.QueryInterface(Ci.nsIInterfaceRequestor)
                                 .getInterface(Ci.nsIDocShell);
     let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
                               .getInterface(Ci.nsIWebProgress);
     let progressListener = {
       onLocationChange(progress, request, location, flags) {
         // Ignore inner-frame events
         if (progress != webProgress) {
@@ -45,17 +45,17 @@ function loadContentWindow(webNavigation
                                     Ci.nsIWebProgress.NOTIFY_LOCATION);
   });
 }
 
 async function takeScreenshot(fullWidth, fullHeight, contentWidth, contentHeight, path, url) {
   try {
     var windowlessBrowser = Services.appShell.createWindowlessBrowser(false);
     // nsIWindowlessBrowser inherits from nsIWebNavigation.
-    let contentWindow = await loadContentWindow(windowlessBrowser, url);
+    let contentWindow = await loadContentWindow(windowlessBrowser, url, Services.scriptSecurityManager.getSystemPrincipal());
     contentWindow.resizeTo(contentWidth, contentHeight);
 
     let canvas = contentWindow.document.createElementNS("http://www.w3.org/1999/xhtml", "html:canvas");
     let context = canvas.getContext("2d");
     let width = fullWidth ? contentWindow.innerWidth + contentWindow.scrollMaxX - contentWindow.scrollMinX
                           : contentWindow.innerWidth;
     let height = fullHeight ? contentWindow.innerHeight + contentWindow.scrollMaxY - contentWindow.scrollMinY
                             : contentWindow.innerHeight;
--- a/browser/components/uitour/UITour.jsm
+++ b/browser/components/uitour/UITour.jsm
@@ -463,32 +463,36 @@ var UITour = {
           const url = new URL(uri);
           // Call our helper to validate extraURLCampaignParams and populate URLSearchParams
           if (!this._populateCampaignParams(url, data.extraURLCampaignParams)) {
             log.warn("showFirefoxAccounts: invalid campaign args specified");
             return;
           }
 
           // We want to replace the current tab.
-          browser.loadURI(url.href);
+          browser.loadURI(url.href, {
+            triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal({}),
+          });
         });
         break;
       }
 
       case "showConnectAnotherDevice": {
         FxAccounts.config.promiseConnectDeviceURI("uitour").then(uri => {
           const url = new URL(uri);
           // Call our helper to validate extraURLCampaignParams and populate URLSearchParams
           if (!this._populateCampaignParams(url, data.extraURLCampaignParams)) {
             log.warn("showConnectAnotherDevice: invalid campaign args specified");
             return;
           }
 
           // We want to replace the current tab.
-          browser.loadURI(url.href);
+          browser.loadURI(url.href, {
+            triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal({}),
+          });
         });
         break;
       }
 
       case "resetFirefox": {
         // Open a reset profile dialog window.
         if (ResetProfile.resetSupported()) {
           ResetProfile.openConfirmationDialog(window);
--- a/browser/modules/ContentSearch.jsm
+++ b/browser/modules/ContentSearch.jsm
@@ -242,16 +242,19 @@ var ContentSearch = {
     // where === "current"), openUILinkIn will not work because that tab is no
     // longer the current one. For this case we manually load the URI.
     if (where === "current") {
       // Since we're going to load the search in the same browser, blur the search
       // UI to prevent further interaction before we start loading.
       this._reply(msg, "Blur");
       browser.loadURI(submission.uri.spec, {
         postData: submission.postData,
+        triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal({
+          userContextId: win.gBrowser.selectedBrowser.getAttribute("userContextId"),
+        }),
       });
     } else {
       let params = {
         postData: submission.postData,
         inBackground: Services.prefs.getBoolPref("browser.tabs.loadInBackground"),
       };
       win.openTrustedLinkIn(submission.uri.spec, where, params);
     }