Merge mozilla-central to mozilla-inbound
authorarthur.iakab <aiakab@mozilla.com>
Tue, 14 May 2019 12:47:02 +0300
changeset 532618 3edc4e3973b2fb85ad78ab499115de094a8b8ceb
parent 532617 4ba247673bf95aac3619129bd4f79e999820a220 (current diff)
parent 532561 db99095ae0b53c30927add34be3dc20a7bdcb52e (diff)
child 532619 1327c28c7bd14cf2b4e8de9e6e33b19d7514a7f7
push id11270
push userrgurzau@mozilla.com
push dateWed, 15 May 2019 15:07:19 +0000
treeherdermozilla-beta@571bc76da583 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone68.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to mozilla-inbound
--- a/browser/base/content/test/performance/browser.ini
+++ b/browser/base/content/test/performance/browser.ini
@@ -34,14 +34,14 @@ skip-if = (os == 'win') || (os == 'mac')
 [browser_tabopen.js]
 skip-if = (verify && (os == 'mac'))
 [browser_tabopen_squeeze.js]
 [browser_tabstrip_overflow_underflow.js]
 skip-if = (verify && !debug && (os == 'win')) || (!debug && (os == 'win') && (bits == 32)) # Bug 1502255
 [browser_tabswitch.js]
 [browser_toolbariconcolor_restyles.js]
 [browser_urlbar_keyed_search.js]
-skip-if = (os == 'linux') || (os == 'win' && debug) || (os == 'win' && bits == 32) # Disabled on Linux and Windows debug due to perma failures. Bug 1392320. Disabled on Win32 because of intermittent OOM failures (bug 1448241).
+skip-if = (os == 'win' && bits == 32) # Disabled on Win32 because of intermittent OOM failures (bug 1448241).
 [browser_urlbar_search.js]
 skip-if = (debug || ccov) && (os == 'linux' || os == 'win') || (os == 'win' && bits == 32) # Disabled on Linux and Windows debug and ccov due to intermittent timeouts. Bug 1414126, bug 1426611. Disabled on Win32 because of intermittent OOM failures (bug 1448241)
 [browser_window_resize.js]
 [browser_windowclose.js]
 [browser_windowopen.js]
--- a/browser/components/sessionstore/test/browser.ini
+++ b/browser/components/sessionstore/test/browser.ini
@@ -95,16 +95,17 @@ skip-if = !e10s || !crashreporter
 skip-if = !e10s || !crashreporter
 [browser_dying_cache.js]
 skip-if = (os == 'win') # bug 1331853
 [browser_dynamic_frames.js]
 [browser_formdata.js]
 skip-if = (verify && debug)
 [browser_formdata_cc.js]
 [browser_formdata_format.js]
+skip-if = !debug && (os == 'linux') # Bug 1535645
 [browser_formdata_password.js]
 support-files = file_formdata_password.html
 [browser_formdata_xpath.js]
 [browser_frametree.js]
 [browser_frame_history.js]
 skip-if = (verify && (os == 'win' || os == 'mac'))
 [browser_global_store.js]
 [browser_history_persist.js]
