Bug 1362034 - Have addTab() provide the correct triggering principal draft
authorJonathan Kingston <jkt@mozilla.com>
Thu, 14 Jun 2018 11:30:41 -0700
changeset 809261 c18101fedb5817a2db11aefb084595a70f9ab214
parent 809260 1c33a38da75d550750923358e73d7af2102b9c1d
push id113603
push userbmo:jkt@mozilla.com
push dateThu, 21 Jun 2018 16:24:04 +0000
bugs1362034
milestone62.0a1
Bug 1362034 - Have addTab() provide the correct triggering principal MozReview-Commit-ID: I9RXGk2kybu
accessible/tests/mochitest/name/test_browserui.xul
browser/base/content/browser-captivePortal.js
browser/base/content/browser.js
browser/base/content/tabbrowser.js
browser/base/content/test/general/browser_beforeunload_duplicate_dialogs.js
browser/base/content/test/general/browser_bug462673.js
browser/base/content/test/general/browser_bug963945.js
browser/base/content/test/general/browser_tab_dragdrop2.js
browser/base/content/test/general/browser_trackingUI_5.js
browser/base/content/test/general/browser_utilityOverlay.js
browser/base/content/test/metaTags/browser_meta_tags.js
browser/base/content/test/performance/head.js
browser/base/content/test/referrer/head.js
browser/base/content/test/tabs/browser_new_tab_insert_position.js
browser/base/content/test/tabs/browser_pinnedTabs_closeByKeyboard.js
browser/base/content/test/tabs/browser_positional_attributes.js
browser/base/content/test/trackingUI/browser_trackingUI_state.js
browser/base/content/test/urlbar/browser_URLBarSetURI.js
browser/base/content/test/urlbar/browser_urlbarSearchSingleWordNotification.js
browser/base/content/utilityOverlay.js
browser/components/extensions/parent/ext-browser.js
browser/components/extensions/parent/ext-tabs.js
browser/components/migration/tests/marionette/test_refresh_firefox.py
browser/components/nsBrowserGlue.js
browser/components/privatebrowsing/test/browser/browser_privatebrowsing_aboutSessionRestore.js
browser/components/privatebrowsing/test/browser/browser_privatebrowsing_cache.js
browser/components/privatebrowsing/test/browser/browser_privatebrowsing_favicon.js
browser/components/privatebrowsing/test/browser/browser_privatebrowsing_geoprompt.js
browser/components/privatebrowsing/test/browser/browser_privatebrowsing_localStorage.js
browser/components/privatebrowsing/test/browser/browser_privatebrowsing_localStorage_before_after.js
browser/components/privatebrowsing/test/browser/browser_privatebrowsing_noSessionRestoreMenuOption.js
browser/components/privatebrowsing/test/browser/browser_privatebrowsing_protocolhandler.js
browser/components/safebrowsing/content/test/browser_whitelisted.js
browser/components/sessionstore/SessionStore.jsm
browser/components/sessionstore/content/aboutSessionRestore.js
browser/components/sessionstore/test/browser_354894_perwindowpb.js
browser/components/sessionstore/test/browser_625016.js
browser/components/sessionstore/test/browser_819510_perwindowpb.js
browser/components/sessionstore/test/browser_broadcast.js
browser/components/sessionstore/test/browser_cleaner.js
browser/components/sessionstore/test/browser_crashedTabs.js
browser/components/sessionstore/test/browser_dying_cache.js
browser/components/sessionstore/test/browser_privatetabs.js
browser/components/sessionstore/test/browser_scrollPositions.js
browser/components/sessionstore/test/browser_scrollPositionsReaderMode.js
browser/components/sessionstore/test/browser_switch_remoteness.js
browser/components/sessionstore/test/browser_undoCloseById.js
browser/components/sessionstore/test/browser_windowStateContainer.js
devtools/client/commandline/test/helpers.js
devtools/client/debugger/test/mochitest/head.js
devtools/client/framework/devtools-browser.js
devtools/client/netmonitor/src/utils/firefox/open-request-in-tab.js
devtools/client/performance-new/browser.js
devtools/client/responsive.html/browser/swap.js
devtools/client/styleeditor/test/head.js
devtools/client/webconsole/test/mochitest/browser_console_webconsole_private_browsing.js
devtools/startup/devtools-startup.js
devtools/startup/tests/browser/browser_shim_disable_devtools.js
docshell/base/nsDocShell.cpp
docshell/test/browser/head.js
dom/base/test/browser_blocking_image.js
dom/broadcastchannel/tests/browser_private_browsing.js
dom/cache/test/mochitest/browser_cache_pb_window.js
dom/html/test/browser_bug1108547.js
dom/indexedDB/test/browser_permissionsPromptDeny.js
dom/indexedDB/test/browser_perwindow_privateBrowsing.js
dom/quota/test/browser_permissionsPromptUnknown.js
dom/tests/browser/browser_localStorage_privatestorageevent.js
dom/tests/browser/helper_largeAllocation.js
testing/mochitest/BrowserTestUtils/BrowserTestUtils.jsm
testing/mochitest/bootstrap.js
testing/mochitest/browser-test.js
toolkit/components/extensions/test/browser/browser_ext_themes_tab_separators.js
toolkit/components/normandy/lib/Heartbeat.jsm
toolkit/content/tests/browser/browser_block_autoplay_media.js
toolkit/content/tests/browser/browser_block_autoplay_media_pausedAfterPlay.js
toolkit/content/tests/browser/browser_block_autoplay_playAfterTabVisible.js
toolkit/content/tests/browser/browser_block_multipleMedia.js
toolkit/content/tests/browser/browser_block_notInTreeAudio.js
toolkit/content/tests/browser/browser_block_playMediaInMuteTab.js
toolkit/content/tests/browser/browser_block_plugIn.js
toolkit/content/tests/browser/browser_block_silentAudioTrack_media.js
toolkit/content/tests/browser/browser_block_webAudio.js
toolkit/content/tests/browser/browser_resume_bkg_video_on_tab_hover.js
--- a/accessible/tests/mochitest/name/test_browserui.xul
+++ b/accessible/tests/mochitest/name/test_browserui.xul
@@ -27,17 +27,17 @@
     function addTab(aURL)
     {
       this.eventSeq = [
         new invokerChecker(EVENT_DOCUMENT_LOAD_COMPLETE, tabDocumentAt, 1)
       ];
 
       this.invoke = function addTab_invoke()
       {
-        tabBrowser().addTab(aURL);
+        tabBrowser().addTrustedTab(aURL);
       }
 
       this.getID = function addTab_getID()
       {
         return "add tab: " + aURL;
       }
     }
 
--- a/browser/base/content/browser-captivePortal.js
+++ b/browser/base/content/browser-captivePortal.js
@@ -242,17 +242,20 @@ var CaptivePortalWatcher = {
   ensureCaptivePortalTab() {
     let tab;
     if (this._captivePortalTab) {
       tab = this._captivePortalTab.get();
     }
 
     // If the tab is gone or going, we need to open a new one.
     if (!tab || tab.closing || !tab.parentNode) {
-      tab = gBrowser.addTab(this.canonicalURL, { ownerTab: gBrowser.selectedTab });
+      tab = gBrowser.addWebTab(this.canonicalURL, {
+        ownerTab: gBrowser.selectedTab,
+        triggeringPrincipal: gBrowser.contentPrincipal,
+      });
       this._captivePortalTab = Cu.getWeakReference(tab);
     }
 
     gBrowser.selectedTab = tab;
 
     let canonicalURI = makeURI(this.canonicalURL);
 
     // When we are no longer captive, close the tab if it's at the canonical URL.
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -532,17 +532,18 @@ const gStoragePressureObserver = {
     let usage = subject.QueryInterface(Ci.nsISupportsPRUint64).data;
     let prefStrBundle = document.getElementById("bundle_preferences");
     let brandShortName = document.getElementById("bundle_brand").getString("brandShortName");
     buttons.push({
       label: prefStrBundle.getString("spaceAlert.learnMoreButton.label"),
       accessKey: prefStrBundle.getString("spaceAlert.learnMoreButton.accesskey"),
       callback(notificationBar, button) {
         let learnMoreURL = Services.urlFormatter.formatURLPref("app.support.baseURL") + "storage-permissions";
-        gBrowser.selectedTab = gBrowser.addTab(learnMoreURL);
+        // This is a content URL, loaded from trusted UX.
+        gBrowser.selectedTab = gBrowser.addTrustedTab(learnMoreURL);
       }
     });
     if (usage < USAGE_THRESHOLD_BYTES) {
       // The firefox-used space < 5GB, then warn user to free some disk space.
       // This is because this usage is small and not the main cause for space issue.
       // In order to avoid the bad and wrong impression among users that
       // firefox eats disk space a lot, indicate users to clean up other disk space.
       msg = prefStrBundle.getFormattedString("spaceAlert.under5GB.message", [brandShortName]);
@@ -4123,17 +4124,18 @@ const BrowserSearch = {
       return null;
     }
 
     let inBackground = Services.prefs.getBoolPref("browser.search.context.loadInBackground");
     openLinkIn(submission.uri.spec,
                useNewTab ? "tab" : "current",
                { postData: submission.postData,
                  inBackground,
-                 relatedToCurrent: true });
+                 relatedToCurrent: true,
+                 triggeringPrincipal: gBrowser.contentPrincipal });
 
     return engine;
   },
 
   /**
    * Just like _loadSearch, but preserving an old API.
    *
    * @return string Name of the search engine used to perform a search or null
@@ -4611,21 +4613,24 @@ function createReopenInContainerMenu(eve
 /**
  * Reopen the tab in another container.
  */
 function reopenInContainer(event) {
   let userContextId = parseInt(event.target.getAttribute("data-usercontextid"));
   let currentTab = TabContextMenu.contextTab;
   let isSelected = (gBrowser.selectedTab == currentTab);
   let uri = currentTab.linkedBrowser.currentURI.spec;
-
-  let newTab = gBrowser.addTab(uri, {
+  let triggeringPrincipal = Services.scriptSecurityManager
+                                    .createCodebasePrincipal(currentTab.linkedBrowser.currentURI, { userContextId });
+
+  let newTab = gBrowser.addWebTab(uri, {
     userContextId,
     pinned: currentTab.pinned,
     index: currentTab._tPos + 1,
+    triggeringPrincipal
   });
 
   // Carry over some configuration.
   if (isSelected) {
     gBrowser.selectedTab = newTab;
   }
   if (currentTab.muted) {
     if (!newTab.muted) {
@@ -6369,17 +6374,17 @@ function middleMousePaste(event) {
       Cu.reportError(ex);
     }
 
     if (where != "current" ||
         lastLocationChange == gBrowser.selectedBrowser.lastLocationChange) {
       openUILink(data.url, event,
                  { ignoreButton: true,
                    disallowInheritPrincipal: !data.mayInheritPrincipal,
-                   triggeringPrincipal: gBrowser.selectedBrowser.contentPrincipal,
+                   triggeringPrincipal: gBrowser.contentPrincipal,
                  });
     }
   });
 
   if (event instanceof Event) {
     event.stopPropagation();
   }
 }
@@ -7625,17 +7630,18 @@ const gAccessibilityServiceIndicator = {
     return Services.prefs.getBoolPref("accessibility.indicator.enabled");
   },
 
   handleEvent({ key, type }) {
     if ((type === "keypress" && [" ", "Enter"].includes(key)) ||
          type === "click") {
       let a11yServicesSupportURL =
         Services.urlFormatter.formatURLPref("accessibility.support.url");
-      gBrowser.selectedTab = gBrowser.addTab(a11yServicesSupportURL);
+      // This is a known URL coming from trusted UI
+      gBrowser.selectedTab = gBrowser.addTrustedTab(a11yServicesSupportURL);
       Services.telemetry.scalarSet("a11y.indicator_acted_on", true);
     }
   },
 
   uninit() {
     Services.prefs.removeObserver("accessibility.indicator.enabled", this);
     Services.obs.removeObserver(this, "a11y-init-or-shutdown");
     this.update();
--- a/browser/base/content/tabbrowser.js
+++ b/browser/base/content/tabbrowser.js
@@ -1269,17 +1269,16 @@ window._gBrowser = {
     // Don't replace an initially set label with the URL while the tab
     // is loading.
     if (aTab._labelIsInitialTitle) {
       if (!title) {
         return false;
       }
       delete aTab._labelIsInitialTitle;
     }
-
     let isContentTitle = false;
     if (title) {
       isContentTitle = true;
     } else if (aTab.hasAttribute("customizemode")) {
       let brandBundle = document.getElementById("bundle_brand");
       let brandShortName = brandBundle.getString("brandShortName");
       title = gNavigatorBundle.getFormattedString("customizeMode.tabTitle",
                                                   [brandShortName]);
@@ -1405,16 +1404,22 @@ window._gBrowser = {
       aOpener = params.opener;
       aOpenerBrowser = params.openerBrowser;
       aCreateLazyBrowser = params.createLazyBrowser;
       aNextTabParentId = params.nextTabParentId;
       aFocusUrlBar = params.focusUrlBar;
       aName = params.name;
     }
 
+    // all callers of loadOneTab need to pass a valid triggeringPrincipal.
+    if (!aTriggeringPrincipal) {
+      dump("\n\n XXX (loadOneTab) has no triggeringPrincipal: " + Components.stack.caller + "\n\n");
+      throw new Error("Required argument triggeringPrincipal missing within loadOneTab");
+    }
+
     var bgLoad = (aLoadInBackground != null) ? aLoadInBackground :
       Services.prefs.getBoolPref("browser.tabs.loadInBackground");
     var owner = bgLoad ? null : this.selectedTab;
 
     var tab = this.addTab(aURI, {
       triggeringPrincipal: aTriggeringPrincipal,
       referrerURI: aReferrerURI,
       referrerPolicy: aReferrerPolicy,
@@ -2139,16 +2144,37 @@ window._gBrowser = {
     tab.removeAttribute("linkedpanel");
 
     this._createLazyBrowser(tab);
 
     let evt = new CustomEvent("TabBrowserDiscarded", { bubbles: true });
     tab.dispatchEvent(evt);
   },
 
+  /**
+   * Loads a tab with the triggering principal of the existing browser
+   */
+  addWebTab(aURI, params = {}) {
+    if (params.triggeringPrincipal) {
+      params.triggeringPrincipal = Services.scriptSecurityManager.createNullPrincipal({});
+    } else if (Services.scriptSecurityManager.isSystemPrincipal(params.triggeringPrincipal)) {
+      throw new Error("System principal should never be passed into addWebTab()");
+    }
+    return this.addTab(aURI, params);
+  },
+
+  /**
+   * Must only be used sparingly for content that came from Chrome context
+   * If in doubt use addWebTab
+   */
+  addTrustedTab(aURI, params = {}) {
+    params.triggeringPrincipal = Services.scriptSecurityManager.getSystemPrincipal();
+    return this.addTab(aURI, params);
+  },
+
   // eslint-disable-next-line complexity
   addTab(aURI, aReferrerURI, aCharset, aPostData, aOwner, aAllowThirdPartyFixup) {
     "use strict";
 
     var aTriggeringPrincipal;
     var aReferrerPolicy;
     var aFromExternal;
     var aRelatedToCurrent;
@@ -2204,16 +2230,24 @@ window._gBrowser = {
       aNoInitialLabel = params.noInitialLabel;
       aFocusUrlBar = params.focusUrlBar;
       aName = params.name;
       aBulkOrderedOpen = params.bulkOrderedOpen;
       aIndex = params.index;
       aPinned = params.pinned;
     }
 
+    // all callers of addTab that pass a params object need to pass
+    // a valid triggeringPrincipal.
+    if (!aTriggeringPrincipal) {
+      dump("\n\n XXX (addTab) has no triggeringPrincipal: " + Components.stack.caller + "\n\n");
+      throw new Error("Required argument triggeringPrincipal missing within addTab");
+    }
+
+
     // if we're adding tabs, we're past interrupt mode, ditch the owner
     if (this.selectedTab.owner) {
       this.selectedTab.owner = null;
     }
 
     // Find the tab that opened this one, if any. This is used for
     // determining positioning, and inherited attributes such as the
     // user context ID.
@@ -2834,17 +2868,19 @@ window._gBrowser = {
     this._removingTabs.push(aTab);
     this._visibleTabs = null; // invalidate cache
 
     // Invalidate hovered tab state tracking for this closing tab.
     if (this.tabContainer._hoveredTab == aTab)
       aTab._mouseleave();
 
     if (newTab)
-      this.addTab(BROWSER_NEW_TAB_URL, { skipAnimation: true });
+      this.addTrustedTab(BROWSER_NEW_TAB_URL, {
+        skipAnimation: true,
+      });
     else
       TabBarVisibility.update();
 
     // We're committed to closing the tab now.
     // Dispatch a notification.
     // We dispatch it before any teardown so that event listeners can
     // inspect the tab that's about to close.
     var evt = new CustomEvent("TabClose", { bubbles: true, detail: { adoptedBy: aAdoptedByTab } });
@@ -3550,17 +3586,17 @@ window._gBrowser = {
     if (aIndex < numPinned || (aTab.pinned && aIndex == numPinned)) {
       params.pinned = true;
     }
 
     if (aTab.hasAttribute("usercontextid")) {
       // new tab must have the same usercontextid as the old one
       params.userContextId = aTab.getAttribute("usercontextid");
     }
-    let newTab = this.addTab("about:blank", params);
+    let newTab = this.addTrustedTab("about:blank", params);
     let newBrowser = this.getBrowserForTab(newTab);
 
     // Stop the about:blank load.
     newBrowser.stop();
     // Make sure it has a docshell.
     newBrowser.docShell;
 
     // We need to select the tab before calling swapBrowsersAndCloseOther
--- a/browser/base/content/test/general/browser_beforeunload_duplicate_dialogs.js
+++ b/browser/base/content/test/general/browser_beforeunload_duplicate_dialogs.js
@@ -40,17 +40,17 @@ add_task(async function closeLastTabInWi
   ok(newWin.closed, "Window should be closed.");
 });
 
 add_task(async function closeWindowWithMultipleTabsIncludingOneBeforeUnload() {
   Services.prefs.setBoolPref("browser.tabs.warnOnClose", false);
   let newWin = await promiseOpenAndLoadWindow({}, true);
   let firstTab = newWin.gBrowser.selectedTab;
   await promiseTabLoadEvent(firstTab, TEST_PAGE);
-  await promiseTabLoadEvent(newWin.gBrowser.addTab(), "http://example.com/");
+  await promiseTabLoadEvent(BrowserTestUtils.addTab(newWin.gBrowser, "http://example.com/"));
   let windowClosedPromise = promiseWindowWillBeClosed(newWin);
   expectingDialog = true;
   newWin.BrowserTryToCloseWindow();
   await windowClosedPromise;
   ok(!expectingDialog, "There should have been a dialog.");
   ok(newWin.closed, "Window should be closed.");
   Services.prefs.clearUserPref("browser.tabs.warnOnClose");
 });
--- a/browser/base/content/test/general/browser_bug462673.js
+++ b/browser/base/content/test/general/browser_bug462673.js
@@ -15,17 +15,17 @@ add_task(async function() {
 
 add_task(async function() {
   var win = openDialog(getBrowserURL(), "_blank", "chrome,all,dialog=no");
   await SimpleTest.promiseFocus(win);
 
   let tab = win.gBrowser.tabContainer.firstChild;
   await promiseTabLoadEvent(tab, getRootDirectory(gTestPath) + "test_bug462673.html");
 
-  var newTab = win.gBrowser.addTab();
+  var newTab = BrowserTestUtils.addTab(win.gBrowser);
   var newBrowser = newTab.linkedBrowser;
   win.gBrowser.removeTab(tab);
   ok(!win.closed, "Window stays open");
   if (!win.closed) {
     is(win.gBrowser.tabContainer.childElementCount, 1, "Window has one tab");
     is(win.gBrowser.browsers.length, 1, "Window has one browser");
     is(win.gBrowser.selectedTab, newTab, "Remaining tab is selected");
     is(win.gBrowser.selectedBrowser, newBrowser, "Browser for remaining tab is selected");
--- a/browser/base/content/test/general/browser_bug963945.js
+++ b/browser/base/content/test/general/browser_bug963945.js
@@ -5,17 +5,17 @@
 /*
  * This test ensures the about:addons tab is only
  * opened one time when in private browsing.
  */
 
 add_task(async function test() {
   let win = await BrowserTestUtils.openNewBrowserWindow({private: true});
 
-  let tab = win.gBrowser.selectedTab = win.gBrowser.addTab("about:addons");
+  let tab = win.gBrowser.selectedTab = BrowserTestUtils.addTab(win.gBrowser, "about:addons");
   await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
   await promiseWaitForFocus(win);
 
   EventUtils.synthesizeKey("a", { ctrlKey: true, shiftKey: true }, win);
 
   is(win.gBrowser.tabs.length, 2, "about:addons tab was re-focused.");
   is(win.gBrowser.currentURI.spec, "about:addons", "Addons tab was opened.");
 
--- a/browser/base/content/test/general/browser_tab_dragdrop2.js
+++ b/browser/base/content/test/general/browser_tab_dragdrop2.js
@@ -12,17 +12,17 @@ add_task(async function() {
   let args = "chrome,all,dialog=no";
   let win = window.openDialog(getBrowserURL(), "_blank", args, URI);
 
   // Wait until the tests were run.
   await promiseTestsDone(win);
   ok(true, "tests succeeded");
 
   // Create a second tab so that we can move the original one out.
-  win.gBrowser.addTab("about:blank", {skipAnimation: true});
+  BrowserTestUtils.addTab(win.gBrowser, "about:blank", {skipAnimation: true});
 
   // Tear off the original tab.
   let browser = win.gBrowser.selectedBrowser;
   let tabClosed = promiseWaitForEvent(browser, "pagehide", true);
   let win2 = win.gBrowser.replaceTabWithWindow(win.gBrowser.tabs[0]);
 
   // Add a 'TestsDone' event listener to ensure that the docShells is properly
   // swapped to the new window instead of the page being loaded again. If this
--- a/browser/base/content/test/general/browser_trackingUI_5.js
+++ b/browser/base/content/test/general/browser_trackingUI_5.js
@@ -91,17 +91,17 @@ add_task(async function testExceptionAdd
   let tabReloadPromise = promiseTabLoadEvent(tab);
   clickButton("#tracking-action-unblock");
   is(identityPopupState(), "closed", "foobar");
 
   await tabReloadPromise;
   testTrackingPageUnblocked();
 
   info("Test that the exception is remembered across tabs in the same private window");
-  tab = browser.selectedTab = browser.addTab();
+  tab = browser.selectedTab = BrowserTestUtils.addTab(browser);
 
   info("Load a test page containing tracking elements");
   await promiseTabLoadEvent(tab, TRACKING_PAGE);
   testTrackingPageUnblocked();
 
   await promiseWindowClosed(privateWin);
 });
 
--- a/browser/base/content/test/general/browser_utilityOverlay.js
+++ b/browser/base/content/test/general/browser_utilityOverlay.js
@@ -85,17 +85,17 @@ function test_getTopWin() {
 function test_getBoolPref() {
   is(getBoolPref("browser.search.openintab", false), false, "getBoolPref");
   is(getBoolPref("this.pref.doesnt.exist", true), true, "getBoolPref fallback");
   is(getBoolPref("this.pref.doesnt.exist", false), false, "getBoolPref fallback #2");
   runNextTest();
 }
 
 function test_openNewTabWith() {
-  openNewTabWith("http://example.com/");
+  openNewTabWith("http://example.com/", null, {triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal({})});
   let tab = gBrowser.selectedTab = gBrowser.tabs[1];
   BrowserTestUtils.browserLoaded(tab.linkedBrowser).then(() => {
     is(tab.linkedBrowser.currentURI.spec, "http://example.com/", "example.com loaded");
     gBrowser.removeCurrentTab();
     runNextTest();
   });
 }
 
--- a/browser/base/content/test/metaTags/browser_meta_tags.js
+++ b/browser/base/content/test/metaTags/browser_meta_tags.js
@@ -27,17 +27,17 @@ add_task(async function test_metadata() 
  * make sure the extra tab does not cause the debounce logic to be skipped. If
  * incorrectly skipped, the updated metadata would not include the delayed meta.
  */
 add_task(async function multiple_tabs() {
   const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_PATH);
 
   // Add a background tab to cause another page to load *without* putting the
   // desired URL in a background tab, which results in its timers being throttled.
-  gBrowser.addTab();
+  BrowserTestUtils.addTab(gBrowser);
 
   // Wait until places has stored the page info
   const pageInfo = await waitForPageInfo(TEST_PATH);
   is(pageInfo.description, "og:description", "got the correct description");
   is(pageInfo.previewImageURL.href, "https://test.com/og-image-secure-url.jpg", "got the correct preview image");
 
   BrowserTestUtils.removeTab(tab);
   BrowserTestUtils.removeTab(gBrowser.selectedTab);
--- a/browser/base/content/test/performance/head.js
+++ b/browser/base/content/test/performance/head.js
@@ -302,17 +302,21 @@ function computeMaxTabCount() {
  *        How many about:blank tabs to open.
  */
 async function createTabs(howMany) {
   let uris = [];
   while (howMany--) {
     uris.push("about:blank");
   }
 
-  gBrowser.loadTabs(uris, true, false);
+  gBrowser.loadTabs(uris, {
+      inBackground: true,
+      replace: false,
+      triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
+  });
 
   await BrowserTestUtils.waitForCondition(() => {
     return Array.from(gBrowser.tabs).every(tab => tab._fullyOpen);
   });
 }
 
 /**
  * Removes all of the tabs except the originally selected
--- a/browser/base/content/test/referrer/head.js
+++ b/browser/base/content/test/referrer/head.js
@@ -205,17 +205,17 @@ function referrerTestCaseLoaded(aTestNum
                              REFERRER_POLICYSERVER_URL_ATTRIBUTE;
   let url = test.fromScheme + server +
             "?scheme=" + escape(test.toScheme) +
             "&policy=" + escape(test.policy || "") +
             "&rel=" + escape(test.rel || "") +
             "&cross=" + escape(test.cross || "");
   let browser = gTestWindow.gBrowser;
   return BrowserTestUtils.openNewForegroundTab(browser, () => {
-    browser.selectedTab = browser.addTab(url, aParams);
+    browser.selectedTab = BrowserTestUtils.addTab(browser, url, aParams);
   }, false, true);
 }
 
 /**
  * Checks the result of the referrer test, and moves on to the next test.
  * @param aTestNumber The test number - 0, 1, 2, ...
  * @param aNewWindow The new window where the referrer target opened, or null.
  * @param aNewTab The new tab where the referrer target opened, or null.
--- a/browser/base/content/test/tabs/browser_new_tab_insert_position.js
+++ b/browser/base/content/test/tabs/browser_new_tab_insert_position.js
@@ -149,17 +149,20 @@ async function doTest(aInsertRelatedAfte
   verifyTabState(newState);
 
   // Bug 1442679 - Test bulk opening with loadTabs loads the tabs in order
 
   let loadPromises = Promise.all(bulkLoad.map(url => BrowserTestUtils.waitForNewTab(gBrowser, url, false, true)));
   // loadTabs will insertAfterCurrent
   let nextTab = aInsertAfterCurrent ? gBrowser.selectedTab._tPos + 1 : gBrowser.tabs.length;
 
-  gBrowser.loadTabs(bulkLoad, true);
+  gBrowser.loadTabs(bulkLoad, {
+    inBackground: true,
+    triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
+  });
   await loadPromises;
   for (let i = nextTab, j = 0; j < bulkLoad.length; i++, j++) {
     is(gBrowser.tabs[i].linkedBrowser.currentURI.spec, bulkLoad[j], `bulkLoad tab pos ${i} matched`);
   }
 
   // Now we want to test that positioning remains correct after a session restore.
 
   // Restore pre-test state so we can restore and test tab ordering.
--- a/browser/base/content/test/tabs/browser_pinnedTabs_closeByKeyboard.js
+++ b/browser/base/content/test/tabs/browser_pinnedTabs_closeByKeyboard.js
@@ -16,17 +16,17 @@ function test() {
        "menu_close should always have key_close set");
   }
 
   let unpinnedTab = gBrowser.selectedTab;
   ok(!unpinnedTab.pinned, "We should have started with a regular tab selected");
 
   testState(false);
 
-  let pinnedTab = gBrowser.addTab();
+  let pinnedTab = BrowserTestUtils.addTab(gBrowser);
   gBrowser.pinTab(pinnedTab);
 
   // Just pinning the tab shouldn't change the key state.
   testState(false);
 
   // Test key state after selecting a tab.
   gBrowser.selectedTab = pinnedTab;
   testState(true);
@@ -40,17 +40,17 @@ function test() {
   // Test the key state after un/pinning the tab.
   gBrowser.unpinTab(pinnedTab);
   testState(false);
 
   gBrowser.pinTab(pinnedTab);
   testState(true);
 
   // Test that accel+w in a pinned tab selects the next tab.
-  let pinnedTab2 = gBrowser.addTab();
+  let pinnedTab2 = BrowserTestUtils.addTab(gBrowser);
   gBrowser.pinTab(pinnedTab2);
   gBrowser.selectedTab = pinnedTab;
 
   EventUtils.synthesizeKey("w", { accelKey: true });
   is(gBrowser.tabs.length, 3, "accel+w in a pinned tab didn't close it");
   is(gBrowser.selectedTab, unpinnedTab, "accel+w in a pinned tab selected the first unpinned tab");
 
   // Test the key state after removing the tab.
--- a/browser/base/content/test/tabs/browser_positional_attributes.js
+++ b/browser/base/content/test/tabs/browser_positional_attributes.js
@@ -1,16 +1,18 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var tabs = [];
 
 function addTab(aURL) {
-  tabs.push(gBrowser.addTab(aURL, {skipAnimation: true}));
+  tabs.push(BrowserTestUtils.addTab(gBrowser, aURL, {
+    skipAnimation: true,
+  }));
 }
 
 function switchTab(index) {
   return BrowserTestUtils.switchTab(gBrowser, gBrowser.tabs[index]);
 }
 
 function testAttrib(tabIndex, attrib, expected) {
   is(gBrowser.tabs[tabIndex].hasAttribute(attrib), expected,
--- a/browser/base/content/test/trackingUI/browser_trackingUI_state.js
+++ b/browser/base/content/test/trackingUI/browser_trackingUI_state.js
@@ -149,17 +149,17 @@ async function testTrackingProtectionDis
   await promiseTabLoadEvent(tab, TRACKING_PAGE);
   testTrackingPageUnblocked();
 }
 
 add_task(async function testNormalBrowsing() {
   await UrlClassifierTestUtils.addTestTrackers();
 
   tabbrowser = gBrowser;
-  let tab = tabbrowser.selectedTab = tabbrowser.addTab();
+  let tab = tabbrowser.selectedTab = BrowserTestUtils.addTab(tabbrowser);
 
   TrackingProtection = gBrowser.ownerGlobal.TrackingProtection;
   ok(TrackingProtection, "TP is attached to the browser window");
   is(TrackingProtection.enabled, Services.prefs.getBoolPref(PREF),
     "TP.enabled is based on the original pref value");
 
   Services.prefs.setBoolPref(PREF, true);
   ok(TrackingProtection.enabled, "TP is enabled after setting the pref");
@@ -170,17 +170,17 @@ add_task(async function testNormalBrowsi
   ok(!TrackingProtection.enabled, "TP is disabled after setting the pref");
 
   await testTrackingProtectionDisabled(tab);
 });
 
 add_task(async function testPrivateBrowsing() {
   let privateWin = await BrowserTestUtils.openNewBrowserWindow({private: true});
   tabbrowser = privateWin.gBrowser;
-  let tab = tabbrowser.selectedTab = tabbrowser.addTab();
+  let tab = tabbrowser.selectedTab = BrowserTestUtils.addTab(tabbrowser);
 
   TrackingProtection = tabbrowser.ownerGlobal.TrackingProtection;
   ok(TrackingProtection, "TP is attached to the private window");
   is(TrackingProtection.enabled, Services.prefs.getBoolPref(PB_PREF),
     "TP.enabled is based on the pb pref value");
 
   Services.prefs.setBoolPref(PB_PREF, true);
   ok(TrackingProtection.enabled, "TP is enabled after setting the pref");
--- a/browser/base/content/test/urlbar/browser_URLBarSetURI.js
+++ b/browser/base/content/test/urlbar/browser_URLBarSetURI.js
@@ -62,17 +62,17 @@ var tests = [
       next();
     });
   }
 ];
 
 function loadTabInWindow(win, callback) {
   info("Loading tab");
   let url = "http://user:pass@example.com/";
-  let tab = win.gBrowser.selectedTab = win.gBrowser.addTab(url);
+  let tab = win.gBrowser.selectedTab = BrowserTestUtils.addTab(win.gBrowser, url);
   BrowserTestUtils.browserLoaded(tab.linkedBrowser, false, url).then(() => {
     info("Tab loaded");
     is(win.gURLBar.textValue, "example.com", "URL bar had user/pass stripped initially");
     callback(tab);
   }, true);
 }
 
 function openToolbarCustomizationUI(aCallback, aBrowserWin) {
--- a/browser/base/content/test/urlbar/browser_urlbarSearchSingleWordNotification.js
+++ b/browser/base/content/test/urlbar/browser_urlbarSearchSingleWordNotification.js
@@ -157,17 +157,17 @@ function get_test_function_for_localhost
     // check pref value
     let prefValue = Services.prefs.getBoolPref(pref);
     is(prefValue, !isPrivate, "Pref should have the correct state.");
 
     await docLoadPromise;
     browser.removeTab(tab);
 
     // Now try again with the pref set.
-    tab = browser.selectedTab = browser.addTab("about:blank");
+    tab = browser.selectedTab = BrowserTestUtils.addTab(browser, "about:blank");
     await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
     // In a private window, the notification should appear again.
     await runURLBarSearchTest({
       valueToOpen: hostName,
       expectSearch: isPrivate,
       expectNotification: isPrivate,
       aWindow: win,
     });
--- a/browser/base/content/utilityOverlay.js
+++ b/browser/base/content/utilityOverlay.js
@@ -210,16 +210,18 @@ function openTrustedLinkIn(url, where, a
  */
 function openWebLinkIn(url, where, params) {
   if (!params) {
     params = {};
   }
 
   if (!params.triggeringPrincipal) {
     params.triggeringPrincipal = Services.scriptSecurityManager.createNullPrincipal({});
+  } else if (Services.scriptSecurityManager.isSystemPrincipal(params.triggeringPrincipal)) {
+    throw new Error("System principal should never be passed into addWebTab()");
   }
 
   openUILinkIn(url, where, params);
 }
 
 /* openUILinkIn opens a URL in a place specified by the parameter |where|.
  *
  * |where| can be:
--- a/browser/components/extensions/parent/ext-browser.js
+++ b/browser/components/extensions/parent/ext-browser.js
@@ -46,17 +46,20 @@ const getSender = (extension, target, se
 
 // Used by Extension.jsm
 global.tabGetSender = getSender;
 
 /* eslint-disable mozilla/balanced-listeners */
 extensions.on("uninstalling", (msg, extension) => {
   if (extension.uninstallURL) {
     let browser = windowTracker.topWindow.gBrowser;
-    browser.addTab(extension.uninstallURL, {relatedToCurrent: true});
+    browser.addTab(extension.uninstallURL, {
+      relatedToCurrent: true,
+      triggeringPrincipal: extension.principal,
+    });
   }
 });
 
 extensions.on("page-shutdown", (type, context) => {
   if (context.viewType == "tab") {
     if (context.extension.id !== context.xulBrowser.contentPrincipal.addonId) {
       // Only close extension tabs.
       // This check prevents about:addons from closing when it contains a
--- a/browser/components/extensions/parent/ext-tabs.js
+++ b/browser/components/extensions/parent/ext-tabs.js
@@ -512,27 +512,31 @@ this.tabs = class extends ExtensionAPI {
                 resolve(window);
               };
               Services.obs.addObserver(obs, "browser-delayed-startup-finished");
             } else {
               resolve(window);
             }
           }).then(window => {
             let url;
+            let principal = context.principal;
 
             if (createProperties.url !== null) {
               url = context.uri.resolve(createProperties.url);
 
               if (!context.checkLoadURL(url, {dontReportErrors: true})) {
                 return Promise.reject({message: `Illegal URL: ${url}`});
               }
 
               if (createProperties.openInReaderMode) {
                 url = `about:reader?url=${encodeURIComponent(url)}`;
+                principal = Services.scriptSecurityManager.getSystemPrincipal();
               }
+            } else {
+              principal = Services.scriptSecurityManager.getSystemPrincipal();
             }
 
             if (createProperties.cookieStoreId && !extension.hasPermission("cookies")) {
               return Promise.reject({message: `No permission for cookieStoreId: ${createProperties.cookieStoreId}`});
             }
 
             let options = {};
             if (createProperties.cookieStoreId) {
@@ -577,16 +581,17 @@ this.tabs = class extends ExtensionAPI {
             if (createProperties.index != null) {
               options.index = createProperties.index;
             }
 
             if (createProperties.pinned != null) {
               options.pinned = createProperties.pinned;
             }
 
+            options.triggeringPrincipal = principal;
             let nativeTab = window.gBrowser.addTab(url || window.BROWSER_NEW_TAB_URL, options);
 
             let active = true;
             if (createProperties.active !== null) {
               active = createProperties.active;
             }
             if (active) {
               window.gBrowser.selectedTab = nativeTab;
--- a/browser/components/migration/tests/marionette/test_refresh_firefox.py
+++ b/browser/components/migration/tests/marionette/test_refresh_firefox.py
@@ -159,17 +159,19 @@ class TestFirefoxRefresh(MarionetteTestC
                     marionetteScriptFinished();
                   }
                 });
               }
             }
           });
           let expectedTabs = new Set();
           for (let url of expectedURLs) {
-            expectedTabs.add(gBrowser.addTab(url));
+            expectedTabs.add(gBrowser.addTab(url, {
+              triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
+            }));
           }
           // Close any other tabs that might be open:
           let allTabs = Array.from(gBrowser.tabs);
           for (let tab of allTabs) {
             if (!expectedTabs.has(tab)) {
               gBrowser.removeTab(tab);
             }
           }
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -2597,17 +2597,20 @@ BrowserGlue.prototype = {
 
       const openTab = async (URI) => {
         let tab;
         if (!win) {
           win = await this._openURLInNewWindow(URI.uri);
           let tabs = win.gBrowser.tabs;
           tab = tabs[tabs.length - 1];
         } else {
-          tab = win.gBrowser.addTab(URI.uri);
+          // TODO check if we need system
+          tab = win.gBrowser.addWebTab(URI.uri, {
+            triggeringPrincipal: win.gBrowser.contentPrincipal,
+          });
         }
         tab.setAttribute("attention", true);
         return tab;
       };
 
       const firstTab = await openTab(URIs[0]);
       await Promise.all(URIs.slice(1).map(URI => openTab(URI)));
 
@@ -2677,17 +2680,20 @@ BrowserGlue.prototype = {
       imageURL = "chrome://branding/content/icon64.png";
     }
     let win = BrowserWindowTracker.getTopWindow({private: false});
     if (!win) {
       win = await this._openURLInNewWindow(url);
       let tabs = win.gBrowser.tabs;
       tab = tabs[tabs.length - 1];
     } else {
-      tab = win.gBrowser.addTab(url);
+      // TODO verify if this needs system
+      tab = win.gBrowser.addWebTab(url, {
+        triggeringPrincipal: win.gBrowser.contentPrincipal,
+      });
     }
     tab.setAttribute("attention", true);
     let clickCallback = (subject, topic, data) => {
       if (topic != "alertclickcallback")
         return;
       win.gBrowser.selectedTab = tab;
     };
 
@@ -2710,17 +2716,19 @@ BrowserGlue.prototype = {
     let clickCallback = async (subject, topic, data) => {
       if (topic != "alertclickcallback")
         return;
       let url = await FxAccounts.config.promiseManageDevicesURI("device-connected-notification");
       let win = BrowserWindowTracker.getTopWindow({private: false});
       if (!win) {
         this._openURLInNewWindow(url);
       } else {
-        win.gBrowser.addTab(url);
+        win.gBrowser.addWebTab(url, {
+          triggeringPrincipal: win.gBrowser.contentPrincipal,
+        });
       }
     };
 
     try {
       this.AlertsService.showAlertNotification(null, title, body, true, null, clickCallback);
     } catch (ex) {
       Cu.reportError("Error notifying of a new Sync device: " + ex);
     }
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_aboutSessionRestore.js
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_aboutSessionRestore.js
@@ -4,17 +4,17 @@
 
 // This test checks that the session restore button from about:sessionrestore
 // is disabled in private mode
 add_task(async function testNoSessionRestoreButton() {
   // Opening, then closing, a private window shouldn't create session data.
   (await BrowserTestUtils.openNewBrowserWindow({private: true})).close();
 
   let win = await BrowserTestUtils.openNewBrowserWindow({private: true});
-  let tab = win.gBrowser.addTab("about:sessionrestore");
+  let tab = BrowserTestUtils.addTab(win.gBrowser, "about:sessionrestore");
   let browser = tab.linkedBrowser;
 
   await BrowserTestUtils.browserLoaded(browser);
 
   await ContentTask.spawn(browser, null, async function() {
     Assert.ok(content.document.getElementById("errorTryAgain").disabled,
       "The Restore about:sessionrestore button should be disabled");
   });
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_cache.js
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_cache.js
@@ -56,17 +56,17 @@ function getStorageEntryCount(device, go
 
 function get_cache_for_private_window() {
   let win = whenNewWindowLoaded({private: true}, function() {
 
     executeSoon(function() {
 
       ok(true, "The private window got loaded");
 
-      let tab = win.gBrowser.addTab("http://example.org");
+      let tab = BrowserTestUtils.addTab(win.gBrowser, "http://example.org");
       win.gBrowser.selectedTab = tab;
       let newTabBrowser = win.gBrowser.getBrowserForTab(tab);
 
       BrowserTestUtils.browserLoaded(newTabBrowser).then(function() {
         executeSoon(function() {
 
           getStorageEntryCount("private", function(nrEntriesP) {
             ok(nrEntriesP >= 1, "Memory cache reports some entries from example.org domain");
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_favicon.js
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_favicon.js
@@ -166,17 +166,17 @@ async function assignCookies(aBrowser, a
   await ContentTask.spawn(tabInfo.browser, aCookieValue, async function(value) {
     content.document.cookie = value;
   });
 
   BrowserTestUtils.removeTab(tabInfo.tab);
 }
 
 async function openTab(aBrowser, aURL) {
-  let tab = aBrowser.addTab(aURL);
+  let tab = BrowserTestUtils.addTab(aBrowser, aURL);
 
   // Select tab and make sure its browser is focused.
   aBrowser.selectedTab = tab;
   tab.ownerGlobal.focus();
 
   let browser = aBrowser.getBrowserForTab(tab);
   await BrowserTestUtils.browserLoaded(browser);
   return {tab, browser};
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_geoprompt.js
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_geoprompt.js
@@ -6,17 +6,17 @@
 // control inside the private browsing mode.
 
 add_task(async function test() {
   const testPageURL = "https://example.com/browser/" +
     "browser/components/privatebrowsing/test/browser/browser_privatebrowsing_geoprompt_page.html";
 
   function checkGeolocation(aPrivateMode, aWindow) {
     return (async function() {
-      aWindow.gBrowser.selectedTab = aWindow.gBrowser.addTab(testPageURL);
+      aWindow.gBrowser.selectedTab = BrowserTestUtils.addTab(aWindow.gBrowser, testPageURL);
       await BrowserTestUtils.browserLoaded(aWindow.gBrowser.selectedBrowser);
 
       let notification = aWindow.PopupNotifications.getNotification("geolocation");
 
       // Wait until the notification is available.
       while (!notification) {
         await new Promise(resolve => { executeSoon(resolve); });
         notification = aWindow.PopupNotifications.getNotification("geolocation");
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_localStorage.js
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_localStorage.js
@@ -4,17 +4,17 @@
 
  add_task(async function test() {
   requestLongerTimeout(2);
   const page1 = "http://mochi.test:8888/browser/browser/components/privatebrowsing/test/browser/" +
                 "browser_privatebrowsing_localStorage_page1.html";
 
   let win = await BrowserTestUtils.openNewBrowserWindow({private: true});
 
-  win.gBrowser.selectedTab = win.gBrowser.addTab(page1);
+  win.gBrowser.selectedTab = BrowserTestUtils.addTab(win.gBrowser, page1);
   let browser = win.gBrowser.selectedBrowser;
   await BrowserTestUtils.browserLoaded(browser);
 
   browser.loadURI(
     "http://mochi.test:8888/browser/browser/components/privatebrowsing/test/browser/" +
     "browser_privatebrowsing_localStorage_page2.html");
   await BrowserTestUtils.browserLoaded(browser);
 
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_localStorage_before_after.js
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_localStorage_before_after.js
@@ -10,25 +10,25 @@
 // Step 2: Load the same page in a non-private tab, ensuring that the storage instance reports only one item
 //   existing.
 
 add_task(async function test() {
   let prefix = "http://mochi.test:8888/browser/browser/components/privatebrowsing/test/browser/";
 
   // Step 1.
   let privateWin = await BrowserTestUtils.openNewBrowserWindow({private: true});
-  let privateBrowser = privateWin.gBrowser.addTab(
+  let privateBrowser = BrowserTestUtils.addTab(privateWin.gBrowser,
     prefix + "browser_privatebrowsing_localStorage_before_after_page.html").linkedBrowser;
   await BrowserTestUtils.browserLoaded(privateBrowser);
 
   is(privateBrowser.contentTitle, "1", "localStorage should contain 1 item");
 
   // Step 2.
   let win = await BrowserTestUtils.openNewBrowserWindow();
-  let browser = win.gBrowser.addTab(
+  let browser = BrowserTestUtils.addTab(win.gBrowser,
     prefix + "browser_privatebrowsing_localStorage_before_after_page2.html").linkedBrowser;
   await BrowserTestUtils.browserLoaded(browser);
 
   is(browser.contentTitle, "null|0", "localStorage should contain 0 items");
 
   // Cleanup
   await BrowserTestUtils.closeWindow(privateWin);
   await BrowserTestUtils.closeWindow(win);
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_noSessionRestoreMenuOption.js
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_noSessionRestoreMenuOption.js
@@ -3,17 +3,17 @@
 /**
  * Tests that if we open a tab within a private browsing window, and then
  * close that private browsing window, that subsequent private browsing
  * windows do not allow the command for restoring the last session.
  */
 add_task(async function test_no_session_restore_menu_option() {
   let win = await BrowserTestUtils.openNewBrowserWindow({ private: true });
   ok(true, "The first private window got loaded");
-  win.gBrowser.addTab("about:mozilla");
+  BrowserTestUtils.addTab(win.gBrowser, "about:mozilla");
   await BrowserTestUtils.closeWindow(win);
 
   win = await BrowserTestUtils.openNewBrowserWindow({ private: true });
   let srCommand = win.document.getElementById("Browser:RestoreLastSession");
   ok(srCommand, "The Session Restore command should exist");
   is(PrivateBrowsingUtils.isWindowPrivate(win), true,
      "PrivateBrowsingUtils should report the correct per-window private browsing status");
   is(srCommand.hasAttribute("disabled"), true,
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_protocolhandler.js
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_protocolhandler.js
@@ -7,17 +7,17 @@
 // inside the private browsing mode.
 
 add_task(async function test() {
   let notificationValue = "Protocol Registration: testprotocol";
   let testURI = "https://example.com/browser/" +
     "browser/components/privatebrowsing/test/browser/browser_privatebrowsing_protocolhandler_page.html";
 
   let doTest = async function(aIsPrivateMode, aWindow) {
-    let tab = aWindow.gBrowser.selectedTab = aWindow.gBrowser.addTab(testURI);
+    let tab = aWindow.gBrowser.selectedTab = BrowserTestUtils.addTab(aWindow.gBrowser, testURI);
     await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
 
     let promiseFinished = PromiseUtils.defer();
     setTimeout(function() {
       let notificationBox = aWindow.gBrowser.getNotificationBox();
       let notification = notificationBox.getNotificationWithValue(notificationValue);
 
       if (aIsPrivateMode) {
--- a/browser/components/safebrowsing/content/test/browser_whitelisted.js
+++ b/browser/components/safebrowsing/content/test/browser_whitelisted.js
@@ -22,17 +22,17 @@ function testWhitelistedPage(window) {
   var getmeout_button = window.document.getElementById("getMeOutButton");
   var ignorewarning_button = window.document.getElementById("ignoreWarningButton");
   ok(!getmeout_button, "GetMeOut button not present");
   ok(!ignorewarning_button, "IgnoreWarning button not present");
 }
 
 add_task(async function testNormalBrowsing() {
   tabbrowser = gBrowser;
-  let tab = tabbrowser.selectedTab = tabbrowser.addTab();
+  let tab = tabbrowser.selectedTab = BrowserTestUtils.addTab(tabbrowser);
 
   info("Load a test page that's whitelisted");
   Services.prefs.setCharPref(PREF_WHITELISTED_HOSTNAMES, "example.com,www.ItIsaTrap.org,example.net");
   await promiseTabLoadEvent(tab, TEST_PAGE, "load");
   testWhitelistedPage(tab.ownerGlobal);
 
   info("Load a test page that's no longer whitelisted");
   Services.prefs.setCharPref(PREF_WHITELISTED_HOSTNAMES, "");
--- a/browser/components/sessionstore/SessionStore.jsm
+++ b/browser/components/sessionstore/SessionStore.jsm
@@ -2427,18 +2427,24 @@ var SessionStoreInternal = {
     }
     if (!aWindow.gBrowser) {
       throw Components.Exception("Invalid window object: no gBrowser", Cr.NS_ERROR_INVALID_ARG);
     }
 
     // Create a new tab.
     let userContextId = aTab.getAttribute("usercontextid");
     let newTab = aTab == aWindow.gBrowser.selectedTab ?
-      aWindow.gBrowser.addTab(null, {relatedToCurrent: true, ownerTab: aTab, userContextId}) :
-      aWindow.gBrowser.addTab(null, {userContextId});
+      aWindow.gBrowser.addTrustedTab(null, {
+        relatedToCurrent: true,
+        ownerTab: aTab,
+        userContextId,
+      }) :
+      aWindow.gBrowser.addTrustedTab(null, {
+        userContextId,
+      });
 
     // Start the throbber to pretend we're doing something while actually
     // waiting for data from the frame script.
     newTab.setAttribute("busy", "true");
 
     // Collect state before flushing.
     let tabState = TabState.collect(aTab, TAB_CUSTOM_VALUES.get(aTab));
 
@@ -2516,17 +2522,17 @@ var SessionStoreInternal = {
       throw Components.Exception("Invalid index: not in the closed tabs", Cr.NS_ERROR_INVALID_ARG);
     }
 
     // fetch the data of closed tab, while removing it from the array
     let {state, pos} = this.removeClosedTabData(closedTabs, aIndex);
 
     // create a new tab
     let tabbrowser = aWindow.gBrowser;
-    let tab = tabbrowser.selectedTab = tabbrowser.addTab(null, state);
+    let tab = tabbrowser.selectedTab = tabbrowser.addTrustedTab(null, state);
 
     // restore tab content
     this.restoreTab(tab, state);
 
     // restore the tab's position
     tabbrowser.moveTabTo(tab, pos);
 
     // Notify of changes to closed objects.
@@ -3485,23 +3491,24 @@ var SessionStoreInternal = {
           activeIndex = Math.min(activeIndex, tabData.entries.length - 1);
           activeIndex = Math.max(activeIndex, 0);
           url = tabData.entries[activeIndex].url;
         }
 
         // Setting noInitialLabel is a perf optimization. Rendering tab labels
         // would make resizing the tabs more expensive as we're adding them.
         // Each tab will get its initial label set in restoreTab.
-        tab = tabbrowser.addTab(url,
-                                { createLazyBrowser,
-                                  skipAnimation: true,
-                                  noInitialLabel: true,
-                                  userContextId,
-                                  skipBackgroundNotify: true,
-                                  bulkOrderedOpen: true });
+        tab = tabbrowser.addTrustedTab(url, {
+          createLazyBrowser,
+          skipAnimation: true,
+          noInitialLabel: true,
+          userContextId,
+          skipBackgroundNotify: true,
+          bulkOrderedOpen: true,
+        });
 
         if (select) {
           let leftoverTab = tabbrowser.selectedTab;
           tabbrowser.selectedTab = tab;
           tabbrowser.removeTab(leftoverTab);
         }
       }
 
--- a/browser/components/sessionstore/content/aboutSessionRestore.js
+++ b/browser/components/sessionstore/content/aboutSessionRestore.js
@@ -261,17 +261,17 @@ function toggleRowChecked(aIx) {
   // we only disable the button when there's no cancel button.
   if (document.getElementById("errorCancel")) {
     document.getElementById("errorTryAgain").disabled = !gTreeData.some(isChecked);
   }
 }
 
 function restoreSingleTab(aIx, aShifted) {
   var tabbrowser = getBrowserWindow().gBrowser;
-  var newTab = tabbrowser.addTab();
+  var newTab = tabbrowser.addTrustedTab();
   var item = gTreeData[aIx];
 
   var tabState = gStateObject.windows[item.parent.ix]
                              .tabs[aIx - gTreeData.indexOf(item.parent) - 1];
   // ensure tab would be visible on the tabstrip.
   tabState.hidden = false;
   SessionStore.setTabState(newTab, JSON.stringify(tabState));
 
--- a/browser/components/sessionstore/test/browser_354894_perwindowpb.js
+++ b/browser/components/sessionstore/test/browser_354894_perwindowpb.js
@@ -158,17 +158,17 @@ let setupTest = async function(options, 
 
 /**
  * Loads a TEST_URLS into a browser window.
  *
  * @param win (Window)
  *        The browser window to load the tabs in
  */
 function injectTestTabs(win) {
-  let promises = TEST_URLS.map(url => win.gBrowser.addTab(url))
+  let promises = TEST_URLS.map(url => BrowserTestUtils.addTab(win.gBrowser, url))
                           .map(tab => BrowserTestUtils.browserLoaded(tab.linkedBrowser));
   return Promise.all(promises);
 }
 
 /**
  * Attempts to close a window via BrowserTryToCloseWindow so that
  * we get the browser-lastwindow-close-requested and
  * browser-lastwindow-close-granted observer notifications.
@@ -308,17 +308,17 @@ add_task(async function test_open_close_
     let popupPromise = BrowserTestUtils.waitForNewWindow();
     openDialog(location, "popup", POPUP_FEATURES, TEST_URLS[0]);
     let popup = await popupPromise;
 
     let popup2Promise = BrowserTestUtils.waitForNewWindow();
     openDialog(location, "popup2", POPUP_FEATURES, TEST_URLS[1]);
     let popup2 = await popup2Promise;
 
-    popup2.gBrowser.addTab(TEST_URLS[0]);
+    BrowserTestUtils.addTab(popup2.gBrowser, TEST_URLS[0]);
 
     let closed = await closeWindowForRestoration(newWin);
     ok(closed, "Should be able to close the window");
 
     await BrowserTestUtils.closeWindow(popup2);
 
     newWin = await promiseNewWindowLoaded();
 
@@ -373,17 +373,17 @@ add_task(async function test_open_close_
 
     let closed = await closeWindowForRestoration(popup);
     ok(closed, "Should be able to close the window");
 
     popupPromise = BrowserTestUtils.waitForNewWindow();
     openDialog(location, "popup", POPUP_FEATURES, TEST_URLS[1]);
     popup = await popupPromise;
 
-    popup.gBrowser.addTab(TEST_URLS[0]);
+    BrowserTestUtils.addTab(popup.gBrowser, TEST_URLS[0]);
     is(popup.gBrowser.browsers.length, 2,
        "Did not restore to the popup window (2)");
 
     await BrowserTestUtils.closeWindow(popup);
 
     newWin = await promiseNewWindowLoaded();
     isnot(newWin.gBrowser.browsers.length, 2,
           "Did not restore the popup window");
--- a/browser/components/sessionstore/test/browser_625016.js
+++ b/browser/components/sessionstore/test/browser_625016.js
@@ -20,17 +20,17 @@ add_task(async function setup() {
   forgetClosedWindows();
   is(ss.getClosedWindowCount(), 0, "starting with no closed windows");
 });
 
 add_task(async function new_window() {
   let newWin;
   try {
     newWin = await promiseNewWindowLoaded();
-    let tab = newWin.gBrowser.addTab("http://example.com/browser_625016.js?" + Math.random());
+    let tab = BrowserTestUtils.addTab(newWin.gBrowser, "http://example.com/browser_625016.js?" + Math.random());
     await promiseBrowserLoaded(tab.linkedBrowser);
 
     // Double check that we have no closed windows
     is(ss.getClosedWindowCount(), 0, "no closed windows on first save");
 
     await BrowserTestUtils.closeWindow(newWin);
     newWin = null;
 
--- a/browser/components/sessionstore/test/browser_819510_perwindowpb.js
+++ b/browser/components/sessionstore/test/browser_819510_perwindowpb.js
@@ -108,12 +108,12 @@ add_task(async function test_3() {
     "Closed windows are not private");
 
   // Cleanup.
   await promiseAllButPrimaryWindowClosed();
   forgetClosedWindows();
 });
 
 async function promiseTabLoad(win, url) {
-  let tab = win.gBrowser.addTab(url);
+  let tab = BrowserTestUtils.addTab(win.gBrowser, url);
   await promiseBrowserLoaded(tab.linkedBrowser);
   await TabStateFlusher.flush(tab.linkedBrowser);
 }
--- a/browser/components/sessionstore/test/browser_broadcast.js
+++ b/browser/components/sessionstore/test/browser_broadcast.js
@@ -111,17 +111,17 @@ add_task(async function flush_on_tabclos
 
 function promiseNewWindow() {
   return new Promise(resolve => {
     whenNewWindowLoaded({private: false}, resolve);
   });
 }
 
 async function createTabWithStorageData(urls, win = window) {
-  let tab = win.gBrowser.addTab();
+  let tab = BrowserTestUtils.addTab(win.gBrowser);
   let browser = tab.linkedBrowser;
 
   for (let url of urls) {
     browser.loadURI(url);
     await promiseBrowserLoaded(browser);
     await modifySessionStorage(browser, {test: INITIAL_VALUE});
   }
 
--- a/browser/components/sessionstore/test/browser_cleaner.js
+++ b/browser/components/sessionstore/test/browser_cleaner.js
@@ -45,17 +45,17 @@ add_task(async function init() {
 add_task(async function test_open_and_close() {
   let newTab1 = BrowserTestUtils.addTab(gBrowser, URL_TAB1);
   await promiseBrowserLoaded(newTab1.linkedBrowser);
 
   let newTab2 = BrowserTestUtils.addTab(gBrowser, URL_TAB2);
   await promiseBrowserLoaded(newTab2.linkedBrowser);
 
   let newWin = await promiseNewWindowLoaded();
-  let tab = newWin.gBrowser.addTab(URL_NEWWIN);
+  let tab = BrowserTestUtils.addTab(newWin.gBrowser, URL_NEWWIN);
 
   await promiseBrowserLoaded(tab.linkedBrowser);
 
   await TabStateFlusher.flushWindow(window);
   await TabStateFlusher.flushWindow(newWin);
 
   info("1. Making sure that before closing, we don't have closedAt");
   // For the moment, no "closedAt"
--- a/browser/components/sessionstore/test/browser_crashedTabs.js
+++ b/browser/components/sessionstore/test/browser_crashedTabs.js
@@ -295,17 +295,17 @@ add_task(async function test_revive_all_
 
   browser.loadURI(PAGE_1);
   await promiseBrowserLoaded(browser);
 
   // In order to see a second about:tabcrashed page, we'll need
   // a second window, since only selected tabs will show
   // about:tabcrashed.
   let win2 = await BrowserTestUtils.openNewBrowserWindow();
-  let newTab2 = win2.gBrowser.addTab(PAGE_1, { sameProcessAsFrameLoader: browser.frameLoader });
+  let newTab2 = BrowserTestUtils.addTab(win2.gBrowser, PAGE_1, { sameProcessAsFrameLoader: browser.frameLoader });
   win2.gBrowser.selectedTab = newTab2;
   let browser2 = newTab2.linkedBrowser;
   ok(browser2.isRemoteBrowser, "Should be a remote browser");
   await promiseBrowserLoaded(browser2);
 
   browser.loadURI(PAGE_1);
   await promiseBrowserLoaded(browser);
 
@@ -402,17 +402,17 @@ add_task(async function test_hide_restor
   gBrowser.selectedTab = newTab;
 
   browser.loadURI(PAGE_2);
   await promiseBrowserLoaded(browser);
 
   // Load up a second window so we can get another tab to show
   // about:tabcrashed
   let win2 = await BrowserTestUtils.openNewBrowserWindow();
-  let newTab3 = win2.gBrowser.addTab(PAGE_2, { sameProcessAsFrameLoader: browser.frameLoader });
+  let newTab3 = BrowserTestUtils.addTab(win2.gBrowser, PAGE_2, { sameProcessAsFrameLoader: browser.frameLoader });
   win2.gBrowser.selectedTab = newTab3;
   let otherWinBrowser = newTab3.linkedBrowser;
   await promiseBrowserLoaded(otherWinBrowser);
   // We'll need to make sure the second tab's browser has finished
   // sending its AboutTabCrashedReady event before we know for
   // sure whether or not we're showing the right Restore buttons.
   let otherBrowserReady = promiseTabCrashedReady(otherWinBrowser);
 
--- a/browser/components/sessionstore/test/browser_dying_cache.js
+++ b/browser/components/sessionstore/test/browser_dying_cache.js
@@ -11,17 +11,17 @@ add_task(async function test() {
   let win = await promiseNewWindowLoaded();
 
   // Load some URL in the current tab.
   let flags = Ci.nsIWebNavigation.LOAD_FLAGS_REPLACE_HISTORY;
   win.gBrowser.selectedBrowser.loadURI("about:robots", { flags });
   await promiseBrowserLoaded(win.gBrowser.selectedBrowser);
 
   // Open a second tab and close the first one.
-  let tab = win.gBrowser.addTab("about:mozilla");
+  let tab = BrowserTestUtils.addTab(win.gBrowser, "about:mozilla");
   await promiseBrowserLoaded(tab.linkedBrowser);
   await TabStateFlusher.flush(tab.linkedBrowser);
   await promiseRemoveTabAndSessionState(win.gBrowser.tabs[0]);
 
   // Make sure our window is still tracked by sessionstore
   // and the window state is as expected.
   ok("__SSi" in win, "window is being tracked by sessionstore");
   ss.setWindowValue(win, "foo", "bar");
--- a/browser/components/sessionstore/test/browser_privatetabs.js
+++ b/browser/components/sessionstore/test/browser_privatetabs.js
@@ -67,31 +67,31 @@ add_task(async function() {
   forgetClosedWindows();
 
   // Create a new window to attach our frame script to.
   let win = await promiseNewWindowLoaded();
   let mm = win.getGroupMessageManager("browsers");
   mm.loadFrameScript(FRAME_SCRIPT, true);
 
   // Create a new tab in the new window that will load the frame script.
-  let tab = win.gBrowser.addTab("about:mozilla");
+  let tab = BrowserTestUtils.addTab(win.gBrowser, "about:mozilla");
   let browser = tab.linkedBrowser;
   await promiseBrowserLoaded(browser);
   await TabStateFlusher.flush(browser);
 
   // Check that we consider the tab as private.
   let state = JSON.parse(ss.getTabState(tab));
   ok(state.isPrivate, "tab considered private");
 
   // Ensure we don't allow restoring closed private tabs in non-private windows.
   win.gBrowser.removeTab(tab);
   is(ss.getClosedTabCount(win), 0, "no tabs to restore");
 
   // Create a new tab in the new window that will load the frame script.
-  tab = win.gBrowser.addTab("about:mozilla");
+  tab = BrowserTestUtils.addTab(win.gBrowser, "about:mozilla");
   browser = tab.linkedBrowser;
   await promiseBrowserLoaded(browser);
   await TabStateFlusher.flush(browser);
 
   // Check that we consider the tab as private.
   state = JSON.parse(ss.getTabState(tab));
   ok(state.isPrivate, "tab considered private");
 
@@ -104,17 +104,17 @@ add_task(async function() {
 add_task(async function() {
   // Clear the list of closed windows.
   forgetClosedWindows();
 
   // Create a new window to attach our frame script to.
   let win = await promiseNewWindowLoaded({private: true});
 
   // Create a new tab in the new window that will load the frame script.
-  let tab = win.gBrowser.addTab("about:mozilla");
+  let tab = BrowserTestUtils.addTab(win.gBrowser, "about:mozilla");
   let browser = tab.linkedBrowser;
   await promiseBrowserLoaded(browser);
   await TabStateFlusher.flush(browser);
 
   // Check that we consider the tab as private.
   let state = JSON.parse(ss.getTabState(tab));
   ok(state.isPrivate, "tab considered private");
 
--- a/browser/components/sessionstore/test/browser_scrollPositions.js
+++ b/browser/components/sessionstore/test/browser_scrollPositions.js
@@ -111,17 +111,17 @@ add_task(async function test_scroll_nest
  * a restored window (bug 1228518).
  * Also test that scroll positions for previous session history entries
  * are preserved as well (bug 1265818).
  */
 add_task(async function test_scroll_background_tabs() {
   pushPrefs(["browser.sessionstore.restore_on_demand", true]);
 
   let newWin = await BrowserTestUtils.openNewBrowserWindow();
-  let tab = newWin.gBrowser.addTab(URL);
+  let tab = BrowserTestUtils.addTab(newWin.gBrowser, URL);
   let browser = tab.linkedBrowser;
   await BrowserTestUtils.browserLoaded(browser);
 
   // Scroll down a little.
   await sendMessage(browser, "ss-test:setScrollPosition", {x: SCROLL_X, y: SCROLL_Y});
   await checkScroll(tab, {scroll: SCROLL_STR}, "scroll on first page is fine");
 
   // Navigate to a different page and scroll there as well.
--- a/browser/components/sessionstore/test/browser_scrollPositionsReaderMode.js
+++ b/browser/components/sessionstore/test/browser_scrollPositionsReaderMode.js
@@ -16,17 +16,17 @@ requestLongerTimeout(2);
 /**
  * Test that scroll positions of about reader page after restoring background
  * tabs in a restored window (bug 1153393).
  */
 add_task(async function test_scroll_background_about_reader_tabs() {
   pushPrefs(["browser.sessionstore.restore_on_demand", true]);
 
   let newWin = await BrowserTestUtils.openNewBrowserWindow();
-  let tab = newWin.gBrowser.addTab(READER_MODE_URL);
+  let tab = BrowserTestUtils.addTab(newWin.gBrowser, READER_MODE_URL);
   let browser = tab.linkedBrowser;
   await Promise.all([
     BrowserTestUtils.browserLoaded(browser),
     BrowserTestUtils.waitForContentEvent(browser, "AboutReaderContentReady")
   ]);
 
   // Scroll down a little.
   await sendMessage(browser, "ss-test:setScrollPosition", {x: 0, y: SCROLL_READER_MODE_Y});
--- a/browser/components/sessionstore/test/browser_switch_remoteness.js
+++ b/browser/components/sessionstore/test/browser_switch_remoteness.js
@@ -11,17 +11,17 @@ function countHistoryEntries(browser, ex
   });
 }
 
 add_task(async function() {
   // Open a new window.
   let win = await promiseNewWindowLoaded();
 
   // Add a new tab.
-  let tab = win.gBrowser.addTab("about:blank");
+  let tab = BrowserTestUtils.addTab(win.gBrowser, "about:blank");
   let browser = tab.linkedBrowser;
   await promiseBrowserLoaded(browser);
   ok(browser.isRemoteBrowser, "browser is remote");
 
   // Get the maximum number of preceding entries to save.
   const MAX_BACK = Services.prefs.getIntPref("browser.sessionstore.max_serialize_back");
   ok(MAX_BACK > -1, "check that the default has a value that caps data");
 
--- a/browser/components/sessionstore/test/browser_undoCloseById.js
+++ b/browser/components/sessionstore/test/browser_undoCloseById.js
@@ -3,17 +3,17 @@
 
 /**
  * This test is for the undoCloseById function.
  */
 
 ChromeUtils.import("resource:///modules/sessionstore/SessionStore.jsm");
 
 async function openAndCloseTab(window, url) {
-  let tab = window.gBrowser.addTab(url);
+  let tab = BrowserTestUtils.addTab(window.gBrowser, url);
   await promiseBrowserLoaded(tab.linkedBrowser, true, url);
   await TabStateFlusher.flush(tab.linkedBrowser);
   await promiseRemoveTabAndSessionState(tab);
 }
 
 async function openWindow(url) {
   let win = await promiseNewWindowLoaded();
   let flags = Ci.nsIWebNavigation.LOAD_FLAGS_REPLACE_HISTORY;
--- a/browser/components/sessionstore/test/browser_windowStateContainer.js
+++ b/browser/components/sessionstore/test/browser_windowStateContainer.js
@@ -8,17 +8,19 @@ add_task(async function setup() {
   });
 });
 
 add_task(async function() {
   let win = await BrowserTestUtils.openNewBrowserWindow();
 
   // Create 4 tabs with different userContextId.
   for (let userContextId = 1; userContextId < 5; userContextId++) {
-    let tab = win.gBrowser.addTab("http://example.com/", {userContextId});
+    let tab = BrowserTestUtils.addTab(win.gBrowser, "http://example.com/", {
+      userContextId,
+    });
     await promiseBrowserLoaded(tab.linkedBrowser);
     await TabStateFlusher.flush(tab.linkedBrowser);
   }
 
   // Move the default tab of window to the end.
   // We want the 1st tab to have non-default userContextId, so later when we
   // restore into win2 we can test restore into an existing tab with different
   // userContextId.
@@ -31,17 +33,19 @@ add_task(async function() {
                  "1st Window: tabs[" + i + "].userContextId should exist.");
   }
 
   let win2 = await BrowserTestUtils.openNewBrowserWindow();
 
   // Create tabs with different userContextId, but this time we create them with
   // fewer tabs and with different order with win.
   for (let userContextId = 3; userContextId > 0; userContextId--) {
-    let tab = win2.gBrowser.addTab("http://example.com/", {userContextId});
+    let tab = BrowserTestUtils.addTab(win2.gBrowser, "http://example.com/", {
+      userContextId,
+    });
     await promiseBrowserLoaded(tab.linkedBrowser);
     await TabStateFlusher.flush(tab.linkedBrowser);
   }
 
   await setWindowState(win2, winState, true);
 
   for (let i = 0; i < 4; i++) {
     let browser = win2.gBrowser.tabs[i].linkedBrowser;
@@ -71,33 +75,37 @@ add_task(async function() {
   await BrowserTestUtils.closeWindow(win);
   await BrowserTestUtils.closeWindow(win2);
 });
 
 add_task(async function() {
   let win = await BrowserTestUtils.openNewBrowserWindow();
   await TabStateFlusher.flush(win.gBrowser.selectedBrowser);
 
-  let tab = win.gBrowser.addTab("http://example.com/", { userContextId: 1 });
+  let tab = BrowserTestUtils.addTab(win.gBrowser, "http://example.com/", {
+    userContextId: 1,
+  });
   await promiseBrowserLoaded(tab.linkedBrowser);
   await TabStateFlusher.flush(tab.linkedBrowser);
 
   // win should have 1 default tab, and 1 container tab.
   Assert.equal(win.gBrowser.tabs.length, 2, "win should have 2 tabs");
 
   let winState = JSON.parse(ss.getWindowState(win));
 
   for (let i = 0; i < 2; i++) {
     Assert.equal(winState.windows[0].tabs[i].userContextId, i,
                  "1st Window: tabs[" + i + "].userContextId should be " + i);
   }
 
   let win2 = await BrowserTestUtils.openNewBrowserWindow();
 
-  let tab2 = win2.gBrowser.addTab("http://example.com/", { userContextId: 1 });
+  let tab2 = BrowserTestUtils.addTab(win2.gBrowser, "http://example.com/", {
+    userContextId: 1,
+  });
   await promiseBrowserLoaded(tab2.linkedBrowser);
   await TabStateFlusher.flush(tab2.linkedBrowser);
 
   // Move the first normal tab to end, so the first tab of win2 will be a
   // container tab.
   win2.gBrowser.moveTabTo(win2.gBrowser.tabs[0], win2.gBrowser.tabs.length - 1);
   await TabStateFlusher.flush(win2.gBrowser.tabs[0].linkedBrowser);
 
--- a/devtools/client/commandline/test/helpers.js
+++ b/devtools/client/commandline/test/helpers.js
@@ -66,17 +66,17 @@ var { helpers, assert } = (function () {
   helpers.addTab = function (url, callback, options) {
     waitForExplicitFinish();
 
     options = options || {};
     options.chromeWindow = options.chromeWindow || window;
     options.isFirefox = true;
 
     var tabbrowser = options.chromeWindow.gBrowser;
-    options.tab = tabbrowser.addTab();
+    options.tab = BrowserTestUtils.addTab(tabbrowser);
     tabbrowser.selectedTab = options.tab;
     options.browser = tabbrowser.getBrowserForTab(options.tab);
     options.target = TargetFactory.forTab(options.tab);
 
     var loaded = BrowserTestUtils.browserLoaded(options.browser).then(function () {
       var reply = callback.call(null, options);
 
       return Promise.resolve(reply).catch(function (error) {
@@ -112,17 +112,17 @@ var { helpers, assert } = (function () {
   helpers.openTab = function (url, options) {
     waitForExplicitFinish();
 
     options = options || {};
     options.chromeWindow = options.chromeWindow || window;
     options.isFirefox = true;
 
     var tabbrowser = options.chromeWindow.gBrowser;
-    options.tab = tabbrowser.addTab();
+    options.tab = BrowserTestUtils.addTab(tabbrowser);
     tabbrowser.selectedTab = options.tab;
     options.browser = tabbrowser.getBrowserForTab(options.tab);
     options.target = TargetFactory.forTab(options.tab);
 
     return helpers.navigate(url, options);
   };
 
 /**
--- a/devtools/client/debugger/test/mochitest/head.js
+++ b/devtools/client/debugger/test/mochitest/head.js
@@ -86,17 +86,17 @@ function getChromeWindow(aWindow) {
 this.addTab = function addTab(aUrl, aWindow) {
   info("Adding tab: " + aUrl);
 
   let deferred = promise.defer();
   let targetWindow = aWindow || window;
   let targetBrowser = targetWindow.gBrowser;
 
   targetWindow.focus();
-  let tab = targetBrowser.selectedTab = targetBrowser.addTab(aUrl);
+  let tab = targetBrowser.selectedTab = BrowserTestUtils.addTab(targetBrowser, aUrl);
   let linkedBrowser = tab.linkedBrowser;
 
   info("Loading frame script with url " + FRAME_SCRIPT_URL + ".");
   linkedBrowser.messageManager.loadFrameScript(FRAME_SCRIPT_URL, false);
 
   BrowserTestUtils.browserLoaded(linkedBrowser)
     .then(function () {
       info("Tab added and finished loading: " + aUrl);
--- a/devtools/client/framework/devtools-browser.js
+++ b/devtools/client/framework/devtools-browser.js
@@ -270,25 +270,25 @@ var gDevToolsBrowser = exports.gDevTools
   },
 
   /**
    * Open a tab on "about:debugging", optionally pre-select a given tab.
    */
    // Used by browser-sets.inc, command
   openAboutDebugging(gBrowser, hash) {
     const url = "about:debugging" + (hash ? "#" + hash : "");
-    gBrowser.selectedTab = gBrowser.addTab(url);
+    gBrowser.selectedTab = gBrowser.addTrustedTab(url);
   },
 
   /**
    * Open a tab to allow connects to a remote browser
    */
    // Used by browser-sets.inc, command
   openConnectScreen(gBrowser) {
-    gBrowser.selectedTab = gBrowser.addTab("chrome://devtools/content/framework/connect/connect.xhtml");
+    gBrowser.selectedTab = gBrowser.addTrustedTab("chrome://devtools/content/framework/connect/connect.xhtml");
   },
 
   /**
    * Open WebIDE
    */
    // Used by browser-sets.inc, command
    //         itself, webide widget
   openWebIDE() {
--- a/devtools/client/netmonitor/src/utils/firefox/open-request-in-tab.js
+++ b/devtools/client/netmonitor/src/utils/firefox/open-request-in-tab.js
@@ -29,17 +29,20 @@ function openRequestInTab(url, requestPo
   if (rawData && rawData.text) {
     const stringStream = getInputStreamFromString(rawData.text);
     postData = Cc["@mozilla.org/network/mime-input-stream;1"]
       .createInstance(Ci.nsIMIMEInputStream);
     postData.addHeader("Content-Type", "application/x-www-form-urlencoded");
     postData.setData(stringStream);
   }
 
-  win.gBrowser.selectedTab = win.gBrowser.addTab(url, null, null, postData);
+  win.gBrowser.selectedTab = win.gBrowser.addWebTab(url, {
+    triggeringPrincipal: win.gBrowser.contentPrincipal,
+    postData,
+  });
 }
 
 function getInputStreamFromString(data) {
   const stringStream = Cc["@mozilla.org/io/string-input-stream;1"]
     .createInstance(Ci.nsIStringInputStream);
   stringStream.data = data;
   return stringStream;
 }
--- a/devtools/client/performance-new/browser.js
+++ b/devtools/client/performance-new/browser.js
@@ -23,17 +23,19 @@ function receiveProfile(profile) {
   // of hosts.
   const win = Services.wm.getMostRecentWindow("navigator:browser");
   if (!win) {
     throw new Error("No browser window");
   }
   const browser = win.gBrowser;
   Services.focus.activeWindow = win;
 
-  const tab = browser.addTab("https://perf-html.io/from-addon");
+  const tab = browser.addWebTab("https://perf-html.io/from-addon", {
+    triggeringPrincipal: browser.contentPrincipal,
+  });
   browser.selectedTab = tab;
   const mm = tab.linkedBrowser.messageManager;
   mm.loadFrameScript(
     "chrome://devtools/content/performance-new/frame-script.js",
     false
   );
   mm.sendAsyncMessage("devtools:perf-html-transfer-profile", profile);
 }
--- a/devtools/client/responsive.html/browser/swap.js
+++ b/devtools/client/responsive.html/browser/swap.js
@@ -49,21 +49,21 @@ function swapToInnerBrowser({ tab, conta
       bubbles: true,
     });
     from.dispatchEvent(event);
   };
 
   // A version of `gBrowser.addTab` that absorbs the `TabOpen` event.
   // The swap process uses a temporary tab, and there's no real need for others to hear
   // about it.  This hides the temporary tab from things like WebExtensions.
-  const addTabSilently = (uri, options) => {
+  const addTrustedTabSilently = (uri, options) => {
     browserWindow.addEventListener("TabOpen", event => {
       event.stopImmediatePropagation();
     }, { capture: true, once: true });
-    return gBrowser.addTab(uri, options);
+    return gBrowser.addTrustedTab(uri, options);
   };
 
   // A version of `gBrowser.swapBrowsersAndCloseOther` that absorbs the `TabClose` event.
   // The swap process uses a temporary tab, and there's no real need for others to hear
   // about it.  This hides the temporary tab from things like WebExtensions.
   const swapBrowsersAndCloseOtherSilently = (ourTab, otherTab) => {
     browserWindow.addEventListener("TabClose", event => {
       event.stopImmediatePropagation();
@@ -122,17 +122,17 @@ function swapToInnerBrowser({ tab, conta
       // strange intermediate states.
       tab.linkedBrowser.style.visibility = "hidden";
 
       // Freeze navigation temporarily to avoid "blinking" in the location bar.
       freezeNavigationState(tab);
 
       // 1. Create a temporary, hidden tab to load the tool UI.
       debug("Add blank tool tab");
-      const containerTab = addTabSilently("about:blank", {
+      const containerTab = addTrustedTabSilently("about:blank", {
         skipAnimation: true,
         forceNotRemote: true,
         userContextId: tab.userContextId,
       });
       gBrowser.hideTab(containerTab);
       const containerBrowser = containerTab.linkedBrowser;
       // Even though we load the `containerURL` with `LOAD_FLAGS_BYPASS_HISTORY` below,
       // `SessionHistory.jsm` has a fallback path for tabs with no history which
@@ -235,17 +235,17 @@ function swapToInnerBrowser({ tab, conta
       // strange intermediate states.
       tab.linkedBrowser.style.visibility = "hidden";
 
       // 1. Stop the tunnel between outer and inner browsers.
       tunnel.stop();
       tunnel = null;
 
       // 2. Create a temporary, hidden tab to hold the content.
-      const contentTab = addTabSilently("about:blank", {
+      const contentTab = addTrustedTabSilently("about:blank", {
         skipAnimation: true,
         userContextId: tab.userContextId,
       });
       gBrowser.hideTab(contentTab);
       const contentBrowser = contentTab.linkedBrowser;
 
       // 3. Mark the content tab browser's docshell as active so the frame
       //    is created eagerly and will be ready to swap.
--- a/devtools/client/styleeditor/test/head.js
+++ b/devtools/client/styleeditor/test/head.js
@@ -23,17 +23,17 @@ const TEST_HOST = "mochi.test:8888";
  */
 var addTab = function(url, win) {
   info("Adding a new tab with URL: '" + url + "'");
 
   return new Promise(resolve => {
     const targetWindow = win || window;
     const targetBrowser = targetWindow.gBrowser;
 
-    const tab = targetBrowser.selectedTab = targetBrowser.addTab(url);
+    const tab = targetBrowser.selectedTab = BrowserTestUtils.addTab(targetBrowser, url);
     BrowserTestUtils.browserLoaded(targetBrowser.selectedBrowser)
       .then(function() {
         info("URL '" + url + "' loading complete");
         resolve(tab);
       });
   });
 };
 
--- a/devtools/client/webconsole/test/mochitest/browser_console_webconsole_private_browsing.js
+++ b/devtools/client/webconsole/test/mochitest/browser_console_webconsole_private_browsing.js
@@ -26,17 +26,17 @@ const PRIVATE_TEST_URI = `data:text/html
   </script>`;
 
 add_task(async function() {
   await addTab(NON_PRIVATE_TEST_URI);
 
   const privateWindow = await openNewBrowserWindow({ private: true });
   ok(PrivateBrowsingUtils.isWindowPrivate(privateWindow), "window is private");
   const privateBrowser = privateWindow.gBrowser;
-  privateBrowser.selectedTab = privateBrowser.addTab(PRIVATE_TEST_URI);
+  privateBrowser.selectedTab = BrowserTestUtils.addTab(privateBrowser, PRIVATE_TEST_URI);
   const privateTab = privateBrowser.selectedTab;
 
   info("private tab opened");
   ok(PrivateBrowsingUtils.isBrowserPrivate(privateBrowser.selectedBrowser),
     "tab window is private");
 
   let hud = await openConsole(privateTab);
   ok(hud, "web console opened");
--- a/devtools/startup/devtools-startup.js
+++ b/devtools/startup/devtools-startup.js
@@ -684,17 +684,17 @@ DevToolsStartup.prototype = {
       params.push("keyid=" + keyId);
     }
 
     if (params.length > 0) {
       url += "?" + params.join("&");
     }
 
     // Set relatedToCurrent: true to open the tab next to the current one.
-    gBrowser.selectedTab = gBrowser.addTab(url, {relatedToCurrent: true});
+    gBrowser.selectedTab = gBrowser.addTrustedTab(url, {relatedToCurrent: true});
   },
 
   handleConsoleFlag: function(cmdLine) {
     const window = Services.wm.getMostRecentWindow("devtools:webconsole");
     if (!window) {
       const require = this.initDevTools("CommandLine");
       const { HUDService } = require("devtools/client/webconsole/hudservice");
       HUDService.toggleBrowserConsole().catch(console.error);
--- a/devtools/startup/tests/browser/browser_shim_disable_devtools.js
+++ b/devtools/startup/tests/browser/browser_shim_disable_devtools.js
@@ -29,17 +29,18 @@ add_task(async function() {
     CustomizableUI.removeWidgetFromArea("developer-button");
   }
 
   info("Open a new window, all window-specific hooks for DevTools will be disabled.");
   const win = OpenBrowserWindow({private: false});
   await waitForDelayedStartupFinished(win);
 
   info("Open a new tab on the new window to ensure the focus is on the new window");
-  const tab = win.gBrowser.addTab("data:text/html;charset=utf-8,<title>foo</title>");
+  const tab = BrowserTestUtils.addTab(win.gBrowser,
+    "data:text/html;charset=utf-8,<title>foo</title>");
   await BrowserTestUtils.browserLoaded(win.gBrowser.getBrowserForTab(tab));
 
   info("Synthesize a DevTools shortcut, the toolbox should not open on this new window.");
   synthesizeToggleToolboxKey(win);
 
   // There is no event to wait for here as this shortcut should have no effect.
   /* eslint-disable mozilla/no-arbitrary-setTimeout */
   await new Promise(r => setTimeout(r, 1000));
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -963,16 +963,17 @@ nsDocShell::LoadURI(nsIURI* aURI,
     else {
       triggeringPrincipal = nsContentUtils::GetSystemPrincipal();
     }
   }
 
   uint32_t flags = 0;
 
   if (inheritPrincipal) {
+    MOZ_ASSERT(!nsContentUtils::IsSystemPrincipal(principalToInherit), "Should not inherit SystemPrincipal");
     flags |= INTERNAL_LOAD_FLAGS_INHERIT_PRINCIPAL;
   }
 
   if (!sendReferrer) {
     flags |= INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER;
   }
 
   if (aLoadFlags & LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP) {
--- a/docshell/test/browser/head.js
+++ b/docshell/test/browser/head.js
@@ -43,17 +43,18 @@ function timelineTestOpenUrl(url) {
 
   let tabSwitchPromise = new Promise((resolve, reject) => {
     window.gBrowser.addEventListener("TabSwitchDone", function() {
       resolve();
     }, {once: true});
   });
 
   let loadPromise = new Promise(function(resolve, reject) {
-    let tab = window.gBrowser.selectedTab = window.gBrowser.addTab(url);
+    let browser = window.gBrowser;
+    let tab = browser.selectedTab = BrowserTestUtils.addTab(browser, url);
     let linkedBrowser = tab.linkedBrowser;
 
     BrowserTestUtils.browserLoaded(linkedBrowser).then(() => resolve(tab));
   });
 
   return Promise.all([tabSwitchPromise, loadPromise]).then(([_, tab]) => tab);
 }
 
--- a/dom/base/test/browser_blocking_image.js
+++ b/dom/base/test/browser_blocking_image.js
@@ -1,15 +1,15 @@
 const TEST_URI = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "https://example.com") + "file_blocking_image.html";
 
 /**
  * Loading an image from https:// should work.
  */
 add_task(async function load_image_from_https_test() {
-  let tab = gBrowser.addTab(TEST_URI);
+  let tab = BrowserTestUtils.addTab(gBrowser, TEST_URI);
   await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
 
   gBrowser.selectedTab = tab;
 
   await ContentTask.spawn(tab.linkedBrowser, { }, async function() {
     function imgListener(img) {
       return new Promise((resolve, reject) => {
         img.addEventListener("load", () => resolve());
@@ -33,17 +33,17 @@ add_task(async function load_image_from_
 
   gBrowser.removeTab(tab);
 });
 
 /**
  * Loading an image from http:// should be rejected.
  */
 add_task(async function load_image_from_http_test() {
-  let tab = gBrowser.addTab(TEST_URI);
+  let tab = BrowserTestUtils.addTab(gBrowser, TEST_URI);
   await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
 
   gBrowser.selectedTab = tab;
 
   await ContentTask.spawn(tab.linkedBrowser, { }, async function () {
     function imgListener(img) {
       return new Promise((resolve, reject) => {
         img.addEventListener("load", () => reject());
@@ -69,17 +69,17 @@ add_task(async function load_image_from_
   gBrowser.removeTab(tab);
 });
 
 /**
  * Loading an image from http:// immediately after loading from https://
  * The load from https:// should be replaced.
  */
 add_task(async function load_https_and_http_test() {
-  let tab = gBrowser.addTab(TEST_URI);
+  let tab = BrowserTestUtils.addTab(gBrowser, TEST_URI);
   await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
 
   // Clear image cache, otherwise in non-e10s mode the image might be cached by
   // previous test, and make the image from https is loaded immediately.
   let imgTools = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools);
   let imageCache = imgTools.getImgCacheForDocument(window.document);
   imageCache.clearCache(false); // false=content
 
@@ -116,17 +116,17 @@ add_task(async function load_https_and_h
 /**
  * Loading an image from https.
  * Then after we have size information of the image, we immediately change the
  * location to a http:// site (hence should be blocked by CSP). This will make
  * the 2nd request as a PENDING_REQUEST, also blocking 2nd load shouldn't change
  * the imageBlockingStatus value.
  */
 add_task(async function block_pending_request_test() {
-  let tab = gBrowser.addTab(TEST_URI);
+  let tab = BrowserTestUtils.addTab(gBrowser, TEST_URI);
   await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
 
   gBrowser.selectedTab = tab;
 
   await ContentTask.spawn(tab.linkedBrowser, { }, async function () {
     let wrapper = {
       _resolve: null,
       _sizeAvail: false,
--- a/dom/broadcastchannel/tests/browser_private_browsing.js
+++ b/dom/broadcastchannel/tests/browser_private_browsing.js
@@ -15,21 +15,21 @@ add_task(async function() {
   var win2 = OpenBrowserWindow({private: false});
   var win2Promise = new win2.Promise(resolve => {
     win2.addEventListener("load", function() {
       resolve();
     }, {once: true});
   });
   await win2Promise;
 
-  var tab1 = win1.gBrowser.addTab(URL);
+  var tab1 = BrowserTestUtils.addTab(win1.gBrowser, URL);
   await BrowserTestUtils.browserLoaded(win1.gBrowser.getBrowserForTab(tab1));
   var browser1 = gBrowser.getBrowserForTab(tab1);
 
-  var tab2 = win2.gBrowser.addTab(URL);
+  var tab2 = BrowserTestUtils.addTab(win2.gBrowser, URL);
   await BrowserTestUtils.browserLoaded(win2.gBrowser.getBrowserForTab(tab2));
   var browser2 = gBrowser.getBrowserForTab(tab2);
 
   var p1 = ContentTask.spawn(browser1, null, function(opts) {
     return new content.window.Promise(resolve => {
       content.window.bc = new content.window.BroadcastChannel('foobar');
       content.window.bc.onmessage = function(e) { resolve(e.data); }
     });
--- a/dom/cache/test/mochitest/browser_cache_pb_window.js
+++ b/dom/cache/test/mochitest/browser_cache_pb_window.js
@@ -98,17 +98,17 @@ function test() {
   let privateWin, privateTab;
   waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({'set': [['dom.caches.enabled', true],
                                      ['dom.caches.testing.enabled', true]]}
   ).then(() => {
     return BrowserTestUtils.openNewBrowserWindow({private: true});
   }).then(pw => {
     privateWin = pw;
-    privateTab = pw.gBrowser.addTab("http://example.com/");
+    privateTab = BrowserTestUtils.addTab(pw.gBrowser, "http://example.com/");
     return BrowserTestUtils.browserLoaded(privateTab.linkedBrowser);
   }).then(tab => {
     return Promise.all([
       testMatch(privateTab.linkedBrowser),
       testHas(privateTab.linkedBrowser),
       testOpen(privateTab.linkedBrowser),
       testDelete(privateTab.linkedBrowser),
       testKeys(privateTab.linkedBrowser),
--- a/dom/html/test/browser_bug1108547.js
+++ b/dom/html/test/browser_bug1108547.js
@@ -38,17 +38,17 @@ function runPass(getterFile, finishedCal
 
     // Now, open a private window.
     privateWin = OpenBrowserWindow({private: true});
       whenDelayedStartupFinished(privateWin, afterPrivateWindowOpened);
   }
 
   function afterPrivateWindowOpened() {
     // In the private window, open the getter file, and wait for a new tab to be opened.
-    privateWin.gBrowser.selectedTab = privateWin.gBrowser.addTab(rootDir + getterFile);
+    privateWin.gBrowser.selectedTab = BrowserTestUtils.addTab(privateWin.gBrowser, rootDir + getterFile);
     testBrowser = privateWin.gBrowser.selectedBrowser;
     privateWin.gBrowser.tabContainer.addEventListener("TabOpen", onNewTabOpened, true);
   }
 
   function fetchResult() {
     return ContentTask.spawn(testBrowser, null, function() {
       return content.document.getElementById("result").textContent;
     });
@@ -72,17 +72,17 @@ function runPass(getterFile, finishedCal
 
     // Open a new private window, this time to set a cookie inside it.
     privateWin = OpenBrowserWindow({private: true});
       whenDelayedStartupFinished(privateWin, afterPrivateWindowOpened2);
   }
 
   function afterPrivateWindowOpened2() {
     // In the private window, open the setter file, and wait for it to load.
-    privateWin.gBrowser.selectedTab = privateWin.gBrowser.addTab(rootDir + "file_bug1108547-1.html");
+    privateWin.gBrowser.selectedTab = BrowserTestUtils.addTab(privateWin.gBrowser, rootDir + "file_bug1108547-1.html");
     BrowserTestUtils.browserLoaded(privateWin.gBrowser.selectedBrowser).then(afterOpenCookieSetter2);
   }
 
   function afterOpenCookieSetter2() {
     // We're done with the private window now, close it.
     privateWin.close();
 
     // Now try to read the cookie in a normal window, and wait for a new tab to be opened.
--- a/dom/indexedDB/test/browser_permissionsPromptDeny.js
+++ b/dom/indexedDB/test/browser_permissionsPromptDeny.js
@@ -44,17 +44,17 @@ add_task(async function test2() {
   registerPopupEventHandler("popupshown", function() {
     ok(false, "prompt shown");
   }, win);
   registerPopupEventHandler("popuphidden", function() {
     ok(false, "prompt hidden");
   }, win);
 
   info("creating private tab");
-  win.gBrowser.selectedTab = win.gBrowser.addTab();
+  win.gBrowser.selectedTab = BrowserTestUtils.addTab(win.gBrowser);
 
   info("loading test page: " + testPageURL);
   win.gBrowser.selectedBrowser.loadURI(testPageURL);
   await waitForMessage("InvalidStateError", win.gBrowser);
 
   is(getPermission(testPageURL, "indexedDB"),
      Ci.nsIPermissionManager.DENY_ACTION,
      "Correct permission set");
--- a/dom/indexedDB/test/browser_perwindow_privateBrowsing.js
+++ b/dom/indexedDB/test/browser_perwindow_privateBrowsing.js
@@ -21,15 +21,15 @@ add_task(async function test1() {
   gBrowser.removeCurrentTab();
 });
 
 add_task(async function test2() {
   info("creating private window");
   let win = await BrowserTestUtils.openNewBrowserWindow({ private: true });
 
   info("creating tab");
-  win.gBrowser.selectedTab = win.gBrowser.addTab();
+  win.gBrowser.selectedTab = BrowserTestUtils.addTab(win.gBrowser);
   win.gBrowser.selectedBrowser.loadURI(testPageURL);
   await waitForMessage("InvalidStateError", win.gBrowser);
   win.gBrowser.removeCurrentTab();
   await BrowserTestUtils.closeWindow(win);
   win = null;
 });
--- a/dom/quota/test/browser_permissionsPromptUnknown.js
+++ b/dom/quota/test/browser_permissionsPromptUnknown.js
@@ -17,17 +17,17 @@ add_task(async function testPermissionUn
   registerPopupEventHandler("popupshown", function () {
     ok(false, "Shouldn't show a popup this time");
   }, win);
   registerPopupEventHandler("popuphidden", function () {
     ok(false, "Shouldn't show a popup this time");
   }, win);
 
   info("Creating private tab");
-  win.gBrowser.selectedTab = win.gBrowser.addTab();
+  win.gBrowser.selectedTab = BrowserTestUtils.addTab(win.gBrowser);
 
   info("Loading test page: " + testPageURL);
   win.gBrowser.selectedBrowser.loadURI(testPageURL);
   await waitForMessage(false, win.gBrowser);
 
   is(getPermission(testPageURL, "persistent-storage"),
      Ci.nsIPermissionManager.UNKNOWN_ACTION,
      "Correct permission set");
--- a/dom/tests/browser/browser_localStorage_privatestorageevent.js
+++ b/dom/tests/browser/browser_localStorage_privatestorageevent.js
@@ -10,21 +10,21 @@ add_task(async function() {
   await new pubWin.Promise(resolve => {
     pubWin.addEventListener('load', function() {
       resolve();
     }, {once: true});
   });
 
   var URL = "http://mochi.test:8888/browser/dom/tests/browser/page_privatestorageevent.html";
 
-  var privTab = privWin.gBrowser.addTab(URL);
+  var privTab = BrowserTestUtils.addTab(privWin.gBrowser, URL);
   await BrowserTestUtils.browserLoaded(privWin.gBrowser.getBrowserForTab(privTab));
   var privBrowser = gBrowser.getBrowserForTab(privTab);
 
-  var pubTab = pubWin.gBrowser.addTab(URL);
+  var pubTab = BrowserTestUtils.addTab(pubWin.gBrowser, URL);
   await BrowserTestUtils.browserLoaded(pubWin.gBrowser.getBrowserForTab(pubTab));
   var pubBrowser = gBrowser.getBrowserForTab(pubTab);
 
   // Check if pubWin can see privWin's storage events
   await ContentTask.spawn(pubBrowser, null, function(opts) {
     content.window.gotStorageEvent = false;
     content.window.addEventListener('storage', ev => {
       content.window.gotStorageEvent = true;
--- a/dom/tests/browser/helper_largeAllocation.js
+++ b/dom/tests/browser/helper_largeAllocation.js
@@ -415,17 +415,18 @@ async function largeAllocSuccessTests() 
 
     let pid2 = await getPID(aBrowser);
 
     isnot(pid1, pid2, "PIDs 1 and 2 should not match");
     is(true, await getInLAProc(aBrowser));
 
     let newWindow = await BrowserTestUtils.openNewBrowserWindow();
 
-    newWindow.gBrowser.adoptTab(gBrowser.getTabForBrowser(aBrowser), 0);
+    newWindow.gBrowser.adoptTab(gBrowser.getTabForBrowser(aBrowser), 0, null,
+                                Services.scriptSecurityManager.getSystemPrincipal());
     let newTab = newWindow.gBrowser.tabs[0];
 
     is(newTab.linkedBrowser.currentURI.spec, TEST_URI);
     is(newTab.linkedBrowser.remoteType, "webLargeAllocation");
     let pid3 = await getPID(newTab.linkedBrowser);
 
     is(pid2, pid3, "PIDs 2 and 3 should match");
     is(true, await getInLAProc(newTab.linkedBrowser));
--- a/testing/mochitest/BrowserTestUtils/BrowserTestUtils.jsm
+++ b/testing/mochitest/BrowserTestUtils/BrowserTestUtils.jsm
@@ -191,17 +191,17 @@ var BrowserTestUtils = {
       }
 
       promises = [
         BrowserTestUtils.switchTab(tabbrowser, function() {
           if (typeof opening == "function") {
             opening();
             tab = tabbrowser.selectedTab;
           } else {
-            tabbrowser.selectedTab = tab = tabbrowser.addTab(opening);
+            tabbrowser.selectedTab = tab = BrowserTestUtils.addTab(tabbrowser, opening);
           }
         })
       ];
 
       if (aWaitForLoad) {
         promises.push(BrowserTestUtils.browserLoaded(tab.linkedBrowser));
       }
       if (aWaitForStateStop) {
--- a/testing/mochitest/bootstrap.js
+++ b/testing/mochitest/bootstrap.js
@@ -78,17 +78,18 @@ var WindowListener = {
 function loadMochitest(e) {
   let flavor = e.detail[0];
   let url = e.detail[1];
 
   let win = Services.wm.getMostRecentWindow("navigator:browser");
   win.removeEventListener("mochitest-load", loadMochitest);
 
   // for mochitest-plain, navigating to the url is all we need
-  win.loadURI(url);
+  win.loadURI(url, null, null, null, null, null, null, null,
+    Services.scriptSecurityManager.getSystemPrincipal());
   if (flavor == "mochitest") {
     return;
   }
 
   WindowListener.setupWindow(win);
   Services.wm.addListener(WindowListener);
 
   loadChromeScripts(win);
--- a/testing/mochitest/browser-test.js
+++ b/testing/mochitest/browser-test.js
@@ -567,17 +567,20 @@ Tester.prototype = {
           }));
         }
         gBrowser.removeTab(lastTab);
       }
     }
 
     // Replace the last tab with a fresh one
     if (window.gBrowser) {
-      gBrowser.addTab("about:blank", { skipAnimation: true });
+      gBrowser.addTab("about:blank", {
+        skipAnimation: true,
+        triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
+      });
       gBrowser.removeTab(gBrowser.selectedTab, { skipPermitUnload: true });
       gBrowser.stop();
     }
 
     // Remove stale windows
     this.structuredLogger.info("checking window state");
     let windowsEnum = Services.wm.getEnumerator(null);
     while (windowsEnum.hasMoreElements()) {
--- a/toolkit/components/extensions/test/browser/browser_ext_themes_tab_separators.js
+++ b/toolkit/components/extensions/test/browser/browser_ext_themes_tab_separators.js
@@ -13,17 +13,17 @@ add_task(async function test_support_tab
         },
       },
     },
   });
   await extension.startup();
 
   info("Checking background tab separator color");
 
-  let tab = gBrowser.addTab("about:blank");
+  let tab = BrowserTestUtils.addTab(gBrowser, "about:blank");
 
   Assert.equal(window.getComputedStyle(tab, "::before").borderLeftColor,
                `rgb(${hexToRGB(TAB_SEPARATOR_COLOR).join(", ")})`,
                "Left separator has right color.");
 
   Assert.equal(window.getComputedStyle(tab, "::after").borderLeftColor,
                `rgb(${hexToRGB(TAB_SEPARATOR_COLOR).join(", ")})`,
                "Right separator has right color.");
--- a/toolkit/components/normandy/lib/Heartbeat.jsm
+++ b/toolkit/components/normandy/lib/Heartbeat.jsm
@@ -341,17 +341,21 @@ var Heartbeat = class {
     }
 
     // Open the engagement tab if we have a valid engagement URL.
     if (this.options.postAnswerUrl) {
       for (const key in engagementParams) {
         this.options.postAnswerUrl.searchParams.append(key, engagementParams[key]);
       }
       // Open the engagement URL in a new tab.
-      this.chromeWindow.gBrowser.selectedTab = this.chromeWindow.gBrowser.addTab(this.options.postAnswerUrl.toString());
+      // TODO check do we really need system here?
+      // toolkit/components/normandy/test/browser/browser_Heartbeat.js fails without
+      this.chromeWindow.gBrowser.selectedTab = this.chromeWindow.gBrowser.addWebTab(this.options.postAnswerUrl.toString(), {
+        triggeringPrincipal: this.chromeWindow.gBrowser.contentPrincipal,
+      });
     }
 
     this.endTimerIfPresent("surveyEndTimer");
 
     this.engagementCloseTimer = setTimeout(() => this.close(), NOTIFICATION_TIME);
   }
 
   endTimerIfPresent(timerName) {
--- a/toolkit/content/tests/browser/browser_block_autoplay_media.js
+++ b/toolkit/content/tests/browser/browser_block_autoplay_media.js
@@ -21,26 +21,26 @@ add_task(async function setup_test_prefe
   await SpecialPowers.pushPrefEnv({"set": [
     ["media.useAudioChannelService.testing", true],
     ["media.block-autoplay-until-in-foreground", true]
   ]});
 });
 
 add_task(async function block_autoplay_media() {
   info("- open new background tab1 -");
-  let tab1 = window.gBrowser.addTab("about:blank");
+  let tab1 = BrowserTestUtils.addTab(window.gBrowser, "about:blank");
   tab1.linkedBrowser.loadURI(PAGE);
   await BrowserTestUtils.browserLoaded(tab1.linkedBrowser);
 
   info("- should block autoplay media for non-visited tab1 -");
   await ContentTask.spawn(tab1.linkedBrowser, SuspendedType.SUSPENDED_BLOCK,
                           check_audio_suspended);
 
   info("- open new background tab2 -");
-  let tab2 = window.gBrowser.addTab("about:blank");
+  let tab2 = BrowserTestUtils.addTab(window.gBrowser, "about:blank");
   tab2.linkedBrowser.loadURI(PAGE);
   await BrowserTestUtils.browserLoaded(tab2.linkedBrowser);
 
   info("- should block autoplay for non-visited tab2 -");
   await ContentTask.spawn(tab2.linkedBrowser, SuspendedType.SUSPENDED_BLOCK,
                           check_audio_suspended);
 
   info("- select tab1 as foreground tab -");
--- a/toolkit/content/tests/browser/browser_block_autoplay_media_pausedAfterPlay.js
+++ b/toolkit/content/tests/browser/browser_block_autoplay_media_pausedAfterPlay.js
@@ -32,27 +32,27 @@ add_task(async function setup_test_prefe
   await SpecialPowers.pushPrefEnv({"set": [
     ["media.useAudioChannelService.testing", true],
     ["media.block-autoplay-until-in-foreground", true]
   ]});
 });
 
 add_task(async function block_autoplay_media() {
   info("- open new background tab1, and check tab1's media suspend type -");
-  let tab1 = window.gBrowser.addTab("about:blank");
+  let tab1 = BrowserTestUtils.addTab(window.gBrowser, "about:blank");
   tab1.linkedBrowser.loadURI(PAGE_SHOULD_NOT_PLAY);
   await BrowserTestUtils.browserLoaded(tab1.linkedBrowser);
   await ContentTask.spawn(tab1.linkedBrowser, SuspendedType.NONE_SUSPENDED,
                           check_audio_suspended);
 
   info("- the tab1 should not be blocked -");
   await waitForTabBlockEvent(tab1, false);
 
   info("- open new background tab2, and check tab2's media suspend type -");
-  let tab2 = window.gBrowser.addTab("about:blank");
+  let tab2 = BrowserTestUtils.addTab(window.gBrowser, "about:blank");
   tab2.linkedBrowser.loadURI(PAGE_SHOULD_PLAY);
   await BrowserTestUtils.browserLoaded(tab2.linkedBrowser);
   await ContentTask.spawn(tab2.linkedBrowser, SuspendedType.SUSPENDED_BLOCK,
                           check_audio_suspended);
 
   info("- the tab2 should be blocked -");
   await waitForTabBlockEvent(tab2, true);
 
--- a/toolkit/content/tests/browser/browser_block_autoplay_playAfterTabVisible.js
+++ b/toolkit/content/tests/browser/browser_block_autoplay_playAfterTabVisible.js
@@ -53,17 +53,17 @@ add_task(async function setup_test_prefe
 
 /**
  * This test is used for testing the visible tab which was not resumed yet.
  * If the tab doesn't have any media component, it won't be resumed even it
  * has already gone to foreground until we start audio.
  */
 add_task(async function media_should_be_able_to_play_in_visible_tab() {
   info("- open new background tab, and check tab's media pause state -");
-  let tab = window.gBrowser.addTab("about:blank");
+  let tab = BrowserTestUtils.addTab(window.gBrowser, "about:blank");
   tab.linkedBrowser.loadURI(PAGE);
   await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
   await ContentTask.spawn(tab.linkedBrowser, true,
                           check_audio_pause_state);
 
   info("- select tab as foreground tab, and tab's media should still be paused -");
   await BrowserTestUtils.switchTab(window.gBrowser, tab);
   await ContentTask.spawn(tab.linkedBrowser, true,
--- a/toolkit/content/tests/browser/browser_block_multipleMedia.js
+++ b/toolkit/content/tests/browser/browser_block_multipleMedia.js
@@ -79,17 +79,17 @@ add_task(async function setup_test_prefe
   await SpecialPowers.pushPrefEnv({"set": [
     ["media.useAudioChannelService.testing", true],
     ["media.block-autoplay-until-in-foreground", true]
   ]});
 });
 
 add_task(async function block_multiple_media() {
   info("- open new background tab -");
-  let tab = window.gBrowser.addTab("about:blank");
+  let tab = BrowserTestUtils.addTab(window.gBrowser, "about:blank");
   let browser = tab.linkedBrowser;
   browser.loadURI(PAGE);
   await BrowserTestUtils.browserLoaded(browser);
 
   info("- tab should be blocked -");
   await waitForTabBlockEvent(tab, true);
 
   info("- autoplay media should be blocked -");
--- a/toolkit/content/tests/browser/browser_block_notInTreeAudio.js
+++ b/toolkit/content/tests/browser/browser_block_notInTreeAudio.js
@@ -47,17 +47,17 @@ add_task(async function setup_test_prefe
   await SpecialPowers.pushPrefEnv({"set": [
     ["media.useAudioChannelService.testing", true],
     ["media.block-autoplay-until-in-foreground", true]
   ]});
 });
 
 add_task(async function block_not_in_tree_media() {
   info("- open new background tab -");
-  let tab = window.gBrowser.addTab("about:blank");
+  let tab = BrowserTestUtils.addTab(window.gBrowser, "about:blank");
   tab.linkedBrowser.loadURI(PAGE);
   await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
 
   info("- tab should not be blocked -");
   await waitForTabBlockEvent(tab, false);
 
   info("- check audio's suspend state -");
   await ContentTask.spawn(tab.linkedBrowser, SuspendedType.NONE_SUSPENDED,
--- a/toolkit/content/tests/browser/browser_block_playMediaInMuteTab.js
+++ b/toolkit/content/tests/browser/browser_block_playMediaInMuteTab.js
@@ -58,17 +58,17 @@ add_task(async function setup_test_prefe
   await SpecialPowers.pushPrefEnv({"set": [
     ["media.useAudioChannelService.testing", true],
     ["media.block-autoplay-until-in-foreground", true]
   ]});
 });
 
 add_task(async function unblock_icon_should_disapear_after_resume_tab() {
   info("- open new background tab -");
-  let tab = window.gBrowser.addTab("about:blank");
+  let tab = BrowserTestUtils.addTab(window.gBrowser, "about:blank");
   tab.linkedBrowser.loadURI(PAGE);
   await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
 
   info("- audio doesn't be started in beginning -");
   await ContentTask.spawn(tab.linkedBrowser, true,
                           check_audio_pause_state);
 
   info("- audio shouldn't be muted or blocked -");
--- a/toolkit/content/tests/browser/browser_block_plugIn.js
+++ b/toolkit/content/tests/browser/browser_block_plugIn.js
@@ -5,17 +5,17 @@ add_task(async function setup_test_prefe
   await SpecialPowers.pushPrefEnv({"set": [
     ["media.useAudioChannelService.testing", true],
     ["media.block-autoplay-until-in-foreground", true]
   ]});
 });
 
 add_task(async function block_plug_in() {
   info("- open new background tab -");
-  let tab = window.gBrowser.addTab("about:blank");
+  let tab = BrowserTestUtils.addTab(window.gBrowser, "about:blank");
   tab.linkedBrowser.loadURI(PAGE);
   await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
 
   info("- tab should be blocked -");
   await waitForTabBlockEvent(tab, true);
 
   info("- switch tab -");
   await BrowserTestUtils.switchTab(window.gBrowser, tab);
--- a/toolkit/content/tests/browser/browser_block_silentAudioTrack_media.js
+++ b/toolkit/content/tests/browser/browser_block_silentAudioTrack_media.js
@@ -29,17 +29,17 @@ add_task(async function setup_test_prefe
   await SpecialPowers.pushPrefEnv({"set": [
     ["media.useAudioChannelService.testing", true],
     ["media.block-autoplay-until-in-foreground", true]
   ]});
 });
 
 add_task(async function unblock_icon_should_disapear_after_resume_tab() {
   info("- open new background tab -");
-  let tab = window.gBrowser.addTab("about:blank");
+  let tab = BrowserTestUtils.addTab(window.gBrowser, "about:blank");
   tab.linkedBrowser.loadURI(PAGE);
   await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
 
   info("- the suspend state of tab should be suspend-block -");
   await ContentTask.spawn(tab.linkedBrowser, SuspendedType.SUSPENDED_BLOCK,
                           check_audio_suspended);
 
   info("- tab should display unblocking icon -");
@@ -59,17 +59,17 @@ add_task(async function unblock_icon_sho
   await waitForTabPlayingEvent(tab, false);
 
   info("- remove tab -");
   BrowserTestUtils.removeTab(tab);
 });
 
 add_task(async function should_not_show_sound_indicator_after_resume_tab() {
   info("- open new background tab -");
-  let tab = window.gBrowser.addTab("about:blank");
+  let tab = BrowserTestUtils.addTab(window.gBrowser, "about:blank");
   tab.linkedBrowser.loadURI(PAGE);
   await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
 
   info("- the suspend state of tab should be suspend-block -");
   await ContentTask.spawn(tab.linkedBrowser, SuspendedType.SUSPENDED_BLOCK,
                           check_audio_suspended);
 
   info("- tab should display unblocking icon -");
--- a/toolkit/content/tests/browser/browser_block_webAudio.js
+++ b/toolkit/content/tests/browser/browser_block_webAudio.js
@@ -11,17 +11,17 @@ add_task(async function setup_test_prefe
   await SpecialPowers.pushPrefEnv({"set": [
     ["media.useAudioChannelService.testing", true],
     ["media.block-autoplay-until-in-foreground", true]
   ]});
 });
 
 add_task(async function block_web_audio() {
   info("- open new background tab -");
-  let tab = window.gBrowser.addTab("about:blank");
+  let tab = BrowserTestUtils.addTab(window.gBrowser, "about:blank");
   tab.linkedBrowser.loadURI(PAGE);
   await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
 
   info("- tab should be blocked -");
   await waitForTabBlockEvent(tab, true);
 
   info("- switch tab -");
   await BrowserTestUtils.switchTab(window.gBrowser, tab);
--- a/toolkit/content/tests/browser/browser_resume_bkg_video_on_tab_hover.js
+++ b/toolkit/content/tests/browser/browser_resume_bkg_video_on_tab_hover.js
@@ -97,17 +97,17 @@ add_task(async function setup_test_prefe
 
 /**
  * TODO : add the following user-level tests after fixing bug1029451.
  * test1 - only affect the unselected tab
  * test2 - only affect the tab with suspended video
  */
 add_task(async function resume_and_suspend_background_video_decoding() {
   info("- open new background tab -");
-  let tab = window.gBrowser.addTab("about:blank");
+  let tab = BrowserTestUtils.addTab(window.gBrowser, "about:blank");
   let browser = tab.linkedBrowser;
 
   info("- before loading media, we shoudn't send the tab hover msg for tab -");
   await check_should_not_send_unselected_tab_hover_msg(browser);
   browser.loadURI(PAGE);
   await BrowserTestUtils.browserLoaded(browser);
 
   info("- should suspend background video decoding -");