--- a/devtools/client/webconsole/components/message-types/ConsoleApiCall.js
+++ b/devtools/client/webconsole/components/message-types/ConsoleApiCall.js
@@ -88,18 +88,27 @@ function ConsoleApiCall(props) {
     messageBody = dom.span({className: "cm-variable"}, "console.table()");
   } else if (parameters) {
     messageBody = formatReps(messageBodyConfig);
     if (prefix) {
       messageBody.unshift(dom.span({
         className: "console-message-prefix",
       }, `${prefix}: `));
     }
-  } else {
+  } else if (typeof messageText === "string") {
     messageBody = messageText;
+  } else if (messageText) {
+    messageBody = GripMessageBody({
+      dispatch,
+      messageId,
+      grip: messageText,
+      serviceContainer,
+      useQuotes: false,
+      type,
+    });
   }
 
   let attachment = null;
   if (type === "table") {
     attachment = ConsoleTable({
       dispatch,
       id: message.id,
       serviceContainer,
@@ -135,16 +144,17 @@ function ConsoleApiCall(props) {
     stacktrace,
     attachment,
     serviceContainer,
     dispatch,
     indent,
     timeStamp,
     timestampsVisible,
     parameters,
+    message,
     maybeScrollToBottom,
   });
 }
 
 function formatReps(options = {}) {
   const {
     dispatch,
     loadedObjectProperties,
--- a/devtools/client/webconsole/test/components/console-api-call.log-messages.test.js
+++ b/devtools/client/webconsole/test/components/console-api-call.log-messages.test.js
@@ -3,16 +3,18 @@
 "use strict";
 
 // Test utils.
 const expect = require("expect");
 const { render } = require("enzyme");
 
 // React
 const { createFactory } = require("devtools/client/shared/vendor/react");
+const { setupStore } = require("devtools/client/webconsole/test/helpers");
+const Provider = createFactory(require("react-redux").Provider);
 
 // Components under test.
 const ConsoleApiCall = createFactory(require("devtools/client/webconsole/components/message-types/ConsoleApiCall"));
 
 const { prepareMessage } = require("devtools/client/webconsole/utils/messages");
 const serviceContainer = require("devtools/client/webconsole/test/fixtures/serviceContainer");
 
 describe("ConsoleAPICall component:", () => {
@@ -31,16 +33,29 @@ describe("ConsoleAPICall component:", ()
       const message = prepareMessage(logMessageStubPacket, {getNextId: () => "1"});
       const wrapper = render(ConsoleApiCall({ message, serviceContainer }));
 
       expect(wrapper.find(".message-body").text()).toBe("foobar test");
 
       // There should not be the location
       expect(wrapper.find(".message-location").text()).toBe("");
     });
+
+    it("renders longString logMessage grips", () => {
+      const message =
+        prepareMessage(logMessageLongStringStubPacket, {getNextId: () => "1"});
+
+      // We need to wrap the ConsoleApiElement in a Provider in order for the
+      // ObjectInspector to work.
+      const wrapper = render(
+        Provider({ store: setupStore() }, ConsoleApiCall({ message, serviceContainer }))
+      );
+
+      expect(wrapper.find(".message-body").text()).toInclude(initialText);
+    });
   });
 });
 
 // Stub packet
 const cachedLogMessageStubPacket = {
   "from": "server1.conn1.consoleActor2",
   "message": "foobar test",
   "timeStamp": "1493370184067",
@@ -48,8 +63,25 @@ const cachedLogMessageStubPacket = {
 };
 
 const logMessageStubPacket = {
   "from": "server1.conn0.consoleActor2",
   "type": "logMessage",
   "message": "foobar test",
   "timeStamp": 1519052480060,
 };
+
+const multilineFullText = `a\n${Array(20000)
+  .fill("a")
+  .join("")}`;
+const fullTextLength = multilineFullText.length;
+const initialText = multilineFullText.substring(0, 10000);
+const logMessageLongStringStubPacket = {
+  "from": "server1.conn0.consoleActor2",
+  "type": "logMessage",
+  "message": {
+    type: "longString",
+    initial: initialText,
+    length: fullTextLength,
+    actor: "server1.conn1.child1/longString58",
+  },
+  "timeStamp": 1519052480060,
+};
--- a/devtools/client/webconsole/test/fixtures/stubs/evaluationResult.js
+++ b/devtools/client/webconsole/test/fixtures/stubs/evaluationResult.js
@@ -101,61 +101,18 @@ stubPreparedMessages.set(`1 + @`, new Co
   "level": "error",
   "category": null,
   "messageText": "SyntaxError: illegal character",
   "parameters": [
     {
       "type": "undefined"
     }
   ],
-  "repeatId": "{\"frame\":{\"source\":\"debugger eval code\",\"line\":1,\"column\":4},\"groupId\":null,\"indent\":0,\"level\":\"error\",\"messageText\":\"SyntaxError: illegal character\",\"parameters\":[{\"type\":\"undefined\"}],\"source\":\"javascript\",\"type\":\"result\",\"userProvidedStyles\":null,\"stacktrace\":[{\"filename\":\"resource://devtools/server/actors/webconsole/eval-with-debugger.js\",\"sourceId\":null,\"lineNumber\":134,\"columnNumber\":28,\"functionName\":\"getEvalResult\"},{\"filename\":\"resource://devtools/server/actors/webconsole/eval-with-debugger.js\",\"sourceId\":null,\"lineNumber\":105,\"columnNumber\":18,\"functionName\":\"exports.evalWithDebugger\"},{\"filename\":\"resource://devtools/server/actors/webconsole.js\",\"sourceId\":null,\"lineNumber\":1005,\"columnNumber\":22,\"functionName\":\"evaluateJS\"},{\"filename\":\"self-hosted\",\"sourceId\":null,\"lineNumber\":1005,\"columnNumber\":17,\"functionName\":\"evaluateJS\"},{\"filename\":\"resource://devtools/server/main.js\",\"sourceId\":null,\"lineNumber\":1291,\"columnNumber\":58,\"functionName\":\"onPacket\"},{\"filename\":\"resource://devtools/shared/transport/child-transport.js\",\"sourceId\":null,\"lineNumber\":66,\"columnNumber\":16,\"functionName\":\"receiveMessage\"}]}",
-  "stacktrace": [
-    {
-      "filename": "resource://devtools/server/actors/webconsole/eval-with-debugger.js",
-      "sourceId": null,
-      "lineNumber": 134,
-      "columnNumber": 28,
-      "functionName": "getEvalResult"
-    },
-    {
-      "filename": "resource://devtools/server/actors/webconsole/eval-with-debugger.js",
-      "sourceId": null,
-      "lineNumber": 105,
-      "columnNumber": 18,
-      "functionName": "exports.evalWithDebugger"
-    },
-    {
-      "filename": "resource://devtools/server/actors/webconsole.js",
-      "sourceId": null,
-      "lineNumber": 1005,
-      "columnNumber": 22,
-      "functionName": "evaluateJS"
-    },
-    {
-      "filename": "self-hosted",
-      "sourceId": null,
-      "lineNumber": 1005,
-      "columnNumber": 17,
-      "functionName": "evaluateJS"
-    },
-    {
-      "filename": "resource://devtools/server/main.js",
-      "sourceId": null,
-      "lineNumber": 1291,
-      "columnNumber": 58,
-      "functionName": "onPacket"
-    },
-    {
-      "filename": "resource://devtools/shared/transport/child-transport.js",
-      "sourceId": null,
-      "lineNumber": 66,
-      "columnNumber": 16,
-      "functionName": "receiveMessage"
-    }
-  ],
+  "repeatId": "{\"frame\":{\"source\":\"debugger eval code\",\"line\":1,\"column\":4},\"groupId\":null,\"indent\":0,\"level\":\"error\",\"messageText\":\"SyntaxError: illegal character\",\"parameters\":[{\"type\":\"undefined\"}],\"source\":\"javascript\",\"type\":\"result\",\"userProvidedStyles\":null,\"stacktrace\":null}",
+  "stacktrace": null,
   "frame": {
     "source": "debugger eval code",
     "line": 1,
     "column": 4
   },
   "groupId": null,
   "errorMessageName": "JSMSG_ILLEGAL_CHARACTER",
   "exceptionDocURL": "https://developer.mozilla.org/docs/Web/JavaScript/Reference/Errors/Illegal_character?utm_source=mozilla&utm_medium=firefox-console-errors&utm_campaign=default",
@@ -485,60 +442,17 @@ stubPackets.set(`1 + @`, {
       "stack": "",
       "fileName": "debugger eval code",
       "lineNumber": 1,
       "columnNumber": 4
     }
   },
   "exceptionMessage": "SyntaxError: illegal character",
   "exceptionDocURL": "https://developer.mozilla.org/docs/Web/JavaScript/Reference/Errors/Illegal_character?utm_source=mozilla&utm_medium=firefox-console-errors&utm_campaign=default",
-  "exceptionStack": [
-    {
-      "filename": "resource://devtools/server/actors/webconsole/eval-with-debugger.js",
-      "sourceId": null,
-      "lineNumber": 134,
-      "columnNumber": 28,
-      "functionName": "getEvalResult"
-    },
-    {
-      "filename": "resource://devtools/server/actors/webconsole/eval-with-debugger.js",
-      "sourceId": null,
-      "lineNumber": 105,
-      "columnNumber": 18,
-      "functionName": "exports.evalWithDebugger"
-    },
-    {
-      "filename": "resource://devtools/server/actors/webconsole.js",
-      "sourceId": null,
-      "lineNumber": 1005,
-      "columnNumber": 22,
-      "functionName": "evaluateJS"
-    },
-    {
-      "filename": "self-hosted",
-      "sourceId": null,
-      "lineNumber": 1005,
-      "columnNumber": 17,
-      "functionName": "evaluateJS"
-    },
-    {
-      "filename": "resource://devtools/server/main.js",
-      "sourceId": null,
-      "lineNumber": 1291,
-      "columnNumber": 58,
-      "functionName": "onPacket"
-    },
-    {
-      "filename": "resource://devtools/shared/transport/child-transport.js",
-      "sourceId": null,
-      "lineNumber": 66,
-      "columnNumber": 16,
-      "functionName": "receiveMessage"
-    }
-  ],
+  "exceptionStack": null,
   "errorMessageName": "JSMSG_ILLEGAL_CHARACTER",
   "frame": {
     "source": "debugger eval code",
     "line": 1,
     "column": 4
   },
   "helperResult": null,
   "notes": null
--- a/devtools/client/webconsole/test/mochitest/browser_webconsole_eval_error.js
+++ b/devtools/client/webconsole/test/mochitest/browser_webconsole_eval_error.js
@@ -14,9 +14,23 @@ const TEST_URI = "http://example.com/bro
 add_task(async function() {
   const hud = await openNewTabAndConsole(TEST_URI);
 
   hud.jsterm.execute("throwErrorObject()");
   await checkMessageStack(hud, "ThrowErrorObject", [6, 1]);
 
   hud.jsterm.execute("throwValue(40 + 2)");
   await checkMessageStack(hud, "42", [14, 10, 1]);
+
+  hud.jsterm.execute(`
+    a = () => {throw "bloop"};
+    b =  () => a();
+    c =  () => b();
+    d =  () => c();
+    d();
+  `);
+  await checkMessageStack(hud, "Error: bloop", [2, 3, 4, 5, 6]);
+
+  hud.jsterm.execute(`1 + @`);
+  const messageNode = await waitFor(() => findMessage(hud, "illegal character"));
+  is(messageNode.querySelector(".frames"), null,
+    "There's no stacktrace for a SyntaxError evaluation");
 });
--- a/devtools/server/actors/webconsole/utils.js
+++ b/devtools/server/actors/webconsole/utils.js
@@ -202,23 +202,37 @@ var WebConsoleUtils = {
    * @param array stack
    *        An array of frames, with the topmost first, and each of which has a
    *        'filename' property.
    * @return array
    *         An array of stack frames with any devtools server frames removed.
    *         The original array is not modified.
    */
   removeFramesAboveDebuggerEval(stack) {
-    // Remove any frames for server code above the debugger eval.
-    const evalIndex = stack.findIndex(({ filename }) => {
-      return filename == "debugger eval code";
+    const debuggerEvalFilename = "debugger eval code";
+
+    // Remove any frames for server code above the last debugger eval frame.
+    const evalIndex = stack.findIndex(({ filename }, idx, arr) => {
+      const nextFrame = arr[idx + 1];
+      return filename == debuggerEvalFilename
+        && (!nextFrame || nextFrame.filename !== debuggerEvalFilename);
     });
     if (evalIndex != -1) {
       return stack.slice(0, evalIndex + 1);
     }
+
+    // In some cases (e.g. evaluated expression with SyntaxError), we might not have a
+    // "debugger eval code" frame but still have internal ones. If that's the case, we
+    // return null as the end user shouldn't see those frames.
+    if (stack.some(({ filename }) =>
+      filename && filename.startsWith("resource://devtools/"))
+    ) {
+      return null;
+    }
+
     return stack;
   },
 };
 
 exports.WebConsoleUtils = WebConsoleUtils;
 
 /**
  * WebConsole commands manager.
--- a/dom/base/Document.cpp
+++ b/dom/base/Document.cpp
@@ -6883,17 +6883,19 @@ nsViewportInfo Document::GetViewportInfo
     CSSSize viewportSize(viewportWidth, viewportWidth * aspectRatio);
     ScreenIntSize fakeDesktopSize = RoundedToInt(viewportSize * scaleToFit);
     return nsViewportInfo(fakeDesktopSize, scaleToFit,
                           nsViewportInfo::ZoomFlag::AllowZoom);
   }
 
   if (!nsLayoutUtils::ShouldHandleMetaViewport(this)) {
     return nsViewportInfo(aDisplaySize, defaultScale,
-                          nsViewportInfo::ZoomFlag::DisallowZoom);
+                          nsLayoutUtils::AllowZoomingForDocument(this)
+                              ? nsViewportInfo::ZoomFlag::AllowZoom
+                              : nsViewportInfo::ZoomFlag::DisallowZoom);
   }
 
   // In cases where the width of the CSS viewport is less than or equal to the
   // width of the display (i.e. width <= device-width) then we disable
   // double-tap-to-zoom behaviour. See bug 941995 for details.
 
   switch (mViewportType) {
     case DisplayWidthHeight:
--- a/dom/base/nsGlobalWindowInner.cpp
+++ b/dom/base/nsGlobalWindowInner.cpp
@@ -384,18 +384,18 @@ class nsGlobalWindowObserver final : pub
                                   const char16_t* aStorageType,
                                   bool aPrivateBrowsing) override {
     if (mWindow) {
       mWindow->ObserveStorageNotification(aEvent, aStorageType,
                                           aPrivateBrowsing);
     }
   }
 
-  nsIPrincipal* GetPrincipal() const override {
-    return mWindow ? mWindow->GetPrincipal() : nullptr;
+  nsIPrincipal* GetEffectiveStoragePrincipal() const override {
+    return mWindow ? mWindow->GetEffectiveStoragePrincipal() : nullptr;
   }
 
   bool IsPrivateBrowsing() const override {
     return mWindow ? mWindow->IsPrivateBrowsing() : false;
   }
 
   nsIEventTarget* GetEventTarget() const override {
     return mWindow ? mWindow->EventTargetFor(TaskCategory::Other) : nullptr;
@@ -4349,18 +4349,19 @@ Storage* nsGlobalWindowInner::GetSession
     nsCOMPtr<nsIDOMStorageManager> storageManager =
         do_QueryInterface(docShell, &rv);
     if (NS_FAILED(rv)) {
       aError.Throw(rv);
       return nullptr;
     }
 
     RefPtr<Storage> storage;
-    aError = storageManager->CreateStorage(this, principal, documentURI,
-                                           IsPrivateBrowsing(),
+    // No StoragePrincipal for sessions.
+    aError = storageManager->CreateStorage(this, principal, principal,
+                                           documentURI, IsPrivateBrowsing(),
                                            getter_AddRefs(storage));
     if (aError.Failed()) {
       return nullptr;
     }
 
     mSessionStorage = storage;
     MOZ_ASSERT(mSessionStorage);
 
@@ -4401,17 +4402,17 @@ Storage* nsGlobalWindowInner::GetLocalSt
 
   nsContentUtils::StorageAccess access =
       nsContentUtils::StorageAllowedForWindow(this);
 
   // We allow partitioned localStorage only to some hosts.
   if (access == nsContentUtils::StorageAccess::ePartitionedOrDeny) {
     if (!mDoc) {
       access = nsContentUtils::StorageAccess::eDeny;
-    } else {
+    } else if (!StaticPrefs::privacy_storagePrincipal_enabledForTrackers()) {
       nsCOMPtr<nsIURI> uri;
       Unused << mDoc->NodePrincipal()->GetURI(getter_AddRefs(uri));
       static const char* kPrefName =
           "privacy.restrict3rdpartystorage.partitionedHosts";
       if (!uri || !nsContentUtils::IsURIInPrefList(uri, kPrefName)) {
         access = nsContentUtils::StorageAccess::eDeny;
       }
     }
@@ -4427,17 +4428,18 @@ Storage* nsGlobalWindowInner::GetLocalSt
     }
     return nullptr;
   }
 
   // Note that this behavior is observable: if we grant storage permission to a
   // tracker, we pass from the partitioned LocalStorage to the 'normal'
   // LocalStorage. The previous data is lost and the 2 window.localStorage
   // objects, before and after the permission granted, will be different.
-  if (access != nsContentUtils::StorageAccess::ePartitionedOrDeny &&
+  if ((StaticPrefs::privacy_storagePrincipal_enabledForTrackers() ||
+       access != nsContentUtils::StorageAccess::ePartitionedOrDeny) &&
       (!mLocalStorage ||
        mLocalStorage->Type() == Storage::ePartitionedLocalStorage)) {
     RefPtr<Storage> storage;
 
     if (NextGenLocalStorageEnabled()) {
       aError = LSObject::CreateForWindow(this, getter_AddRefs(storage));
     } else {
       nsresult rv;
@@ -4457,18 +4459,24 @@ Storage* nsGlobalWindowInner::GetLocalSt
       }
 
       nsIPrincipal* principal = GetPrincipal();
       if (!principal) {
         aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
         return nullptr;
       }
 
-      aError = storageManager->CreateStorage(this, principal, documentURI,
-                                             IsPrivateBrowsing(),
+      nsIPrincipal* storagePrincipal = GetEffectiveStoragePrincipal();
+      if (!storagePrincipal) {
+        aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
+        return nullptr;
+      }
+
+      aError = storageManager->CreateStorage(this, principal, storagePrincipal,
+                                             documentURI, IsPrivateBrowsing(),
                                              getter_AddRefs(storage));
     }
 
     if (aError.Failed()) {
       return nullptr;
     }
 
     mLocalStorage = storage;
@@ -4478,21 +4486,30 @@ Storage* nsGlobalWindowInner::GetLocalSt
   if (access == nsContentUtils::StorageAccess::ePartitionedOrDeny &&
       !mLocalStorage) {
     nsIPrincipal* principal = GetPrincipal();
     if (!principal) {
       aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
       return nullptr;
     }
 
-    mLocalStorage = new PartitionedLocalStorage(this, principal);
-  }
-
-  MOZ_ASSERT((access == nsContentUtils::StorageAccess::ePartitionedOrDeny) ==
-             (mLocalStorage->Type() == Storage::ePartitionedLocalStorage));
+    nsIPrincipal* storagePrincipal = GetEffectiveStoragePrincipal();
+    if (!storagePrincipal) {
+      aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
+      return nullptr;
+    }
+
+    mLocalStorage =
+        new PartitionedLocalStorage(this, principal, storagePrincipal);
+  }
+
+  MOZ_ASSERT_IF(
+      !StaticPrefs::privacy_storagePrincipal_enabledForTrackers(),
+      (access == nsContentUtils::StorageAccess::ePartitionedOrDeny) ==
+          (mLocalStorage->Type() == Storage::ePartitionedLocalStorage));
 
   return mLocalStorage;
 }
 
 IDBFactory* nsGlobalWindowInner::GetIndexedDB(ErrorResult& aError) {
   if (!mIndexedDB) {
     // This may keep mIndexedDB null without setting an error.
     aError = IDBFactory::CreateForWindow(this, getter_AddRefs(mIndexedDB));
@@ -4869,16 +4886,21 @@ void nsGlobalWindowInner::ObserveStorage
     return;
   }
 
   nsIPrincipal* principal = GetPrincipal();
   if (!principal) {
     return;
   }
 
+  nsIPrincipal* storagePrincipal = GetEffectiveStoragePrincipal();
+  if (!storagePrincipal) {
+    return;
+  }
+
   bool fireMozStorageChanged = false;
   nsAutoString eventType;
   eventType.AssignLiteral("storage");
 
   if (!NS_strcmp(aStorageType, u"sessionStorage")) {
     RefPtr<Storage> changingStorage = aEvent->GetStorageArea();
     MOZ_ASSERT(changingStorage);
 
@@ -4909,18 +4931,18 @@ void nsGlobalWindowInner::ObserveStorage
     if (fireMozStorageChanged) {
       eventType.AssignLiteral("MozSessionStorageChanged");
     }
   }
 
   else {
     MOZ_ASSERT(!NS_strcmp(aStorageType, u"localStorage"));
 
-    MOZ_DIAGNOSTIC_ASSERT(
-        StorageUtils::PrincipalsEqual(aEvent->GetPrincipal(), principal));
+    MOZ_DIAGNOSTIC_ASSERT(StorageUtils::PrincipalsEqual(aEvent->GetPrincipal(),
+                                                        storagePrincipal));
 
     fireMozStorageChanged =
         mLocalStorage && mLocalStorage == aEvent->GetStorageArea();
 
     if (fireMozStorageChanged) {
       eventType.AssignLiteral("MozLocalStorageChanged");
     }
   }
--- a/dom/html/MediaDocument.h
+++ b/dom/html/MediaDocument.h
@@ -48,17 +48,17 @@ class MediaDocument : public nsHTMLDocum
   // Check whether initial setup has been done.
   MOZ_MUST_USE bool InitialSetupHasBeenDone() const {
     return mDidInitialDocumentSetup;
   }
 
   virtual nsresult CreateSyntheticDocument();
 
   friend class MediaDocumentStreamListener;
-  nsresult StartLayout();
+  virtual nsresult StartLayout();
 
   void GetFileName(nsAString& aResult, nsIChannel* aChannel);
 
   nsresult LinkStylesheet(const nsAString& aStylesheet);
   nsresult LinkScript(const nsAString& aScript);
 
   // |aFormatNames[]| needs to have four elements in the following order:
   // a format name with neither dimension nor file, a format name with
--- a/dom/html/PluginDocument.cpp
+++ b/dom/html/PluginDocument.cpp
@@ -42,18 +42,16 @@ class PluginDocument final : public Medi
 
   void SetScriptGlobalObject(
       nsIScriptGlobalObject* aScriptGlobalObject) override;
   bool CanSavePresentation(nsIRequest* aNewRequest) override;
 
   const nsCString& GetType() const { return mMimeType; }
   Element* GetPluginContent() { return mPluginContent; }
 
-  void StartLayout() { MediaDocument::StartLayout(); }
-
   virtual void Destroy() override {
     if (mStreamListener) {
       mStreamListener->DropDocumentRef();
     }
     MediaDocument::Destroy();
   }
 
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(PluginDocument, MediaDocument)
--- a/dom/html/VideoDocument.cpp
+++ b/dom/html/VideoDocument.cpp
@@ -33,22 +33,23 @@ class VideoDocument final : public Media
 
   virtual void Destroy() override {
     if (mStreamListener) {
       mStreamListener->DropDocumentRef();
     }
     MediaDocument::Destroy();
   }
 
+  nsresult StartLayout() override;
+
  protected:
+  nsresult CreateVideoElement();
   // Sets document <title> to reflect the file name and description.
   void UpdateTitle(nsIChannel* aChannel);
 
-  nsresult CreateSyntheticVideoDocument();
-
   RefPtr<MediaDocumentStreamListener> mStreamListener;
 };
 
 nsresult VideoDocument::StartDocumentLoad(const char* aCommand,
                                           nsIChannel* aChannel,
                                           nsILoadGroup* aLoadGroup,
                                           nsISupports* aContainer,
                                           nsIStreamListener** aDocListener,
@@ -57,47 +58,55 @@ nsresult VideoDocument::StartDocumentLoa
       aCommand, aChannel, aLoadGroup, aContainer, aDocListener, aReset, aSink);
   NS_ENSURE_SUCCESS(rv, rv);
 
   mStreamListener = new MediaDocumentStreamListener(this);
   NS_ADDREF(*aDocListener = mStreamListener);
   return rv;
 }
 
+nsresult VideoDocument::StartLayout() {
+  // Create video element, and begin loading the media resource. Note we
+  // delay creating the video element until now (we're called from
+  // MediaDocumentStreamListener::OnStartRequest) as the PresShell is likely
+  // to have been created by now, so the MediaDecoder will be able to tell
+  // what kind of compositor we have, so the video element knows whether
+  // it can create a hardware accelerated video decoder or not.
+  nsresult rv = CreateVideoElement();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = MediaDocument::StartLayout();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
 void VideoDocument::SetScriptGlobalObject(
     nsIScriptGlobalObject* aScriptGlobalObject) {
   // Set the script global object on the superclass before doing
   // anything that might require it....
   MediaDocument::SetScriptGlobalObject(aScriptGlobalObject);
 
   if (aScriptGlobalObject && !InitialSetupHasBeenDone()) {
-    // Create synthetic document
-#ifdef DEBUG
-    nsresult rv =
-#endif
-        CreateSyntheticVideoDocument();
+    DebugOnly<nsresult> rv = CreateSyntheticDocument();
     NS_ASSERTION(NS_SUCCEEDED(rv), "failed to create synthetic video document");
 
     if (!nsContentUtils::IsChildOfSameType(this)) {
       LinkStylesheet(NS_LITERAL_STRING(
           "resource://content-accessible/TopLevelVideoDocument.css"));
       LinkStylesheet(NS_LITERAL_STRING(
           "chrome://global/skin/media/TopLevelVideoDocument.css"));
       LinkScript(NS_LITERAL_STRING(
           "chrome://global/content/TopLevelVideoDocument.js"));
     }
     InitialSetupDone();
   }
 }
 
-nsresult VideoDocument::CreateSyntheticVideoDocument() {
-  // make our generic document
-  nsresult rv = MediaDocument::CreateSyntheticDocument();
-  NS_ENSURE_SUCCESS(rv, rv);
-
+nsresult VideoDocument::CreateVideoElement() {
   Element* body = GetBodyElement();
   if (!body) {
     NS_WARNING("no body on video document!");
     return NS_ERROR_FAILURE;
   }
 
   // make content
   RefPtr<mozilla::dom::NodeInfo> nodeInfo;
--- a/dom/interfaces/storage/nsIDOMStorageManager.idl
+++ b/dom/interfaces/storage/nsIDOMStorageManager.idl
@@ -34,23 +34,26 @@ interface nsIDOMStorageManager : nsISupp
    * Returns instance of DOM storage object for given principal.
    * A new object is always returned and it is ensured there is
    * a storage for the scope created.
    *
    * @param aWindow
    *    The parent window.
    * @param aPrincipal
    *    Principal to bound storage to.
+   * @param aStoragePrincipal
+   *    StoragePrincipal to bound storage to.
    * @param aDocumentURI
    *    URL of the demanding document, used for DOM storage event only.
    * @param aPrivate
    *    Whether the demanding document is running in Private Browsing mode or not.
    */
   Storage createStorage(in mozIDOMWindow aWindow,
                         in nsIPrincipal aPrincipal,
+                        in nsIPrincipal aStoragePrincipal,
                         in AString aDocumentURI,
                         [optional] in bool aPrivate);
   /**
    * DEPRECATED.  The only good reason to use this was if you were writing a
    * test and wanted to hackily determine if a preload happened.  That's now
    * covered by `nsILocalStorageManager.isPreloaded` and you should use that if
    * that's what you want.  If LSNG is in use, this will throw.
    *
@@ -58,21 +61,24 @@ interface nsIDOMStorageManager : nsISupp
    * If there is no storage managed for the scope, then null is returned and
    * no object is created.  Otherwise, an object (new) for the existing storage
    * scope is returned.
    *
    * @param aWindow
    *    The parent window.
    * @param aPrincipal
    *    Principal to bound storage to.
+   * @param aStoragePrincipal
+   *    StoragePrincipal to bound storage to.
    * @param aPrivate
    *    Whether the demanding document is running in Private Browsing mode or not.
    */
   Storage getStorage(in mozIDOMWindow aWindow,
                      in nsIPrincipal aPrincipal,
+                     in nsIPrincipal aStoragePrincipal,
                      [optional] in bool aPrivate);
 
   /**
    * Clones given storage into this storage manager.
    *
    * @param aStorageToCloneFrom
    *    The storage to copy all items from into this manager.  Manager will then
    *    return a new and independent object that contains snapshot of data from
--- a/dom/localstorage/ActorsParent.cpp
+++ b/dom/localstorage/ActorsParent.cpp
@@ -34,16 +34,17 @@
 #include "mozilla/dom/quota/UsageInfo.h"
 #include "mozilla/ipc/BackgroundChild.h"
 #include "mozilla/ipc/BackgroundParent.h"
 #include "mozilla/ipc/PBackgroundChild.h"
 #include "mozilla/ipc/PBackgroundParent.h"
 #include "mozilla/ipc/PBackgroundSharedTypes.h"
 #include "mozilla/Logging.h"
 #include "mozilla/storage/Variant.h"
+#include "mozilla/StoragePrincipalHelper.h"
 #include "nsClassHashtable.h"
 #include "nsDataHashtable.h"
 #include "nsExceptionHandler.h"
 #include "nsInterfaceHashtable.h"
 #include "nsIObjectInputStream.h"
 #include "nsIObjectOutputStream.h"
 #include "nsISimpleEnumerator.h"
 #include "nsNetUtil.h"
@@ -3375,16 +3376,17 @@ bool DeallocPBackgroundLSObserverParent(
   // Transfer ownership back from IPDL.
   RefPtr<Observer> actor = dont_AddRef(static_cast<Observer*>(aActor));
 
   return true;
 }
 
 bool VerifyPrincipalInfo(const Maybe<ContentParentId>& aContentParentId,
                          const PrincipalInfo& aPrincipalInfo,
+                         const PrincipalInfo& aStoragePrincipalInfo,
                          const Maybe<nsID>& aClientId) {
   AssertIsOnBackgroundThread();
 
   if (NS_WARN_IF(!QuotaManager::IsPrincipalInfoValid(aPrincipalInfo))) {
     ASSERT_UNLESS_FUZZING();
     return false;
   }
 
@@ -3392,17 +3394,19 @@ bool VerifyPrincipalInfo(const Maybe<Con
     RefPtr<ClientManagerService> svc = ClientManagerService::GetInstance();
     if (svc &&
         !svc->HasWindow(aContentParentId, aPrincipalInfo, aClientId.ref())) {
       ASSERT_UNLESS_FUZZING();
       return false;
     }
   }
 
-  return true;
+  return StoragePrincipalHelper::
+      VerifyValidStoragePrincipalInfoForPrincipalInfo(aStoragePrincipalInfo,
+                                                      aPrincipalInfo);
 }
 
 bool VerifyOriginKey(const nsACString& aOriginKey,
                      const PrincipalInfo& aPrincipalInfo) {
   AssertIsOnBackgroundThread();
 
   nsCString originAttrSuffix;
   nsCString originKey;
@@ -3427,18 +3431,19 @@ bool VerifyRequestParams(const Maybe<Con
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aParams.type() != LSRequestParams::T__None);
 
   switch (aParams.type()) {
     case LSRequestParams::TLSRequestPreloadDatastoreParams: {
       const LSRequestCommonParams& params =
           aParams.get_LSRequestPreloadDatastoreParams().commonParams();
 
-      if (NS_WARN_IF(!VerifyPrincipalInfo(aContentParentId,
-                                          params.principalInfo(), Nothing()))) {
+      if (NS_WARN_IF(
+              !VerifyPrincipalInfo(aContentParentId, params.principalInfo(),
+                                   params.storagePrincipalInfo(), Nothing()))) {
         ASSERT_UNLESS_FUZZING();
         return false;
       }
 
       if (NS_WARN_IF(
               !VerifyOriginKey(params.originKey(), params.principalInfo()))) {
         ASSERT_UNLESS_FUZZING();
         return false;
@@ -3447,19 +3452,19 @@ bool VerifyRequestParams(const Maybe<Con
     }
 
     case LSRequestParams::TLSRequestPrepareDatastoreParams: {
       const LSRequestPrepareDatastoreParams& params =
           aParams.get_LSRequestPrepareDatastoreParams();
 
       const LSRequestCommonParams& commonParams = params.commonParams();
 
-      if (NS_WARN_IF(!VerifyPrincipalInfo(aContentParentId,
-                                          commonParams.principalInfo(),
-                                          params.clientId()))) {
+      if (NS_WARN_IF(!VerifyPrincipalInfo(
+              aContentParentId, commonParams.principalInfo(),
+              commonParams.storagePrincipalInfo(), params.clientId()))) {
         ASSERT_UNLESS_FUZZING();
         return false;
       }
 
       if (NS_WARN_IF(!VerifyOriginKey(commonParams.originKey(),
                                       commonParams.principalInfo()))) {
         ASSERT_UNLESS_FUZZING();
         return false;
@@ -3467,17 +3472,18 @@ bool VerifyRequestParams(const Maybe<Con
       break;
     }
 
     case LSRequestParams::TLSRequestPrepareObserverParams: {
       const LSRequestPrepareObserverParams& params =
           aParams.get_LSRequestPrepareObserverParams();
 
       if (NS_WARN_IF(!VerifyPrincipalInfo(
-              aContentParentId, params.principalInfo(), params.clientId()))) {
+              aContentParentId, params.principalInfo(),
+              params.storagePrincipalInfo(), params.clientId()))) {
         ASSERT_UNLESS_FUZZING();
         return false;
       }
       break;
     }
 
     default:
       MOZ_CRASH("Should never get here!");
@@ -3588,18 +3594,19 @@ bool VerifyRequestParams(const Maybe<Con
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aParams.type() != LSSimpleRequestParams::T__None);
 
   switch (aParams.type()) {
     case LSSimpleRequestParams::TLSSimpleRequestPreloadedParams: {
       const LSSimpleRequestPreloadedParams& params =
           aParams.get_LSSimpleRequestPreloadedParams();
 
-      if (NS_WARN_IF(!VerifyPrincipalInfo(aContentParentId,
-                                          params.principalInfo(), Nothing()))) {
+      if (NS_WARN_IF(
+              !VerifyPrincipalInfo(aContentParentId, params.principalInfo(),
+                                   params.storagePrincipalInfo(), Nothing()))) {
         ASSERT_UNLESS_FUZZING();
         return false;
       }
       break;
     }
 
     default:
       MOZ_CRASH("Should never get here!");
@@ -6599,25 +6606,26 @@ nsresult PrepareDatastoreOp::Open() {
   MOZ_ASSERT(mState == State::Opening);
   MOZ_ASSERT(mNestedState == NestedState::BeforeNesting);
 
   if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
       !MayProceedOnNonOwningThread()) {
     return NS_ERROR_FAILURE;
   }
 
-  const PrincipalInfo& principalInfo = mParams.principalInfo();
-
-  if (principalInfo.type() == PrincipalInfo::TSystemPrincipalInfo) {
+  const PrincipalInfo& storagePrincipalInfo = mParams.storagePrincipalInfo();
+
+  if (storagePrincipalInfo.type() == PrincipalInfo::TSystemPrincipalInfo) {
     QuotaManager::GetInfoForChrome(&mSuffix, &mGroup, &mOrigin);
   } else {
-    MOZ_ASSERT(principalInfo.type() == PrincipalInfo::TContentPrincipalInfo);
+    MOZ_ASSERT(storagePrincipalInfo.type() ==
+               PrincipalInfo::TContentPrincipalInfo);
 
     QuotaManager::GetInfoFromValidatedPrincipalInfo(
-        principalInfo, &mSuffix, &mGroup, &mMainThreadOrigin);
+        storagePrincipalInfo, &mSuffix, &mGroup, &mMainThreadOrigin);
   }
 
   mState = State::Nesting;
   mNestedState = NestedState::CheckExistingOperations;
 
   MOZ_ALWAYS_SUCCEEDS(OwningEventTarget()->Dispatch(this, NS_DISPATCH_NORMAL));
 
   return NS_OK;
@@ -6629,27 +6637,29 @@ nsresult PrepareDatastoreOp::CheckExisti
   MOZ_ASSERT(mNestedState == NestedState::CheckExistingOperations);
   MOZ_ASSERT(gPrepareDatastoreOps);
 
   if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
       !MayProceed()) {
     return NS_ERROR_FAILURE;
   }
 
-  const PrincipalInfo& principalInfo = mParams.principalInfo();
+  const PrincipalInfo& storagePrincipalInfo = mParams.storagePrincipalInfo();
 
   nsCString originAttrSuffix;
   uint32_t privateBrowsingId;
 
-  if (principalInfo.type() == PrincipalInfo::TSystemPrincipalInfo) {
+  if (storagePrincipalInfo.type() == PrincipalInfo::TSystemPrincipalInfo) {
     privateBrowsingId = 0;
   } else {
-    MOZ_ASSERT(principalInfo.type() == PrincipalInfo::TContentPrincipalInfo);
-
-    const ContentPrincipalInfo& info = principalInfo.get_ContentPrincipalInfo();
+    MOZ_ASSERT(storagePrincipalInfo.type() ==
+               PrincipalInfo::TContentPrincipalInfo);
+
+    const ContentPrincipalInfo& info =
+        storagePrincipalInfo.get_ContentPrincipalInfo();
     const OriginAttributes& attrs = info.attrs();
     attrs.CreateSuffix(originAttrSuffix);
 
     privateBrowsingId = attrs.mPrivateBrowsingId;
   }
 
   mArchivedOriginScope = ArchivedOriginScope::CreateFromOrigin(
       originAttrSuffix, mParams.originKey());
@@ -7905,25 +7915,26 @@ nsresult PrepareObserverOp::Open() {
   AssertIsOnOwningThread();
   MOZ_ASSERT(mState == State::Opening);
 
   if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
       !MayProceedOnNonOwningThread()) {
     return NS_ERROR_FAILURE;
   }
 
-  const PrincipalInfo& principalInfo = mParams.principalInfo();
-
-  if (principalInfo.type() == PrincipalInfo::TSystemPrincipalInfo) {
+  const PrincipalInfo& storagePrincipalInfo = mParams.storagePrincipalInfo();
+
+  if (storagePrincipalInfo.type() == PrincipalInfo::TSystemPrincipalInfo) {
     QuotaManager::GetInfoForChrome(nullptr, nullptr, &mOrigin);
   } else {
-    MOZ_ASSERT(principalInfo.type() == PrincipalInfo::TContentPrincipalInfo);
-
-    QuotaManager::GetInfoFromValidatedPrincipalInfo(principalInfo, nullptr,
-                                                    nullptr, &mOrigin);
+    MOZ_ASSERT(storagePrincipalInfo.type() ==
+               PrincipalInfo::TContentPrincipalInfo);
+
+    QuotaManager::GetInfoFromValidatedPrincipalInfo(storagePrincipalInfo,
+                                                    nullptr, nullptr, &mOrigin);
   }
 
   mState = State::SendingReadyMessage;
   MOZ_ALWAYS_SUCCEEDS(OwningEventTarget()->Dispatch(this, NS_DISPATCH_NORMAL));
 
   return NS_OK;
 }
 
@@ -8046,25 +8057,26 @@ nsresult PreloadedOp::Open() {
   AssertIsOnOwningThread();
   MOZ_ASSERT(mState == State::Opening);
 
   if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
       !MayProceedOnNonOwningThread()) {
     return NS_ERROR_FAILURE;
   }
 
-  const PrincipalInfo& principalInfo = mParams.principalInfo();
-
-  if (principalInfo.type() == PrincipalInfo::TSystemPrincipalInfo) {
+  const PrincipalInfo& storagePrincipalInfo = mParams.storagePrincipalInfo();
+
+  if (storagePrincipalInfo.type() == PrincipalInfo::TSystemPrincipalInfo) {
     QuotaManager::GetInfoForChrome(nullptr, nullptr, &mOrigin);
   } else {
-    MOZ_ASSERT(principalInfo.type() == PrincipalInfo::TContentPrincipalInfo);
-
-    QuotaManager::GetInfoFromValidatedPrincipalInfo(principalInfo, nullptr,
-                                                    nullptr, &mOrigin);
+    MOZ_ASSERT(storagePrincipalInfo.type() ==
+               PrincipalInfo::TContentPrincipalInfo);
+
+    QuotaManager::GetInfoFromValidatedPrincipalInfo(storagePrincipalInfo,
+                                                    nullptr, nullptr, &mOrigin);
   }
 
   mState = State::SendingResults;
   MOZ_ALWAYS_SUCCEEDS(OwningEventTarget()->Dispatch(this, NS_DISPATCH_NORMAL));
 
   return NS_OK;
 }
 
@@ -8965,21 +8977,21 @@ nsresult QuotaClient::CreateArchivedOrig
                                               &attrs))) {
       return NS_ERROR_FAILURE;
     }
 
     ContentPrincipalInfo contentPrincipalInfo;
     contentPrincipalInfo.attrs() = attrs;
     contentPrincipalInfo.spec() = spec;
 
-    PrincipalInfo principalInfo(contentPrincipalInfo);
+    PrincipalInfo storagePrincipalInfo(contentPrincipalInfo);
 
     nsCString originAttrSuffix;
     nsCString originKey;
-    rv = GenerateOriginKey2(principalInfo, originAttrSuffix, originKey);
+    rv = GenerateOriginKey2(storagePrincipalInfo, originAttrSuffix, originKey);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     archivedOriginScope =
         ArchivedOriginScope::CreateFromOrigin(originAttrSuffix, originKey);
   } else if (aOriginScope.IsPrefix()) {
     nsCString spec;
@@ -8988,21 +9000,21 @@ nsresult QuotaClient::CreateArchivedOrig
                                               spec, &attrs))) {
       return NS_ERROR_FAILURE;
     }
 
     ContentPrincipalInfo contentPrincipalInfo;
     contentPrincipalInfo.attrs() = attrs;
     contentPrincipalInfo.spec() = spec;
 
-    PrincipalInfo principalInfo(contentPrincipalInfo);
+    PrincipalInfo storagePrincipalInfo(contentPrincipalInfo);
 
     nsCString originAttrSuffix;
     nsCString originKey;
-    rv = GenerateOriginKey2(principalInfo, originAttrSuffix, originKey);
+    rv = GenerateOriginKey2(storagePrincipalInfo, originAttrSuffix, originKey);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     archivedOriginScope = ArchivedOriginScope::CreateFromPrefix(originKey);
   } else if (aOriginScope.IsPattern()) {
     archivedOriginScope =
         ArchivedOriginScope::CreateFromPattern(aOriginScope.GetPattern());
--- a/dom/localstorage/LSObject.cpp
+++ b/dom/localstorage/LSObject.cpp
@@ -190,18 +190,19 @@ class RequestHelper final : public Runna
   NS_DECL_NSIRUNNABLE
 
   // LSRequestChildCallback
   void OnResponse(const LSRequestResponse& aResponse) override;
 };
 
 }  // namespace
 
-LSObject::LSObject(nsPIDOMWindowInner* aWindow, nsIPrincipal* aPrincipal)
-    : Storage(aWindow, aPrincipal),
+LSObject::LSObject(nsPIDOMWindowInner* aWindow, nsIPrincipal* aPrincipal,
+                   nsIPrincipal* aStoragePrincipal)
+    : Storage(aWindow, aPrincipal, aStoragePrincipal),
       mPrivateBrowsingId(0),
       mInExplicitSnapshot(false) {
   AssertIsOnOwningThread();
   MOZ_ASSERT(NextGenLocalStorageEnabled());
 }
 
 LSObject::~LSObject() {
   AssertIsOnOwningThread();
@@ -239,64 +240,80 @@ void LSObject::Initialize() {
 
 // static
 nsresult LSObject::CreateForWindow(nsPIDOMWindowInner* aWindow,
                                    Storage** aStorage) {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aWindow);
   MOZ_ASSERT(aStorage);
   MOZ_ASSERT(NextGenLocalStorageEnabled());
-  MOZ_ASSERT(nsContentUtils::StorageAllowedForWindow(aWindow) >
+  MOZ_ASSERT(nsContentUtils::StorageAllowedForWindow(aWindow) !=
              nsContentUtils::StorageAccess::eDeny);
 
   nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(aWindow);
   MOZ_ASSERT(sop);
 
   nsCOMPtr<nsIPrincipal> principal = sop->GetPrincipal();
   if (NS_WARN_IF(!principal)) {
     return NS_ERROR_FAILURE;
   }
 
+  nsCOMPtr<nsIPrincipal> storagePrincipal = sop->GetEffectiveStoragePrincipal();
+  if (NS_WARN_IF(!storagePrincipal)) {
+    return NS_ERROR_FAILURE;
+  }
+
   if (nsContentUtils::IsSystemPrincipal(principal)) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   // localStorage is not available on some pages on purpose, for example
   // about:home. Match the old implementation by using GenerateOriginKey
   // for the check.
   nsCString originAttrSuffix;
   nsCString originKey;
-  nsresult rv = GenerateOriginKey(principal, originAttrSuffix, originKey);
+  nsresult rv =
+      GenerateOriginKey(storagePrincipal, originAttrSuffix, originKey);
   if (NS_FAILED(rv)) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   nsAutoPtr<PrincipalInfo> principalInfo(new PrincipalInfo());
   rv = PrincipalToPrincipalInfo(principal, principalInfo);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   MOZ_ASSERT(principalInfo->type() == PrincipalInfo::TContentPrincipalInfo);
 
-  if (NS_WARN_IF(!QuotaManager::IsPrincipalInfoValid(*principalInfo))) {
+  nsAutoPtr<PrincipalInfo> storagePrincipalInfo(new PrincipalInfo());
+  rv = PrincipalToPrincipalInfo(storagePrincipal, storagePrincipalInfo);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  MOZ_ASSERT(storagePrincipalInfo->type() ==
+             PrincipalInfo::TContentPrincipalInfo);
+
+  if (NS_WARN_IF(!QuotaManager::IsPrincipalInfoValid(*storagePrincipalInfo))) {
     return NS_ERROR_FAILURE;
   }
 
   nsCString suffix;
   nsCString origin;
-  rv = QuotaManager::GetInfoFromPrincipal(principal, &suffix, nullptr, &origin);
+  rv = QuotaManager::GetInfoFromPrincipal(storagePrincipal, &suffix, nullptr,
+                                          &origin);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   MOZ_ASSERT(originAttrSuffix == suffix);
 
   uint32_t privateBrowsingId;
-  rv = principal->GetPrivateBrowsingId(&privateBrowsingId);
+  rv = storagePrincipal->GetPrivateBrowsingId(&privateBrowsingId);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   Maybe<ClientInfo> clientInfo = aWindow->GetClientInfo();
   if (clientInfo.isNothing()) {
     return NS_ERROR_FAILURE;
   }
@@ -306,61 +323,75 @@ nsresult LSObject::CreateForWindow(nsPID
   nsString documentURI;
   if (nsCOMPtr<Document> doc = aWindow->GetExtantDoc()) {
     rv = doc->GetDocumentURI(documentURI);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
 
-  RefPtr<LSObject> object = new LSObject(aWindow, principal);
+  RefPtr<LSObject> object = new LSObject(aWindow, principal, storagePrincipal);
   object->mPrincipalInfo = std::move(principalInfo);
+  object->mStoragePrincipalInfo = std::move(storagePrincipalInfo);
   object->mPrivateBrowsingId = privateBrowsingId;
   object->mClientId = clientId;
   object->mOrigin = origin;
   object->mOriginKey = originKey;
   object->mDocumentURI = documentURI;
 
   object.forget(aStorage);
   return NS_OK;
 }
 
 // static
 nsresult LSObject::CreateForPrincipal(nsPIDOMWindowInner* aWindow,
                                       nsIPrincipal* aPrincipal,
+                                      nsIPrincipal* aStoragePrincipal,
                                       const nsAString& aDocumentURI,
                                       bool aPrivate, LSObject** aObject) {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aPrincipal);
+  MOZ_ASSERT(aStoragePrincipal);
   MOZ_ASSERT(aObject);
 
   nsCString originAttrSuffix;
   nsCString originKey;
-  nsresult rv = GenerateOriginKey(aPrincipal, originAttrSuffix, originKey);
+  nsresult rv =
+      GenerateOriginKey(aStoragePrincipal, originAttrSuffix, originKey);
   if (NS_FAILED(rv)) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   nsAutoPtr<PrincipalInfo> principalInfo(new PrincipalInfo());
   rv = PrincipalToPrincipalInfo(aPrincipal, principalInfo);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   MOZ_ASSERT(principalInfo->type() == PrincipalInfo::TContentPrincipalInfo ||
              principalInfo->type() == PrincipalInfo::TSystemPrincipalInfo);
 
-  if (NS_WARN_IF(!QuotaManager::IsPrincipalInfoValid(*principalInfo))) {
+  nsAutoPtr<PrincipalInfo> storagePrincipalInfo(new PrincipalInfo());
+  rv = PrincipalToPrincipalInfo(aStoragePrincipal, storagePrincipalInfo);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  MOZ_ASSERT(
+      storagePrincipalInfo->type() == PrincipalInfo::TContentPrincipalInfo ||
+      storagePrincipalInfo->type() == PrincipalInfo::TSystemPrincipalInfo);
+
+  if (NS_WARN_IF(!QuotaManager::IsPrincipalInfoValid(*storagePrincipalInfo))) {
     return NS_ERROR_FAILURE;
   }
 
   nsCString suffix;
   nsCString origin;
 
-  if (principalInfo->type() == PrincipalInfo::TSystemPrincipalInfo) {
+  if (storagePrincipalInfo->type() == PrincipalInfo::TSystemPrincipalInfo) {
     QuotaManager::GetInfoForChrome(&suffix, nullptr, &origin);
   } else {
     rv = QuotaManager::GetInfoFromPrincipal(aPrincipal, &suffix, nullptr,
                                             &origin);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
@@ -372,18 +403,20 @@ nsresult LSObject::CreateForPrincipal(ns
     Maybe<ClientInfo> clientInfo = aWindow->GetClientInfo();
     if (clientInfo.isNothing()) {
       return NS_ERROR_FAILURE;
     }
 
     clientId = Some(clientInfo.ref().Id());
   }
 
-  RefPtr<LSObject> object = new LSObject(aWindow, aPrincipal);
+  RefPtr<LSObject> object =
+      new LSObject(aWindow, aPrincipal, aStoragePrincipal);
   object->mPrincipalInfo = std::move(principalInfo);
+  object->mStoragePrincipalInfo = std::move(storagePrincipalInfo);
   object->mPrivateBrowsingId = aPrivate ? 1 : 0;
   object->mClientId = clientId;
   object->mOrigin = origin;
   object->mOriginKey = originKey;
   object->mDocumentURI = aDocumentURI;
 
   object.forget(aObject);
   return NS_OK;
@@ -806,16 +839,17 @@ nsresult LSObject::EnsureDatabase() {
   PBackgroundChild* backgroundActor =
       BackgroundChild::GetOrCreateForCurrentThread();
   if (NS_WARN_IF(!backgroundActor)) {
     return NS_ERROR_FAILURE;
   }
 
   LSRequestCommonParams commonParams;
   commonParams.principalInfo() = *mPrincipalInfo;
+  commonParams.storagePrincipalInfo() = *mStoragePrincipalInfo;
   commonParams.originKey() = mOriginKey;
 
   LSRequestPrepareDatastoreParams params;
   params.commonParams() = commonParams;
   params.clientId() = mClientId;
 
   LSRequestResponse response;
 
@@ -839,17 +873,17 @@ nsresult LSObject::EnsureDatabase() {
   // Note that we now can't error out, otherwise parent will keep an extra
   // strong reference to the datastore.
 
   RefPtr<LSDatabase> database = new LSDatabase(mOrigin);
 
   LSDatabaseChild* actor = new LSDatabaseChild(database);
 
   MOZ_ALWAYS_TRUE(backgroundActor->SendPBackgroundLSDatabaseConstructor(
-      actor, *mPrincipalInfo, mPrivateBrowsingId, datastoreId));
+      actor, *mStoragePrincipalInfo, mPrivateBrowsingId, datastoreId));
 
   database->SetActor(actor);
 
   mDatabase = std::move(database);
 
   return NS_OK;
 }
 
@@ -874,16 +908,17 @@ nsresult LSObject::EnsureObserver() {
   mObserver = LSObserver::Get(mOrigin);
 
   if (mObserver) {
     return NS_OK;
   }
 
   LSRequestPrepareObserverParams params;
   params.principalInfo() = *mPrincipalInfo;
+  params.storagePrincipalInfo() = *mStoragePrincipalInfo;
   params.clientId() = mClientId;
 
   LSRequestResponse response;
 
   nsresult rv = DoRequestSynchronously(params, response);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
@@ -927,18 +962,18 @@ void LSObject::DropObserver() {
     mObserver = nullptr;
   }
 }
 
 void LSObject::OnChange(const nsAString& aKey, const nsAString& aOldValue,
                         const nsAString& aNewValue) {
   AssertIsOnOwningThread();
 
-  NotifyChange(/* aStorage */ this, Principal(), aKey, aOldValue, aNewValue,
-               /* aStorageType */ kLocalStorageType, mDocumentURI,
+  NotifyChange(/* aStorage */ this, StoragePrincipal(), aKey, aOldValue,
+               aNewValue, /* aStorageType */ kLocalStorageType, mDocumentURI,
                /* aIsPrivate */ !!mPrivateBrowsingId,
                /* aImmediateDispatch */ false);
 }
 
 nsresult LSObject::EndExplicitSnapshotInternal() {
   AssertIsOnOwningThread();
 
   // Can be only called if the mInExplicitSnapshot flag is true.
--- a/dom/localstorage/LSObject.h
+++ b/dom/localstorage/LSObject.h
@@ -56,16 +56,17 @@ class LSRequestResponse;
  * parent Datastore at the moment the Snapshot was created.
  */
 class LSObject final : public Storage {
   typedef mozilla::ipc::PrincipalInfo PrincipalInfo;
 
   friend nsGlobalWindowInner;
 
   nsAutoPtr<PrincipalInfo> mPrincipalInfo;
+  nsAutoPtr<PrincipalInfo> mStoragePrincipalInfo;
 
   RefPtr<LSDatabase> mDatabase;
   RefPtr<LSObserver> mObserver;
 
   uint32_t mPrivateBrowsingId;
   Maybe<nsID> mClientId;
   nsCString mOrigin;
   nsCString mOriginKey;
@@ -87,16 +88,17 @@ class LSObject final : public Storage {
    * system principal where CreateForWindow does not.  This is also why aPrivate
    * exists separate from the principal; because the system principal can never
    * be mutated to have a private browsing id even though it can be used in a
    * window/document marked as private browsing.  That's a legacy issue that is
    * being dealt with, but it's why it exists here.
    */
   static nsresult CreateForPrincipal(nsPIDOMWindowInner* aWindow,
                                      nsIPrincipal* aPrincipal,
+                                     nsIPrincipal* aStoragePrincipal,
                                      const nsAString& aDocumentURI,
                                      bool aPrivate, LSObject** aObject);
 
   /**
    * Used for requests from the parent process to the parent process; in that
    * case we want ActorsParent to know our event-target and this is better than
    * trying to tunnel the pointer through IPC.
    */
@@ -172,17 +174,18 @@ class LSObject final : public Storage {
                            ErrorResult& aError) override;
 
   //////////////////////////////////////////////////////////////////////////////
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(LSObject, Storage)
 
  private:
-  LSObject(nsPIDOMWindowInner* aWindow, nsIPrincipal* aPrincipal);
+  LSObject(nsPIDOMWindowInner* aWindow, nsIPrincipal* aPrincipal,
+           nsIPrincipal* aStoragePrincipal);
 
   ~LSObject();
 
   nsresult DoRequestSynchronously(const LSRequestParams& aParams,
                                   LSRequestResponse& aResponse);
 
   nsresult EnsureDatabase();
 
--- a/dom/localstorage/LocalStorageManager2.cpp
+++ b/dom/localstorage/LocalStorageManager2.cpp
@@ -186,41 +186,46 @@ LocalStorageManager2::PrecacheStorage(ns
   // process, triggered by the official preloading spot,
   // ContentParent::AboutToLoadHttpFtpDocumentForChild.
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 LocalStorageManager2::CreateStorage(mozIDOMWindow* aWindow,
                                     nsIPrincipal* aPrincipal,
+                                    nsIPrincipal* aStoragePrincipal,
                                     const nsAString& aDocumentURI,
                                     bool aPrivate, Storage** _retval) {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aPrincipal);
+  MOZ_ASSERT(aStoragePrincipal);
   MOZ_ASSERT(_retval);
 
   nsCOMPtr<nsPIDOMWindowInner> inner = nsPIDOMWindowInner::From(aWindow);
 
   RefPtr<LSObject> object;
-  nsresult rv = LSObject::CreateForPrincipal(inner, aPrincipal, aDocumentURI,
+  nsresult rv = LSObject::CreateForPrincipal(inner, aPrincipal,
+                                             aStoragePrincipal, aDocumentURI,
                                              aPrivate, getter_AddRefs(object));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   object.forget(_retval);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 LocalStorageManager2::GetStorage(mozIDOMWindow* aWindow,
-                                 nsIPrincipal* aPrincipal, bool aPrivate,
+                                 nsIPrincipal* aPrincipal,
+                                 nsIPrincipal* aStoragePrincipal, bool aPrivate,
                                  Storage** _retval) {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aPrincipal);
+  MOZ_ASSERT(aStoragePrincipal);
   MOZ_ASSERT(_retval);
 
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 LocalStorageManager2::CloneStorage(Storage* aStorageToCloneFrom) {
   MOZ_ASSERT(NS_IsMainThread());
@@ -278,16 +283,17 @@ LocalStorageManager2::Preload(nsIPrincip
     rv = CreatePromise(aContext, getter_AddRefs(promise));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
 
   LSRequestCommonParams commonParams;
   commonParams.principalInfo() = *principalInfo;
+  commonParams.storagePrincipalInfo() = *principalInfo;
   commonParams.originKey() = originKey;
 
   LSRequestPreloadDatastoreParams params(commonParams);
 
   RefPtr<AsyncRequestHelper> helper =
       new AsyncRequestHelper(this, promise, params);
 
   // This will start and finish the async request on the DOM File thread.
@@ -324,16 +330,18 @@ LocalStorageManager2::IsPreloaded(nsIPri
 
   LSSimpleRequestPreloadedParams params;
 
   rv = CheckedPrincipalToPrincipalInfo(aPrincipal, params.principalInfo());
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
+  params.storagePrincipalInfo() = params.principalInfo();
+
   rv = StartSimpleRequest(promise, params);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   promise.forget(_retval);
   return NS_OK;
 }
--- a/dom/localstorage/PBackgroundLSSharedTypes.ipdlh
+++ b/dom/localstorage/PBackgroundLSSharedTypes.ipdlh
@@ -11,16 +11,17 @@ using mozilla::dom::LSValue
   from "mozilla/dom/LSValue.h";
 
 namespace mozilla {
 namespace dom {
 
 struct LSRequestCommonParams
 {
   PrincipalInfo principalInfo;
+  PrincipalInfo storagePrincipalInfo;
   nsCString originKey;
 };
 
 struct LSRequestPreloadDatastoreParams
 {
   LSRequestCommonParams commonParams;
 };
 
@@ -28,29 +29,31 @@ struct LSRequestPrepareDatastoreParams
 {
   LSRequestCommonParams commonParams;
   nsID? clientId;
 };
 
 struct LSRequestPrepareObserverParams
 {
   PrincipalInfo principalInfo;
+  PrincipalInfo storagePrincipalInfo;
   nsID? clientId;
 };
 
 union LSRequestParams
 {
   LSRequestPreloadDatastoreParams;
   LSRequestPrepareDatastoreParams;
   LSRequestPrepareObserverParams;
 };
 
 struct LSSimpleRequestPreloadedParams
 {
   PrincipalInfo principalInfo;
+  PrincipalInfo storagePrincipalInfo;
 };
 
 union LSSimpleRequestParams
 {
   LSSimpleRequestPreloadedParams;
 };
 
 /**
--- a/dom/localstorage/test/unit/head.js
+++ b/dom/localstorage/test/unit/head.js
@@ -227,17 +227,17 @@ function getCurrentPrincipal() {
   return Cc["@mozilla.org/systemprincipal;1"].createInstance(Ci.nsIPrincipal);
 }
 
 function getLocalStorage(principal) {
   if (!principal) {
     principal = getCurrentPrincipal();
   }
 
-  return Services.domStorageManager.createStorage(null, principal, "");
+  return Services.domStorageManager.createStorage(null, principal, principal, "");
 }
 
 function requestFinished(request) {
   return new Promise(function(resolve, reject) {
     request.callback = function(requestInner) {
       if (requestInner.resultCode == Cr.NS_OK) {
         resolve(requestInner.result);
       } else {
--- a/dom/quota/test/unit/test_localStorageArchive4upgrade.js
+++ b/dom/quota/test/unit/test_localStorageArchive4upgrade.js
@@ -24,17 +24,17 @@ async function testSteps() {
 
   const data = [
     { key: "foo0", value: "bar" },
     { key: "foo1", value: "A" },
     { key: "foo2", value: "A".repeat(100) },
   ];
 
   function getLocalStorage(principal) {
-    return Services.domStorageManager.createStorage(null, principal, "");
+    return Services.domStorageManager.createStorage(null, principal, principal, "");
   }
 
   info("Clearing");
 
   let request = clear();
   await requestFinished(request);
 
   info("Installing package");
--- a/dom/storage/LocalStorage.cpp
+++ b/dom/storage/LocalStorage.cpp
@@ -49,18 +49,19 @@ NS_INTERFACE_MAP_END_INHERITING(Storage)
 
 NS_IMPL_ADDREF_INHERITED(LocalStorage, Storage)
 NS_IMPL_RELEASE_INHERITED(LocalStorage, Storage)
 
 LocalStorage::LocalStorage(nsPIDOMWindowInner* aWindow,
                            LocalStorageManager* aManager,
                            LocalStorageCache* aCache,
                            const nsAString& aDocumentURI,
-                           nsIPrincipal* aPrincipal, bool aIsPrivate)
-    : Storage(aWindow, aPrincipal),
+                           nsIPrincipal* aPrincipal,
+                           nsIPrincipal* aStoragePrincipal, bool aIsPrivate)
+    : Storage(aWindow, aPrincipal, aStoragePrincipal),
       mManager(aManager),
       mCache(aCache),
       mDocumentURI(aDocumentURI),
       mIsPrivate(aIsPrivate) {
   mCache->Preload();
 }
 
 LocalStorage::~LocalStorage() {}
@@ -158,19 +159,19 @@ void LocalStorage::Clear(nsIPrincipal& a
 
   if (!aRv.ErrorCodeIs(NS_SUCCESS_DOM_NO_OPERATION)) {
     OnChange(VoidString(), VoidString(), VoidString());
   }
 }
 
 void LocalStorage::OnChange(const nsAString& aKey, const nsAString& aOldValue,
                             const nsAString& aNewValue) {
-  NotifyChange(/* aStorage */ this, Principal(), aKey, aOldValue, aNewValue,
-               /* aStorageType */ u"localStorage", mDocumentURI, mIsPrivate,
-               /* aImmediateDispatch */ false);
+  NotifyChange(/* aStorage */ this, StoragePrincipal(), aKey, aOldValue,
+               aNewValue, /* aStorageType */ u"localStorage", mDocumentURI,
+               mIsPrivate, /* aImmediateDispatch */ false);
 }
 
 void LocalStorage::ApplyEvent(StorageEvent* aStorageEvent) {
   MOZ_ASSERT(aStorageEvent);
 
   nsAutoString key;
   nsAutoString old;
   nsAutoString value;
--- a/dom/storage/LocalStorage.h
+++ b/dom/storage/LocalStorage.h
@@ -27,17 +27,18 @@ class LocalStorage final : public Storag
   LocalStorageManager* GetManager() const { return mManager; }
 
   LocalStorageCache const* GetCache() const { return mCache; }
 
   const nsString& DocumentURI() const { return mDocumentURI; }
 
   LocalStorage(nsPIDOMWindowInner* aWindow, LocalStorageManager* aManager,
                LocalStorageCache* aCache, const nsAString& aDocumentURI,
-               nsIPrincipal* aPrincipal, bool aIsPrivate);
+               nsIPrincipal* aPrincipal, nsIPrincipal* aStoragePrincipal,
+               bool aIsPrivate);
 
   bool IsForkOf(const Storage* aOther) const override;
 
   // WebIDL
 
   int64_t GetOriginQuotaUsage() const override;
 
   uint32_t GetLength(nsIPrincipal& aSubjectPrincipal,
--- a/dom/storage/LocalStorageManager.cpp
+++ b/dom/storage/LocalStorageManager.cpp
@@ -193,17 +193,18 @@ void LocalStorageManager::DropCache(Loca
   }
 
   CacheOriginHashtable* table = mCaches.LookupOrAdd(aCache->OriginSuffix());
   table->RemoveEntry(aCache->OriginNoSuffix());
 }
 
 nsresult LocalStorageManager::GetStorageInternal(
     CreateMode aCreateMode, mozIDOMWindow* aWindow, nsIPrincipal* aPrincipal,
-    const nsAString& aDocumentURI, bool aPrivate, Storage** aRetval) {
+    nsIPrincipal* aStoragePrincipal, const nsAString& aDocumentURI,
+    bool aPrivate, Storage** aRetval) {
   nsAutoCString originAttrSuffix;
   nsAutoCString originKey;
 
   nsresult rv = GenerateOriginKey(aPrincipal, originAttrSuffix, originKey);
   if (NS_FAILED(rv)) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
@@ -265,46 +266,51 @@ nsresult LocalStorageManager::GetStorage
 
     cache->SetActor(actor);
 #endif
   }
 
   if (aRetval) {
     nsCOMPtr<nsPIDOMWindowInner> inner = nsPIDOMWindowInner::From(aWindow);
 
-    RefPtr<Storage> storage = new LocalStorage(inner, this, cache, aDocumentURI,
-                                               aPrincipal, aPrivate);
+    RefPtr<Storage> storage =
+        new LocalStorage(inner, this, cache, aDocumentURI, aPrincipal,
+                         aStoragePrincipal, aPrivate);
     storage.forget(aRetval);
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 LocalStorageManager::PrecacheStorage(nsIPrincipal* aPrincipal,
                                      Storage** aRetval) {
   return GetStorageInternal(CreateMode::CreateIfShouldPreload, nullptr,
-                            aPrincipal, EmptyString(), false, aRetval);
+                            aPrincipal, aPrincipal, EmptyString(), false,
+                            aRetval);
 }
 
 NS_IMETHODIMP
 LocalStorageManager::CreateStorage(mozIDOMWindow* aWindow,
                                    nsIPrincipal* aPrincipal,
+                                   nsIPrincipal* aStoragePrincipal,
                                    const nsAString& aDocumentURI, bool aPrivate,
                                    Storage** aRetval) {
   return GetStorageInternal(CreateMode::CreateAlways, aWindow, aPrincipal,
-                            aDocumentURI, aPrivate, aRetval);
+                            aStoragePrincipal, aDocumentURI, aPrivate, aRetval);
 }
 
 NS_IMETHODIMP
 LocalStorageManager::GetStorage(mozIDOMWindow* aWindow,
-                                nsIPrincipal* aPrincipal, bool aPrivate,
+                                nsIPrincipal* aPrincipal,
+                                nsIPrincipal* aStoragePrincipal, bool aPrivate,
                                 Storage** aRetval) {
   return GetStorageInternal(CreateMode::UseIfExistsNeverCreate, aWindow,
-                            aPrincipal, EmptyString(), aPrivate, aRetval);
+                            aPrincipal, aStoragePrincipal, EmptyString(),
+                            aPrivate, aRetval);
 }
 
 NS_IMETHODIMP
 LocalStorageManager::CloneStorage(Storage* aStorage) {
   // Cloning is supported only for sessionStorage
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
--- a/dom/storage/LocalStorageManager.h
+++ b/dom/storage/LocalStorageManager.h
@@ -96,16 +96,17 @@ class LocalStorageManager final : public
     CreateAlways,
     // PrecacheStorage: Create only if the database says we ShouldPreloadOrigin.
     CreateIfShouldPreload
   };
 
   // Helper for creation of DOM storage objects
   nsresult GetStorageInternal(CreateMode aCreate, mozIDOMWindow* aWindow,
                               nsIPrincipal* aPrincipal,
+                              nsIPrincipal* aStoragePrincipal,
                               const nsAString& aDocumentURI, bool aPrivate,
                               Storage** aRetval);
 
   // Suffix->origin->cache map
   typedef nsTHashtable<LocalStorageCacheHashKey> CacheOriginHashtable;
   nsClassHashtable<nsCStringHashKey, CacheOriginHashtable> mCaches;
 
   void ClearCaches(uint32_t aUnloadFlags,
--- a/dom/storage/PartitionedLocalStorage.cpp
+++ b/dom/storage/PartitionedLocalStorage.cpp
@@ -15,19 +15,21 @@ namespace dom {
 NS_IMPL_CYCLE_COLLECTION_INHERITED(PartitionedLocalStorage, Storage);
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PartitionedLocalStorage)
 NS_INTERFACE_MAP_END_INHERITING(Storage)
 
 NS_IMPL_ADDREF_INHERITED(PartitionedLocalStorage, Storage)
 NS_IMPL_RELEASE_INHERITED(PartitionedLocalStorage, Storage)
 
-PartitionedLocalStorage::PartitionedLocalStorage(nsPIDOMWindowInner* aWindow,
-                                                 nsIPrincipal* aPrincipal)
-    : Storage(aWindow, aPrincipal), mCache(new SessionStorageCache()) {}
+PartitionedLocalStorage::PartitionedLocalStorage(
+    nsPIDOMWindowInner* aWindow, nsIPrincipal* aPrincipal,
+    nsIPrincipal* aStoragePrincipal)
+    : Storage(aWindow, aPrincipal, aStoragePrincipal),
+      mCache(new SessionStorageCache()) {}
 
 PartitionedLocalStorage::~PartitionedLocalStorage() {}
 
 int64_t PartitionedLocalStorage::GetOriginQuotaUsage() const {
   return mCache->GetOriginQuotaUsage(SessionStorageCache::eSessionSetType);
 }
 
 uint32_t PartitionedLocalStorage::GetLength(nsIPrincipal& aSubjectPrincipal,
--- a/dom/storage/PartitionedLocalStorage.h
+++ b/dom/storage/PartitionedLocalStorage.h
@@ -19,18 +19,18 @@ class SessionStorageCache;
 // PartitionedLocalStorage is a in-memory-only storage exposed to trackers. It
 // doesn't share data with other contexts.
 
 class PartitionedLocalStorage final : public Storage {
  public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(PartitionedLocalStorage, Storage)
 
-  PartitionedLocalStorage(nsPIDOMWindowInner* aWindow,
-                          nsIPrincipal* aPrincipal);
+  PartitionedLocalStorage(nsPIDOMWindowInner* aWindow, nsIPrincipal* aPrincipal,
+                          nsIPrincipal* aStoragePrincipal);
 
   StorageType Type() const override { return ePartitionedLocalStorage; }
 
   int64_t GetOriginQuotaUsage() const override;
 
   bool IsForkOf(const Storage* aStorage) const override;
 
   // WebIDL
--- a/dom/storage/SessionStorage.cpp
+++ b/dom/storage/SessionStorage.cpp
@@ -30,27 +30,28 @@ NS_INTERFACE_MAP_END_INHERITING(Storage)
 NS_IMPL_ADDREF_INHERITED(SessionStorage, Storage)
 NS_IMPL_RELEASE_INHERITED(SessionStorage, Storage)
 
 SessionStorage::SessionStorage(nsPIDOMWindowInner* aWindow,
                                nsIPrincipal* aPrincipal,
                                SessionStorageCache* aCache,
                                SessionStorageManager* aManager,
                                const nsAString& aDocumentURI, bool aIsPrivate)
-    : Storage(aWindow, aPrincipal),
+    : Storage(aWindow, aPrincipal, aPrincipal),
       mCache(aCache),
       mManager(aManager),
       mDocumentURI(aDocumentURI),
       mIsPrivate(aIsPrivate) {
   MOZ_ASSERT(aCache);
 }
 
 SessionStorage::~SessionStorage() {}
 
 already_AddRefed<SessionStorage> SessionStorage::Clone() const {
+  MOZ_ASSERT(Principal() == StoragePrincipal());
   RefPtr<SessionStorage> storage =
       new SessionStorage(GetParentObject(), Principal(), mCache, mManager,
                          mDocumentURI, mIsPrivate);
   return storage.forget();
 }
 
 int64_t SessionStorage::GetOriginQuotaUsage() const {
   return mCache->GetOriginQuotaUsage(DATASET);
--- a/dom/storage/SessionStorageManager.cpp
+++ b/dom/storage/SessionStorageManager.cpp
@@ -66,16 +66,17 @@ SessionStorageManager::PrecacheStorage(n
                                        Storage** aRetval) {
   // Nothing to preload.
   return NS_OK;
 }
 
 NS_IMETHODIMP
 SessionStorageManager::CreateStorage(mozIDOMWindow* aWindow,
                                      nsIPrincipal* aPrincipal,
+                                     nsIPrincipal* aStoragePrincipal,
                                      const nsAString& aDocumentURI,
                                      bool aPrivate, Storage** aRetval) {
   nsAutoCString originKey;
   nsAutoCString originAttributes;
   nsresult rv = GenerateOriginKey(aPrincipal, originAttributes, originKey);
   if (NS_FAILED(rv)) {
     return NS_ERROR_NOT_AVAILABLE;
   }
@@ -89,27 +90,29 @@ SessionStorageManager::CreateStorage(moz
   RefPtr<SessionStorageCache> cache;
   if (!table->Get(originKey, getter_AddRefs(cache))) {
     cache = new SessionStorageCache();
     table->Put(originKey, cache);
   }
 
   nsCOMPtr<nsPIDOMWindowInner> inner = nsPIDOMWindowInner::From(aWindow);
 
+  // No StoragePrincipal for sessionStorage.
   RefPtr<SessionStorage> storage = new SessionStorage(
       inner, aPrincipal, cache, this, aDocumentURI, aPrivate);
 
   storage.forget(aRetval);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 SessionStorageManager::GetStorage(mozIDOMWindow* aWindow,
-                                  nsIPrincipal* aPrincipal, bool aPrivate,
-                                  Storage** aRetval) {
+                                  nsIPrincipal* aPrincipal,
+                                  nsIPrincipal* aStoragePrincipal,
+                                  bool aPrivate, Storage** aRetval) {
   *aRetval = nullptr;
 
   nsAutoCString originKey;
   nsAutoCString originAttributes;
   nsresult rv = GenerateOriginKey(aPrincipal, originAttributes, originKey);
   if (NS_FAILED(rv)) {
     return rv;
   }
--- a/dom/storage/Storage.cpp
+++ b/dom/storage/Storage.cpp
@@ -11,28 +11,33 @@
 #include "nsIPrincipal.h"
 #include "nsPIDOMWindow.h"
 
 namespace mozilla {
 namespace dom {
 
 static const char kStorageEnabled[] = "dom.storage.enabled";
 
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Storage, mWindow, mPrincipal)
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Storage, mWindow, mPrincipal,
+                                      mStoragePrincipal)
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(Storage)
 NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(Storage, LastRelease())
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Storage)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
-Storage::Storage(nsPIDOMWindowInner* aWindow, nsIPrincipal* aPrincipal)
-    : mWindow(aWindow), mPrincipal(aPrincipal), mIsSessionOnly(false) {
+Storage::Storage(nsPIDOMWindowInner* aWindow, nsIPrincipal* aPrincipal,
+                 nsIPrincipal* aStoragePrincipal)
+    : mWindow(aWindow),
+      mPrincipal(aPrincipal),
+      mStoragePrincipal(aStoragePrincipal),
+      mIsSessionOnly(false) {
   MOZ_ASSERT(aPrincipal);
 
   if (nsContentUtils::IsSystemPrincipal(mPrincipal)) {
     mIsSessionOnly = false;
   } else if (mWindow) {
     uint32_t rejectedReason = 0;
     nsContentUtils::StorageAccess access =
         nsContentUtils::StorageAllowedForWindow(mWindow, &rejectedReason);
@@ -40,17 +45,17 @@ Storage::Storage(nsPIDOMWindowInner* aWi
     MOZ_ASSERT(access != nsContentUtils::StorageAccess::eDeny ||
                rejectedReason ==
                    nsIWebProgressListener::STATE_COOKIES_BLOCKED_FOREIGN);
 
     mIsSessionOnly = access <= nsContentUtils::StorageAccess::eSessionScoped;
   }
 }
 
-Storage::~Storage() {}
+Storage::~Storage() = default;
 
 /* static */
 bool Storage::StoragePrefIsEnabled() {
   return mozilla::Preferences::GetBool(kStorageEnabled);
 }
 
 bool Storage::CanUseStorage(nsIPrincipal& aSubjectPrincipal) {
   if (!StoragePrefIsEnabled()) {
--- a/dom/storage/Storage.h
+++ b/dom/storage/Storage.h
@@ -21,17 +21,18 @@ class nsPIDOMWindowInner;
 namespace mozilla {
 namespace dom {
 
 class Storage : public nsISupports, public nsWrapperCache {
  public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Storage)
 
-  Storage(nsPIDOMWindowInner* aWindow, nsIPrincipal* aPrincipal);
+  Storage(nsPIDOMWindowInner* aWindow, nsIPrincipal* aPrincipal,
+          nsIPrincipal* aStoragePrincipal);
 
   static bool StoragePrefIsEnabled();
 
   enum StorageType {
     eSessionStorage,
     eLocalStorage,
     ePartitionedLocalStorage,
   };
@@ -39,16 +40,18 @@ class Storage : public nsISupports, publ
   virtual StorageType Type() const = 0;
 
   virtual bool IsForkOf(const Storage* aStorage) const = 0;
 
   virtual int64_t GetOriginQuotaUsage() const = 0;
 
   nsIPrincipal* Principal() const { return mPrincipal; }
 
+  nsIPrincipal* StoragePrincipal() const { return mStoragePrincipal; }
+
   // WebIDL
   JSObject* WrapObject(JSContext* aCx,
                        JS::Handle<JSObject*> aGivenProto) override;
 
   nsPIDOMWindowInner* GetParentObject() const { return mWindow; }
 
   virtual uint32_t GetLength(nsIPrincipal& aSubjectPrincipal,
                              ErrorResult& aRv) = 0;
@@ -136,16 +139,17 @@ class Storage : public nsISupports, publ
   // The method checks whether the caller can use a storage.
   bool CanUseStorage(nsIPrincipal& aSubjectPrincipal);
 
   virtual void LastRelease() {}
 
  private:
   nsCOMPtr<nsPIDOMWindowInner> mWindow;
   nsCOMPtr<nsIPrincipal> mPrincipal;
+  nsCOMPtr<nsIPrincipal> mStoragePrincipal;
 
   // Whether storage is set to persist data only per session, may change
   // dynamically and is set by CanUseStorage function that is called
   // before any operation on the storage.
   bool mIsSessionOnly : 1;
 };
 
 }  // namespace dom
--- a/dom/storage/StorageNotifierService.cpp
+++ b/dom/storage/StorageNotifierService.cpp
@@ -71,17 +71,17 @@ void StorageNotifierService::Broadcast(S
     // principal).
     if (aPrivateBrowsing != observer->IsPrivateBrowsing()) {
       continue;
     }
 
     // No reasons to continue if the principal of the event doesn't match with
     // the window's one.
     if (!StorageUtils::PrincipalsEqual(aEvent->GetPrincipal(),
-                                       observer->GetPrincipal())) {
+                                       observer->GetEffectiveStoragePrincipal())) {
       continue;
     }
 
     RefPtr<Runnable> r = NS_NewRunnableFunction(
         "StorageNotifierService::Broadcast",
         [observer, event, aStorageType, aPrivateBrowsing]() {
           observer->ObserveStorageNotification(event, aStorageType,
                                                aPrivateBrowsing);
--- a/dom/storage/StorageNotifierService.h
+++ b/dom/storage/StorageNotifierService.h
@@ -26,17 +26,17 @@ class StorageNotificationObserver {
   NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
 
   virtual void ObserveStorageNotification(StorageEvent* aEvent,
                                           const char16_t* aStorageType,
                                           bool aPrivateBrowsing) = 0;
 
   virtual bool IsPrivateBrowsing() const = 0;
 
-  virtual nsIPrincipal* GetPrincipal() const = 0;
+  virtual nsIPrincipal* GetEffectiveStoragePrincipal() const = 0;
 
   virtual nsIEventTarget* GetEventTarget() const = 0;
 };
 
 /**
  * A specialized version of the observer service that uses the custom
  * StorageNotificationObserver so that principal checks can happen in this class
  * rather than in the nsIObserver::observe method where they used to happen.
--- a/dom/tests/browser/browser_localStorage_e10s.js
+++ b/dom/tests/browser/browser_localStorage_e10s.js
@@ -83,17 +83,17 @@ function clearOriginStorageEnsuringNoPre
       };
     });
     return promise;
   }
 
   // We want to use createStorage to force the cache to be created so we can
   // issue the clear.  It's possible for getStorage to return false but for the
   // origin preload hash to still have our origin in it.
-  let storage = Services.domStorageManager.createStorage(null, principal, "");
+  let storage = Services.domStorageManager.createStorage(null, principal, principal, "");
   storage.clear();
 
   // We also need to trigger a flush os that mOriginsHavingData gets updated.
   // The inherent flush race is fine here because
   return triggerAndWaitForLocalStorageFlush();
 }
 
 async function verifyTabPreload(knownTab, expectStorageExists) {
@@ -102,17 +102,17 @@ async function verifyTabPreload(knownTab
     HELPER_PAGE_ORIGIN,
     function(origin) {
       let principal =
         Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(
           origin);
       if (Services.lsm.nextGenLocalStorageEnabled) {
         return Services.lsm.isPreloaded(principal);
       }
-      return !!Services.domStorageManager.getStorage(null, principal);
+      return !!Services.domStorageManager.getStorage(null, principal, principal);
     });
   is(storageExists, expectStorageExists, "Storage existence === preload");
 }
 
 /**
  * Instruct the given tab to execute the given series of mutations.  For
  * simplicity, the mutations representation matches the expected events rep.
  */
--- a/dom/tests/mochitest/localstorage/test_localStorageFromChrome.xhtml
+++ b/dom/tests/mochitest/localstorage/test_localStorageFromChrome.xhtml
@@ -16,17 +16,17 @@ async function startTest()
     .getService(Components.interfaces.nsIIOService);
   var ssm = Components.classes["@mozilla.org/scriptsecuritymanager;1"]
     .getService(Components.interfaces.nsIScriptSecurityManager);
   var dsm = Components.classes["@mozilla.org/dom/localStorage-manager;1"]
     .getService(Components.interfaces.nsIDOMStorageManager);
 
   var uri = ios.newURI(url);
   var principal = ssm.createCodebasePrincipal(uri, {});
-  var storage = dsm.createStorage(window, principal, "");
+  var storage = dsm.createStorage(window, principal, principal, "");
 
   storage.setItem("chromekey", "chromevalue");
 
   var aframe = document.getElementById("aframe");
   aframe.onload = function()
   {
     is(storage.getItem("chromekey"), "chromevalue");
     is(aframe.contentDocument.getElementById("data").innerHTML, "chromevalue");
--- a/gfx/layers/apz/test/mochitest/apz_test_utils.js
+++ b/gfx/layers/apz/test/mochitest/apz_test_utils.js
@@ -796,18 +796,16 @@ function getPrefs(ident) {
         // want those pans to turn into fling animations, so we increase the
         // fling min velocity requirement absurdly high.
         ["apz.fling_min_velocity_threshold", "10000"],
         // The helper_div_pan's div gets a displayport on scroll, but if the
         // test takes too long the displayport can expire before the new scroll
         // position is synced back to the main thread. So we disable displayport
         // expiry for these tests.
         ["apz.displayport_expiry_ms", 0],
-        // All of test cases should define viewport meta tag.
-        ["dom.meta-viewport.enabled", true],
       ];
     case "TOUCH_ACTION":
       return [
         ...getPrefs("TOUCH_EVENTS:PAN"),
         ["layout.css.touch_action.enabled", true],
         ["apz.test.fails_with_native_injection", getPlatform() == "windows"],
       ];
     default:
--- a/gfx/layers/apz/test/mochitest/test_group_minimum_scale_size.html
+++ b/gfx/layers/apz/test/mochitest/test_group_minimum_scale_size.html
@@ -22,18 +22,18 @@ const prefs = [
   // out of the test. So we disable displayport expiry for these tests.
   ["apz.displayport_expiry_ms", 0],
   // Prevent the dynamic toolbar from interfering with main-thread scroll
   // offset values.
   ["browser.chrome.dynamictoolbar", false],
   // Explicitly enable pinch-zooming, so this test can run on desktop
   // even though zooming isn't enabled by default on desktop yet.
   ["apz.allow_zooming", true],
-  // Pinch-zooming currently requires meta viewport support (this requirement
-  // will eventually be removed).
+  // Similarly, explicitly enable support for meta viewport tags (which the
+  // test cases use) so they're processed even on desktop.
   ["dom.meta-viewport.enabled", true],
   // We use the Visual Viewport API to tell the visual viewport offset.
   ["dom.visualviewport.enabled", true],
 ];
 
 const subtests = [
   { file: "helper_minimum_scale_1_0.html", prefs },
   { file: "helper_no_scalable_with_initial_scale.html", prefs },
--- a/gfx/layers/apz/test/mochitest/test_group_touchevents-2.html
+++ b/gfx/layers/apz/test/mochitest/test_group_touchevents-2.html
@@ -8,17 +8,16 @@
   <script type="application/javascript" src="apz_test_utils.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <script type="application/javascript">
 
 var isWindows = getPlatform() == "windows";
 
 const shared_prefs = [
   ["apz.test.fails_with_native_injection", isWindows],
-  ["dom.meta-viewport.enabled", true],
   ["dom.w3c_touch_events.legacy_apis.enabled", true],
 ];
 
 var subtests = [
   // Taps on media elements to make sure the touchend event is delivered
   // properly. We increase the long-tap timeout to ensure it doesn't get trip
   // during the tap.
   // Also this test (on Windows) cannot satisfy the OS requirement of providing
--- a/gfx/layers/apz/test/mochitest/test_group_touchevents-3.html
+++ b/gfx/layers/apz/test/mochitest/test_group_touchevents-3.html
@@ -5,20 +5,21 @@
   <title>Various touch tests that spawn in new windows (3)</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
   <script type="application/javascript" src="apz_test_utils.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <script type="application/javascript">
 
 var touch_action_prefs = getPrefs("TOUCH_ACTION");
+var with_meta_viewport = [...touch_action_prefs, ["dom.meta-viewport.enabled", true]];
 
 var subtests = [
   // Simple test to exercise touch-action CSS property
-  {"file": "helper_touch_action.html", "prefs": touch_action_prefs},
+  {"file": "helper_touch_action.html", "prefs": with_meta_viewport},
   // More complex touch-action tests, with overlapping regions and such
   {"file": "helper_touch_action_complex.html", "prefs": touch_action_prefs},
   // Tests that touch-action CSS properties are handled in APZ without waiting
   // on the main-thread, when possible
   {"file": "helper_touch_action_regions.html", "prefs": touch_action_prefs},
   // Tests that touch-action inside zero-opacity items are respected
   {"file": "helper_touch_action_zero_opacity_bug1500864.html", "prefs": touch_action_prefs},
 
--- a/gfx/layers/apz/test/mochitest/test_group_touchevents-4.html
+++ b/gfx/layers/apz/test/mochitest/test_group_touchevents-4.html
@@ -4,24 +4,24 @@
   <meta charset="utf-8">
   <title>Various touch tests that spawn in new windows (4)</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
   <script type="application/javascript" src="apz_test_utils.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <script type="application/javascript">
 
-var basic_pan_prefs = getPrefs("TOUCH_EVENTS:PAN");
 var touch_action_prefs = getPrefs("TOUCH_ACTION");
 
 var subtests = [
   // clicking on element with :active::after CSS property
-  {"file": "helper_bug1473108.html", "prefs": [["dom.meta-viewport.enabled", true]]},
+  {"file": "helper_bug1473108.html"},
   // Resetting isFirstPaint shouldn't clobber the visual viewport
-  {"file": "helper_bug1509575.html", "prefs": basic_pan_prefs},
+  {"file": "helper_bug1509575.html", "prefs": [["dom.meta-viewport.enabled", true],
+                                               ...getPrefs("TOUCH_EVENTS:PAN")]},
   // Exercise one of the main-thread touch-action determination codepaths.
   {"file": "helper_bug1506497_touch_action_fixed_on_fixed.html", "prefs": touch_action_prefs},
   // Add new subtests here. If this starts timing out because it's taking too
   // long, create a test_group_touchevents-5.html file. Refer to 1423011#c57
   // for more details.
 ];
 
 if (isApzEnabled()) {
--- a/gfx/layers/apz/test/mochitest/test_group_touchevents.html
+++ b/gfx/layers/apz/test/mochitest/test_group_touchevents.html
@@ -13,29 +13,27 @@ var basic_pan_prefs = getPrefs("TOUCH_EV
 
 var subtests = [
   // Simple tests to exercise basic panning behaviour
   {"file": "helper_basic_pan.html", "prefs": basic_pan_prefs},
   {"file": "helper_div_pan.html", "prefs": basic_pan_prefs},
   {"file": "helper_iframe_pan.html", "prefs": basic_pan_prefs},
 
   // Simple test to exercise touch-tapping behaviour
-  {"file": "helper_tap.html", "prefs": [["dom.meta-viewport.enabled", true]]},
+  {"file": "helper_tap.html"},
   // Tapping, but with a full-zoom applied
-  {"file": "helper_tap_fullzoom.html", "prefs": [["dom.meta-viewport.enabled", true]]},
+  {"file": "helper_tap_fullzoom.html"},
 
   // For the following two tests, disable displayport suppression to make sure it
   // doesn't interfere with the test by scheduling paints non-deterministically.
   {"file": "helper_scrollto_tap.html?true",
-   "prefs": [["apz.paint_skipping.enabled", true],
-             ["dom.meta-viewport.enabled", true]],
+   "prefs": [["apz.paint_skipping.enabled", true]],
    "dp_suppression": false},
   {"file": "helper_scrollto_tap.html?false",
-   "prefs": [["apz.paint_skipping.enabled", false],
-             ["dom.meta-viewport.enabled", true]],
+   "prefs": [["apz.paint_skipping.enabled", false]],
    "dp_suppression": false},
 
   // Add new subtests to test_group_touch_events-4.html, not this file.
 ];
 
 if (isApzEnabled()) {
   ok(window.TouchEvent, "Check if TouchEvent is supported (it should be, the test harness forces it on everywhere)");
   if (getPlatform() == "android") {
--- a/gfx/layers/apz/test/mochitest/test_group_zoom.html
+++ b/gfx/layers/apz/test/mochitest/test_group_zoom.html
@@ -24,18 +24,18 @@ var prefs = [
   // out of the test. So we disable displayport expiry for these tests.
   ["apz.displayport_expiry_ms", 0],
   // Prevent the dynamic toolbar from interfering with main-thread scroll
   // offset values.
   ["browser.chrome.dynamictoolbar", false],
   // Explicitly enable pinch-zooming, so this test can run on desktop
   // even though zooming isn't enabled by default on desktop yet.
   ["apz.allow_zooming", true],
-  // Pinch-zooming currently requires meta viewport support (this requirement
-  // will eventually be removed).
+  // Similarly, explicitly enable support for meta viewport tags (which the
+  // test cases use) so they're processed even on desktop.
   ["dom.meta-viewport.enabled", true],
   // Increase the content response timeout because some tests do preventDefault
   // and we want to make sure APZ actually waits for them.
   ["apz.content_response_timeout", 60000],
 ];
 
 // Increase the tap timeouts so the double-tap is still detected in case of
 // random delays during testing.
--- a/js/src/vm/Debugger-inl.h
+++ b/js/src/vm/Debugger-inl.h
@@ -51,67 +51,67 @@
 /* static */ inline bool js::Debugger::checkNoExecute(JSContext* cx,
                                                       HandleScript script) {
   if (!cx->realm()->isDebuggee() || !cx->noExecuteDebuggerTop) {
     return true;
   }
   return slowPathCheckNoExecute(cx, script);
 }
 
-/* static */ js::ResumeMode js::Debugger::onEnterFrame(JSContext* cx,
+/* static */ inline js::ResumeMode js::Debugger::onEnterFrame(JSContext* cx,
                                                        AbstractFramePtr frame) {
   MOZ_ASSERT_IF(frame.hasScript() && frame.script()->isDebuggee(),
                 frame.isDebuggee());
   if (!frame.isDebuggee()) {
     return ResumeMode::Continue;
   }
   return slowPathOnEnterFrame(cx, frame);
 }
 
-/* static */ js::ResumeMode js::Debugger::onResumeFrame(
+/* static */ inline js::ResumeMode js::Debugger::onResumeFrame(
     JSContext* cx, AbstractFramePtr frame) {
   MOZ_ASSERT_IF(frame.hasScript() && frame.script()->isDebuggee(),
                 frame.isDebuggee());
   if (!frame.isDebuggee()) {
     return ResumeMode::Continue;
   }
   return slowPathOnResumeFrame(cx, frame);
 }
 
-/* static */ js::ResumeMode js::Debugger::onDebuggerStatement(
+/* static */ inline js::ResumeMode js::Debugger::onDebuggerStatement(
     JSContext* cx, AbstractFramePtr frame) {
   if (!cx->realm()->isDebuggee()) {
     return ResumeMode::Continue;
   }
   return slowPathOnDebuggerStatement(cx, frame);
 }
 
-/* static */ js::ResumeMode js::Debugger::onExceptionUnwind(
+/* static */ inline js::ResumeMode js::Debugger::onExceptionUnwind(
     JSContext* cx, AbstractFramePtr frame) {
   if (!cx->realm()->isDebuggee()) {
     return ResumeMode::Continue;
   }
   return slowPathOnExceptionUnwind(cx, frame);
 }
 
-/* static */ void js::Debugger::onNewWasmInstance(
+/* static */ inline void js::Debugger::onNewWasmInstance(
     JSContext* cx, Handle<WasmInstanceObject*> wasmInstance) {
   if (cx->realm()->isDebuggee()) {
     slowPathOnNewWasmInstance(cx, wasmInstance);
   }
 }
 
-/* static */ void js::Debugger::onNewPromise(JSContext* cx,
+/* static */ inline void js::Debugger::onNewPromise(JSContext* cx,
                                              Handle<PromiseObject*> promise) {
   if (MOZ_UNLIKELY(cx->realm()->isDebuggee())) {
     slowPathPromiseHook(cx, Debugger::OnNewPromise, promise);
   }
 }
 
-/* static */ void js::Debugger::onPromiseSettled(
+/* static */ inline void js::Debugger::onPromiseSettled(
     JSContext* cx, Handle<PromiseObject*> promise) {
   if (MOZ_UNLIKELY(promise->realm()->isDebuggee())) {
     slowPathPromiseHook(cx, Debugger::OnPromiseSettled, promise);
   }
 }
 
 inline js::Debugger* js::DebuggerEnvironment::owner() const {
   JSObject* dbgobj = &getReservedSlot(OWNER_SLOT).toObject();
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -927,19 +927,16 @@ ResumeMode Debugger::slowPathOnResumeFra
         }
       }
     }
   }
 
   return slowPathOnEnterFrame(cx, frame);
 }
 
-static void DebuggerFrame_maybeDecrementFrameScriptStepModeCount(
-    FreeOp* fop, AbstractFramePtr frame, NativeObject* frameobj);
-
 /*
  * RAII class to mark a generator as "running" temporarily while running
  * debugger code.
  *
  * When Debugger::slowPathOnLeaveFrame is called for a frame that is yielding
  * or awaiting, its generator is in the "suspended" state. Letting script
  * observe this state, with the generator on stack yet also reenterable, would
  * be bad, so we mark it running while we fire events.
@@ -4492,18 +4489,17 @@ void Debugger::removeDebuggeeGlobal(Free
   // This is a bug, since it's observable and contrary to the spec. One
   // possible fix would be to put such objects into a compartment-wide bag
   // which slowPathOnLeaveFrame would have to examine.
   for (FrameMap::Enum e(frames); !e.empty(); e.popFront()) {
     AbstractFramePtr frame = e.front().key();
     DebuggerFrame* frameobj = e.front().value();
     if (frame.hasGlobal(global)) {
       frameobj->freeFrameIterData(fop);
-      DebuggerFrame_maybeDecrementFrameScriptStepModeCount(fop, frame,
-                                                           frameobj);
+      frameobj->maybeDecrementFrameScriptStepModeCount(fop, frame);
       e.removeFront();
     }
   }
 
   // Clear this global's generators from generatorFrames as well.
   //
   // This method can be called either from script (dbg.removeDebuggee) or
   // from an awkward time during GC sweeping. In the latter case, skip this
@@ -7778,17 +7774,17 @@ bool Debugger::replaceFrameGuts(JSContex
       // removeFromDebuggerFramesOnExit and removeToDebuggerFramesOnExit
       // must both run for the same reason given above.
       //
       // The difference is that the current frameobj is no longer in its
       // Debugger's frame map, so it will not be cleaned up by neither
       // lambda. Manually clean it up here.
       FreeOp* fop = cx->runtime()->defaultFreeOp();
       frameobj->freeFrameIterData(fop);
-      DebuggerFrame_maybeDecrementFrameScriptStepModeCount(fop, to, frameobj);
+      frameobj->maybeDecrementFrameScriptStepModeCount(fop, to);
 
       ReportOutOfMemory(cx);
       return false;
     }
   }
 
   // All frames successfuly replaced, cancel the rollback.
   removeToDebuggerFramesOnExit.release();
@@ -7807,18 +7803,17 @@ bool Debugger::inFrameMaps(AbstractFrame
 /* static */
 void Debugger::removeFromFrameMapsAndClearBreakpointsIn(JSContext* cx,
                                                         AbstractFramePtr frame,
                                                         bool suspending) {
   forEachDebuggerFrame(frame, [&](DebuggerFrame* frameobj) {
     FreeOp* fop = cx->runtime()->defaultFreeOp();
     frameobj->freeFrameIterData(fop);
     if (!suspending) {
-      DebuggerFrame_maybeDecrementFrameScriptStepModeCount(fop, frame,
-                                                           frameobj);
+      frameobj->maybeDecrementFrameScriptStepModeCount(fop, frame);
     }
 
     Debugger* dbg = Debugger::fromChildJSObject(frameobj);
     dbg->frames.remove(frame);
 
     if (!suspending && frame.isGeneratorFrame()) {
       // Terminally exiting a generator.
       auto* genObj = GetGeneratorObjectForFrame(cx, frame);
@@ -9533,19 +9528,18 @@ void DebuggerFrame::setOnPopHandler(OnPo
   if (prior && prior != handler) {
     prior->drop();
   }
 
   setReservedSlot(ONPOP_HANDLER_SLOT,
                   handler ? PrivateValue(handler) : UndefinedValue());
 }
 
-static bool DebuggerFrame_requireLive(JSContext* cx,
-                                      HandleDebuggerFrame frame) {
-  if (!frame->isLive()) {
+bool DebuggerFrame::requireLive(JSContext* cx) {
+  if (!isLive()) {
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                               JSMSG_DEBUG_NOT_LIVE, "Debugger.Frame");
     return false;
   }
 
   return true;
 }
 
@@ -9581,21 +9575,20 @@ bool DebuggerFrame::requireScriptReferen
 
 void DebuggerFrame::freeFrameIterData(FreeOp* fop) {
   if (FrameIter::Data* data = frameIterData()) {
     fop->delete_(data);
     setPrivate(nullptr);
   }
 }
 
-static void DebuggerFrame_maybeDecrementFrameScriptStepModeCount(
-    FreeOp* fop, AbstractFramePtr frame, NativeObject* frameobj) {
+void DebuggerFrame::maybeDecrementFrameScriptStepModeCount(
+    FreeOp* fop, AbstractFramePtr frame) {
   // If this frame has an onStep handler, decrement the script's count.
-  if (frameobj->getReservedSlot(DebuggerFrame::ONSTEP_HANDLER_SLOT)
-          .isUndefined()) {
+  if (getReservedSlot(ONSTEP_HANDLER_SLOT).isUndefined()) {
     return;
   }
   if (frame.isWasmDebugFrame()) {
     wasm::Instance* instance = frame.wasmInstance();
     instance->debug().decrementStepModeCount(
         fop, frame.asWasmDebugFrame()->funcIndex());
   } else {
     frame.script()->decrementStepModeCount(fop);
@@ -9624,64 +9617,63 @@ void DebuggerFrame::trace(JSTracer* trc,
     onStepHandler->trace(trc);
   }
   OnPopHandler* onPopHandler = obj->as<DebuggerFrame>().onPopHandler();
   if (onPopHandler) {
     onPopHandler->trace(trc);
   }
 }
 
-static DebuggerFrame* DebuggerFrame_checkThis(JSContext* cx,
-                                              const CallArgs& args,
-                                              const char* fnname,
-                                              bool checkLive) {
+/* static */
+DebuggerFrame* DebuggerFrame::checkThis(JSContext* cx, const CallArgs& args,
+                                        const char* fnname, bool checkLive) {
   JSObject* thisobj = NonNullObject(cx, args.thisv());
   if (!thisobj) {
     return nullptr;
   }
-  if (thisobj->getClass() != &DebuggerFrame::class_) {
+  if (thisobj->getClass() != &class_) {
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                               JSMSG_INCOMPATIBLE_PROTO, "Debugger.Frame",
                               fnname, thisobj->getClass()->name);
     return nullptr;
   }
 
   RootedDebuggerFrame frame(cx, &thisobj->as<DebuggerFrame>());
 
   // Forbid Debugger.Frame.prototype, which is of class DebuggerFrame::class_
   // but isn't really a working Debugger.Frame object. The prototype object
   // is distinguished by having a nullptr private value. Also, forbid popped
   // frames.
   if (!frame->getPrivate() &&
-      frame->getReservedSlot(DebuggerFrame::OWNER_SLOT).isUndefined()) {
+      frame->getReservedSlot(OWNER_SLOT).isUndefined()) {
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
                               JSMSG_INCOMPATIBLE_PROTO, "Debugger.Frame",
                               fnname, "prototype object");
     return nullptr;
   }
 
   if (checkLive) {
-    if (!DebuggerFrame_requireLive(cx, frame)) {
+    if (!frame->requireLive(cx)) {
       return nullptr;
     }
   }
 
   return frame;
 }
 
 /*
  * Methods can use THIS_DEBUGGER_FRAME to check that `this` is a Debugger.Frame
  * object and get it in a local Rooted.
  *
  * Methods that need the AbstractFramePtr should use THIS_FRAME.
  */
-#define THIS_DEBUGGER_FRAME(cx, argc, vp, fnname, args, frame)                \
-  CallArgs args = CallArgsFromVp(argc, vp);                                   \
-  RootedDebuggerFrame frame(cx,                                               \
-                            DebuggerFrame_checkThis(cx, args, fnname, true)); \
+#define THIS_DEBUGGER_FRAME(cx, argc, vp, fnname, args, frame)                 \
+  CallArgs args = CallArgsFromVp(argc, vp);                                    \
+  RootedDebuggerFrame frame(cx,                                                \
+                            DebuggerFrame::checkThis(cx, args, fnname, true)); \
   if (!frame) return false;
 
 #define THIS_FRAME(cx, argc, vp, fnname, args, thisobj, iter, frame) \
   THIS_DEBUGGER_FRAME(cx, argc, vp, fnname, args, thisobj);          \
   FrameIter iter(*thisobj->frameIterData());                         \
   AbstractFramePtr frame = iter.abstractFramePtr()
 
 /* static */
@@ -9814,17 +9806,17 @@ bool DebuggerFrame::olderGetter(JSContex
     return false;
   }
 
   args.rval().setObjectOrNull(result);
   return true;
 }
 
 // The getter used for each element of frame.arguments.
-// See DebuggerFrame_getArguments.
+// See DebuggerFrame::getArguments.
 static bool DebuggerArguments_getArg(JSContext* cx, unsigned argc, Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
   int32_t i = args.callee().as<JSFunction>().getExtendedSlot(0).toInt32();
 
   // Check that the this value is an Arguments object.
   RootedObject argsobj(cx, NonNullObject(cx, args.thisv()));
   if (!argsobj) {
     return false;
@@ -9935,17 +9927,18 @@ bool DebuggerFrame::argumentsGetter(JSCo
   if (!DebuggerFrame::getArguments(cx, frame, &result)) {
     return false;
   }
 
   args.rval().setObjectOrNull(result);
   return true;
 }
 
-static bool DebuggerFrame_getScript(JSContext* cx, unsigned argc, Value* vp) {
+/* static */
+bool DebuggerFrame::getScript(JSContext* cx, unsigned argc, Value* vp) {
   THIS_FRAME(cx, argc, vp, "get script", args, thisobj, frameIter, frame);
   Debugger* debug = Debugger::fromChildJSObject(thisobj);
 
   RootedObject scriptObject(cx);
   if (frame.isWasmDebugFrame()) {
     RootedWasmInstanceObject instance(cx, frame.wasmInstance()->object());
     scriptObject = debug->wrapWasmScript(cx, instance);
     if (!scriptObject) {
@@ -9975,18 +9968,17 @@ bool DebuggerFrame::offsetGetter(JSConte
 
   args.rval().setNumber(double(result));
   return true;
 }
 
 /* static */
 bool DebuggerFrame::liveGetter(JSContext* cx, unsigned argc, Value* vp) {
   CallArgs args = CallArgsFromVp(argc, vp);
-  RootedDebuggerFrame frame(
-      cx, DebuggerFrame_checkThis(cx, args, "get live", false));
+  RootedDebuggerFrame frame(cx, checkThis(cx, args, "get live", false));
   if (!frame) {
     return false;
   }
 
   args.rval().setBoolean(frame->isLive());
   return true;
 }
 
@@ -10153,17 +10145,17 @@ const JSPropertySpec DebuggerFrame::prop
     JS_PSG("arguments", DebuggerFrame::argumentsGetter, 0),
     JS_PSG("callee", DebuggerFrame::calleeGetter, 0),
     JS_PSG("constructing", DebuggerFrame::constructingGetter, 0),
     JS_PSG("environment", DebuggerFrame::environmentGetter, 0),
     JS_PSG("generator", DebuggerFrame::generatorGetter, 0),
     JS_PSG("live", DebuggerFrame::liveGetter, 0),
     JS_PSG("offset", DebuggerFrame::offsetGetter, 0),
     JS_PSG("older", DebuggerFrame::olderGetter, 0),
-    JS_PSG("script", DebuggerFrame_getScript, 0),
+    JS_PSG("script", DebuggerFrame::getScript, 0),
     JS_PSG("this", DebuggerFrame::thisGetter, 0),
     JS_PSG("type", DebuggerFrame::typeGetter, 0),
     JS_PSG("implementation", DebuggerFrame::implementationGetter, 0),
     JS_PSGS("onStep", DebuggerFrame::onStepGetter, DebuggerFrame::onStepSetter,
             0),
     JS_PSGS("onPop", DebuggerFrame::onPopGetter, DebuggerFrame::onPopSetter, 0),
     JS_PS_END};
 
--- a/js/src/vm/Debugger.h
+++ b/js/src/vm/Debugger.h
@@ -1450,18 +1450,18 @@ class DebuggerFrame : public NativeObjec
 
   static void trace(JSTracer* trc, JSObject* obj);
 
   static NativeObject* initClass(JSContext* cx, HandleObject dbgCtor,
                                  Handle<GlobalObject*> global);
   static DebuggerFrame* create(JSContext* cx, HandleObject proto,
                                const FrameIter& iter,
                                HandleNativeObject debugger);
-  void freeFrameIterData(FreeOp* fop);
 
+  static MOZ_MUST_USE bool getScript(JSContext* cx, unsigned argc, Value* vp);
   static MOZ_MUST_USE bool getArguments(JSContext* cx,
                                         HandleDebuggerFrame frame,
                                         MutableHandleDebuggerArguments result);
   static MOZ_MUST_USE bool getCallee(JSContext* cx, HandleDebuggerFrame frame,
                                      MutableHandleDebuggerObject result);
   static MOZ_MUST_USE bool getIsConstructing(JSContext* cx,
                                              HandleDebuggerFrame frame,
                                              bool& result);
@@ -1485,16 +1485,22 @@ class DebuggerFrame : public NativeObjec
   static MOZ_MUST_USE bool eval(JSContext* cx, HandleDebuggerFrame frame,
                                 mozilla::Range<const char16_t> chars,
                                 HandleObject bindings,
                                 const EvalOptions& options,
                                 ResumeMode& resumeMode,
                                 MutableHandleValue value,
                                 MutableHandleSavedFrame exnStack);
 
+  MOZ_MUST_USE bool requireLive(JSContext* cx);
+  static MOZ_MUST_USE DebuggerFrame* checkThis(JSContext* cx,
+                                               const CallArgs& args,
+                                               const char* fnname,
+                                               bool checkLive);
+
   bool isLive() const;
   OnStepHandler* onStepHandler() const;
   OnPopHandler* onPopHandler() const;
   void setOnPopHandler(OnPopHandler* handler);
 
   /*
    * Called after a generator/async frame is resumed, before exposing this
    * Debugger.Frame object to any hooks.
@@ -1548,16 +1554,19 @@ class DebuggerFrame : public NativeObjec
   static MOZ_MUST_USE bool evalMethod(JSContext* cx, unsigned argc, Value* vp);
   static MOZ_MUST_USE bool evalWithBindingsMethod(JSContext* cx, unsigned argc,
                                                   Value* vp);
 
   Debugger* owner() const;
 
  public:
   FrameIter::Data* frameIterData() const;
+  void freeFrameIterData(FreeOp* fop);
+  void maybeDecrementFrameScriptStepModeCount(FreeOp* fop,
+                                              AbstractFramePtr frame);
 };
 
 class DebuggerObject : public NativeObject {
  public:
   static const Class class_;
 
   static NativeObject* initClass(JSContext* cx, Handle<GlobalObject*> global,
                                  HandleObject debugCtor);
@@ -1863,24 +1872,24 @@ class BreakpointSite {
   // List of all js::Breakpoints at this instruction.
   using BreakpointList =
       mozilla::DoublyLinkedList<js::Breakpoint, SiteLinkAccess<js::Breakpoint>>;
   BreakpointList breakpoints;
   size_t enabledCount; /* number of breakpoints in the list that are enabled */
 
  protected:
   virtual void recompile(FreeOp* fop) = 0;
-  inline bool isEnabled() const { return enabledCount > 0; }
+  bool isEnabled() const { return enabledCount > 0; }
 
  public:
   BreakpointSite(Type type);
   Breakpoint* firstBreakpoint() const;
   virtual ~BreakpointSite() {}
   bool hasBreakpoint(Breakpoint* bp);
-  inline Type type() const { return type_; }
+  Type type() const { return type_; }
 
   void inc(FreeOp* fop);
   void dec(FreeOp* fop);
   bool isEmpty() const;
   virtual void destroyIfEmpty(FreeOp* fop) = 0;
 
   inline JSBreakpointSite* asJS();
   inline WasmBreakpointSite* asWasm();
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -10490,20 +10490,21 @@ nsresult PresShell::SetIsActive(bool aIs
   return rv;
 }
 
 RefPtr<MobileViewportManager> PresShell::GetMobileViewportManager() const {
   return mMobileViewportManager;
 }
 
 void PresShell::UpdateViewportOverridden(bool aAfterInitialization) {
-  // Determine if we require a MobileViewportManager. This logic is
-  // equivalent to ShouldHandleMetaViewport, which will check gfxPrefs if
-  // there are not meta viewport overrides.
-  bool needMVM = nsLayoutUtils::ShouldHandleMetaViewport(mDocument);
+  // Determine if we require a MobileViewportManager. We need one any
+  // time we allow resolution zooming for a document, and any time we
+  // want to obey <meta name="viewport"> tags for it.
+  bool needMVM = nsLayoutUtils::ShouldHandleMetaViewport(mDocument) ||
+                 nsLayoutUtils::AllowZoomingForDocument(mDocument);
 
   if (needMVM == !!mMobileViewportManager) {
     // Either we've need one and we've already got it, or we don't need one
     // and don't have it. Either way, we're done.
     return;
   }
 
   if (needMVM) {
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -3018,17 +3018,17 @@ class nsLayoutUtils {
   static void ComputeFontVariations(
       const nsCSSValuePairList* aVariationsList,
       nsTArray<gfxFontVariation>& aVariationSettings);
 
   static uint32_t ParseFontLanguageOverride(const nsAString& aLangTag);
 
   /**
    * Returns true if there are any preferences or overrides that indicate a
-   * need to create a MobileViewportManager.
+   * need to handle <meta name="viewport"> tags.
    */
   static bool ShouldHandleMetaViewport(const mozilla::dom::Document*);
 
   /**
    * Resolve a CSS <length-percentage> value to a definite size.
    */
   template <bool clampNegativeResultToZero>
   static nscoord ResolveToLength(const LengthPercentage& aLengthPercentage,
--- a/layout/painting/FrameLayerBuilder.cpp
+++ b/layout/painting/FrameLayerBuilder.cpp
@@ -2063,16 +2063,17 @@ void FrameLayerBuilder::FlashPaint(gfxCo
 
 DisplayItemData* FrameLayerBuilder::GetDisplayItemData(nsIFrame* aFrame,
                                                        uint32_t aKey) {
   const SmallPointerArray<DisplayItemData>& array = aFrame->DisplayItemData();
   for (uint32_t i = 0; i < array.Length(); i++) {
     DisplayItemData* item =
         DisplayItemData::AssertDisplayItemData(array.ElementAt(i));
     if (item->mDisplayItemKey == aKey &&
+        item->FirstFrame() == aFrame &&
         item->mLayer->Manager() == mRetainingManager) {
       return item;
     }
   }
   return nullptr;
 }
 
 #ifdef MOZ_DUMP_PAINTING
--- a/layout/painting/FrameLayerBuilder.h
+++ b/layout/painting/FrameLayerBuilder.h
@@ -82,16 +82,17 @@ class DisplayItemData final {
   uint32_t GetDisplayItemKey() { return mDisplayItemKey; }
   layers::Layer* GetLayer() const { return mLayer; }
   nsDisplayItemGeometry* GetGeometry() const { return mGeometry.get(); }
   const DisplayItemClip& GetClip() const { return mClip; }
   void Invalidate() { mIsInvalid = true; }
   void ClearAnimationCompositorState();
   void SetItem(nsDisplayItem* aItem) { mItem = aItem; }
   nsDisplayItem* GetItem() const { return mItem; }
+  nsIFrame* FirstFrame() const { return mFrameList[0]; }
 
   bool HasMergedFrames() const { return mFrameList.Length() > 1; }
 
   static DisplayItemData* AssertDisplayItemData(DisplayItemData* aData);
 
   void* operator new(size_t sz, nsPresContext* aPresContext) {
     // Check the recycle list first.
     return aPresContext->PresShell()->AllocateByObjectID(
--- a/layout/style/GeckoBindings.cpp
+++ b/layout/style/GeckoBindings.cpp
@@ -2165,16 +2165,30 @@ NS_IMPL_THREADSAFE_FFI_REFCOUNTING(nsCSS
   }
 
 void Gecko_RegisterProfilerThread(const char* name) {
   PROFILER_REGISTER_THREAD(name);
 }
 
 void Gecko_UnregisterProfilerThread() { PROFILER_UNREGISTER_THREAD(); }
 
+#ifdef MOZ_GECKO_PROFILER
+void Gecko_Construct_AutoProfilerLabel(AutoProfilerLabel* aAutoLabel,
+                                       JS::ProfilingCategoryPair aCatPair) {
+  new (aAutoLabel) AutoProfilerLabel(
+      "", nullptr, aCatPair,
+      uint32_t(
+          js::ProfilingStackFrame::Flags::LABEL_DETERMINED_BY_CATEGORY_PAIR));
+}
+
+void Gecko_Destroy_AutoProfilerLabel(AutoProfilerLabel* aAutoLabel) {
+  aAutoLabel->~AutoProfilerLabel();
+}
+#endif
+
 bool Gecko_DocumentRule_UseForPresentation(
     const Document* aDocument, const nsACString* aPattern,
     DocumentMatchingFunction aMatchingFunction) {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsIURI* docURI = aDocument->GetDocumentURI();
   nsAutoCString docURISpec;
   if (docURI) {
--- a/layout/style/GeckoBindings.h
+++ b/layout/style/GeckoBindings.h
@@ -730,16 +730,22 @@ void Gecko_AddPropertyToSet(nsCSSPropert
                                          const nsStyle##name* other);        \
   void Gecko_Destroy_nsStyle##name(nsStyle##name* ptr);
 #include "nsStyleStructList.h"
 #undef STYLE_STRUCT
 
 void Gecko_RegisterProfilerThread(const char* name);
 void Gecko_UnregisterProfilerThread();
 
+#ifdef MOZ_GECKO_PROFILER
+void Gecko_Construct_AutoProfilerLabel(mozilla::AutoProfilerLabel*,
+                                       JS::ProfilingCategoryPair);
+void Gecko_Destroy_AutoProfilerLabel(mozilla::AutoProfilerLabel*);
+#endif
+
 bool Gecko_DocumentRule_UseForPresentation(
     const mozilla::dom::Document*, const nsACString* aPattern,
     mozilla::css::DocumentMatchingFunction);
 
 // Allocator hinting.
 void Gecko_SetJemallocThreadLocalArena(bool enabled);
 
 // Pseudo-element flags.
--- a/layout/style/ServoBindings.toml
+++ b/layout/style/ServoBindings.toml
@@ -30,16 +30,17 @@ headers = [
     "mozilla/ComputedStyle.h",
     "mozilla/ServoTraversalStatistics.h",
     "mozilla/SizeOfState.h",
     "nsCSSProps.h",
     "nsContentUtils.h",
     "nsNameSpaceManager.h",
     "nsMediaFeatures.h",
     "nsXBLBinding.h",
+    "GeckoProfiler.h",
 ]
 raw-lines = [
     # FIXME(emilio): Incrementally remove these "pub use"s. Probably
     # mozilla::css and mozilla::dom are easier.
     "pub use self::root::*;",
     "pub use self::root::mozilla::*;",
     "pub use self::root::mozilla::css::*;",
     "pub use self::root::mozilla::dom::*;",
@@ -154,16 +155,17 @@ whitelist-vars = [
     "SERVO_CSS_PSEUDO_ELEMENT_FLAGS_.*",
     "kNameSpaceID_.*",
     "kGenericFont_.*",
     "kPresContext_.*",
     "nsContentUtils_.*",
     "GECKO_IS_NIGHTLY",
     "mozilla::detail::gGkAtoms",
     "mozilla::detail::kGkAtomsArrayOffset",
+    "mozilla::profiler::detail::RacyFeatures::sActiveAndFeatures",
 ]
 # TODO(emilio): A bunch of types here can go away once we generate bindings and
 # structs together.
 whitelist-types = [
     "RawGecko.*",
     "RawServo.*",
     "ServoCssRules",
     "nsFontFaceRuleContainer",
@@ -323,16 +325,17 @@ whitelist-types = [
     "mozilla::DefaultDelete",
     "mozilla::Side",
     "mozilla::binding_danger::AssertAndSuppressCleanupPolicy",
     "mozilla::ParsingMode",
     "mozilla::InheritTarget",
     "mozilla::dom::MediaList",
     "mozilla::StyleRuleInclusion",
     "nsStyleTransformMatrix::MatrixTransformOperator",
+    "mozilla::profiler::detail::RacyFeatures",
 ]
 opaque-types = [
     "mozilla::StyleThinArc", # https://github.com/rust-lang/rust-bindgen/issues/1557
     "std::pair__PCCP",
     "std::namespace::atomic___base", "std::atomic__My_base",
     "std::atomic",
     "std::atomic___base",
     # We want everything but FontVariation and Float to be opaque but we don't
--- a/netwerk/protocol/http/nsHttpConnectionMgr.cpp
+++ b/netwerk/protocol/http/nsHttpConnectionMgr.cpp
@@ -561,17 +561,20 @@ class nsCompleteUpgradeData : public ARe
 
   nsCOMPtr<nsISocketTransport> mSocketTransport;
   nsCOMPtr<nsIAsyncInputStream> mSocketIn;
   nsCOMPtr<nsIAsyncOutputStream> mSocketOut;
 
   bool mJsWrapped;
 
  private:
-  virtual ~nsCompleteUpgradeData() = default;
+  virtual ~nsCompleteUpgradeData() {
+    NS_ReleaseOnMainThreadSystemGroup("nsCompleteUpgradeData.mUpgradeListener",
+                                      mUpgradeListener.forget());
+  }
 };
 
 nsresult nsHttpConnectionMgr::CompleteUpgrade(
     nsHttpTransaction* aTrans, nsIHttpUpgradeListener* aUpgradeListener) {
   // test if aUpgradeListener is a wrapped JsObject
   nsCOMPtr<nsIXPConnectWrappedJS> wrapper = do_QueryInterface(aUpgradeListener);
 
   bool wrapped = !!wrapper;
--- a/servo/components/style/Cargo.toml
+++ b/servo/components/style/Cargo.toml
@@ -17,16 +17,17 @@ doctest = false
 
 [features]
 gecko = ["nsstring", "style_traits/gecko", "fallible/known_system_malloc"]
 use_bindgen = ["bindgen", "regex", "toml"]
 servo = ["serde", "style_traits/servo", "servo_atoms", "servo_config", "html5ever",
          "cssparser/serde", "encoding_rs", "malloc_size_of/servo", "arrayvec/use_union",
          "servo_url", "string_cache", "crossbeam-channel", "to_shmem/servo", "servo_arc/servo"]
 gecko_debug = []
+gecko_profiler = []
 
 [dependencies]
 app_units = "0.7"
 arrayvec = "0.4.6"
 atomic_refcell = "0.1"
 bitflags = "1.0"
 byteorder = "1.0"
 cssparser = "0.25"
--- a/servo/components/style/driver.rs
+++ b/servo/components/style/driver.rs
@@ -129,16 +129,17 @@ pub fn traverse_dom<E, D>(
             // depth for all the children.
             if pool.is_some() && discovered.len() > WORK_UNIT_MAX {
                 let pool = pool.unwrap();
                 maybe_tls = Some(ScopedTLS::<ThreadLocalStyleContext<E>>::new(pool));
                 let root_opaque = root.as_node().opaque();
                 let drain = discovered.drain(..);
                 pool.install(|| {
                     rayon::scope(|scope| {
+                        profiler_label!(Style);
                         parallel::traverse_nodes(
                             drain,
                             DispatchMode::TailCall,
                             /* recursion_ok = */ true,
                             root_opaque,
                             PerLevelTraversalData {
                                 current_dom_depth: depth,
                             },
--- a/servo/components/style/gecko/mod.rs
+++ b/servo/components/style/gecko/mod.rs
@@ -8,16 +8,18 @@
 mod non_ts_pseudo_class_list;
 
 pub mod arc_types;
 pub mod boxed_types;
 pub mod conversions;
 pub mod data;
 pub mod media_features;
 pub mod media_queries;
+#[cfg(feature = "gecko_profiler")]
+pub mod profiler;
 pub mod pseudo_element;
 pub mod restyle_damage;
 pub mod rules;
 pub mod selector_parser;
 pub mod snapshot;
 pub mod snapshot_helpers;
 pub mod traversal;
 pub mod url;
new file mode 100644
--- /dev/null
+++ b/servo/components/style/gecko/profiler.rs
@@ -0,0 +1,76 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+//! Gecko profiler support.
+//!
+//! Use the `profiler_label!` macro from macros.rs.
+
+use crate::gecko_bindings::structs;
+
+/// A label describing a category of work that style threads can perform.
+pub enum ProfilerLabel {
+    /// Style computation.
+    Style,
+    /// Style sheet parsing.
+    Parse,
+}
+
+/// RAII object that constructs and destroys a C++ AutoProfilerLabel object
+/// pointed to be the specified reference.
+#[cfg(feature = "gecko_profiler")]
+pub struct AutoProfilerLabel<'a>(&'a mut structs::AutoProfilerLabel);
+
+#[cfg(feature = "gecko_profiler")]
+impl<'a> AutoProfilerLabel<'a> {
+    /// Creates a new AutoProfilerLabel with the specified label type.
+    ///
+    /// unsafe since the caller must ensure that `label` is allocated on the
+    /// stack.
+    #[inline]
+    pub unsafe fn new(
+        label: &mut structs::AutoProfilerLabel,
+        label_type: ProfilerLabel,
+    ) -> AutoProfilerLabel {
+        let category_pair = match label_type {
+            ProfilerLabel::Style => structs::JS::ProfilingCategoryPair_LAYOUT_StyleComputation,
+            ProfilerLabel::Parse => structs::JS::ProfilingCategoryPair_LAYOUT_CSSParsing,
+        };
+        structs::Gecko_Construct_AutoProfilerLabel(label, category_pair);
+        AutoProfilerLabel(label)
+    }
+}
+
+#[cfg(feature = "gecko_profiler")]
+impl<'a> Drop for AutoProfilerLabel<'a> {
+    #[inline]
+    fn drop(&mut self) {
+        unsafe {
+            structs::Gecko_Destroy_AutoProfilerLabel(self.0);
+        }
+    }
+}
+
+/// Whether the Gecko profiler is currently active.
+///
+/// This implementation must be kept in sync with
+/// `mozilla::profiler::detail::RacyFeatures::IsActive`.
+#[cfg(feature = "gecko_profiler")]
+#[inline]
+pub fn profiler_is_active() -> bool {
+    use self::structs::profiler::detail;
+    use std::mem;
+    use std::sync::atomic::{AtomicU32, Ordering};
+
+    let active_and_features: &AtomicU32 = unsafe {
+        mem::transmute(&detail::RacyFeatures_sActiveAndFeatures)
+    };
+    (active_and_features.load(Ordering::Relaxed) & detail::RacyFeatures_Active) != 0
+}
+
+/// Always false when the Gecko profiler is disabled.
+#[cfg(not(feature = "gecko_profiler"))]
+#[inline]
+pub fn profiler_is_active() -> bool {
+    false
+}
--- a/servo/components/style/macros.rs
+++ b/servo/components/style/macros.rs
@@ -99,8 +99,38 @@ macro_rules! define_keyword_type {
                 input
                     .expect_ident_matching($css)
                     .map(|_| $name)
                     .map_err(|e| e.into())
             }
         }
     };
 }
+
+/// Place a Gecko profiler label on the stack.
+///
+/// The `label_type` argument must be the name of a variant of `ProfilerLabel`.
+#[cfg(feature = "gecko_profiler")]
+#[macro_export]
+macro_rules! profiler_label {
+    ($label_type:ident) => {
+        let mut _profiler_label: $crate::gecko_bindings::structs::AutoProfilerLabel = unsafe {
+            ::std::mem::uninitialized()
+        };
+        let _profiler_label = if $crate::gecko::profiler::profiler_is_active() {
+            unsafe {
+                Some($crate::gecko::profiler::AutoProfilerLabel::new(
+                    &mut _profiler_label,
+                    $crate::gecko::profiler::ProfilerLabel::$label_type,
+                ))
+            }
+        } else {
+            None
+        };
+    }
+}
+
+/// No-op when the Gecko profiler is not available.
+#[cfg(not(feature = "gecko_profiler"))]
+#[macro_export]
+macro_rules! profiler_label {
+    ($label_type:ident) => {}
+}
--- a/servo/components/style/parallel.rs
+++ b/servo/components/style/parallel.rs
@@ -272,23 +272,25 @@ pub fn traverse_nodes<'a, 'scope, E, D, 
     // In the common case, our children fit within a single work unit, in which
     // case we can pass the SmallVec directly and avoid extra allocation.
     if nodes.len() <= WORK_UNIT_MAX {
         let work: WorkUnit<E::ConcreteNode> = nodes.collect();
         if may_dispatch_tail {
             top_down_dom(&work, root, traversal_data, scope, pool, traversal, tls);
         } else {
             scope.spawn(move |scope| {
+                profiler_label!(Style);
                 let work = work;
                 top_down_dom(&work, root, traversal_data, scope, pool, traversal, tls);
             });
         }
     } else {
         for chunk in nodes.chunks(WORK_UNIT_MAX).into_iter() {
             let nodes: WorkUnit<E::ConcreteNode> = chunk.collect();
             let traversal_data_copy = traversal_data.clone();
             scope.spawn(move |scope| {
+                profiler_label!(Style);
                 let n = nodes;
                 top_down_dom(&*n, root, traversal_data_copy, scope, pool, traversal, tls)
             });
         }
     }
 }
--- a/servo/ports/geckolib/Cargo.toml
+++ b/servo/ports/geckolib/Cargo.toml
@@ -6,16 +6,17 @@ license = "MPL-2.0"
 
 [lib]
 name = "geckoservo"
 path = "lib.rs"
 
 [features]
 bindgen = ["style/use_bindgen"]
 gecko_debug = ["style/gecko_debug", "nsstring/gecko_debug"]
+gecko_profiler = ["style/gecko_profiler"]
 
 [dependencies]
 atomic_refcell = "0.1"
 cssparser = "0.25"
 cstr = "0.1.2"
 libc = "0.2"
 log = {version = "0.4", features = ["release_max_level_info"]}
 malloc_size_of = {path = "../../components/malloc_size_of"}
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -13,16 +13,17 @@ use selectors::{NthIndexCache, SelectorL
 use servo_arc::{Arc, ArcBorrow, RawOffsetArc};
 use smallvec::SmallVec;
 use std::cell::RefCell;
 use std::collections::BTreeSet;
 use std::fmt::Write;
 use std::iter;
 use std::os::raw::c_void;
 use std::ptr;
+use style::profiler_label;
 use style::applicable_declarations::ApplicableDeclarationBlock;
 use style::author_styles::AuthorStyles;
 use style::context::ThreadLocalStyleContext;
 use style::context::{CascadeInputs, QuirksMode, SharedStyleContext, StyleContext};
 use style::counter_style;
 use style::data::{self, ElementStyles};
 use style::dom::{ShowSubtreeData, TDocument, TElement, TNode};
 use style::driver;
@@ -1406,16 +1407,17 @@ pub unsafe extern "C" fn Servo_StyleShee
         mode_to_origin(mode),
         quirks_mode.into(),
         line_number_offset,
         should_record_use_counters,
     );
 
     if let Some(thread_pool) = STYLE_THREAD_POOL.style_thread_pool.as_ref() {
         thread_pool.spawn(|| {
+            profiler_label!(Parse);
             async_parser.parse();
         });
     } else {
         async_parser.parse();
     }
 }
 
 #[no_mangle]
--- a/taskcluster/docker/diffoscope/get_and_diffoscope
+++ b/taskcluster/docker/diffoscope/get_and_diffoscope
@@ -15,37 +15,37 @@ mkdir a b
 if [ "$TASKCLUSTER_ROOT_URL" = "https://taskcluster.net" ]; then
     queue_base='https://queue.taskcluster.net/v1'
 else
     queue_base="$TASKCLUSTER_ROOT_URL/api/queue/v1"
 fi
 
 case "$ORIG_URL" in
 */target.zip|*/target.apk)
-	curl -sL "$ORIG_URL" > a.zip
-	curl -sL "$NEW_URL" > b.zip
+	curl -L "$ORIG_URL" > a.zip
+	curl -L "$NEW_URL" > b.zip
 	unzip -d a a.zip
 	unzip -d b b.zip
 	;;
 */target.tar.bz2)
-	curl -sL "$ORIG_URL" | tar -C a -jxf -
-	curl -sL "$NEW_URL" | tar -C b -jxf -
+	curl -L "$ORIG_URL" | tar -C a -jxf -
+	curl -L "$NEW_URL" | tar -C b -jxf -
 	;;
 */target.dmg)
 	# We don't have mach available to call mach artifact toolchain.
 	# This is the trivial equivalent for those toolchains we use here.
 	for t in $MOZ_TOOLCHAINS; do
-		curl -sL $queue_base/task/${t#*@}/artifacts/${t%@*} | tar -Jxf -
+		curl -L $queue_base/task/${t#*@}/artifacts/${t%@*} | tar -Jxf -
 	done
 	for tool in lipo otool; do
 		ln -s /builds/worker/cctools/bin/x86_64-darwin*-$tool bin/$tool
 	done
 	export PATH=$PATH:/builds/worker/bin
-	curl -sL "$ORIG_URL" > a.dmg
-	curl -sL "$NEW_URL" > b.dmg
+	curl -L "$ORIG_URL" > a.dmg
+	curl -L "$NEW_URL" > b.dmg
 	for i in a b; do
 		dmg/dmg extract $i.dmg $i.hfs
 		dmg/hfsplus $i.hfs extractall / $i
 	done
 	;;
 esac
 
 case "$ORIG_URL" in
--- a/taskcluster/taskgraph/target_tasks.py
+++ b/taskcluster/taskgraph/target_tasks.py
@@ -205,17 +205,21 @@ def target_tasks_ash(full_task_graph, pa
         # no non-e10s tests
         if task.attributes.get('unittest_suite'):
             if not task.attributes.get('e10s'):
                 return False
         # don't upload symbols
         if task.attributes['kind'] == 'upload-symbols':
             return False
         return True
-    return [l for l, t in full_task_graph.tasks.iteritems() if filter(t)]
+
+    return [l for l, t in full_task_graph.tasks.iteritems()
+            if filter(t)
+            and standard_filter(t, parameters)
+            and filter_out_nightly(t, parameters)]
 
 
 @_target_task('graphics_tasks')
 def target_tasks_graphics(full_task_graph, parameters, graph_config):
     """In addition to doing the filtering by project that the 'default'
        filter does, also remove artifact builds because we have csets on
        the graphics branch that aren't on the candidate branches of artifact
        builds"""
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/service-workers/service-worker/update-missing-import-scripts.https.html.ini
@@ -0,0 +1,3 @@
+[update-missing-import-scripts.https.html]
+  disabled:
+    if debug and (os == "win"): https://bugzilla.mozilla.org/show_bug.cgi?id=1525580
--- a/toolkit/components/antitracking/StoragePrincipalHelper.cpp
+++ b/toolkit/components/antitracking/StoragePrincipalHelper.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "StoragePrincipalHelper.h"
 
+#include "mozilla/ipc/PBackgroundSharedTypes.h"
 #include "mozilla/ScopeExit.h"
 #include "mozilla/StaticPrefs.h"
 #include "nsContentUtils.h"
 #include "nsIHttpChannel.h"
 
 namespace mozilla {
 
 namespace {
@@ -92,9 +93,93 @@ nsresult StoragePrincipalHelper::Prepare
     return NS_OK;
   }
 
   aOriginAttributes.SetFirstPartyDomain(false, principalURI,
                                         true /* aForced */);
   return NS_OK;
 }
 
+// static
+bool StoragePrincipalHelper::VerifyValidStoragePrincipalInfoForPrincipalInfo(
+    const mozilla::ipc::PrincipalInfo& aStoragePrincipalInfo,
+    const mozilla::ipc::PrincipalInfo& aPrincipalInfo) {
+  if (aStoragePrincipalInfo.type() != aPrincipalInfo.type()) {
+    return false;
+  }
+
+  if (aStoragePrincipalInfo.type() ==
+      mozilla::ipc::PrincipalInfo::TContentPrincipalInfo) {
+    const mozilla::ipc::ContentPrincipalInfo& spInfo =
+        aStoragePrincipalInfo.get_ContentPrincipalInfo();
+    const mozilla::ipc::ContentPrincipalInfo& pInfo =
+        aPrincipalInfo.get_ContentPrincipalInfo();
+
+    if (!spInfo.attrs().EqualsIgnoringFPD(pInfo.attrs()) ||
+        spInfo.originNoSuffix() != pInfo.originNoSuffix() ||
+        spInfo.spec() != pInfo.spec() || spInfo.domain() != pInfo.domain() ||
+        spInfo.baseDomain() != pInfo.baseDomain() ||
+        spInfo.securityPolicies().Length() !=
+            pInfo.securityPolicies().Length()) {
+      return false;
+    }
+
+    for (uint32_t i = 0; i < spInfo.securityPolicies().Length(); ++i) {
+      if (spInfo.securityPolicies()[i].policy() !=
+              pInfo.securityPolicies()[i].policy() ||
+          spInfo.securityPolicies()[i].reportOnlyFlag() !=
+              pInfo.securityPolicies()[i].reportOnlyFlag() ||
+          spInfo.securityPolicies()[i].deliveredViaMetaTagFlag() !=
+              pInfo.securityPolicies()[i].deliveredViaMetaTagFlag()) {
+        return false;
+      }
+    }
+
+    return true;
+  }
+
+  if (aStoragePrincipalInfo.type() ==
+      mozilla::ipc::PrincipalInfo::TSystemPrincipalInfo) {
+    // Nothing to check here.
+    return true;
+  }
+
+  if (aStoragePrincipalInfo.type() ==
+      mozilla::ipc::PrincipalInfo::TNullPrincipalInfo) {
+    const mozilla::ipc::NullPrincipalInfo& spInfo =
+        aStoragePrincipalInfo.get_NullPrincipalInfo();
+    const mozilla::ipc::NullPrincipalInfo& pInfo =
+        aPrincipalInfo.get_NullPrincipalInfo();
+
+    return spInfo.spec() == pInfo.spec() &&
+           spInfo.attrs().EqualsIgnoringFPD(pInfo.attrs());
+  }
+
+  if (aStoragePrincipalInfo.type() ==
+      mozilla::ipc::PrincipalInfo::TExpandedPrincipalInfo) {
+    const mozilla::ipc::ExpandedPrincipalInfo& spInfo =
+        aStoragePrincipalInfo.get_ExpandedPrincipalInfo();
+    const mozilla::ipc::ExpandedPrincipalInfo& pInfo =
+        aPrincipalInfo.get_ExpandedPrincipalInfo();
+
+    if (!spInfo.attrs().EqualsIgnoringFPD(pInfo.attrs())) {
+      return false;
+    }
+
+    if (spInfo.allowlist().Length() != pInfo.allowlist().Length()) {
+      return false;
+    }
+
+    for (uint32_t i = 0; i < spInfo.allowlist().Length(); ++i) {
+      if (!VerifyValidStoragePrincipalInfoForPrincipalInfo(
+              spInfo.allowlist()[i], pInfo.allowlist()[i])) {
+        return false;
+      }
+    }
+
+    return true;
+  }
+
+  MOZ_CRASH("Invalid principalInfo type");
+  return false;
+}
+
 }  // namespace mozilla
--- a/toolkit/components/antitracking/StoragePrincipalHelper.h
+++ b/toolkit/components/antitracking/StoragePrincipalHelper.h
@@ -116,22 +116,30 @@
  * ServiceWorkers in partitioned context, this part must be revisited.
  */
 
 class nsIChannel;
 class nsIPrincipal;
 
 namespace mozilla {
 
+namespace ipc {
+class PrincipalInfo;
+}
+
 class OriginAttributes;
 
 class StoragePrincipalHelper final {
  public:
   static nsresult Create(nsIChannel* aChannel, nsIPrincipal* aPrincipal,
                          nsIPrincipal** aStoragePrincipal);
 
   static nsresult PrepareOriginAttributes(nsIChannel* aChannel,
                                           OriginAttributes& aOriginAttributes);
+
+  static bool VerifyValidStoragePrincipalInfoForPrincipalInfo(
+      const mozilla::ipc::PrincipalInfo& aStoragePrincipalInfo,
+      const mozilla::ipc::PrincipalInfo& aPrincipalInfo);
 };
 
 }  // namespace mozilla
 
 #endif  // mozilla_StoragePrincipalHelper_h
--- a/toolkit/components/antitracking/test/browser/browser_partitionedLocalStorage_events.js
+++ b/toolkit/components/antitracking/test/browser/browser_partitionedLocalStorage_events.js
@@ -1,530 +1,564 @@
-// A same origin (and same-process via setting "dom.ipc.processCount" to 1)
-// top-level window with access to real localStorage does not share storage
-// with an ePartitionOrDeny iframe that should have PartitionedLocalStorage and
-// no storage events are received in either direction.  (Same-process in order
-// to avoid having to worry about any e10s propagation issues.)
-add_task(async _ => {
-  await SpecialPowers.pushPrefEnv({"set": [
-    ["dom.ipc.processCount", 1],
-    ["network.cookie.cookieBehavior", Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER],
-    ["privacy.trackingprotection.enabled", false],
-    ["privacy.trackingprotection.pbmode.enabled", false],
-    ["privacy.trackingprotection.annotate_channels", true],
-    ["privacy.restrict3rdpartystorage.partitionedHosts", "tracking.example.org,tracking.example.com"],
-  ]});
+function runAllTests(withStoragePrincipalEnabled, lsngEnabled) {
+  // A same origin (and same-process via setting "dom.ipc.processCount" to 1)
+  // top-level window with access to real localStorage does not share storage
+  // with an ePartitionOrDeny iframe that should have PartitionedLocalStorage and
+  // no storage events are received in either direction.  (Same-process in order
+  // to avoid having to worry about any e10s propagation issues.)
+  add_task(async _ => {
+    await SpecialPowers.pushPrefEnv({"set": [
+      ["dom.ipc.processCount", 1],
+      ["network.cookie.cookieBehavior", Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER],
+      ["privacy.trackingprotection.enabled", false],
+      ["privacy.trackingprotection.pbmode.enabled", false],
+      ["privacy.trackingprotection.annotate_channels", true],
+      ["privacy.restrict3rdpartystorage.partitionedHosts", "tracking.example.org,tracking.example.com"],
+      ["privacy.storagePrincipal.enabledForTrackers", withStoragePrincipalEnabled],
+      ["dom.storage.next_gen", lsngEnabled],
+    ]});
 
-  await UrlClassifierTestUtils.addTestTrackers();
+    await UrlClassifierTestUtils.addTestTrackers();
 
-  info("Creating a non-tracker top-level context");
-  let normalTab = BrowserTestUtils.addTab(gBrowser, TEST_DOMAIN + TEST_PATH + "page.html");
-  let normalBrowser = gBrowser.getBrowserForTab(normalTab);
-  await BrowserTestUtils.browserLoaded(normalBrowser);
+    info("Creating a non-tracker top-level context");
+    let normalTab = BrowserTestUtils.addTab(gBrowser, TEST_DOMAIN + TEST_PATH + "page.html");
+    let normalBrowser = gBrowser.getBrowserForTab(normalTab);
+    await BrowserTestUtils.browserLoaded(normalBrowser);
 
-  info("Creating a tracker top-level context");
-  let trackerTab = BrowserTestUtils.addTab(gBrowser, TEST_3RD_PARTY_DOMAIN + TEST_PATH + "page.html");
-  let trackerBrowser = gBrowser.getBrowserForTab(trackerTab);
-  await BrowserTestUtils.browserLoaded(trackerBrowser);
+    info("Creating a tracker top-level context");
+    let trackerTab = BrowserTestUtils.addTab(gBrowser, TEST_3RD_PARTY_DOMAIN + TEST_PATH + "page.html");
+    let trackerBrowser = gBrowser.getBrowserForTab(trackerTab);
+    await BrowserTestUtils.browserLoaded(trackerBrowser);
 
-  info("The non-tracker page opens a tracker iframe");
-  await ContentTask.spawn(normalBrowser, {
-      page: TEST_3RD_PARTY_DOMAIN + TEST_PATH + "localStorageEvents.html",
-    }, async obj => {
-      let ifr = content.document.createElement("iframe");
-      ifr.setAttribute("id", "ifr");
-      ifr.setAttribute("src", obj.page);
+    info("The non-tracker page opens a tracker iframe");
+    await ContentTask.spawn(normalBrowser, {
+        page: TEST_3RD_PARTY_DOMAIN + TEST_PATH + "localStorageEvents.html",
+      }, async obj => {
+        let ifr = content.document.createElement("iframe");
+        ifr.setAttribute("id", "ifr");
+        ifr.setAttribute("src", obj.page);
 
-      info("Iframe loading...");
-      await new content.Promise(resolve => {
-        ifr.onload = resolve;
-        content.document.body.appendChild(ifr);
-      });
+        info("Iframe loading...");
+        await new content.Promise(resolve => {
+          ifr.onload = resolve;
+          content.document.body.appendChild(ifr);
+        });
 
-      info("Setting localStorage value...");
-      ifr.contentWindow.postMessage("setValue", "*");
+        info("Setting localStorage value...");
+        ifr.contentWindow.postMessage("setValue", "*");
 
-      info("Getting the value...");
-      let value = await new Promise(resolve => {
-        content.addEventListener("message", e => {
-          resolve(e.data);
-        }, {once: true});
-        ifr.contentWindow.postMessage("getValue", "*");
-      });
+        info("Getting the value...");
+        let value = await new Promise(resolve => {
+          content.addEventListener("message", e => {
+            resolve(e.data);
+          }, {once: true});
+          ifr.contentWindow.postMessage("getValue", "*");
+        });
 
-      ok(value.startsWith("tracker-"), "The value is correctly set by the tracker");
-    }
-  );
+        ok(value.startsWith("tracker-"), "The value is correctly set by the tracker");
+      }
+    );
 
-  info("The tracker page should not have received events");
-  await ContentTask.spawn(trackerBrowser, null, async _ => {
-      is(content.localStorage.foo, undefined, "Undefined value!");
-      content.localStorage.foo = "normal-" + Math.random();
-    }
-  );
+    info("The tracker page should not have received events");
+    await ContentTask.spawn(trackerBrowser, null, async _ => {
+        is(content.localStorage.foo, undefined, "Undefined value!");
+        content.localStorage.foo = "normal-" + Math.random();
+      }
+    );
 
-  info("Let's see if non-tracker page has received events");
-  await ContentTask.spawn(normalBrowser, null, async _ => {
-      let ifr = content.document.getElementById("ifr");
+    info("Let's see if non-tracker page has received events");
+    await ContentTask.spawn(normalBrowser, null, async _ => {
+        let ifr = content.document.getElementById("ifr");
 
-      info("Getting the value...");
-      let value = await new Promise(resolve => {
-        content.addEventListener("message", e => {
-          resolve(e.data);
-        }, {once: true});
-        ifr.contentWindow.postMessage("getValue", "*");
-      });
+        info("Getting the value...");
+        let value = await new Promise(resolve => {
+          content.addEventListener("message", e => {
+            resolve(e.data);
+          }, {once: true});
+          ifr.contentWindow.postMessage("getValue", "*");
+        });
 
-      ok(value.startsWith("tracker-"), "The value is correctly set by the tracker");
+        ok(value.startsWith("tracker-"), "The value is correctly set by the tracker");
 
-      info("Getting the events...");
-      let events = await new Promise(resolve => {
-        content.addEventListener("message", e => {
-          resolve(e.data);
-        }, {once: true});
-        ifr.contentWindow.postMessage("getEvents", "*");
-      });
+        info("Getting the events...");
+        let events = await new Promise(resolve => {
+          content.addEventListener("message", e => {
+            resolve(e.data);
+          }, {once: true});
+          ifr.contentWindow.postMessage("getEvents", "*");
+        });
 
-      is(events, 0, "No events");
-    }
-  );
+        is(events, 0, "No events");
+      }
+    );
 
-  BrowserTestUtils.removeTab(trackerTab);
-  BrowserTestUtils.removeTab(normalTab);
-});
+    BrowserTestUtils.removeTab(trackerTab);
+    BrowserTestUtils.removeTab(normalTab);
+  });
 
-// Two ePartitionOrDeny iframes in the same tab in the same origin don"t see
-// the same localStorage values and no storage events are received from each
-// other.
-add_task(async _ => {
-  await SpecialPowers.pushPrefEnv({"set": [
-    ["dom.ipc.processCount", 1],
-    ["network.cookie.cookieBehavior", Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER],
-    ["privacy.trackingprotection.enabled", false],
-    ["privacy.trackingprotection.pbmode.enabled", false],
-    ["privacy.trackingprotection.annotate_channels", true],
-    ["privacy.restrict3rdpartystorage.partitionedHosts", "tracking.example.org,tracking.example.com"],
-  ]});
+  // Two ePartitionOrDeny iframes in the same tab in the same origin don't see
+  // the same localStorage values and no storage events are received from each
+  // other.
+  add_task(async _ => {
+    await SpecialPowers.pushPrefEnv({"set": [
+      ["dom.ipc.processCount", 1],
+      ["network.cookie.cookieBehavior", Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER],
+      ["privacy.trackingprotection.enabled", false],
+      ["privacy.trackingprotection.pbmode.enabled", false],
+      ["privacy.trackingprotection.annotate_channels", true],
+      ["privacy.restrict3rdpartystorage.partitionedHosts", "tracking.example.org,tracking.example.com"],
+      ["privacy.storagePrincipal.enabledForTrackers", withStoragePrincipalEnabled],
+    ]});
 
-  await UrlClassifierTestUtils.addTestTrackers();
+    await UrlClassifierTestUtils.addTestTrackers();
 
-  info("Creating a non-tracker top-level context");
-  let normalTab = BrowserTestUtils.addTab(gBrowser, TEST_DOMAIN + TEST_PATH + "page.html");
-  let normalBrowser = gBrowser.getBrowserForTab(normalTab);
-  await BrowserTestUtils.browserLoaded(normalBrowser);
+    info("Creating a non-tracker top-level context");
+    let normalTab = BrowserTestUtils.addTab(gBrowser, TEST_DOMAIN + TEST_PATH + "page.html");
+    let normalBrowser = gBrowser.getBrowserForTab(normalTab);
+    await BrowserTestUtils.browserLoaded(normalBrowser);
 
-  info("The non-tracker page opens a tracker iframe");
-  await ContentTask.spawn(normalBrowser, {
-      page: TEST_3RD_PARTY_DOMAIN + TEST_PATH + "localStorageEvents.html",
-    }, async obj => {
-      let ifr1 = content.document.createElement("iframe");
-      ifr1.setAttribute("id", "ifr1");
-      ifr1.setAttribute("src", obj.page);
+    info("The non-tracker page opens a tracker iframe");
+    await ContentTask.spawn(normalBrowser, {
+        page: TEST_3RD_PARTY_DOMAIN + TEST_PATH + "localStorageEvents.html",
+        withStoragePrincipalEnabled,
+      }, async obj => {
+        let ifr1 = content.document.createElement("iframe");
+        ifr1.setAttribute("id", "ifr1");
+        ifr1.setAttribute("src", obj.page);
 
-      info("Iframe 1 loading...");
-      await new content.Promise(resolve => {
-        ifr1.onload = resolve;
-        content.document.body.appendChild(ifr1);
-      });
+        info("Iframe 1 loading...");
+        await new content.Promise(resolve => {
+          ifr1.onload = resolve;
+          content.document.body.appendChild(ifr1);
+        });
 
-      let ifr2 = content.document.createElement("iframe");
-      ifr2.setAttribute("id", "ifr2");
-      ifr2.setAttribute("src", obj.page);
+        let ifr2 = content.document.createElement("iframe");
+        ifr2.setAttribute("id", "ifr2");
+        ifr2.setAttribute("src", obj.page);
 
-      info("Iframe 2 loading...");
-      await new content.Promise(resolve => {
-        ifr2.onload = resolve;
-        content.document.body.appendChild(ifr2);
-      });
-
-      info("Setting localStorage value in ifr1...");
-      ifr1.contentWindow.postMessage("setValue", "*");
+        info("Iframe 2 loading...");
+        await new content.Promise(resolve => {
+          ifr2.onload = resolve;
+          content.document.body.appendChild(ifr2);
+        });
 
-      info("Getting the value from ifr1...");
-      let value = await new Promise(resolve => {
-        content.addEventListener("message", e => {
-          resolve(e.data);
-        }, {once: true});
-        ifr1.contentWindow.postMessage("getValue", "*");
-      });
+        info("Setting localStorage value in ifr1...");
+        ifr1.contentWindow.postMessage("setValue", "*");
 
-      ok(value.startsWith("tracker-"), "The value is correctly set in ifr1");
+        info("Getting the value from ifr1...");
+        let value = await new Promise(resolve => {
+          content.addEventListener("message", e => {
+            resolve(e.data);
+          }, {once: true});
+          ifr1.contentWindow.postMessage("getValue", "*");
+        });
 
-      info("Getting the value from ifr2...");
-      value = await new Promise(resolve => {
-        content.addEventListener("message", e => {
-          resolve(e.data);
-        }, {once: true});
-        ifr2.contentWindow.postMessage("getValue", "*");
-      });
+        ok(value.startsWith("tracker-"), "The value is correctly set in ifr1");
 
-      is(value, null, "The value is nt set in ifr2");
+        info("Getting the value from ifr2...");
+        value = await new Promise(resolve => {
+          content.addEventListener("message", e => {
+            resolve(e.data);
+          }, {once: true});
+          ifr2.contentWindow.postMessage("getValue", "*");
+        });
 
-      info("Getting the events received by ifr2...");
-      let events = await new Promise(resolve => {
-        content.addEventListener("message", e => {
-          resolve(e.data);
-        }, {once: true});
-        ifr2.contentWindow.postMessage("getEvents", "*");
-      });
-
-      is(events, 0, "No events");
-    }
-  );
+        if (obj.withStoragePrincipalEnabled) {
+          ok(value.startsWith("tracker-"), "The value is correctly set in ifr2");
+        } else {
+          is(value, null, "The value is not set in ifr2");
+        }
 
-  BrowserTestUtils.removeTab(normalTab);
-});
+        info("Getting the events received by ifr2...");
+        let events = await new Promise(resolve => {
+          content.addEventListener("message", e => {
+            resolve(e.data);
+          }, {once: true});
+          ifr2.contentWindow.postMessage("getEvents", "*");
+        });
 
-// Same as the previous test but with a cookie behavior of BEHAVIOR_ACCEPT
-// instead of BEHAVIOR_REJECT_TRACKER so the iframes get real, persistent
-// localStorage instead of partitioned localStorage.
-add_task(async _ => {
-  await SpecialPowers.pushPrefEnv({"set": [
-    ["dom.ipc.processCount", 1],
-    ["network.cookie.cookieBehavior", Ci.nsICookieService.BEHAVIOR_ACCEPT],
-    ["privacy.trackingprotection.enabled", false],
-    ["privacy.trackingprotection.pbmode.enabled", false],
-    ["privacy.trackingprotection.annotate_channels", true],
-    ["privacy.restrict3rdpartystorage.partitionedHosts", "tracking.example.org,tracking.example.com"],
-  ]});
+        if (obj.withStoragePrincipalEnabled) {
+          is(events, 1, "1 event received");
+        } else {
+          is(events, 0, "No events");
+        }
+      }
+    );
 
-  await UrlClassifierTestUtils.addTestTrackers();
+    BrowserTestUtils.removeTab(normalTab);
+  });
 
-  info("Creating a non-tracker top-level context");
-  let normalTab = BrowserTestUtils.addTab(gBrowser, TEST_DOMAIN + TEST_PATH + "page.html");
-  let normalBrowser = gBrowser.getBrowserForTab(normalTab);
-  await BrowserTestUtils.browserLoaded(normalBrowser);
+  // Same as the previous test but with a cookie behavior of BEHAVIOR_ACCEPT
+  // instead of BEHAVIOR_REJECT_TRACKER so the iframes get real, persistent
+  // localStorage instead of partitioned localStorage.
+  add_task(async _ => {
+    await SpecialPowers.pushPrefEnv({"set": [
+      ["dom.ipc.processCount", 1],
+      ["network.cookie.cookieBehavior", Ci.nsICookieService.BEHAVIOR_ACCEPT],
+      ["privacy.trackingprotection.enabled", false],
+      ["privacy.trackingprotection.pbmode.enabled", false],
+      ["privacy.trackingprotection.annotate_channels", true],
+      ["privacy.restrict3rdpartystorage.partitionedHosts", "tracking.example.org,tracking.example.com"],
+      ["privacy.storagePrincipal.enabledForTrackers", withStoragePrincipalEnabled],
+    ]});
 
-  info("The non-tracker page opens a tracker iframe");
-  await ContentTask.spawn(normalBrowser, {
-      page: TEST_3RD_PARTY_DOMAIN + TEST_PATH + "localStorageEvents.html",
-    }, async obj => {
-      let ifr1 = content.document.createElement("iframe");
-      ifr1.setAttribute("id", "ifr1");
-      ifr1.setAttribute("src", obj.page);
+    await UrlClassifierTestUtils.addTestTrackers();
 
-      info("Iframe 1 loading...");
-      await new content.Promise(resolve => {
-        ifr1.onload = resolve;
-        content.document.body.appendChild(ifr1);
-      });
+    info("Creating a non-tracker top-level context");
+    let normalTab = BrowserTestUtils.addTab(gBrowser, TEST_DOMAIN + TEST_PATH + "page.html");
+    let normalBrowser = gBrowser.getBrowserForTab(normalTab);
+    await BrowserTestUtils.browserLoaded(normalBrowser);
 
-      let ifr2 = content.document.createElement("iframe");
-      ifr2.setAttribute("id", "ifr2");
-      ifr2.setAttribute("src", obj.page);
+    info("The non-tracker page opens a tracker iframe");
+    await ContentTask.spawn(normalBrowser, {
+        page: TEST_3RD_PARTY_DOMAIN + TEST_PATH + "localStorageEvents.html",
+      }, async obj => {
+        let ifr1 = content.document.createElement("iframe");
+        ifr1.setAttribute("id", "ifr1");
+        ifr1.setAttribute("src", obj.page);
 
-      info("Iframe 2 loading...");
-      await new content.Promise(resolve => {
-        ifr2.onload = resolve;
-        content.document.body.appendChild(ifr2);
-      });
+        info("Iframe 1 loading...");
+        await new content.Promise(resolve => {
+          ifr1.onload = resolve;
+          content.document.body.appendChild(ifr1);
+        });
 
-      info("Setting localStorage value in ifr1...");
-      ifr1.contentWindow.postMessage("setValue", "*");
+        let ifr2 = content.document.createElement("iframe");
+        ifr2.setAttribute("id", "ifr2");
+        ifr2.setAttribute("src", obj.page);
 
-      info("Getting the value from ifr1...");
-      let value1 = await new Promise(resolve => {
-        content.addEventListener("message", e => {
-          resolve(e.data);
-        }, {once: true});
-        ifr1.contentWindow.postMessage("getValue", "*");
-      });
+        info("Iframe 2 loading...");
+        await new content.Promise(resolve => {
+          ifr2.onload = resolve;
+          content.document.body.appendChild(ifr2);
+        });
+
+        info("Setting localStorage value in ifr1...");
+        ifr1.contentWindow.postMessage("setValue", "*");
 
-      ok(value1.startsWith("tracker-"), "The value is correctly set in ifr1");
+        info("Getting the value from ifr1...");
+        let value1 = await new Promise(resolve => {
+          content.addEventListener("message", e => {
+            resolve(e.data);
+          }, {once: true});
+          ifr1.contentWindow.postMessage("getValue", "*");
+        });
+
+        ok(value1.startsWith("tracker-"), "The value is correctly set in ifr1");
 
-      info("Getting the value from ifr2...");
-      let value2 = await new Promise(resolve => {
-        content.addEventListener("message", e => {
-          resolve(e.data);
-        }, {once: true});
-        ifr2.contentWindow.postMessage("getValue", "*");
-      });
+        info("Getting the value from ifr2...");
+        let value2 = await new Promise(resolve => {
+          content.addEventListener("message", e => {
+            resolve(e.data);
+          }, {once: true});
+          ifr2.contentWindow.postMessage("getValue", "*");
+        });
 
-      is(value2, value1, "The values match");
+        is(value2, value1, "The values match");
 
-      info("Getting the events received by ifr2...");
-      let events = await new Promise(resolve => {
-        content.addEventListener("message", e => {
-          resolve(e.data);
-        }, {once: true});
-        ifr2.contentWindow.postMessage("getEvents", "*");
-      });
+        info("Getting the events received by ifr2...");
+        let events = await new Promise(resolve => {
+          content.addEventListener("message", e => {
+            resolve(e.data);
+          }, {once: true});
+          ifr2.contentWindow.postMessage("getEvents", "*");
+        });
 
-      is(events, 1, "One event");
-    }
-  );
+        is(events, 1, "One event");
+      }
+    );
 
-  BrowserTestUtils.removeTab(normalTab);
-});
+    BrowserTestUtils.removeTab(normalTab);
+  });
 
-// An ePartitionOrDeny iframe navigated between two distinct pages on the same
-// origin does not see the values stored by the previous iframe.
-add_task(async _ => {
-  await SpecialPowers.pushPrefEnv({"set": [
-    ["dom.ipc.processCount", 1],
-    ["network.cookie.cookieBehavior", Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER],
-    ["privacy.trackingprotection.enabled", false],
-    ["privacy.trackingprotection.pbmode.enabled", false],
-    ["privacy.trackingprotection.annotate_channels", true],
-    ["privacy.restrict3rdpartystorage.partitionedHosts", "tracking.example.org,tracking.example.com"],
-  ]});
+  // An ePartitionOrDeny iframe navigated between two distinct pages on the same
+  // origin does not see the values stored by the previous iframe.
+  add_task(async _ => {
+    await SpecialPowers.pushPrefEnv({"set": [
+      ["dom.ipc.processCount", 1],
+      ["network.cookie.cookieBehavior", Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER],
+      ["privacy.trackingprotection.enabled", false],
+      ["privacy.trackingprotection.pbmode.enabled", false],
+      ["privacy.trackingprotection.annotate_channels", true],
+      ["privacy.restrict3rdpartystorage.partitionedHosts", "tracking.example.org,tracking.example.com"],
+      ["privacy.storagePrincipal.enabledForTrackers", withStoragePrincipalEnabled],
+    ]});
+
+    await UrlClassifierTestUtils.addTestTrackers();
 
-  await UrlClassifierTestUtils.addTestTrackers();
-
-  info("Creating a non-tracker top-level context");
-  let normalTab = BrowserTestUtils.addTab(gBrowser, TEST_DOMAIN + TEST_PATH + "page.html");
-  let normalBrowser = gBrowser.getBrowserForTab(normalTab);
-  await BrowserTestUtils.browserLoaded(normalBrowser);
+    info("Creating a non-tracker top-level context");
+    let normalTab = BrowserTestUtils.addTab(gBrowser, TEST_DOMAIN + TEST_PATH + "page.html");
+    let normalBrowser = gBrowser.getBrowserForTab(normalTab);
+    await BrowserTestUtils.browserLoaded(normalBrowser);
 
-  info("The non-tracker page opens a tracker iframe");
-  await ContentTask.spawn(normalBrowser, {
-      page: TEST_3RD_PARTY_DOMAIN + TEST_PATH + "localStorageEvents.html",
-    }, async obj => {
-      let ifr = content.document.createElement("iframe");
-      ifr.setAttribute("id", "ifr");
-      ifr.setAttribute("src", obj.page);
+    info("The non-tracker page opens a tracker iframe");
+    await ContentTask.spawn(normalBrowser, {
+        page: TEST_3RD_PARTY_DOMAIN + TEST_PATH + "localStorageEvents.html",
+        withStoragePrincipalEnabled,
+      }, async obj => {
+        let ifr = content.document.createElement("iframe");
+        ifr.setAttribute("id", "ifr");
+        ifr.setAttribute("src", obj.page);
+
+        info("Iframe loading...");
+        await new content.Promise(resolve => {
+          ifr.onload = resolve;
+          content.document.body.appendChild(ifr);
+        });
 
-      info("Iframe loading...");
-      await new content.Promise(resolve => {
-        ifr.onload = resolve;
-        content.document.body.appendChild(ifr);
-      });
+        info("Setting localStorage value in ifr...");
+        ifr.contentWindow.postMessage("setValue", "*");
 
-      info("Setting localStorage value in ifr...");
-      ifr.contentWindow.postMessage("setValue", "*");
+        info("Getting the value from ifr...");
+        let value = await new Promise(resolve => {
+          content.addEventListener("message", e => {
+            resolve(e.data);
+          }, {once: true});
+          ifr.contentWindow.postMessage("getValue", "*");
+        });
 
-      info("Getting the value from ifr...");
-      let value = await new Promise(resolve => {
-        content.addEventListener("message", e => {
-          resolve(e.data);
-        }, {once: true});
-        ifr.contentWindow.postMessage("getValue", "*");
-      });
+        ok(value.startsWith("tracker-"), "The value is correctly set in ifr");
 
-      ok(value.startsWith("tracker-"), "The value is correctly set in ifr");
-
-      info("Navigate...");
-      await new content.Promise(resolve => {
-        ifr.onload = resolve;
-        ifr.setAttribute("src", obj.page + "?" + Math.random());
-      });
+        info("Navigate...");
+        await new content.Promise(resolve => {
+          ifr.onload = resolve;
+          ifr.setAttribute("src", obj.page + "?" + Math.random());
+        });
 
-      info("Getting the value from ifr...");
-      value = await new Promise(resolve => {
-        content.addEventListener("message", e => {
-          resolve(e.data);
-        }, {once: true});
-        ifr.contentWindow.postMessage("getValue", "*");
-      });
+        info("Getting the value from ifr...");
+        let value2 = await new Promise(resolve => {
+          content.addEventListener("message", e => {
+            resolve(e.data);
+          }, {once: true});
+          ifr.contentWindow.postMessage("getValue", "*");
+        });
 
-      is(value, null, "The value is undefined");
-    }
-  );
+        if (obj.withStoragePrincipalEnabled) {
+          is(value, value2, "The value is received");
+        } else {
+          is(value2, null, "The value is undefined");
+        }
+      }
+    );
 
-  BrowserTestUtils.removeTab(normalTab);
-});
+    BrowserTestUtils.removeTab(normalTab);
+  });
 
-// Like the previous test, but accepting trackers
-add_task(async _ => {
-  await SpecialPowers.pushPrefEnv({"set": [
-    ["dom.ipc.processCount", 1],
-    ["network.cookie.cookieBehavior", Ci.nsICookieService.BEHAVIOR_ACCEPT],
-    ["privacy.trackingprotection.enabled", false],
-    ["privacy.trackingprotection.pbmode.enabled", false],
-    ["privacy.trackingprotection.annotate_channels", true],
-    ["privacy.restrict3rdpartystorage.partitionedHosts", "tracking.example.org,tracking.example.com"],
-  ]});
+  // Like the previous test, but accepting trackers
+  add_task(async _ => {
+    await SpecialPowers.pushPrefEnv({"set": [
+      ["dom.ipc.processCount", 1],
+      ["network.cookie.cookieBehavior", Ci.nsICookieService.BEHAVIOR_ACCEPT],
+      ["privacy.trackingprotection.enabled", false],
+      ["privacy.trackingprotection.pbmode.enabled", false],
+      ["privacy.trackingprotection.annotate_channels", true],
+      ["privacy.restrict3rdpartystorage.partitionedHosts", "tracking.example.org,tracking.example.com"],
+      ["privacy.storagePrincipal.enabledForTrackers", withStoragePrincipalEnabled],
+    ]});
 
-  await UrlClassifierTestUtils.addTestTrackers();
+    await UrlClassifierTestUtils.addTestTrackers();
 
-  info("Creating a non-tracker top-level context");
-  let normalTab = BrowserTestUtils.addTab(gBrowser, TEST_DOMAIN + TEST_PATH + "page.html");
-  let normalBrowser = gBrowser.getBrowserForTab(normalTab);
-  await BrowserTestUtils.browserLoaded(normalBrowser);
+    info("Creating a non-tracker top-level context");
+    let normalTab = BrowserTestUtils.addTab(gBrowser, TEST_DOMAIN + TEST_PATH + "page.html");
+    let normalBrowser = gBrowser.getBrowserForTab(normalTab);
+    await BrowserTestUtils.browserLoaded(normalBrowser);
 
-  info("The non-tracker page opens a tracker iframe");
-  await ContentTask.spawn(normalBrowser, {
-      page: TEST_3RD_PARTY_DOMAIN + TEST_PATH + "localStorageEvents.html",
-    }, async obj => {
-      let ifr = content.document.createElement("iframe");
-      ifr.setAttribute("id", "ifr");
-      ifr.setAttribute("src", obj.page);
+    info("The non-tracker page opens a tracker iframe");
+    await ContentTask.spawn(normalBrowser, {
+        page: TEST_3RD_PARTY_DOMAIN + TEST_PATH + "localStorageEvents.html",
+      }, async obj => {
+        let ifr = content.document.createElement("iframe");
+        ifr.setAttribute("id", "ifr");
+        ifr.setAttribute("src", obj.page);
 
-      info("Iframe loading...");
-      await new content.Promise(resolve => {
-        ifr.onload = resolve;
-        content.document.body.appendChild(ifr);
-      });
+        info("Iframe loading...");
+        await new content.Promise(resolve => {
+          ifr.onload = resolve;
+          content.document.body.appendChild(ifr);
+        });
 
-      info("Setting localStorage value in ifr...");
-      ifr.contentWindow.postMessage("setValue", "*");
+        info("Setting localStorage value in ifr...");
+        ifr.contentWindow.postMessage("setValue", "*");
 
-      info("Getting the value from ifr...");
-      let value = await new Promise(resolve => {
-        content.addEventListener("message", e => {
-          resolve(e.data);
-        }, {once: true});
-        ifr.contentWindow.postMessage("getValue", "*");
-      });
+        info("Getting the value from ifr...");
+        let value = await new Promise(resolve => {
+          content.addEventListener("message", e => {
+            resolve(e.data);
+          }, {once: true});
+          ifr.contentWindow.postMessage("getValue", "*");
+        });
 
-      ok(value.startsWith("tracker-"), "The value is correctly set in ifr");
+        ok(value.startsWith("tracker-"), "The value is correctly set in ifr");
 
-      info("Navigate...");
-      await new content.Promise(resolve => {
-        ifr.onload = resolve;
-        ifr.setAttribute("src", obj.page + "?" + Math.random());
-      });
+        info("Navigate...");
+        await new content.Promise(resolve => {
+          ifr.onload = resolve;
+          ifr.setAttribute("src", obj.page + "?" + Math.random());
+        });
 
-      info("Getting the value from ifr...");
-      let value2 = await new Promise(resolve => {
-        content.addEventListener("message", e => {
-          resolve(e.data);
-        }, {once: true});
-        ifr.contentWindow.postMessage("getValue", "*");
-      });
+        info("Getting the value from ifr...");
+        let value2 = await new Promise(resolve => {
+          content.addEventListener("message", e => {
+            resolve(e.data);
+          }, {once: true});
+          ifr.contentWindow.postMessage("getValue", "*");
+        });
 
-      is(value, value2, "The value is undefined");
-    }
-  );
+        is(value, value2, "The value is received");
+      }
+    );
 
-  BrowserTestUtils.removeTab(normalTab);
-});
+    BrowserTestUtils.removeTab(normalTab);
+  });
 
-// An ePartitionOrDeny iframe on the same origin that is navigated to itself
-// via window.location.reload() or equivalent does not see the values stored by
-// its previous self.
-add_task(async _ => {
-  await SpecialPowers.pushPrefEnv({"set": [
-    ["dom.ipc.processCount", 1],
-    ["network.cookie.cookieBehavior", Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER],
-    ["privacy.trackingprotection.enabled", false],
-    ["privacy.trackingprotection.pbmode.enabled", false],
-    ["privacy.trackingprotection.annotate_channels", true],
-    ["privacy.restrict3rdpartystorage.partitionedHosts", "tracking.example.org,tracking.example.com"],
-  ]});
+  // An ePartitionOrDeny iframe on the same origin that is navigated to itself
+  // via window.location.reload() or equivalent does not see the values stored
+  // by its previous self.
+  add_task(async _ => {
+    await SpecialPowers.pushPrefEnv({"set": [
+      ["dom.ipc.processCount", 1],
+      ["network.cookie.cookieBehavior", Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER],
+      ["privacy.trackingprotection.enabled", false],
+      ["privacy.trackingprotection.pbmode.enabled", false],
+      ["privacy.trackingprotection.annotate_channels", true],
+      ["privacy.restrict3rdpartystorage.partitionedHosts", "tracking.example.org,tracking.example.com"],
+      ["privacy.storagePrincipal.enabledForTrackers", withStoragePrincipalEnabled],
+    ]});
 
-  await UrlClassifierTestUtils.addTestTrackers();
+    await UrlClassifierTestUtils.addTestTrackers();
 
-  info("Creating a non-tracker top-level context");
-  let normalTab = BrowserTestUtils.addTab(gBrowser, TEST_DOMAIN + TEST_PATH + "page.html");
-  let normalBrowser = gBrowser.getBrowserForTab(normalTab);
-  await BrowserTestUtils.browserLoaded(normalBrowser);
+    info("Creating a non-tracker top-level context");
+    let normalTab = BrowserTestUtils.addTab(gBrowser, TEST_DOMAIN + TEST_PATH + "page.html");
+    let normalBrowser = gBrowser.getBrowserForTab(normalTab);
+    await BrowserTestUtils.browserLoaded(normalBrowser);
 
-  info("The non-tracker page opens a tracker iframe");
-  await ContentTask.spawn(normalBrowser, {
-      page: TEST_3RD_PARTY_DOMAIN + TEST_PATH + "localStorageEvents.html",
-    }, async obj => {
-      let ifr = content.document.createElement("iframe");
-      ifr.setAttribute("id", "ifr");
-      ifr.setAttribute("src", obj.page);
+    info("The non-tracker page opens a tracker iframe");
+    await ContentTask.spawn(normalBrowser, {
+        page: TEST_3RD_PARTY_DOMAIN + TEST_PATH + "localStorageEvents.html",
+        withStoragePrincipalEnabled,
+      }, async obj => {
+        let ifr = content.document.createElement("iframe");
+        ifr.setAttribute("id", "ifr");
+        ifr.setAttribute("src", obj.page);
 
-      info("Iframe loading...");
-      await new content.Promise(resolve => {
-        ifr.onload = resolve;
-        content.document.body.appendChild(ifr);
-      });
+        info("Iframe loading...");
+        await new content.Promise(resolve => {
+          ifr.onload = resolve;
+          content.document.body.appendChild(ifr);
+        });
 
-      info("Setting localStorage value in ifr...");
-      ifr.contentWindow.postMessage("setValue", "*");
+        info("Setting localStorage value in ifr...");
+        ifr.contentWindow.postMessage("setValue", "*");
 
-      info("Getting the value from ifr...");
-      let value = await new Promise(resolve => {
-        content.addEventListener("message", e => {
-          resolve(e.data);
-        }, {once: true});
-        ifr.contentWindow.postMessage("getValue", "*");
-      });
+        info("Getting the value from ifr...");
+        let value = await new Promise(resolve => {
+          content.addEventListener("message", e => {
+            resolve(e.data);
+          }, {once: true});
+          ifr.contentWindow.postMessage("getValue", "*");
+        });
 
-      ok(value.startsWith("tracker-"), "The value is correctly set in ifr");
+        ok(value.startsWith("tracker-"), "The value is correctly set in ifr");
+
+        info("Reload...");
+        await new content.Promise(resolve => {
+          ifr.onload = resolve;
+          ifr.contentWindow.postMessage("reload", "*");
+        });
 
-      info("Reload...");
-      await new content.Promise(resolve => {
-        ifr.onload = resolve;
-        ifr.contentWindow.postMessage("reload", "*");
-      });
+        info("Getting the value from ifr...");
+        let value2 = await new Promise(resolve => {
+          content.addEventListener("message", e => {
+            resolve(e.data);
+          }, {once: true});
+          ifr.contentWindow.postMessage("getValue", "*");
+        });
 
-      info("Getting the value from ifr...");
-      value = await new Promise(resolve => {
-        content.addEventListener("message", e => {
-          resolve(e.data);
-        }, {once: true});
-        ifr.contentWindow.postMessage("getValue", "*");
-      });
+        if (obj.withStoragePrincipalEnabled) {
+          is(value, value2, "The value is equal");
+        } else {
+          is(value2, null, "The value is undefined");
+        }
+      }
+    );
 
-      is(value, null, "The value is undefined");
-    }
-  );
-
-  BrowserTestUtils.removeTab(normalTab);
-});
+    BrowserTestUtils.removeTab(normalTab);
+  });
 
-// Like the previous test, but accepting trackers
-add_task(async _ => {
-  await SpecialPowers.pushPrefEnv({"set": [
-    ["dom.ipc.processCount", 1],
-    ["network.cookie.cookieBehavior", Ci.nsICookieService.BEHAVIOR_ACCEPT],
-    ["privacy.trackingprotection.enabled", false],
-    ["privacy.trackingprotection.pbmode.enabled", false],
-    ["privacy.trackingprotection.annotate_channels", true],
-    ["privacy.restrict3rdpartystorage.partitionedHosts", "tracking.example.org,tracking.example.com"],
-  ]});
+  // Like the previous test, but accepting trackers
+  add_task(async _ => {
+    await SpecialPowers.pushPrefEnv({"set": [
+      ["dom.ipc.processCount", 1],
+      ["network.cookie.cookieBehavior", Ci.nsICookieService.BEHAVIOR_ACCEPT],
+      ["privacy.trackingprotection.enabled", false],
+      ["privacy.trackingprotection.pbmode.enabled", false],
+      ["privacy.trackingprotection.annotate_channels", true],
+      ["privacy.restrict3rdpartystorage.partitionedHosts", "tracking.example.org,tracking.example.com"],
+      ["privacy.storagePrincipal.enabledForTrackers", withStoragePrincipalEnabled],
+    ]});
 
-  await UrlClassifierTestUtils.addTestTrackers();
+    await UrlClassifierTestUtils.addTestTrackers();
 
-  info("Creating a non-tracker top-level context");
-  let normalTab = BrowserTestUtils.addTab(gBrowser, TEST_DOMAIN + TEST_PATH + "page.html");
-  let normalBrowser = gBrowser.getBrowserForTab(normalTab);
-  await BrowserTestUtils.browserLoaded(normalBrowser);
+    info("Creating a non-tracker top-level context");
+    let normalTab = BrowserTestUtils.addTab(gBrowser, TEST_DOMAIN + TEST_PATH + "page.html");
+    let normalBrowser = gBrowser.getBrowserForTab(normalTab);
+    await BrowserTestUtils.browserLoaded(normalBrowser);
 
-  info("The non-tracker page opens a tracker iframe");
-  await ContentTask.spawn(normalBrowser, {
-      page: TEST_3RD_PARTY_DOMAIN + TEST_PATH + "localStorageEvents.html",
-    }, async obj => {
-      let ifr = content.document.createElement("iframe");
-      ifr.setAttribute("id", "ifr");
-      ifr.setAttribute("src", obj.page);
+    info("The non-tracker page opens a tracker iframe");
+    await ContentTask.spawn(normalBrowser, {
+        page: TEST_3RD_PARTY_DOMAIN + TEST_PATH + "localStorageEvents.html",
+      }, async obj => {
+        let ifr = content.document.createElement("iframe");
+        ifr.setAttribute("id", "ifr");
+        ifr.setAttribute("src", obj.page);
 
-      info("Iframe loading...");
-      await new content.Promise(resolve => {
-        ifr.onload = resolve;
-        content.document.body.appendChild(ifr);
-      });
+        info("Iframe loading...");
+        await new content.Promise(resolve => {
+          ifr.onload = resolve;
+          content.document.body.appendChild(ifr);
+        });
 
-      info("Setting localStorage value in ifr...");
-      ifr.contentWindow.postMessage("setValue", "*");
+        info("Setting localStorage value in ifr...");
+        ifr.contentWindow.postMessage("setValue", "*");
 
-      info("Getting the value from ifr...");
-      let value = await new Promise(resolve => {
-        content.addEventListener("message", e => {
-          resolve(e.data);
-        }, {once: true});
-        ifr.contentWindow.postMessage("getValue", "*");
-      });
+        info("Getting the value from ifr...");
+        let value = await new Promise(resolve => {
+          content.addEventListener("message", e => {
+            resolve(e.data);
+          }, {once: true});
+          ifr.contentWindow.postMessage("getValue", "*");
+        });
 
-      ok(value.startsWith("tracker-"), "The value is correctly set in ifr");
+        ok(value.startsWith("tracker-"), "The value is correctly set in ifr");
 
-      info("Reload...");
-      await new content.Promise(resolve => {
-        ifr.onload = resolve;
-        ifr.contentWindow.postMessage("reload", "*");
-      });
+        info("Reload...");
+        await new content.Promise(resolve => {
+          ifr.onload = resolve;
+          ifr.contentWindow.postMessage("reload", "*");
+        });
 
-      info("Getting the value from ifr...");
-      let value2 = await new Promise(resolve => {
-        content.addEventListener("message", e => {
-          resolve(e.data);
-        }, {once: true});
-        ifr.contentWindow.postMessage("getValue", "*");
-      });
+        info("Getting the value from ifr...");
+        let value2 = await new Promise(resolve => {
+          content.addEventListener("message", e => {
+            resolve(e.data);
+          }, {once: true});
+          ifr.contentWindow.postMessage("getValue", "*");
+        });
+
+        is(value, value2, "The value is equal");
+      }
+    );
 
-      is(value, value2, "The value is undefined");
-    }
-  );
-
-  BrowserTestUtils.removeTab(normalTab);
-});
+    BrowserTestUtils.removeTab(normalTab);
+  });
 
-// Cleanup data.
-add_task(async _ => {
-  await new Promise(resolve => {
-    Services.clearData.deleteData(Ci.nsIClearDataService.CLEAR_ALL, value => resolve());
+  // Cleanup data.
+  add_task(async _ => {
+    await new Promise(resolve => {
+      Services.clearData.deleteData(Ci.nsIClearDataService.CLEAR_ALL, value => resolve());
+    });
   });
-});
+}
+
+runAllTests(false, true);
+runAllTests(false, false);
+runAllTests(true, true);
+runAllTests(true, false);
--- a/toolkit/components/extensions/Extension.jsm
+++ b/toolkit/components/extensions/Extension.jsm
@@ -267,17 +267,17 @@ var UninstallObserver = {
       Services.qms.clearStoragesForPrincipal(storagePrincipal);
 
       ExtensionStorageIDB.clearMigratedExtensionPref(addon.id);
 
       // If LSNG is not enabled, we need to clear localStorage explicitly using
       // the old API.
       if (!Services.lsm.nextGenLocalStorageEnabled) {
         // Clear localStorage created by the extension
-        let storage = Services.domStorageManager.getStorage(null, principal);
+        let storage = Services.domStorageManager.getStorage(null, principal, storagePrincipal);
         if (storage) {
           storage.clear();
         }
       }
 
       // Remove any permissions related to the unlimitedStorage permission
       // if we are also removing all the data stored by the extension.
       Services.perms.removeFromPrincipal(principal, "WebExtensions-unlimitedStorage");
--- a/toolkit/components/sessionstore/SessionStoreUtils.cpp
+++ b/toolkit/components/sessionstore/SessionStoreUtils.cpp
@@ -961,36 +961,42 @@ static void ReadAllEntriesFromStorage(
   if (!docShell) {
     return;
   }
 
   Document* doc = aWindow->GetDoc();
   if (!doc) {
     return;
   }
+
   nsCOMPtr<nsIPrincipal> principal = doc->NodePrincipal();
   if (!principal) {
     return;
   }
 
+  nsCOMPtr<nsIPrincipal> storagePrincipal = doc->EffectiveStoragePrincipal();
+  if (!storagePrincipal) {
+    return;
+  }
+
   nsAutoCString origin;
   nsresult rv = principal->GetOrigin(origin);
   if (NS_FAILED(rv) || aVisitedOrigins.Contains(origin)) {
     // Don't read a host twice.
     return;
   }
 
   /* Completed checking for recursion and is about to read storage*/
   nsCOMPtr<nsIDOMStorageManager> storageManager = do_QueryInterface(docShell);
   if (!storageManager) {
     return;
   }
   RefPtr<Storage> storage;
-  storageManager->GetStorage(aWindow->GetCurrentInnerWindow(), principal, false,
-                             getter_AddRefs(storage));
+  storageManager->GetStorage(aWindow->GetCurrentInnerWindow(), principal,
+                             storagePrincipal, false, getter_AddRefs(storage));
   if (!storage) {
     return;
   }
   mozilla::IgnoredErrorResult result;
   uint32_t len = storage->GetLength(*principal, result);
   if (result.Failed() || len == 0) {
     return;
   }
@@ -1108,18 +1114,18 @@ void SessionStoreUtils::RestoreSessionSt
     }
     RefPtr<Storage> storage;
     // There is no need to pass documentURI, it's only used to fill documentURI
     // property of domstorage event, which in this case has no consumer.
     // Prevention of events in case of missing documentURI will be solved in a
     // followup bug to bug 600307.
     // Null window because the current window doesn't match the principal yet
     // and loads about:blank.
-    storageManager->CreateStorage(nullptr, principal, EmptyString(), false,
-                                  getter_AddRefs(storage));
+    storageManager->CreateStorage(nullptr, principal, principal, EmptyString(),
+                                  false, getter_AddRefs(storage));
     if (!storage) {
       continue;
     }
     for (auto& InnerEntry : entry.mValue.Entries()) {
       IgnoredErrorResult result;
       storage->SetItem(InnerEntry.mKey, InnerEntry.mValue, *principal, result);
       if (result.Failed()) {
         NS_WARNING("storage set item failed!");
--- a/toolkit/components/windowwatcher/nsWindowWatcher.cpp
+++ b/toolkit/components/windowwatcher/nsWindowWatcher.cpp
@@ -1179,19 +1179,19 @@ nsresult nsWindowWatcher::OpenWindowInte
         do_QueryInterface(parentDocShell);
     nsCOMPtr<nsIDOMStorageManager> newStorageManager =
         do_QueryInterface(newDocShell);
 
     if (parentStorageManager && newStorageManager) {
       RefPtr<Storage> storage;
       nsCOMPtr<nsPIDOMWindowInner> pInnerWin =
           parentWindow->GetCurrentInnerWindow();
-      parentStorageManager->GetStorage(pInnerWin, subjectPrincipal,
-                                       isPrivateBrowsingWindow,
-                                       getter_AddRefs(storage));
+      parentStorageManager->GetStorage(
+          pInnerWin, subjectPrincipal, subjectPrincipal,
+          isPrivateBrowsingWindow, getter_AddRefs(storage));
       if (storage) {
         newStorageManager->CloneStorage(storage);
       }
     }
   }
 
   if (isNewToplevelWindow) {
     nsCOMPtr<nsIDocShellTreeOwner> newTreeOwner;
--- a/toolkit/content/editMenuOverlay.js
+++ b/toolkit/content/editMenuOverlay.js
@@ -59,17 +59,17 @@ window.addEventListener("DOMContentLoade
 }, { once: true });
 
 // Support context menus on html textareas in the parent process:
 window.addEventListener("contextmenu", (e) => {
   const HTML_NS = "http://www.w3.org/1999/xhtml";
   // Note that there's not a risk of e.target being XBL anonymous content for <textbox> (which manages
   // its own context menu), because e.target will be the XBL binding parent in that case.
   let needsContextMenu = e.target.ownerDocument == document && !e.defaultPrevented && (
-    (e.target.localName == "textarea" && e.target.namespaceURI == HTML_NS)
+    (["textarea", "input"].includes(e.target.localName) && e.target.namespaceURI == HTML_NS)
     || e.target.closest("textbox[is='search-textbox']")
   );
 
   if (!needsContextMenu) {
     return;
   }
 
   let popup = document.getElementById("textbox-contextmenu");
--- a/toolkit/content/tests/chrome/file_edit_contextmenu.xul
+++ b/toolkit/content/tests/chrome/file_edit_contextmenu.xul
@@ -19,12 +19,12 @@
   <command id="cmd_copy" oncommand="goDoCommand('cmd_copy')"/>
   <command id="cmd_paste" oncommand="goDoCommand('cmd_paste')"/>
   <command id="cmd_delete" oncommand="goDoCommand('cmd_delete')"/>
   <command id="cmd_selectAll" oncommand="goDoCommand('cmd_selectAll')"/>
   <command id="cmd_switchTextDirection" oncommand="goDoCommand('cmd_switchTextDirection');"/>
 </commandset>
 
 <html:textarea />
-
+<html:input />
 <textbox is="search-textbox"/>
 
 </window>
--- a/toolkit/content/tests/chrome/test_edit_contextmenu.html
+++ b/toolkit/content/tests/chrome/test_edit_contextmenu.html
@@ -16,16 +16,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 
     async function runTest() {
       let win = window.open("file_edit_contextmenu.xul", "context-menu", "chrome,width=600,height=600");
       await new Promise(r => win.addEventListener("load", r, { once: true}));
       await SimpleTest.promiseFocus(win);
 
       const elements = [
         win.document.querySelector("textarea"),
+        win.document.querySelector("input"),
         win.document.querySelector("textbox[is='search-textbox']"),
       ];
       for (const element of elements) {
         await testElement(element, win);
       }
       SimpleTest.finish();
     }
 
--- a/toolkit/forgetaboutsite/test/unit/test_removeDataFromDomain.js
+++ b/toolkit/forgetaboutsite/test/unit/test_removeDataFromDomain.js
@@ -436,17 +436,17 @@ async function test_cache_cleared() {
   await ForgetAboutSite.removeDataFromDomain("mozilla.org");
   do_test_pending();
 }
 
 async function test_storage_cleared() {
   function getStorageForURI(aURI) {
     let principal = Services.scriptSecurityManager.createCodebasePrincipal(aURI, {});
 
-    return Services.domStorageManager.createStorage(null, principal, "");
+    return Services.domStorageManager.createStorage(null, principal, principal, "");
   }
 
   Services.prefs.setBoolPref("dom.storage.client_validation", false);
 
   let s = [
     getStorageForURI(Services.io.newURI("http://mozilla.org")),
     getStorageForURI(Services.io.newURI("http://my.mozilla.org")),
     getStorageForURI(Services.io.newURI("http://ilovemozilla.org")),
--- a/toolkit/library/rust/shared/Cargo.toml
+++ b/toolkit/library/rust/shared/Cargo.toml
@@ -54,17 +54,17 @@ gecko_debug = ["geckoservo/gecko_debug",
 simd-accel = ["encoding_c/simd-accel", "encoding_glue/simd-accel"]
 moz_memory = ["mp4parse_capi/mp4parse_fallible"]
 moz_places = ["bookmark_sync"]
 spidermonkey_rust = ["jsrust_shared"]
 cranelift_x86 = ["jsrust_shared/cranelift_x86"]
 cranelift_arm32 = ["jsrust_shared/cranelift_arm32"]
 cranelift_arm64 = ["jsrust_shared/cranelift_arm64"]
 cranelift_none = ["jsrust_shared/cranelift_none"]
-gecko_profiler = ["profiler_helper"]
+gecko_profiler = ["profiler_helper", "geckoservo/gecko_profiler"]
 gecko_profiler_parse_elf = ["profiler_helper/parse_elf"]
 new_xulstore = ["xulstore"]
 new_cert_storage = ["cert_storage"]
 
 [lib]
 path = "lib.rs"
 test = false
 doctest = false