merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Tue, 07 Mar 2017 15:13:31 +0100
changeset 396784 47e5c929a4879384d719568ca002b705b6472b7d
parent 396660 1fb56ba248d5018f8a3314f3d5ff3d9a2c044d83 (current diff)
parent 396783 38911b466afc0316f27dd834c58e13d5a7b14dbc (diff)
child 396785 4194c0ebf9db34edab3002d4d2bf2f8100869e07
child 396794 7f4f2c94cb99cc5469f387c1108a2e93bfef2a2a
child 396834 32132a071a4e8202f5d237e2aaba7c1b4972027a
push id1490
push usermtabara@mozilla.com
push dateMon, 31 Jul 2017 14:08:16 +0000
treeherdermozilla-release@70e32e6bf15e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone55.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-inbound to mozilla-central a=merge
caps/nsScriptSecurityManager.cpp
docshell/base/nsDocShell.cpp
dom/base/nsDOMWindowUtils.cpp
dom/indexedDB/test/service_worker.js
dom/indexedDB/test/service_worker_client.html
dom/indexedDB/test/test_serviceworker.html
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/PContent.ipdl
layout/style/test/mochitest.ini
modules/libpref/init/all.js
netwerk/protocol/http/HttpChannelParent.cpp
netwerk/protocol/http/nsHttpHandler.cpp
toolkit/components/extensions/moz.build
toolkit/components/extensions/test/mochitest/mochitest-common.ini
toolkit/components/extensions/test/mochitest/test_ext_contentscript.html
toolkit/components/extensions/test/mochitest/test_ext_i18n.html
toolkit/components/extensions/test/mochitest/test_ext_i18n_css.html
toolkit/components/extensions/test/xpcshell/xpcshell.ini
toolkit/components/telemetry/Histograms.json
toolkit/components/telemetry/TelemetryEnvironment.jsm
widget/windows/nsDataObj.cpp
--- a/browser/base/content/browser-ctrlTab.js
+++ b/browser/base/content/browser-ctrlTab.js
@@ -1,8 +1,9 @@
+
 /* 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/. */
 
 /**
  * Tab previews utility, produces thumbnails
  */
 var tabPreviews = {
@@ -468,21 +469,25 @@ var ctrlTab = {
   },
 
   handleEvent: function ctrlTab_handleEvent(event) {
     switch (event.type) {
       case "SSWindowRestored":
         this._initRecentlyUsedTabs();
         break;
       case "TabAttrModified":
-        // tab attribute modified (e.g. label, busy, image, selected)
-        for (let i = this.previews.length - 1; i >= 0; i--) {
-          if (this.previews[i]._tab && this.previews[i]._tab == event.target) {
-            this.updatePreview(this.previews[i], event.target);
-            break;
+        // tab attribute modified (i.e. label, busy, image, selected)
+        // update preview only if tab attribute modified in the list
+        if (event.detail.changed.some(
+          (elem, ind, arr) => ["label", "busy", "image", "selected"].includes(elem))) {
+          for (let i = this.previews.length - 1; i >= 0; i--) {
+            if (this.previews[i]._tab && this.previews[i]._tab == event.target) {
+              this.updatePreview(this.previews[i], event.target);
+              break;
+            }
           }
         }
         break;
       case "TabSelect":
         this.detachTab(event.target);
         this.attachTab(event.target, 0);
         break;
       case "TabOpen":
--- a/browser/components/sessionstore/SessionStorage.jsm
+++ b/browser/components/sessionstore/SessionStorage.jsm
@@ -69,17 +69,24 @@ var SessionStorageInternal = {
     frameTree.forEach(frame => {
       let principal = getPrincipalForFrame(docShell, frame);
       if (!principal) {
         return;
       }
 
       // Get the origin of the current history entry
       // and use that as a key for the per-principal storage data.
-      let origin = principal.origin;
+      let origin;
+      try {
+        // The origin getter may throw for about:blank iframes as of bug 1340710,
+        // but we should ignore them anyway.
+        origin = principal.origin;
+      } catch (e) {
+        return;
+      }
       if (visitedOrigins.has(origin)) {
         // Don't read a host twice.
         return;
       }
 
       // Mark the current origin as visited.
       visitedOrigins.add(origin);
 
--- a/browser/extensions/mortar/host/common/ppapi-runtime.jsm
+++ b/browser/extensions/mortar/host/common/ppapi-runtime.jsm
@@ -3046,16 +3046,69 @@ dump(`callFromJSON: < ${JSON.stringify(c
     PPB_Instance_Private_ExecuteScript: function(json) {
       let instance = this.instances[json.instance];
       let script = String_PP_Var.getAsJSValue(json.script);
       let result = instance.mm.sendRpcMessage("ppapiflash.js:executeScript", script, { instance })[0];
       return [result, { exception: null }];
       //return [new PP_Var(), { exception: PP_Var.fromJSValue(e) }];
     },
 
+    /**
+    * PP_Resource Create([in] PP_Instance instance,
+    *                    [in] PP_InputEvent_Type type,
+    *                    [in] PP_TimeTicks time_stamp,
+    *                    [in] uint32_t modifiers,
+    *                    [in] uint32_t key_code,
+    *                    [in] PP_Var character_text,
+    *                    [in] PP_Var code);
+    */
+    PPB_KeyboardInputEvent_Create: function(json) {
+      let instance = this.instances[json.instance];
+      let charCode = 0;
+      if (PP_VarType[json.character_text.type] ==
+          PP_VarType.PP_VARTYPE_STRING) {
+        charCode = String_PP_Var.getAsJSValue(json.character_text).charCodeAt(0);
+      }
+      let location = instance.window.KeyboardEvent.DOM_KEY_LOCATION_STANDARD;
+      if (PP_InputEvent_Modifier.PP_INPUTEVENT_MODIFIER_ISLEFT &
+          json.modifiers) {
+        location = instance.window.KeyboardEvent.DOM_KEY_LOCATION_LEFT;
+      } else if (PP_InputEvent_Modifier.PP_INPUTEVENT_MODIFIER_ISRIGHT &
+                 json.modifiers) {
+        location = instance.window.KeyboardEvent.DOM_KEY_LOCATION_RIGHT;
+      } else if(PP_InputEvent_Modifier.PP_INPUTEVENT_MODIFIER_ISKEYPAD &
+                json.modifiers) {
+        location = instance.window.KeyboardEvent.DOM_KEY_LOCATION_NUMPAD;
+      }
+
+      // FIXME I skipped to put |PP_Var code| into keyboardEventInit here
+      // because I neither find any useful |code| value passing into here
+      // nor have any PPB APIs which gets access to |code| now.
+      let keyboardEventInit = {
+        altKey: PP_InputEvent_Modifier.PP_INPUTEVENT_MODIFIER_ALTKEY &
+          json.modifiers,
+        charCode: charCode,
+        ctrlKey: PP_InputEvent_Modifier.PP_INPUTEVENT_MODIFIER_CONTROLKEY &
+          json.modifiers,
+        keyCode: json.key_code,
+        location: location,
+        metaKey: PP_InputEvent_Modifier.PP_INPUTEVENT_MODIFIER_METAKEY &
+          json.modifiers,
+        repeat: PP_InputEvent_Modifier.PP_INPUTEVENT_MODIFIER_ISAUTOREPEAT &
+          json.modifiers,
+        shiftKey: PP_InputEvent_Modifier.PP_INPUTEVENT_MODIFIER_SHIFTKEY &
+          json.modifiers,
+      };
+      let eventName = EventByTypes.get(PP_InputEvent_Type[json.type]);
+      let event = new instance.window.KeyboardEvent(eventName,
+                                                    keyboardEventInit);
+      let resource = new KeyboardInputEvent(instance, event);
+      resource.timeStamp = json.time_stamp;
+      return resource;
+    },
 
     /**
      * PP_Bool IsKeyboardInputEvent([in] PP_Resource resource);
      */
     PPB_KeyboardInputEvent_IsKeyboardInputEvent: function(json) {
       let resource = PP_Resource.lookup(json.resource);
       return resource instanceof KeyboardInputEvent ? PP_Bool.PP_TRUE : PP_Bool.PP_FALSE;
     },
--- a/browser/extensions/mortar/host/pdf/bootstrap.js
+++ b/browser/extensions/mortar/host/pdf/bootstrap.js
@@ -7,63 +7,72 @@ const { classes: Cc, interfaces: Ci, uti
 Cu.import("resource://gre/modules/Services.jsm");
 
 function sandboxScript(sandbox)
 {
   dump("sandboxScript " + sandbox.pluginElement + "\n");
   Components.classes["@mozilla.org/moz/jssubscript-loader;1"].getService(Components.interfaces.mozIJSSubScriptLoader).loadSubScript("resource://ppapipdf.js/ppapi-content-sandbox.js", sandbox);
 }
 
-let plugins;
-function startup(data) {
-  dump(">>>STARTED!!!\n");
-
-  let root = data.installPath.parent.parent;
-  let rpclib = root.clone();
-  let pluginlib = root.clone();
-  let os = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).OS;
-  if (os == "Darwin") {
-    rpclib.appendRelativePath("ppapi/out/rpc.dylib");
-    pluginlib.appendRelativePath("plugin/libpepperpdfium.dylib");
-  } else if (os == "Linux") {
-    rpclib.appendRelativePath("ppapi/out/rpc.so");
-    pluginlib.appendRelativePath("plugin/libpepperpdfium.so");
-  } else if (os == "WINNT") {
-    rpclib.appendRelativePath("ppapi\\out\\rpc.dll");
-    pluginlib.appendRelativePath("plugin\\pepperpdfium.dll");
-  } else {
-    throw("Don't know the path to the libraries for this OS!");
-  }
-  rpclib = rpclib.path;
-  pluginlib = pluginlib.path;
+const handlerURI = "chrome://ppapipdf.js/content/viewer.html";
+let pdfium = {
+  init(bootstrapData) {
+    let root = bootstrapData.installPath.parent.parent;
+    let rpclib = root.clone();
+    let pluginlib = root.clone();
+    let os = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).OS;
+    if (os == "Darwin") {
+      rpclib.appendRelativePath("ppapi/out/rpc.dylib");
+      pluginlib.appendRelativePath("plugin/libpepperpdfium.dylib");
+    } else if (os == "Linux") {
+      rpclib.appendRelativePath("ppapi/out/rpc.so");
+      pluginlib.appendRelativePath("plugin/libpepperpdfium.so");
+    } else if (os == "WINNT") {
+      rpclib.appendRelativePath("ppapi\\out\\rpc.dll");
+      pluginlib.appendRelativePath("plugin\\pepperpdfium.dll");
+    } else {
+      throw("Don't know the path to the libraries for this OS!");
+    }
+    rpclib = rpclib.path;
+    pluginlib = pluginlib.path;
 
-  let pluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
-  let plugin = pluginHost.registerFakePlugin({
-    handlerURI: "chrome://ppapipdf.js/content/viewer.html",
-    mimeEntries: [
-      { type: "application/pdf", extension: "pdf" },
-      { type: "application/vnd.adobe.pdf", extension: "pdf" },
-      { type: "application/vnd.adobe.pdfxml", extension: "pdfxml" },
-      { type: "application/vnd.adobe.x-mars", extension: "mars" },
-      { type: "application/vnd.adobe.xdp+xml", extension: "xdp" },
-      { type: "application/vnd.adobe.xfdf", extension: "xfdf" },
-      { type: "application/vnd.adobe.xfd+xml", extension: "xfd" },
-      { type: "application/vnd.fdf", extension: "fdf" },
-    ],
-    name: "PPAPI PDF plugin",
-    niceName: "PPAPI PDF plugin",
-    version: "1.0",
-    sandboxScript : `(${sandboxScript.toSource()})(this);`,
-    ppapiProcessArgs: [ rpclib, pluginlib ],
-  });
-  plugin.enabledState = Ci.nsIPluginTag.STATE_ENABLED;
+    let pluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
+    let plugin = pluginHost.registerFakePlugin({
+      handlerURI: handlerURI,
+      mimeEntries: [
+        { type: "application/pdf", extension: "pdf" },
+        { type: "application/vnd.adobe.pdf", extension: "pdf" },
+        { type: "application/vnd.adobe.pdfxml", extension: "pdfxml" },
+        { type: "application/vnd.adobe.x-mars", extension: "mars" },
+        { type: "application/vnd.adobe.xdp+xml", extension: "xdp" },
+        { type: "application/vnd.adobe.xfdf", extension: "xfdf" },
+        { type: "application/vnd.adobe.xfd+xml", extension: "xfd" },
+        { type: "application/vnd.fdf", extension: "fdf" },
+      ],
+      name: "PPAPI PDF plugin",
+      niceName: "PPAPI PDF plugin",
+      version: "1.0",
+      sandboxScript : `(${sandboxScript.toSource()})(this);`,
+      ppapiProcessArgs: [ rpclib, pluginlib ],
+    });
+    plugin.enabledState = Ci.nsIPluginTag.STATE_ENABLED;
+    Services.ppmm.addMessageListener("ppapi.js:generateRandomBytes", this.generateRandomBytesListener);
+  },
+  uninit() {
+    let pluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
+    pluginHost.unregisterFakePlugin(handlerURI);
+    Services.ppmm.removeMessageListener("ppapi.js:generateRandomBytes", this.generateRandomBytesListener);
+  },
+  generateRandomBytesListener(data) {
+    let rng = Cc["@mozilla.org/security/random-generator;1"].createInstance(Ci.nsIRandomGenerator);
+    return rng.generateRandomBytes(data);
+  },
+}
 
-  let rng = Cc["@mozilla.org/security/random-generator;1"].createInstance(Ci.nsIRandomGenerator);
-  Services.ppmm.addMessageListener("ppapi.js:generateRandomBytes", ({ data }) => {
-    return rng.generateRandomBytes(data);
-  });
-
+function startup(aData) {
+  dump(">>>STARTED!!!\n");
+  pdfium.init(aData);
   dump("<<<STARTED!!!\n");
 }
-
 function shutdown() {
   dump("SHUTDOWN!!!\n");
+  pdfium.uninit();
 }
--- a/browser/extensions/mortar/host/rpc.h
+++ b/browser/extensions/mortar/host/rpc.h
@@ -469,21 +469,27 @@ static void FromJSON_str_t(JSONIterator&
             tokenValue.replace(it, next + 1, "\n");
             break;
           case 't':
             tokenValue.replace(it, next + 1, "\t");
             break;
           case 'u':
             if (tokenValue.cend() - next >= 5) {
               if (*(next + 1) == '0' &&
-                  *(next + 2) == '0' &&
-                  *(next + 3) == '0' &&
-                  *(next + 4) == '0') {
-                tokenValue.replace(it, next + 5, 1, '\0');
-                break;
+                  *(next + 2) == '0') {
+                unsigned int v;
+                std::stringstream x;
+                x << *(next + 3) << *(next + 4);
+                x >> std::hex >> v;
+                // Handle Control characters code units
+                // from U+0000 to U+001F.
+                if ( v < 0x0020) {
+                  tokenValue.replace(it, next + 5, 1, v);
+                  break;
+                }
               }
               Fail("Need to handle unicode escapes in strings: %s.",
                    tokenValue.c_str());
             }
         }
       }
     }
     value = (char*) malloc(length + 1);
--- a/browser/modules/test/browser/browser_UsageTelemetry_content.js
+++ b/browser/modules/test/browser/browser_UsageTelemetry_content.js
@@ -76,17 +76,17 @@ add_task(function* test_context_menu() {
   Assert.equal(Object.keys(scalars[SCALAR_CONTEXT_MENU]).length, 1,
                "This search must only increment one entry in the scalar.");
 
   // Make sure SEARCH_COUNTS contains identical values.
   checkKeyedHistogram(search_hist, "other-MozSearch.contextmenu", 1);
 
   // Also check events.
   let events = Services.telemetry.snapshotBuiltinEvents(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false);
-  events = events.filter(e => e[1] == "navigation" && e[2] == "search");
+  events = (events.default || []).filter(e => e[1] == "navigation" && e[2] == "search");
   checkEvents(events, [["navigation", "search", "contextmenu", null, {engine: "other-MozSearch"}]]);
 
   contextMenu.hidePopup();
   yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
   yield BrowserTestUtils.removeTab(tab);
 });
 
 add_task(function* test_about_newtab() {
@@ -112,13 +112,13 @@ add_task(function* test_about_newtab() {
   Assert.equal(Object.keys(scalars[SCALAR_ABOUT_NEWTAB]).length, 1,
                "This search must only increment one entry in the scalar.");
 
   // Make sure SEARCH_COUNTS contains identical values.
   checkKeyedHistogram(search_hist, "other-MozSearch.newtab", 1);
 
   // Also check events.
   let events = Services.telemetry.snapshotBuiltinEvents(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false);
-  events = events.filter(e => e[1] == "navigation" && e[2] == "search");
+  events = (events.default || []).filter(e => e[1] == "navigation" && e[2] == "search");
   checkEvents(events, [["navigation", "search", "about_newtab", "enter", {engine: "other-MozSearch"}]]);
 
   yield BrowserTestUtils.removeTab(tab);
 });
--- a/browser/modules/test/browser/browser_UsageTelemetry_content_aboutHome.js
+++ b/browser/modules/test/browser/browser_UsageTelemetry_content_aboutHome.js
@@ -76,13 +76,13 @@ add_task(function* test_abouthome_simple
   Assert.equal(Object.keys(scalars[SCALAR_ABOUT_HOME]).length, 1,
                "This search must only increment one entry in the scalar.");
 
   // Make sure SEARCH_COUNTS contains identical values.
   checkKeyedHistogram(search_hist, "other-MozSearch.abouthome", 1);
 
   // Also check events.
   let events = Services.telemetry.snapshotBuiltinEvents(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false);
-  events = events.filter(e => e[1] == "navigation" && e[2] == "search");
+  events = (events.default || []).filter(e => e[1] == "navigation" && e[2] == "search");
   checkEvents(events, [["navigation", "search", "about_home", "enter", {engine: "other-MozSearch"}]]);
 
   yield BrowserTestUtils.removeTab(tab);
 });
--- a/browser/modules/test/browser/browser_UsageTelemetry_searchbar.js
+++ b/browser/modules/test/browser/browser_UsageTelemetry_searchbar.js
@@ -102,17 +102,17 @@ add_task(function* test_plainQuery() {
   Assert.equal(Object.keys(scalars[SCALAR_SEARCHBAR]).length, 1,
                "This search must only increment one entry in the scalar.");
 
   // Make sure SEARCH_COUNTS contains identical values.
   checkKeyedHistogram(search_hist, "other-MozSearch.searchbar", 1);
 
   // Also check events.
   let events = Services.telemetry.snapshotBuiltinEvents(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false);
-  events = events.filter(e => e[1] == "navigation" && e[2] == "search");
+  events = (events.default || []).filter(e => e[1] == "navigation" && e[2] == "search");
   checkEvents(events, [["navigation", "search", "searchbar", "enter", {engine: "other-MozSearch"}]]);
 
   yield BrowserTestUtils.removeTab(tab);
 });
 
 add_task(function* test_oneOff() {
   // Let's reset the counts.
   Services.telemetry.clearScalars();
@@ -136,17 +136,17 @@ add_task(function* test_oneOff() {
   Assert.equal(Object.keys(scalars[SCALAR_SEARCHBAR]).length, 1,
                "This search must only increment one entry in the scalar.");
 
   // Make sure SEARCH_COUNTS contains identical values.
   checkKeyedHistogram(search_hist, "other-MozSearch2.searchbar", 1);
 
   // Also check events.
   let events = Services.telemetry.snapshotBuiltinEvents(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false);
-  events = events.filter(e => e[1] == "navigation" && e[2] == "search");
+  events = (events.default || []).filter(e => e[1] == "navigation" && e[2] == "search");
   checkEvents(events, [["navigation", "search", "searchbar", "oneoff", {engine: "other-MozSearch2"}]]);
 
   yield BrowserTestUtils.removeTab(tab);
 });
 
 add_task(function* test_suggestion() {
   // Let's reset the counts.
   Services.telemetry.clearScalars();
@@ -182,15 +182,15 @@ add_task(function* test_suggestion() {
                "This search must only increment one entry in the scalar.");
 
   // Make sure SEARCH_COUNTS contains identical values.
   let searchEngineId = "other-" + suggestionEngine.name;
   checkKeyedHistogram(search_hist, searchEngineId + ".searchbar", 1);
 
   // Also check events.
   let events = Services.telemetry.snapshotBuiltinEvents(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false);
-  events = events.filter(e => e[1] == "navigation" && e[2] == "search");
+  events = (events.default || []).filter(e => e[1] == "navigation" && e[2] == "search");
   checkEvents(events, [["navigation", "search", "searchbar", "suggestion", {engine: searchEngineId}]]);
 
   Services.search.currentEngine = previousEngine;
   Services.search.removeEngine(suggestionEngine);
   yield BrowserTestUtils.removeTab(tab);
 });
--- a/browser/modules/test/browser/browser_UsageTelemetry_urlbar.js
+++ b/browser/modules/test/browser/browser_UsageTelemetry_urlbar.js
@@ -121,17 +121,17 @@ add_task(function* test_simpleQuery() {
   Assert.equal(Object.keys(scalars[SCALAR_URLBAR]).length, 1,
                "This search must only increment one entry in the scalar.");
 
   // Make sure SEARCH_COUNTS contains identical values.
   checkKeyedHistogram(search_hist, "other-MozSearch.urlbar", 1);
 
   // Also check events.
   let events = Services.telemetry.snapshotBuiltinEvents(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false);
-  events = events.filter(e => e[1] == "navigation" && e[2] == "search");
+  events = (events.default || []).filter(e => e[1] == "navigation" && e[2] == "search");
   checkEvents(events, [["navigation", "search", "urlbar", "enter", {engine: "other-MozSearch"}]]);
 
   // Check the histograms as well.
   let resultIndexes = resultIndexHist.snapshot();
   checkHistogramResults(resultIndexes, 0, "FX_URLBAR_SELECTED_RESULT_INDEX");
 
   let resultTypes = resultTypeHist.snapshot();
   checkHistogramResults(resultTypes,
@@ -166,17 +166,17 @@ add_task(function* test_searchAlias() {
   Assert.equal(Object.keys(scalars[SCALAR_URLBAR]).length, 1,
                "This search must only increment one entry in the scalar.");
 
   // Make sure SEARCH_COUNTS contains identical values.
   checkKeyedHistogram(search_hist, "other-MozSearch.urlbar", 1);
 
   // Also check events.
   let events = Services.telemetry.snapshotBuiltinEvents(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false);
-  events = events.filter(e => e[1] == "navigation" && e[2] == "search");
+  events = (events.default || []).filter(e => e[1] == "navigation" && e[2] == "search");
   checkEvents(events, [["navigation", "search", "urlbar", "alias", {engine: "other-MozSearch"}]]);
 
   // Check the histograms as well.
   let resultIndexes = resultIndexHist.snapshot();
   checkHistogramResults(resultIndexes, 0, "FX_URLBAR_SELECTED_RESULT_INDEX");
 
   let resultTypes = resultTypeHist.snapshot();
   checkHistogramResults(resultTypes,
@@ -214,17 +214,17 @@ add_task(function* test_oneOff() {
   Assert.equal(Object.keys(scalars[SCALAR_URLBAR]).length, 1,
                "This search must only increment one entry in the scalar.");
 
   // Make sure SEARCH_COUNTS contains identical values.
   checkKeyedHistogram(search_hist, "other-MozSearch.urlbar", 1);
 
   // Also check events.
   let events = Services.telemetry.snapshotBuiltinEvents(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false);
-  events = events.filter(e => e[1] == "navigation" && e[2] == "search");
+  events = (events.default || []).filter(e => e[1] == "navigation" && e[2] == "search");
   checkEvents(events, [["navigation", "search", "urlbar", "oneoff", {engine: "other-MozSearch"}]]);
 
   // Check the histograms as well.
   let resultIndexes = resultIndexHist.snapshot();
   checkHistogramResults(resultIndexes, 0, "FX_URLBAR_SELECTED_RESULT_INDEX");
 
   let resultTypes = resultTypeHist.snapshot();
   checkHistogramResults(resultTypes,
@@ -274,17 +274,17 @@ add_task(function* test_suggestion() {
                "This search must only increment one entry in the scalar.");
 
   // Make sure SEARCH_COUNTS contains identical values.
   let searchEngineId = "other-" + suggestionEngine.name;
   checkKeyedHistogram(search_hist, searchEngineId + ".urlbar", 1);
 
   // Also check events.
   let events = Services.telemetry.snapshotBuiltinEvents(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, false);
-  events = events.filter(e => e[1] == "navigation" && e[2] == "search");
+  events = (events.default || []).filter(e => e[1] == "navigation" && e[2] == "search");
   checkEvents(events, [["navigation", "search", "urlbar", "suggestion", {engine: searchEngineId}]]);
 
   // Check the histograms as well.
   let resultIndexes = resultIndexHist.snapshot();
   checkHistogramResults(resultIndexes, 3, "FX_URLBAR_SELECTED_RESULT_INDEX");
 
   let resultTypes = resultTypeHist.snapshot();
   checkHistogramResults(resultTypes,
--- a/caps/BasePrincipal.cpp
+++ b/caps/BasePrincipal.cpp
@@ -51,17 +51,16 @@ OriginAttributes::InitPrefs()
 }
 
 void
 OriginAttributes::Inherit(const OriginAttributes& aAttrs)
 {
   mAppId = aAttrs.mAppId;
   mInIsolatedMozBrowser = aAttrs.mInIsolatedMozBrowser;
 
-  StripAttributes(STRIP_ADDON_ID);
 
   mUserContextId = aAttrs.mUserContextId;
 
   mPrivateBrowsingId = aAttrs.mPrivateBrowsingId;
   mFirstPartyDomain = aAttrs.mFirstPartyDomain;
 }
 
 void
@@ -91,39 +90,28 @@ OriginAttributes::CreateSuffix(nsACStrin
 {
   UniquePtr<URLParams> params(new URLParams());
   nsAutoString value;
 
   //
   // Important: While serializing any string-valued attributes, perform a
   // release-mode assertion to make sure that they don't contain characters that
   // will break the quota manager when it uses the serialization for file
-  // naming (see addonId below).
+  // naming.
   //
 
   if (mAppId != nsIScriptSecurityManager::NO_APP_ID) {
     value.AppendInt(mAppId);
     params->Set(NS_LITERAL_STRING("appId"), value);
   }
 
   if (mInIsolatedMozBrowser) {
     params->Set(NS_LITERAL_STRING("inBrowser"), NS_LITERAL_STRING("1"));
   }
 
-  if (!mAddonId.IsEmpty()) {
-    if (mAddonId.FindCharInSet(dom::quota::QuotaManager::kReplaceChars) != kNotFound) {
-#ifdef MOZ_CRASHREPORTER
-      CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("Crash_AddonId"),
-                                         NS_ConvertUTF16toUTF8(mAddonId));
-#endif
-      MOZ_CRASH();
-    }
-    params->Set(NS_LITERAL_STRING("addonId"), mAddonId);
-  }
-
   if (mUserContextId != nsIScriptSecurityManager::DEFAULT_USER_CONTEXT_ID) {
     value.Truncate();
     value.AppendInt(mUserContextId);
     params->Set(NS_LITERAL_STRING("userContextId"), value);
   }
 
 
   if (mPrivateBrowsingId) {
@@ -199,18 +187,18 @@ public:
         return false;
       }
 
       mOriginAttributes->mInIsolatedMozBrowser = true;
       return true;
     }
 
     if (aName.EqualsLiteral("addonId")) {
-      MOZ_RELEASE_ASSERT(mOriginAttributes->mAddonId.IsEmpty());
-      mOriginAttributes->mAddonId.Assign(aValue);
+      // No longer supported. Silently ignore so that legacy origin strings
+      // don't cause failures.
       return true;
     }
 
     if (aName.EqualsLiteral("userContextId")) {
       nsresult rv;
       int64_t val  = aValue.ToInteger64(&rv);
       NS_ENSURE_SUCCESS(rv, false);
       NS_ENSURE_TRUE(val <= UINT32_MAX, false);
@@ -294,121 +282,111 @@ OriginAttributes::IsPrivateBrowsing(cons
   OriginAttributes attrs;
   if (NS_WARN_IF(!attrs.PopulateFromOrigin(aOrigin, dummy))) {
     return false;
   }
 
   return !!attrs.mPrivateBrowsingId;
 }
 
-BasePrincipal::BasePrincipal()
+BasePrincipal::BasePrincipal(PrincipalKind aKind)
+  : mKind(aKind)
+  , mDomainSet(false)
 {}
 
 BasePrincipal::~BasePrincipal()
 {}
 
 NS_IMETHODIMP
 BasePrincipal::GetOrigin(nsACString& aOrigin)
 {
-  nsresult rv = GetOriginInternal(aOrigin);
+  nsresult rv = GetOriginNoSuffix(aOrigin);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsAutoCString suffix;
-  mOriginAttributes.CreateSuffix(suffix);
+  rv = GetOriginSuffix(suffix);
+  NS_ENSURE_SUCCESS(rv, rv);
   aOrigin.Append(suffix);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 BasePrincipal::GetOriginNoSuffix(nsACString& aOrigin)
 {
+  if (mOriginNoSuffix) {
+    return mOriginNoSuffix->ToUTF8String(aOrigin);
+  }
   return GetOriginInternal(aOrigin);
 }
 
 bool
 BasePrincipal::Subsumes(nsIPrincipal* aOther, DocumentDomainConsideration aConsideration)
 {
   MOZ_ASSERT(aOther);
+  MOZ_ASSERT_IF(Kind() == eCodebasePrincipal, mOriginSuffix);
 
   // Expanded principals handle origin attributes for each of their
   // sub-principals individually, null principals do only simple checks for
   // pointer equality, and system principals are immune to origin attributes
   // checks, so only do this check for codebase principals.
   if (Kind() == eCodebasePrincipal &&
-      OriginAttributesRef() != Cast(aOther)->OriginAttributesRef()) {
+      mOriginSuffix != Cast(aOther)->mOriginSuffix) {
     return false;
   }
 
   return SubsumesInternal(aOther, aConsideration);
 }
 
 NS_IMETHODIMP
 BasePrincipal::Equals(nsIPrincipal *aOther, bool *aResult)
 {
   NS_ENSURE_TRUE(aOther, NS_ERROR_INVALID_ARG);
-  *aResult = Subsumes(aOther, DontConsiderDocumentDomain) &&
-             Cast(aOther)->Subsumes(this, DontConsiderDocumentDomain);
+
+  *aResult = FastEquals(aOther);
+
   return NS_OK;
 }
 
 NS_IMETHODIMP
 BasePrincipal::EqualsConsideringDomain(nsIPrincipal *aOther, bool *aResult)
 {
   NS_ENSURE_TRUE(aOther, NS_ERROR_INVALID_ARG);
-  *aResult = Subsumes(aOther, ConsiderDocumentDomain) &&
-             Cast(aOther)->Subsumes(this, ConsiderDocumentDomain);
-  return NS_OK;
-}
-
-bool
-BasePrincipal::EqualsIgnoringAddonId(nsIPrincipal *aOther)
-{
-  MOZ_ASSERT(aOther);
 
-  // Note that this will not work for expanded principals, nor is it intended
-  // to.
-  if (!dom::ChromeUtils::IsOriginAttributesEqualIgnoringAddonId(
-          OriginAttributesRef(), Cast(aOther)->OriginAttributesRef())) {
-    return false;
-  }
+  *aResult = FastEqualsConsideringDomain(aOther);
 
-  return SubsumesInternal(aOther, DontConsiderDocumentDomain) &&
-         Cast(aOther)->SubsumesInternal(this, DontConsiderDocumentDomain);
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 BasePrincipal::Subsumes(nsIPrincipal *aOther, bool *aResult)
 {
   NS_ENSURE_TRUE(aOther, NS_ERROR_INVALID_ARG);
-  *aResult = Subsumes(aOther, DontConsiderDocumentDomain);
+
+  *aResult = FastSubsumes(aOther);
+
   return NS_OK;
 }
 
 NS_IMETHODIMP
 BasePrincipal::SubsumesConsideringDomain(nsIPrincipal *aOther, bool *aResult)
 {
   NS_ENSURE_TRUE(aOther, NS_ERROR_INVALID_ARG);
-  *aResult = Subsumes(aOther, ConsiderDocumentDomain);
+
+  *aResult = FastSubsumesConsideringDomain(aOther);
+
   return NS_OK;
 }
 
 NS_IMETHODIMP
 BasePrincipal::SubsumesConsideringDomainIgnoringFPD(nsIPrincipal *aOther,
                                                     bool *aResult)
 {
   NS_ENSURE_TRUE(aOther, NS_ERROR_INVALID_ARG);
 
-  if (Kind() == eCodebasePrincipal &&
-      !dom::ChromeUtils::IsOriginAttributesEqualIgnoringFPD(
-            OriginAttributesRef(), aOther->OriginAttributesRef())) {
-    *aResult = false;
-    return NS_OK;
-  }
-
-  *aResult = SubsumesInternal(aOther, ConsiderDocumentDomain);
+  *aResult = FastSubsumesConsideringDomainIgnoringFPD(aOther);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 BasePrincipal::CheckMayLoad(nsIURI* aURI, bool aReport, bool aAllowIfInheritsPrincipal)
 {
   // Check the internal method first, which allows us to quickly approve loads
@@ -554,18 +532,18 @@ BasePrincipal::GetOriginAttributes(JSCon
     return NS_ERROR_FAILURE;
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 BasePrincipal::GetOriginSuffix(nsACString& aOriginAttributes)
 {
-  mOriginAttributes.CreateSuffix(aOriginAttributes);
-  return NS_OK;
+  MOZ_ASSERT(mOriginSuffix);
+  return mOriginSuffix->ToUTF8String(aOriginAttributes);
 }
 
 NS_IMETHODIMP
 BasePrincipal::GetAppStatus(uint16_t* aAppStatus)
 {
   // TODO: Remove GetAppStatus.
   *aAppStatus = nsIPrincipal::APP_STATUS_NOT_INSTALLED;
   return NS_OK;
@@ -580,23 +558,16 @@ BasePrincipal::GetAppId(uint32_t* aAppId
     return NS_OK;
   }
 
   *aAppId = AppId();
   return NS_OK;
 }
 
 NS_IMETHODIMP
-BasePrincipal::GetAddonId(nsAString& aAddonId)
-{
-  aAddonId.Assign(mOriginAttributes.mAddonId);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 BasePrincipal::GetUserContextId(uint32_t* aUserContextId)
 {
   *aUserContextId = UserContextId();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 BasePrincipal::GetPrivateBrowsingId(uint32_t* aPrivateBrowsingId)
@@ -617,25 +588,29 @@ BasePrincipal::GetUnknownAppId(bool* aUn
 {
   *aUnknownAppId = AppId() == nsIScriptSecurityManager::UNKNOWN_APP_ID;
   return NS_OK;
 }
 
 bool
 BasePrincipal::AddonHasPermission(const nsAString& aPerm)
 {
-  if (mOriginAttributes.mAddonId.IsEmpty()) {
+  nsAutoString addonId;
+  NS_ENSURE_SUCCESS(GetAddonId(addonId), false);
+
+  if (addonId.IsEmpty()) {
     return false;
   }
+
   nsCOMPtr<nsIAddonPolicyService> aps =
     do_GetService("@mozilla.org/addons/policy-service;1");
   NS_ENSURE_TRUE(aps, false);
 
   bool retval = false;
-  nsresult rv = aps->AddonHasPermission(mOriginAttributes.mAddonId, aPerm, &retval);
+  nsresult rv = aps->AddonHasPermission(addonId, aPerm, &retval);
   NS_ENSURE_SUCCESS(rv, false);
   return retval;
 }
 
 already_AddRefed<BasePrincipal>
 BasePrincipal::CreateCodebasePrincipal(nsIURI* aURI, const OriginAttributes& aAttrs)
 {
   // If the URI is supposed to inherit the security context of whoever loads it,
@@ -704,21 +679,45 @@ BasePrincipal::CloneStrippingUserContext
   NS_ENSURE_SUCCESS(rv, nullptr);
 
   return BasePrincipal::CreateCodebasePrincipal(uri, attrs);
 }
 
 bool
 BasePrincipal::AddonAllowsLoad(nsIURI* aURI, bool aExplicit /* = false */)
 {
-  if (mOriginAttributes.mAddonId.IsEmpty()) {
+  nsAutoString addonId;
+  NS_ENSURE_SUCCESS(GetAddonId(addonId), false);
+
+  if (addonId.IsEmpty()) {
     return false;
   }
 
   nsCOMPtr<nsIAddonPolicyService> aps = do_GetService("@mozilla.org/addons/policy-service;1");
   NS_ENSURE_TRUE(aps, false);
 
   bool allowed = false;
-  nsresult rv = aps->AddonMayLoadURI(mOriginAttributes.mAddonId, aURI, aExplicit, &allowed);
+  nsresult rv = aps->AddonMayLoadURI(addonId, aURI, aExplicit, &allowed);
   return NS_SUCCEEDED(rv) && allowed;
 }
 
+void
+BasePrincipal::FinishInit()
+{
+  // First compute the origin suffix since it's infallible.
+  nsAutoCString originSuffix;
+  mOriginAttributes.CreateSuffix(originSuffix);
+  mOriginSuffix = NS_Atomize(originSuffix);
+
+  // Then compute the origin without the suffix.
+  nsAutoCString originNoSuffix;
+  nsresult rv = GetOriginInternal(originNoSuffix);
+  if (NS_FAILED(rv)) {
+    // If GetOriginInternal fails, we will get a null atom for mOriginNoSuffix,
+    // which we deal with anywhere mOriginNoSuffix is used.
+    // Once this is made infallible we can remove those null checks.
+    mOriginNoSuffix = nullptr;
+    return;
+  }
+  mOriginNoSuffix = NS_Atomize(originNoSuffix);
+}
+
 } // namespace mozilla
--- a/caps/BasePrincipal.h
+++ b/caps/BasePrincipal.h
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_BasePrincipal_h
 #define mozilla_BasePrincipal_h
 
 #include "nsJSPrincipals.h"
 
 #include "mozilla/Attributes.h"
+#include "mozilla/dom/ChromeUtils.h"
 #include "mozilla/dom/ChromeUtilsBinding.h"
 #include "nsIScriptSecurityManager.h"
 
 class nsIContentSecurityPolicy;
 class nsIObjectOutputStream;
 class nsIObjectInputStream;
 class nsIURI;
 
@@ -42,40 +43,34 @@ public:
   // This method 'clones' the OriginAttributes ignoring the addonId value becaue
   // this is computed from the principal URI and never propagated.
   void Inherit(const OriginAttributes& aAttrs);
 
   void SetFirstPartyDomain(const bool aIsTopLevelDocument, nsIURI* aURI);
 
   enum {
     STRIP_FIRST_PARTY_DOMAIN = 0x01,
-    STRIP_ADDON_ID = 0x02,
-    STRIP_USER_CONTEXT_ID = 0x04,
+    STRIP_USER_CONTEXT_ID = 0x02,
   };
 
   inline void StripAttributes(uint32_t aFlags)
   {
     if (aFlags & STRIP_FIRST_PARTY_DOMAIN) {
       mFirstPartyDomain.Truncate();
     }
 
-    if (aFlags & STRIP_ADDON_ID) {
-      mAddonId.Truncate();
-    }
-
     if (aFlags & STRIP_USER_CONTEXT_ID) {
       mUserContextId = nsIScriptSecurityManager::DEFAULT_USER_CONTEXT_ID;
     }
   }
 
   bool operator==(const OriginAttributes& aOther) const
   {
     return mAppId == aOther.mAppId &&
            mInIsolatedMozBrowser == aOther.mInIsolatedMozBrowser &&
-           mAddonId == aOther.mAddonId &&
            mUserContextId == aOther.mUserContextId &&
            mPrivateBrowsingId == aOther.mPrivateBrowsingId &&
            mFirstPartyDomain == aOther.mFirstPartyDomain;
   }
 
   bool operator!=(const OriginAttributes& aOther) const
   {
     return !(*this == aOther);
@@ -147,20 +142,16 @@ public:
     if (mAppId.WasPassed() && mAppId.Value() != aAttrs.mAppId) {
       return false;
     }
 
     if (mInIsolatedMozBrowser.WasPassed() && mInIsolatedMozBrowser.Value() != aAttrs.mInIsolatedMozBrowser) {
       return false;
     }
 
-    if (mAddonId.WasPassed() && mAddonId.Value() != aAttrs.mAddonId) {
-      return false;
-    }
-
     if (mUserContextId.WasPassed() && mUserContextId.Value() != aAttrs.mUserContextId) {
       return false;
     }
 
     if (mPrivateBrowsingId.WasPassed() && mPrivateBrowsingId.Value() != aAttrs.mPrivateBrowsingId) {
       return false;
     }
 
@@ -179,21 +170,16 @@ public:
     }
 
     if (mInIsolatedMozBrowser.WasPassed() &&
         aOther.mInIsolatedMozBrowser.WasPassed() &&
         mInIsolatedMozBrowser.Value() != aOther.mInIsolatedMozBrowser.Value()) {
       return false;
     }
 
-    if (mAddonId.WasPassed() && aOther.mAddonId.WasPassed() &&
-        mAddonId.Value() != aOther.mAddonId.Value()) {
-      return false;
-    }
-
     if (mUserContextId.WasPassed() && aOther.mUserContextId.WasPassed() &&
         mUserContextId.Value() != aOther.mUserContextId.Value()) {
       return false;
     }
 
     if (mPrivateBrowsingId.WasPassed() && aOther.mPrivateBrowsingId.WasPassed() &&
         mPrivateBrowsingId.Value() != aOther.mPrivateBrowsingId.Value()) {
       return false;
@@ -213,17 +199,24 @@ public:
  * default implementations and other commonalities between principal
  * implementations.
  *
  * We should merge nsJSPrincipals into this class at some point.
  */
 class BasePrincipal : public nsJSPrincipals
 {
 public:
-  BasePrincipal();
+  enum PrincipalKind {
+    eNullPrincipal,
+    eCodebasePrincipal,
+    eExpandedPrincipal,
+    eSystemPrincipal
+  };
+
+  explicit BasePrincipal(PrincipalKind aKind);
 
   enum DocumentDomainConsideration { DontConsiderDocumentDomain, ConsiderDocumentDomain};
   bool Subsumes(nsIPrincipal* aOther, DocumentDomainConsideration aConsideration);
 
   NS_IMETHOD GetOrigin(nsACString& aOrigin) final;
   NS_IMETHOD GetOriginNoSuffix(nsACString& aOrigin) final;
   NS_IMETHOD Equals(nsIPrincipal* other, bool* _retval) final;
   NS_IMETHOD EqualsConsideringDomain(nsIPrincipal* other, bool* _retval) final;
@@ -239,69 +232,170 @@ public:
   NS_IMETHOD GetIsNullPrincipal(bool* aResult) override;
   NS_IMETHOD GetIsCodebasePrincipal(bool* aResult) override;
   NS_IMETHOD GetIsExpandedPrincipal(bool* aResult) override;
   NS_IMETHOD GetIsSystemPrincipal(bool* aResult) override;
   NS_IMETHOD GetOriginAttributes(JSContext* aCx, JS::MutableHandle<JS::Value> aVal) final;
   NS_IMETHOD GetOriginSuffix(nsACString& aOriginSuffix) final;
   NS_IMETHOD GetAppStatus(uint16_t* aAppStatus) final;
   NS_IMETHOD GetAppId(uint32_t* aAppStatus) final;
-  NS_IMETHOD GetAddonId(nsAString& aAddonId) final;
   NS_IMETHOD GetIsInIsolatedMozBrowserElement(bool* aIsInIsolatedMozBrowserElement) final;
   NS_IMETHOD GetUnknownAppId(bool* aUnknownAppId) final;
   NS_IMETHOD GetUserContextId(uint32_t* aUserContextId) final;
   NS_IMETHOD GetPrivateBrowsingId(uint32_t* aPrivateBrowsingId) final;
 
-  bool EqualsIgnoringAddonId(nsIPrincipal *aOther);
-
   virtual bool AddonHasPermission(const nsAString& aPerm);
 
   virtual bool IsCodebasePrincipal() const { return false; };
 
   static BasePrincipal* Cast(nsIPrincipal* aPrin) { return static_cast<BasePrincipal*>(aPrin); }
   static already_AddRefed<BasePrincipal>
   CreateCodebasePrincipal(nsIURI* aURI, const OriginAttributes& aAttrs);
   static already_AddRefed<BasePrincipal> CreateCodebasePrincipal(const nsACString& aOrigin);
 
-  const OriginAttributes& OriginAttributesRef() override { return mOriginAttributes; }
+  const OriginAttributes& OriginAttributesRef() final { return mOriginAttributes; }
   uint32_t AppId() const { return mOriginAttributes.mAppId; }
   uint32_t UserContextId() const { return mOriginAttributes.mUserContextId; }
   uint32_t PrivateBrowsingId() const { return mOriginAttributes.mPrivateBrowsingId; }
   bool IsInIsolatedMozBrowserElement() const { return mOriginAttributes.mInIsolatedMozBrowser; }
 
-  enum PrincipalKind {
-    eNullPrincipal,
-    eCodebasePrincipal,
-    eExpandedPrincipal,
-    eSystemPrincipal
-  };
-
-  virtual PrincipalKind Kind() = 0;
+  PrincipalKind Kind() const { return mKind; }
 
   already_AddRefed<BasePrincipal> CloneStrippingUserContextIdAndFirstPartyDomain();
 
   // Helper to check whether this principal is associated with an addon that
   // allows unprivileged code to load aURI.  aExplicit == true will prevent
   // use of all_urls permission, requiring the domain in its permissions.
   bool AddonAllowsLoad(nsIURI* aURI, bool aExplicit = false);
 
+  // Call these to avoid the cost of virtual dispatch.
+  inline bool FastEquals(nsIPrincipal* aOther);
+  inline bool FastEqualsConsideringDomain(nsIPrincipal* aOther);
+  inline bool FastSubsumes(nsIPrincipal* aOther);
+  inline bool FastSubsumesConsideringDomain(nsIPrincipal* aOther);
+  inline bool FastSubsumesConsideringDomainIgnoringFPD(nsIPrincipal* aOther);
+
 protected:
   virtual ~BasePrincipal();
 
   virtual nsresult GetOriginInternal(nsACString& aOrigin) = 0;
   // Note that this does not check OriginAttributes. Callers that depend on
   // those must call Subsumes instead.
   virtual bool SubsumesInternal(nsIPrincipal* aOther, DocumentDomainConsideration aConsider) = 0;
 
   // Internal, side-effect-free check to determine whether the concrete
   // principal would allow the load ignoring any common behavior implemented in
   // BasePrincipal::CheckMayLoad.
   virtual bool MayLoadInternal(nsIURI* aURI) = 0;
   friend class ::nsExpandedPrincipal;
 
+  // This function should be called as the last step of the initialization of the
+  // principal objects.  It's typically called as the last step from the Init()
+  // method of the child classes.
+  void FinishInit();
+
   nsCOMPtr<nsIContentSecurityPolicy> mCSP;
   nsCOMPtr<nsIContentSecurityPolicy> mPreloadCSP;
+  nsCOMPtr<nsIAtom> mOriginNoSuffix;
+  nsCOMPtr<nsIAtom> mOriginSuffix;
   OriginAttributes mOriginAttributes;
+  PrincipalKind mKind;
+  bool mDomainSet;
 };
 
+inline bool
+BasePrincipal::FastEquals(nsIPrincipal* aOther)
+{
+  auto other = Cast(aOther);
+  if (Kind() != other->Kind()) {
+    // Principals of different kinds can't be equal.
+    return false;
+  }
+
+  // Two principals are considered to be equal if their origins are the same.
+  // If the two principals are codebase principals, their origin attributes
+  // (aka the origin suffix) must also match.
+  // If the two principals are null principals, they're only equal if they're
+  // the same object.
+  if (Kind() == eNullPrincipal || Kind() == eSystemPrincipal) {
+    return this == other;
+  }
+
+  if (mOriginNoSuffix) {
+    if (Kind() == eCodebasePrincipal) {
+      return mOriginNoSuffix == other->mOriginNoSuffix &&
+             mOriginSuffix == other->mOriginSuffix;
+    }
+
+    MOZ_ASSERT(Kind() == eExpandedPrincipal);
+    return mOriginNoSuffix == other->mOriginNoSuffix;
+  }
+
+  // If mOriginNoSuffix is null on one of our principals, we must fall back
+  // to the slow path.
+  return Subsumes(aOther, DontConsiderDocumentDomain) &&
+         other->Subsumes(this, DontConsiderDocumentDomain);
+}
+
+inline bool
+BasePrincipal::FastEqualsConsideringDomain(nsIPrincipal* aOther)
+{
+  // If neither of the principals have document.domain set, we use the fast path
+  // in Equals().  Otherwise, we fall back to the slow path below.
+  auto other = Cast(aOther);
+  if (!mDomainSet && !other->mDomainSet) {
+    return FastEquals(aOther);
+  }
+
+  return Subsumes(aOther, ConsiderDocumentDomain) &&
+         other->Subsumes(this, ConsiderDocumentDomain);
+}
+
+inline bool
+BasePrincipal::FastSubsumes(nsIPrincipal* aOther)
+{
+  // If two principals are equal, then they both subsume each other.
+  // We deal with two special cases first:
+  // Null principals only subsume each other if they are equal, and are only
+  // equal if they're the same object.
+  // Also, if mOriginNoSuffix is null, FastEquals falls back to the slow path
+  // using Subsumes, so we don't want to use it in that case to avoid an
+  // infinite recursion.
+  auto other = Cast(aOther);
+  if (Kind() == eNullPrincipal && other->Kind() == eNullPrincipal) {
+    return this == other;
+  }
+  if (mOriginNoSuffix && FastEquals(aOther)) {
+    return true;
+  }
+
+  // Otherwise, fall back to the slow path.
+  return Subsumes(aOther, DontConsiderDocumentDomain);
+}
+
+inline bool
+BasePrincipal::FastSubsumesConsideringDomain(nsIPrincipal* aOther)
+{
+  // If neither of the principals have document.domain set, we hand off to
+  // FastSubsumes() which has fast paths for some special cases. Otherwise, we fall
+  // back to the slow path below.
+  if (!mDomainSet && !Cast(aOther)->mDomainSet) {
+    return FastSubsumes(aOther);
+  }
+
+  return Subsumes(aOther, ConsiderDocumentDomain);
+}
+
+inline bool
+BasePrincipal::FastSubsumesConsideringDomainIgnoringFPD(nsIPrincipal* aOther)
+{
+  if (Kind() == eCodebasePrincipal &&
+      !dom::ChromeUtils::IsOriginAttributesEqualIgnoringFPD(
+            mOriginAttributes, Cast(aOther)->mOriginAttributes)) {
+    return false;
+  }
+
+ return SubsumesInternal(aOther, ConsiderDocumentDomain);
+}
+
 } // namespace mozilla
 
 #endif /* mozilla_BasePrincipal_h */
--- a/caps/nsExpandedPrincipal.cpp
+++ b/caps/nsExpandedPrincipal.cpp
@@ -40,29 +40,39 @@ struct OriginComparator
     rv = b->GetOrigin(originB);
     NS_ENSURE_SUCCESS(rv, false);
     return a == b;
   }
 };
 
 nsExpandedPrincipal::nsExpandedPrincipal(nsTArray<nsCOMPtr<nsIPrincipal>> &aWhiteList,
                                          const OriginAttributes& aAttrs)
+  : BasePrincipal(eExpandedPrincipal)
 {
   // We force the principals to be sorted by origin so that nsExpandedPrincipal
   // origins can have a canonical form.
   OriginComparator c;
   for (size_t i = 0; i < aWhiteList.Length(); ++i) {
     mPrincipals.InsertElementSorted(aWhiteList[i], c);
   }
   mOriginAttributes = aAttrs;
 }
 
 nsExpandedPrincipal::~nsExpandedPrincipal()
 { }
 
+already_AddRefed<nsExpandedPrincipal>
+nsExpandedPrincipal::Create(nsTArray<nsCOMPtr<nsIPrincipal>>& aWhiteList,
+                            const OriginAttributes& aAttrs)
+{
+  RefPtr<nsExpandedPrincipal> ep = new nsExpandedPrincipal(aWhiteList, aAttrs);
+  ep->FinishInit();
+  return ep.forget();
+}
+
 NS_IMETHODIMP
 nsExpandedPrincipal::GetDomain(nsIURI** aDomain)
 {
   *aDomain = nullptr;
   return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -155,16 +165,23 @@ nsExpandedPrincipal::GetWhiteList(nsTArr
 }
 
 NS_IMETHODIMP
 nsExpandedPrincipal::GetBaseDomain(nsACString& aBaseDomain)
 {
   return NS_ERROR_NOT_AVAILABLE;
 }
 
+NS_IMETHODIMP
+nsExpandedPrincipal::GetAddonId(nsAString& aAddonId)
+{
+  aAddonId.Truncate();
+  return NS_OK;
+};
+
 bool
 nsExpandedPrincipal::AddonHasPermission(const nsAString& aPerm)
 {
   for (size_t i = 0; i < mPrincipals.Length(); ++i) {
     if (BasePrincipal::Cast(mPrincipals[i])->AddonHasPermission(aPerm)) {
       return true;
     }
   }
--- a/caps/nsExpandedPrincipal.h
+++ b/caps/nsExpandedPrincipal.h
@@ -10,36 +10,39 @@
 #include "nsJSPrincipals.h"
 #include "nsTArray.h"
 #include "nsNetUtil.h"
 #include "mozilla/BasePrincipal.h"
 
 class nsExpandedPrincipal : public nsIExpandedPrincipal
                           , public mozilla::BasePrincipal
 {
-public:
   nsExpandedPrincipal(nsTArray<nsCOMPtr<nsIPrincipal>> &aWhiteList,
                       const mozilla::OriginAttributes& aAttrs);
 
+public:
+  static already_AddRefed<nsExpandedPrincipal>
+  Create(nsTArray<nsCOMPtr<nsIPrincipal>>& aWhiteList,
+         const mozilla::OriginAttributes& aAttrs);
+
   NS_DECL_NSIEXPANDEDPRINCIPAL
   NS_DECL_NSISERIALIZABLE
   NS_IMETHOD_(MozExternalRefCountType) AddRef() override { return nsJSPrincipals::AddRef(); };
   NS_IMETHOD_(MozExternalRefCountType) Release() override { return nsJSPrincipals::Release(); };
   NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr) override;
   NS_IMETHOD GetHashValue(uint32_t* aHashValue) override;
   NS_IMETHOD GetURI(nsIURI** aURI) override;
   NS_IMETHOD GetDomain(nsIURI** aDomain) override;
   NS_IMETHOD SetDomain(nsIURI* aDomain) override;
   NS_IMETHOD GetBaseDomain(nsACString& aBaseDomain) override;
+  NS_IMETHOD GetAddonId(nsAString& aAddonId) override;
   virtual bool AddonHasPermission(const nsAString& aPerm) override;
   virtual nsresult GetScriptLocation(nsACString &aStr) override;
   nsresult GetOriginInternal(nsACString& aOrigin) override;
 
-  PrincipalKind Kind() override { return eExpandedPrincipal; }
-
 protected:
   virtual ~nsExpandedPrincipal();
 
   bool SubsumesInternal(nsIPrincipal* aOther, DocumentDomainConsideration aConsideration) override;
   bool MayLoadInternal(nsIURI* aURI) override;
 
 private:
   nsTArray< nsCOMPtr<nsIPrincipal> > mPrincipals;
--- a/caps/nsNullPrincipal.cpp
+++ b/caps/nsNullPrincipal.cpp
@@ -81,16 +81,18 @@ nsNullPrincipal::Init(const OriginAttrib
                    NS_ERROR_NOT_AVAILABLE);
 
     mURI = aURI;
   } else {
     mURI = nsNullPrincipalURI::Create();
     NS_ENSURE_TRUE(mURI, NS_ERROR_NOT_AVAILABLE);
   }
 
+  FinishInit();
+
   return NS_OK;
 }
 
 nsresult
 nsNullPrincipal::GetScriptLocation(nsACString &aStr)
 {
   return mURI->GetSpec(aStr);
 }
@@ -151,16 +153,23 @@ nsNullPrincipal::MayLoadInternal(nsIURI*
 
 NS_IMETHODIMP
 nsNullPrincipal::GetBaseDomain(nsACString& aBaseDomain)
 {
   // For a null principal, we use our unique uuid as the base domain.
   return mURI->GetPath(aBaseDomain);
 }
 
+NS_IMETHODIMP
+nsNullPrincipal::GetAddonId(nsAString& aAddonId)
+{
+  aAddonId.Truncate();
+  return NS_OK;
+};
+
 /**
  * nsISerializable implementation
  */
 NS_IMETHODIMP
 nsNullPrincipal::Read(nsIObjectInputStream* aStream)
 {
   // Note - nsNullPrincipal use NS_GENERIC_FACTORY_CONSTRUCTOR_INIT, which means
   // that the Init() method has already been invoked by the time we deserialize.
--- a/caps/nsNullPrincipal.h
+++ b/caps/nsNullPrincipal.h
@@ -31,43 +31,45 @@ class nsIURI;
 #define NS_NULLPRINCIPAL_SCHEME "moz-nullprincipal"
 
 class nsNullPrincipal final : public mozilla::BasePrincipal
 {
 public:
   // This should only be used by deserialization, and the factory constructor.
   // Other consumers should use the Create and CreateWithInheritedAttributes
   // methods.
-  nsNullPrincipal() {}
+  nsNullPrincipal()
+    : BasePrincipal(eNullPrincipal)
+  {
+  }
 
   NS_DECL_NSISERIALIZABLE
 
   NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr) override;
   NS_IMETHOD GetHashValue(uint32_t* aHashValue) override;
   NS_IMETHOD GetURI(nsIURI** aURI) override;
   NS_IMETHOD GetDomain(nsIURI** aDomain) override;
   NS_IMETHOD SetDomain(nsIURI* aDomain) override;
   NS_IMETHOD GetBaseDomain(nsACString& aBaseDomain) override;
+  NS_IMETHOD GetAddonId(nsAString& aAddonId) override;
   nsresult GetOriginInternal(nsACString& aOrigin) override;
 
   static already_AddRefed<nsNullPrincipal> CreateWithInheritedAttributes(nsIPrincipal* aInheritFrom);
 
   static already_AddRefed<nsNullPrincipal> CreateWithInheritedAttributes(nsIDocShell* aDocShell);
 
   static already_AddRefed<nsNullPrincipal>
   Create(const mozilla::OriginAttributes& aOriginAttributes = mozilla::OriginAttributes(),
          nsIURI* aURI = nullptr);
 
   nsresult Init(const mozilla::OriginAttributes& aOriginAttributes = mozilla::OriginAttributes(),
                 nsIURI* aURI = nullptr);
 
   virtual nsresult GetScriptLocation(nsACString &aStr) override;
 
-  PrincipalKind Kind() override { return eNullPrincipal; }
-
  protected:
   virtual ~nsNullPrincipal() {}
 
   bool SubsumesInternal(nsIPrincipal* aOther, DocumentDomainConsideration aConsideration) override
   {
     return aOther == this;
   }
 
--- a/caps/nsPrincipal.cpp
+++ b/caps/nsPrincipal.cpp
@@ -22,16 +22,17 @@
 #include "nsIProtocolHandler.h"
 #include "nsError.h"
 #include "nsIContentSecurityPolicy.h"
 #include "nsNetCID.h"
 #include "jswrapper.h"
 
 #include "mozilla/dom/nsCSPContext.h"
 #include "mozilla/dom/ScriptSettings.h"
+#include "mozilla/ClearOnShutdown.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/HashFunctions.h"
 
 using namespace mozilla;
 
 static bool gCodeBasePrincipalSupport = false;
 
 static bool URIIsImmutable(nsIURI* aURI)
@@ -39,16 +40,29 @@ static bool URIIsImmutable(nsIURI* aURI)
   nsCOMPtr<nsIMutable> mutableObj(do_QueryInterface(aURI));
   bool isMutable;
   return
     mutableObj &&
     NS_SUCCEEDED(mutableObj->GetMutable(&isMutable)) &&
     !isMutable;
 }
 
+static nsIAddonPolicyService*
+GetAddonPolicyService(nsresult* aRv)
+{
+  static nsCOMPtr<nsIAddonPolicyService> addonPolicyService;
+
+  *aRv = NS_OK;
+  if (!addonPolicyService) {
+    addonPolicyService = do_GetService("@mozilla.org/addons/policy-service;1", aRv);
+    ClearOnShutdown(&addonPolicyService);
+  }
+  return addonPolicyService;
+}
+
 NS_IMPL_CLASSINFO(nsPrincipal, nullptr, nsIClassInfo::MAIN_THREAD_ONLY,
                   NS_PRINCIPAL_CID)
 NS_IMPL_QUERY_INTERFACE_CI(nsPrincipal,
                            nsIPrincipal,
                            nsISerializable)
 NS_IMPL_CI_INTERFACE_GETTER(nsPrincipal,
                             nsIPrincipal,
                             nsISerializable)
@@ -58,20 +72,22 @@ NS_IMPL_CI_INTERFACE_GETTER(nsPrincipal,
 nsPrincipal::InitializeStatics()
 {
   Preferences::AddBoolVarCache(&gCodeBasePrincipalSupport,
                                "signed.applets.codebase_principal_support",
                                false);
 }
 
 nsPrincipal::nsPrincipal()
-  : mCodebaseImmutable(false)
+  : BasePrincipal(eCodebasePrincipal)
+  , mCodebaseImmutable(false)
   , mDomainImmutable(false)
   , mInitialized(false)
-{ }
+{
+}
 
 nsPrincipal::~nsPrincipal()
 {
   // let's clear the principal within the csp to avoid a tangling pointer
   if (mCSP) {
     static_cast<nsCSPContext*>(mCSP.get())->clearLoadingPrincipal();
   }
 }
@@ -79,20 +95,35 @@ nsPrincipal::~nsPrincipal()
 nsresult
 nsPrincipal::Init(nsIURI *aCodebase, const OriginAttributes& aOriginAttributes)
 {
   NS_ENSURE_STATE(!mInitialized);
   NS_ENSURE_ARG(aCodebase);
 
   mInitialized = true;
 
+  // Assert that the URI we get here isn't any of the schemes that we know we
+  // should not get here.  These schemes always either inherit their principal
+  // or fall back to a null principal.  These are schemes which return
+  // URI_INHERITS_SECURITY_CONTEXT from their protocol handler's
+  // GetProtocolFlags function.
+  bool hasFlag;
+  Unused << hasFlag; // silence possible compiler warnings.
+  MOZ_DIAGNOSTIC_ASSERT(
+      NS_SUCCEEDED(NS_URIChainHasFlags(aCodebase,
+                                       nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT,
+                                       &hasFlag)) &&
+      !hasFlag);
+
   mCodebase = NS_TryToMakeImmutable(aCodebase);
   mCodebaseImmutable = URIIsImmutable(mCodebase);
   mOriginAttributes = aOriginAttributes;
 
+  FinishInit();
+
   return NS_OK;
 }
 
 nsresult
 nsPrincipal::GetScriptLocation(nsACString &aStr)
 {
   return mCodebase->GetSpec(aStr);
 }
@@ -104,16 +135,27 @@ nsPrincipal::GetOriginInternal(nsACStrin
     return NS_ERROR_FAILURE;
   }
 
   nsCOMPtr<nsIURI> origin = NS_GetInnermostURI(mCodebase);
   if (!origin) {
     return NS_ERROR_FAILURE;
   }
 
+  MOZ_ASSERT(!NS_IsAboutBlank(origin),
+             "The inner URI for about:blank must be moz-safe-about:blank");
+
+  if (!nsScriptSecurityManager::GetStrictFileOriginPolicy() &&
+      NS_URIIsLocalFile(origin)) {
+    // If strict file origin policy is not in effect, all local files are
+    // considered to be same-origin, so return a known dummy origin here.
+    aOrigin.AssignLiteral("file://UNIVERSAL_FILE_URI_ORIGIN");
+    return NS_OK;
+  }
+
   nsAutoCString hostPort;
 
   // chrome: URLs don't have a meaningful origin, so make
   // sure we just get the full spec for them.
   // XXX this should be removed in favor of the solution in
   // bug 160042.
   bool isChrome;
   nsresult rv = origin->SchemeIs("chrome", &isChrome);
@@ -134,17 +176,21 @@ nsPrincipal::GetOriginInternal(nsACStrin
   // containing the magic "^" we use as a separating character for origin
   // attributes.
   //
   // These constraints can generally be achieved by restricting .origin to
   // nsIStandardURL-based URIs, but there are a few other URI schemes that we need
   // to handle.
   bool isBehaved;
   if ((NS_SUCCEEDED(origin->SchemeIs("about", &isBehaved)) && isBehaved) ||
-      (NS_SUCCEEDED(origin->SchemeIs("moz-safe-about", &isBehaved)) && isBehaved) ||
+      (NS_SUCCEEDED(origin->SchemeIs("moz-safe-about", &isBehaved)) && isBehaved &&
+       // We generally consider two about:foo origins to be same-origin, but
+       // about:blank is special since it can be generated from different sources.
+       // We check for moz-safe-about:blank since origin is an innermost URI.
+       !origin->GetSpecOrDefault().EqualsLiteral("moz-safe-about:blank")) ||
       (NS_SUCCEEDED(origin->SchemeIs("indexeddb", &isBehaved)) && isBehaved)) {
     rv = origin->GetAsciiSpec(aOrigin);
     NS_ENSURE_SUCCESS(rv, rv);
     // These URIs could technically contain a '^', but they never should.
     if (NS_WARN_IF(aOrigin.FindChar('^', 0) != -1)) {
       aOrigin.Truncate();
       return NS_ERROR_FAILURE;
     }
@@ -286,23 +332,16 @@ nsPrincipal::MayLoadInternal(nsIURI* aUR
       NS_URIIsLocalFile(aURI) &&
       NS_RelaxStrictFileOriginPolicy(aURI, mCodebase)) {
     return true;
   }
 
   return false;
 }
 
-void
-nsPrincipal::SetURI(nsIURI* aURI)
-{
-  mCodebase = NS_TryToMakeImmutable(aURI);
-  mCodebaseImmutable = URIIsImmutable(mCodebase);
-}
-
 NS_IMETHODIMP
 nsPrincipal::GetHashValue(uint32_t* aValue)
 {
   NS_PRECONDITION(mCodebase, "Need a codebase");
 
   *aValue = nsScriptSecurityManager::HashPrincipalByOrigin(this);
   return NS_OK;
 }
@@ -323,16 +362,17 @@ nsPrincipal::GetDomain(nsIURI** aDomain)
   return NS_EnsureSafeToReturn(mDomain, aDomain);
 }
 
 NS_IMETHODIMP
 nsPrincipal::SetDomain(nsIURI* aDomain)
 {
   mDomain = NS_TryToMakeImmutable(aDomain);
   mDomainImmutable = URIIsImmutable(mDomain);
+  mDomainSet = true;
 
   // Recompute all wrappers between compartments using this principal and other
   // non-chrome compartments.
   AutoSafeJSContext cx;
   JSPrincipals *principals = nsJSPrincipals::get(static_cast<nsIPrincipal*>(this));
   bool success = js::RecomputeWrappers(cx, js::ContentCompartmentsOnly(),
                                        js::CompartmentsWithPrincipals(principals));
   NS_ENSURE_TRUE(success, NS_ERROR_FAILURE);
@@ -374,16 +414,45 @@ nsPrincipal::GetBaseDomain(nsACString& a
   if (thirdPartyUtil) {
     return thirdPartyUtil->GetBaseDomain(mCodebase, aBaseDomain);
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsPrincipal::GetAddonId(nsAString& aAddonId)
+{
+  if (mAddonIdCache.isSome()) {
+    aAddonId.Assign(mAddonIdCache.ref());
+    return NS_OK;
+  }
+
+  NS_ENSURE_TRUE(mCodebase, NS_ERROR_FAILURE);
+
+  nsresult rv;
+  bool isMozExt;
+  if (NS_SUCCEEDED(mCodebase->SchemeIs("moz-extension", &isMozExt)) && isMozExt) {
+    nsIAddonPolicyService* addonPolicyService = GetAddonPolicyService(&rv);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsAutoString addonId;
+    rv = addonPolicyService->ExtensionURIToAddonId(mCodebase, addonId);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    mAddonIdCache.emplace(addonId);
+  } else {
+    mAddonIdCache.emplace();
+  }
+
+  aAddonId.Assign(mAddonIdCache.ref());
+  return NS_OK;
+};
+
+NS_IMETHODIMP
 nsPrincipal::Read(nsIObjectInputStream* aStream)
 {
   nsCOMPtr<nsISupports> supports;
   nsCOMPtr<nsIURI> codebase;
   nsresult rv = NS_ReadOptionalObject(aStream, true, getter_AddRefs(supports));
   if (NS_FAILED(rv)) {
     return rv;
   }
--- a/caps/nsPrincipal.h
+++ b/caps/nsPrincipal.h
@@ -20,47 +20,48 @@ class nsPrincipal final : public mozilla
 public:
   NS_DECL_NSISERIALIZABLE
   NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr) override;
   NS_IMETHOD GetHashValue(uint32_t* aHashValue) override;
   NS_IMETHOD GetURI(nsIURI** aURI) override;
   NS_IMETHOD GetDomain(nsIURI** aDomain) override;
   NS_IMETHOD SetDomain(nsIURI* aDomain) override;
   NS_IMETHOD GetBaseDomain(nsACString& aBaseDomain) override;
+  NS_IMETHOD GetAddonId(nsAString& aAddonId) override;
   bool IsCodebasePrincipal() const override { return true; }
   nsresult GetOriginInternal(nsACString& aOrigin) override;
 
   nsPrincipal();
 
   // Init() must be called before the principal is in a usable state.
   nsresult Init(nsIURI* aCodebase,
                 const mozilla::OriginAttributes& aOriginAttributes);
 
   virtual nsresult GetScriptLocation(nsACString& aStr) override;
-  void SetURI(nsIURI* aURI);
 
   /**
    * Called at startup to setup static data, e.g. about:config pref-observers.
    */
   static void InitializeStatics();
 
-  PrincipalKind Kind() override { return eCodebasePrincipal; }
-
   nsCOMPtr<nsIURI> mDomain;
   nsCOMPtr<nsIURI> mCodebase;
   // If mCodebaseImmutable is true, mCodebase is non-null and immutable
   bool mCodebaseImmutable;
   bool mDomainImmutable;
   bool mInitialized;
 
 protected:
   virtual ~nsPrincipal();
 
   bool SubsumesInternal(nsIPrincipal* aOther, DocumentDomainConsideration aConsideration) override;
   bool MayLoadInternal(nsIURI* aURI) override;
+
+private:
+  mozilla::Maybe<nsString> mAddonIdCache;
 };
 
 #define NS_PRINCIPAL_CONTRACTID "@mozilla.org/principal;1"
 #define NS_PRINCIPAL_CID \
 { 0x653e0e4d, 0x3ee4, 0x45fa, \
   { 0xb2, 0x72, 0x97, 0xc2, 0x0b, 0xc0, 0x1e, 0xb8 } }
 
 #endif // nsPrincipal_h__
--- a/caps/nsScriptSecurityManager.cpp
+++ b/caps/nsScriptSecurityManager.cpp
@@ -339,30 +339,16 @@ nsScriptSecurityManager::GetChannelResul
                 principalToInherit.forget(aPrincipal);
                 return NS_OK;
             }
         }
     }
     return GetChannelURIPrincipal(aChannel, aPrincipal);
 }
 
-nsresult
-nsScriptSecurityManager::MaybeSetAddonIdFromURI(OriginAttributes& aAttrs, nsIURI* aURI)
-{
-  nsAutoCString scheme;
-  nsresult rv = aURI->GetScheme(scheme);
-  NS_ENSURE_SUCCESS(rv, rv);
-  if (scheme.EqualsLiteral("moz-extension") && GetAddonPolicyService()) {
-    rv = GetAddonPolicyService()->ExtensionURIToAddonId(aURI, aAttrs.mAddonId);
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-
-  return NS_OK;
-}
-
 /* The principal of the URI that this channel is loading. This is never
  * affected by things like sandboxed loads, or loads where we forcefully
  * inherit the principal.  Think of this as the principal of the server
  * which this channel is loading from.  Most callers should use
  * GetChannelResultPrincipal instead of GetChannelURIPrincipal.  Only
  * call GetChannelURIPrincipal if you are sure that you want the
  * principal that matches the uri, even in cases when the load is
  * sandboxed or when the load could be a blob or data uri (i.e even when
@@ -390,18 +376,16 @@ nsScriptSecurityManager::GetChannelURIPr
     // For subresource loading, the origin attributes of the loadInfo is from
     // its loadingPrincipal.
     OriginAttributes attrs;
 
     // For addons loadInfo might be null.
     if (loadInfo) {
       attrs.Inherit(loadInfo->GetOriginAttributes());
     }
-    rv = MaybeSetAddonIdFromURI(attrs, uri);
-    NS_ENSURE_SUCCESS(rv, rv);
     nsCOMPtr<nsIPrincipal> prin = BasePrincipal::CreateCodebasePrincipal(uri, attrs);
     prin.forget(aPrincipal);
     return *aPrincipal ? NS_OK : NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
 nsScriptSecurityManager::IsSystemPrincipal(nsIPrincipal* aPrincipal,
                                            bool* aIsSystem)
@@ -1160,33 +1144,29 @@ nsScriptSecurityManager::
   NS_ENSURE_STATE(aLoadContext);
   OriginAttributes docShellAttrs;
   bool result = aLoadContext->GetOriginAttributes(docShellAttrs);;
   NS_ENSURE_TRUE(result, NS_ERROR_FAILURE);
 
   OriginAttributes attrs;
   attrs.Inherit(docShellAttrs);
 
-  nsresult rv = MaybeSetAddonIdFromURI(attrs, aURI);
-  NS_ENSURE_SUCCESS(rv, rv);
   nsCOMPtr<nsIPrincipal> prin = BasePrincipal::CreateCodebasePrincipal(aURI, attrs);
   prin.forget(aPrincipal);
   return *aPrincipal ? NS_OK : NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
 nsScriptSecurityManager::GetDocShellCodebasePrincipal(nsIURI* aURI,
                                                       nsIDocShell* aDocShell,
                                                       nsIPrincipal** aPrincipal)
 {
   OriginAttributes attrs;
   attrs.Inherit(nsDocShell::Cast(aDocShell)->GetOriginAttributes());
 
-  nsresult rv = MaybeSetAddonIdFromURI(attrs, aURI);
-  NS_ENSURE_SUCCESS(rv, rv);
   nsCOMPtr<nsIPrincipal> prin = BasePrincipal::CreateCodebasePrincipal(aURI, attrs);
   prin.forget(aPrincipal);
   return *aPrincipal ? NS_OK : NS_ERROR_FAILURE;
 }
 
 // static
 nsIPrincipal*
 nsScriptSecurityManager::doGetObjectPrincipal(JSObject *aObj)
@@ -1328,17 +1308,17 @@ nsresult nsScriptSecurityManager::Init()
         mozilla::services::GetStringBundleService();
     if (!bundleService)
         return NS_ERROR_FAILURE;
 
     rv = bundleService->CreateBundle("chrome://global/locale/security/caps.properties", &sStrBundle);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Create our system principal singleton
-    RefPtr<nsSystemPrincipal> system = new nsSystemPrincipal();
+    RefPtr<nsSystemPrincipal> system = nsSystemPrincipal::Create();
 
     mSystemPrincipal = system;
 
     //-- Register security check callback in the JS engine
     //   Currently this is used to control access to function.caller
     sContext = danger::GetJSContext();
 
     static const JSSecurityCallbacks securityCallbacks = {
--- a/caps/nsScriptSecurityManager.h
+++ b/caps/nsScriptSecurityManager.h
@@ -106,19 +106,16 @@ private:
     InitPrefs();
 
     inline void
     ScriptSecurityPrefChanged();
 
     inline void
     AddSitesToFileURIWhitelist(const nsCString& aSiteList);
 
-    // If aURI is a moz-extension:// URI, set mAddonId to the associated addon.
-    nsresult MaybeSetAddonIdFromURI(mozilla::OriginAttributes& aAttrs, nsIURI* aURI);
-
     nsresult GetChannelResultPrincipal(nsIChannel* aChannel,
                                        nsIPrincipal** aPrincipal,
                                        bool aIgnoreSandboxing);
 
     nsresult
     CheckLoadURIFlags(nsIURI* aSourceURI, nsIURI* aTargetURI, nsIURI* aSourceBaseURI,
                       nsIURI* aTargetBaseURI, uint32_t aFlags);
 
--- a/caps/nsSystemPrincipal.cpp
+++ b/caps/nsSystemPrincipal.cpp
@@ -26,16 +26,24 @@ NS_IMPL_QUERY_INTERFACE_CI(nsSystemPrinc
                            nsIPrincipal,
                            nsISerializable)
 NS_IMPL_CI_INTERFACE_GETTER(nsSystemPrincipal,
                             nsIPrincipal,
                             nsISerializable)
 
 #define SYSTEM_PRINCIPAL_SPEC "[System Principal]"
 
+already_AddRefed<nsSystemPrincipal>
+nsSystemPrincipal::Create()
+{
+  RefPtr<nsSystemPrincipal> sp = new nsSystemPrincipal();
+  sp->FinishInit();
+  return sp.forget();
+}
+
 nsresult
 nsSystemPrincipal::GetScriptLocation(nsACString &aStr)
 {
     aStr.AssignLiteral(SYSTEM_PRINCIPAL_SPEC);
     return NS_OK;
 }
 
 ///////////////////////////////////////
@@ -108,16 +116,23 @@ nsSystemPrincipal::SetDomain(nsIURI* aDo
 
 NS_IMETHODIMP
 nsSystemPrincipal::GetBaseDomain(nsACString& aBaseDomain)
 {
   // No base domain for chrome.
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsSystemPrincipal::GetAddonId(nsAString& aAddonId)
+{
+  aAddonId.Truncate();
+  return NS_OK;
+};
+
 //////////////////////////////////////////
 // Methods implementing nsISerializable //
 //////////////////////////////////////////
 
 NS_IMETHODIMP
 nsSystemPrincipal::Read(nsIObjectInputStream* aStream)
 {
     // no-op: CID is sufficient to identify the mSystemPrincipal singleton
--- a/caps/nsSystemPrincipal.h
+++ b/caps/nsSystemPrincipal.h
@@ -17,43 +17,47 @@
 #define NS_SYSTEMPRINCIPAL_CID \
 { 0x4a6212db, 0xaccb, 0x11d3, \
 { 0xb7, 0x65, 0x0, 0x60, 0xb0, 0xb6, 0xce, 0xcb }}
 #define NS_SYSTEMPRINCIPAL_CONTRACTID "@mozilla.org/systemprincipal;1"
 
 
 class nsSystemPrincipal final : public mozilla::BasePrincipal
 {
+  nsSystemPrincipal()
+    : BasePrincipal(eSystemPrincipal)
+  {
+  }
+
 public:
+  static already_AddRefed<nsSystemPrincipal> Create();
+
   NS_DECL_NSISERIALIZABLE
   NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr) override;
   NS_IMETHOD GetHashValue(uint32_t* aHashValue) override;
   NS_IMETHOD GetURI(nsIURI** aURI) override;
   NS_IMETHOD GetDomain(nsIURI** aDomain) override;
   NS_IMETHOD SetDomain(nsIURI* aDomain) override;
   NS_IMETHOD GetCsp(nsIContentSecurityPolicy** aCsp) override;
   NS_IMETHOD EnsureCSP(nsIDOMDocument* aDocument, nsIContentSecurityPolicy** aCSP) override;
   NS_IMETHOD GetPreloadCsp(nsIContentSecurityPolicy** aPreloadCSP) override;
   NS_IMETHOD EnsurePreloadCSP(nsIDOMDocument* aDocument, nsIContentSecurityPolicy** aCSP) override;
   NS_IMETHOD GetBaseDomain(nsACString& aBaseDomain) override;
+  NS_IMETHOD GetAddonId(nsAString& aAddonId) override;
   nsresult GetOriginInternal(nsACString& aOrigin) override;
 
-  nsSystemPrincipal() {}
-
   virtual nsresult GetScriptLocation(nsACString &aStr) override;
 
 protected:
   virtual ~nsSystemPrincipal(void) {}
 
   bool SubsumesInternal(nsIPrincipal *aOther, DocumentDomainConsideration aConsideration) override
   {
     return true;
   }
 
   bool MayLoadInternal(nsIURI* aURI) override
   {
     return true;
   }
-
-  PrincipalKind Kind() override { return eSystemPrincipal; }
 };
 
 #endif // nsSystemPrincipal_h__
--- a/caps/tests/mochitest/test_addonMayLoad.html
+++ b/caps/tests/mochitest/test_addonMayLoad.html
@@ -15,71 +15,59 @@ https://bugzilla.mozilla.org/show_bug.cg
   const Cc = Components.classes;
   const Ci = Components.interfaces;
   const Cu = Components.utils;
   Cu.import("resource://gre/modules/Services.jsm");
   let ssm = Services.scriptSecurityManager;
   let aps = Cc["@mozilla.org/addons/policy-service;1"].getService(Ci.nsIAddonPolicyService).wrappedJSObject;
 
   SimpleTest.waitForExplicitFinish();
+  let oldAddonIdCallback = aps.setExtensionURIToAddonIdCallback(uri => uri.host);
   SimpleTest.registerCleanupFunction(function() {
-    aps.setAddonLoadURICallback('addonA', null);
-    aps.setAddonLoadURICallback('addonB', null);
+    aps.setAddonLoadURICallback('addona', null);
+    aps.setAddonLoadURICallback('addonb', null);
+    aps.setExtensionURIToAddonIdCallback(oldAddonIdCallback);
   });
 
   function tryLoad(sb, uri) {
     let p = new Promise(function(resolve, reject) {
       Cu.exportFunction(resolve, sb, { defineAs: "finish" });
       Cu.exportFunction(reject, sb, { defineAs: "error" });
       sb.eval("try { (function () { " +
               "  var xhr = new XMLHttpRequest();" +
               "  xhr.onreadystatechange = function() { if (xhr.readyState == XMLHttpRequest.DONE) { finish(xhr.status == 200); } };" +
               "  xhr.open('GET', '" + uri + "', true);" +
               "  xhr.send();" +
               "})() } catch (e) { error(e); }");
     });
     return p;
   }
 
-  let exampleCom_addonA = new Cu.Sandbox(ssm.createCodebasePrincipal(Services.io.newURI('http://example.com'), {addonId: 'addonA'}),
-                                         {wantGlobalProperties: ['XMLHttpRequest']});
-  let nullPrin_addonA = new Cu.Sandbox(ssm.createNullPrincipal({addonId: 'addonA'}),
-                                       {wantGlobalProperties: ['XMLHttpRequest']});
-  let exampleCom_addonB = new Cu.Sandbox(ssm.createCodebasePrincipal(Services.io.newURI('http://example.com'), {addonId: 'addonB'}),
-                                         {wantGlobalProperties: ['XMLHttpRequest']});
+  let addonA = new Cu.Sandbox(ssm.createCodebasePrincipal(Services.io.newURI('moz-extension://addonA/'), {}),
+                              {wantGlobalProperties: ['XMLHttpRequest']});
+  let addonB = new Cu.Sandbox(ssm.createCodebasePrincipal(Services.io.newURI('moz-extension://addonB/'), {}),
+                              {wantGlobalProperties: ['XMLHttpRequest']});
 
   function uriForDomain(d) { return d + '/tests/caps/tests/mochitest/file_data.txt' }
 
-  tryLoad(exampleCom_addonA, uriForDomain('http://example.com'))
+  tryLoad(addonA, uriForDomain('http://test1.example.org'))
   .then(function(success) {
-    ok(success, "same-origin load should succeed for addon A");
-    return tryLoad(nullPrin_addonA, uriForDomain('http://example.com'));
-  }).then(function(success) {
-    ok(!success, "null-principal load should fail for addon A");
-    return tryLoad(exampleCom_addonB, uriForDomain('http://example.com'));
-  }).then(function(success) {
-    ok(success, "same-origin load should succeed for addon B");
-    return tryLoad(exampleCom_addonA, uriForDomain('http://test1.example.org'));
-  }).then(function(success) {
     ok(!success, "cross-origin load should fail for addon A");
-    aps.setAddonLoadURICallback('addonA', function(uri) { return /test1/.test(uri.host); });
-    aps.setAddonLoadURICallback('addonB', function(uri) { return /test2/.test(uri.host); });
-    return tryLoad(exampleCom_addonA, uriForDomain('http://test1.example.org'));
+    aps.setAddonLoadURICallback('addona', function(uri) { return /test1/.test(uri.host); });
+    aps.setAddonLoadURICallback('addonb', function(uri) { return /test2/.test(uri.host); });
+    return tryLoad(addonA, uriForDomain('http://test1.example.org'));
   }).then(function(success) {
     ok(success, "whitelisted cross-origin load of test1 should succeed for addon A");
-    return tryLoad(nullPrin_addonA, uriForDomain('http://test1.example.org'));
-  }).then(function(success) {
-    ok(!success, "whitelisted null principal load of test1 should still fail for addon A");
-    return tryLoad(exampleCom_addonB, uriForDomain('http://test1.example.org'));
+    return tryLoad(addonB, uriForDomain('http://test1.example.org'));
   }).then(function(success) {
     ok(!success, "non-whitelisted cross-origin load of test1 should fail for addon B");
-    return tryLoad(exampleCom_addonB, uriForDomain('http://test2.example.org'));
+    return tryLoad(addonB, uriForDomain('http://test2.example.org'));
   }).then(function(success) {
     ok(success, "whitelisted cross-origin load of test2 should succeed for addon B");
-    return tryLoad(exampleCom_addonA, uriForDomain('http://test2.example.org'));
+    return tryLoad(addonA, uriForDomain('http://test2.example.org'));
   }).then(function(success) {
     ok(!success, "non-whitelisted cross-origin load of test2 should fail for addon A");
     SimpleTest.finish();
   }, function(e) {
     ok(false, "Rejected promise chain: " + e);
     SimpleTest.finish();
   });
 
--- a/caps/tests/mochitest/test_extensionURL.html
+++ b/caps/tests/mochitest/test_extensionURL.html
@@ -88,17 +88,17 @@ https://bugzilla.mozilla.org/show_bug.cg
       var p = new Promise(function(resolve, reject) {
         ifr.onload = function() {
           ok(true, 'Loaded ' + url);
           var prin = SpecialPowers.wrap(ifr.contentWindow).document.nodePrincipal;
           function stripTrailingSlash(s) { return s.replace(/\/$/, ''); };
           is(stripTrailingSlash(prin.URI.spec), url, 'Principal uri is correct: ' + url);
           function stripPath(s) { return s.replace(/(.*\/\/.+)\/.*/, '$1'); };
           is(prin.originNoSuffix, stripPath(url), 'Principal origin is correct: ' + prin.originNoSuffix);
-          is(prin.originAttributes.addonId, 'imaginaryaddon-' + url[url.indexOf('/') + 2], 'addonId is correct');
+          is(prin.addonId, 'imaginaryaddon-' + url[url.indexOf('/') + 2], 'addonId is correct');
           if (/_blank/.test(url)) {
             is(SpecialPowers.wrap(ifr.contentWindow).document.documentElement.innerHTML,
                '<head></head><body></body>', 'blank document looks right');
           } else {
             is(SpecialPowers.wrap(ifr.contentWindow).document.title, 'resource test file',
                'document looks right');
           }
           ifr.remove();
--- a/caps/tests/unit/test_origin.js
+++ b/caps/tests/unit/test_origin.js
@@ -43,30 +43,28 @@ function checkSandboxOriginAttributes(ar
 }
 
 // utility function useful for debugging
 function printAttrs(name, attrs) {
   do_print(name + " {\n" +
            "\tappId: " + attrs.appId + ",\n" +
            "\tuserContextId: " + attrs.userContextId + ",\n" +
            "\tinIsolatedMozBrowser: " + attrs.inIsolatedMozBrowser + ",\n" +
-           "\taddonId: '" + attrs.addonId + "',\n" +
            "\tprivateBrowsingId: '" + attrs.privateBrowsingId + "',\n" +
            "\tfirstPartyDomain: '" + attrs.firstPartyDomain + "'\n}");
 }
 
 
 function checkValues(attrs, values) {
   values = values || {};
   //printAttrs("attrs", attrs);
   //printAttrs("values", values);
   do_check_eq(attrs.appId, values.appId || 0);
   do_check_eq(attrs.userContextId, values.userContextId || 0);
   do_check_eq(attrs.inIsolatedMozBrowser, values.inIsolatedMozBrowser || false);
-  do_check_eq(attrs.addonId, values.addonId || '');
   do_check_eq(attrs.privateBrowsingId, values.privateBrowsingId || '');
   do_check_eq(attrs.firstPartyDomain, values.firstPartyDomain || '');
 }
 
 function run_test() {
   // Attributeless origins.
   do_check_eq(ssm.getSystemPrincipal().origin, '[System Principal]');
   checkOriginAttributes(ssm.getSystemPrincipal());
@@ -121,21 +119,16 @@ function run_test() {
   checkOriginAttributes(nullPrin_appBrowser, {appId: 42, inIsolatedMozBrowser: true}, '^appId=42&inBrowser=1');
   do_check_eq(exampleOrg_appBrowser.origin, 'http://example.org^appId=42&inBrowser=1');
 
   // App and browser, different domain.
   var exampleCom_appBrowser = ssm.createCodebasePrincipal(makeURI('https://www.example.com:123'), {appId: 42, inIsolatedMozBrowser: true});
   checkOriginAttributes(exampleCom_appBrowser, {appId: 42, inIsolatedMozBrowser: true}, '^appId=42&inBrowser=1');
   do_check_eq(exampleCom_appBrowser.origin, 'https://www.example.com:123^appId=42&inBrowser=1');
 
-  // Addon.
-  var exampleOrg_addon = ssm.createCodebasePrincipal(makeURI('http://example.org'), {addonId: 'dummy'});
-  checkOriginAttributes(exampleOrg_addon, { addonId: "dummy" }, '^addonId=dummy');
-  do_check_eq(exampleOrg_addon.origin, 'http://example.org^addonId=dummy');
-
   // First party Uri
   var exampleOrg_firstPartyDomain = ssm.createCodebasePrincipal(makeURI('http://example.org'), {firstPartyDomain: 'example.org'});
   checkOriginAttributes(exampleOrg_firstPartyDomain, { firstPartyDomain: "example.org" }, '^firstPartyDomain=example.org');
   do_check_eq(exampleOrg_firstPartyDomain.origin, 'http://example.org^firstPartyDomain=example.org');
 
   // Make sure we don't crash when serializing principals with UNKNOWN_APP_ID.
   try {
     let binaryStream = Cc["@mozilla.org/binaryoutputstream;1"].
@@ -150,23 +143,16 @@ function run_test() {
   }
 
 
   // Just userContext.
   var exampleOrg_userContext = ssm.createCodebasePrincipal(makeURI('http://example.org'), {userContextId: 42});
   checkOriginAttributes(exampleOrg_userContext, { userContextId: 42 }, '^userContextId=42');
   do_check_eq(exampleOrg_userContext.origin, 'http://example.org^userContextId=42');
 
-  // UserContext and Addon.
-  var exampleOrg_userContextAddon = ssm.createCodebasePrincipal(makeURI('http://example.org'), {addonId: 'dummy', userContextId: 42});
-  var nullPrin_userContextAddon = ssm.createNullPrincipal({addonId: 'dummy', userContextId: 42});
-  checkOriginAttributes(exampleOrg_userContextAddon, {addonId: 'dummy', userContextId: 42}, '^addonId=dummy&userContextId=42');
-  checkOriginAttributes(nullPrin_userContextAddon, {addonId: 'dummy', userContextId: 42}, '^addonId=dummy&userContextId=42');
-  do_check_eq(exampleOrg_userContextAddon.origin, 'http://example.org^addonId=dummy&userContextId=42');
-
   // UserContext and App.
   var exampleOrg_userContextApp = ssm.createCodebasePrincipal(makeURI('http://example.org'), {appId: 24, userContextId: 42});
   var nullPrin_userContextApp = ssm.createNullPrincipal({appId: 24, userContextId: 42});
   checkOriginAttributes(exampleOrg_userContextApp, {appId: 24, userContextId: 42}, '^appId=24&userContextId=42');
   checkOriginAttributes(nullPrin_userContextApp, {appId: 24, userContextId: 42}, '^appId=24&userContextId=42');
   do_check_eq(exampleOrg_userContextApp.origin, 'http://example.org^appId=24&userContextId=42');
 
   checkSandboxOriginAttributes(null, {});
@@ -180,21 +166,18 @@ function run_test() {
   // Check that all of the above are cross-origin.
   checkCrossOrigin(exampleOrg_app, exampleOrg);
   checkCrossOrigin(exampleOrg_app, nullPrin_app);
   checkCrossOrigin(exampleOrg_browser, exampleOrg_app);
   checkCrossOrigin(exampleOrg_browser, nullPrin_browser);
   checkCrossOrigin(exampleOrg_appBrowser, exampleOrg_app);
   checkCrossOrigin(exampleOrg_appBrowser, nullPrin_appBrowser);
   checkCrossOrigin(exampleOrg_appBrowser, exampleCom_appBrowser);
-  checkCrossOrigin(exampleOrg_addon, exampleOrg);
   checkCrossOrigin(exampleOrg_firstPartyDomain, exampleOrg);
   checkCrossOrigin(exampleOrg_userContext, exampleOrg);
-  checkCrossOrigin(exampleOrg_userContextAddon, exampleOrg);
-  checkCrossOrigin(exampleOrg_userContext, exampleOrg_userContextAddon);
   checkCrossOrigin(exampleOrg_userContext, exampleOrg_userContextApp);
 
   // Check Principal kinds.
   function checkKind(prin, kind) {
     do_check_eq(prin.isNullPrincipal, kind == 'nullPrincipal');
     do_check_eq(prin.isCodebasePrincipal, kind == 'codebasePrincipal');
     do_check_eq(prin.isExpandedPrincipal, kind == 'expandedPrincipal');
     do_check_eq(prin.isSystemPrincipal, kind == 'systemPrincipal');
@@ -213,17 +196,16 @@ function run_test() {
   var emptyAttrs = ChromeUtils.fillNonDefaultOriginAttributes({});
   checkValues(emptyAttrs);
 
   var uri = "http://example.org";
   var tests = [
     [ "", {} ],
     [ "^appId=5", {appId: 5} ],
     [ "^userContextId=3", {userContextId: 3} ],
-    [ "^addonId=fooBar", {addonId: "fooBar"} ],
     [ "^inBrowser=1", {inIsolatedMozBrowser: true} ],
     [ "^firstPartyDomain=example.org", {firstPartyDomain: "example.org"} ],
     [ "^appId=3&inBrowser=1&userContextId=6",
       {appId: 3, userContextId: 6, inIsolatedMozBrowser: true} ] ];
 
   // check that we can create an origin attributes from an origin properly
   tests.forEach(t => {
     let attrs = ChromeUtils.createOriginAttributesFromOrigin(uri + t[0]);
@@ -299,9 +281,31 @@ function run_test() {
     let orig = ChromeUtils.createOriginAttributesFromOrigin(uri + t[0]);
     checkValues(orig, t[1]);
     let mod = orig;
     mod['firstPartyDomain'] = "";
     checkValues(mod, t[2]);
     do_check_eq(ChromeUtils.originAttributesToSuffix(mod), t[3]);
   });
 
+  var fileURI = makeURI('file:///foo/bar').QueryInterface(Ci.nsIFileURL);
+  var fileTests = [
+    [true, fileURI.spec],
+    [false, "file://UNIVERSAL_FILE_URI_ORIGIN"],
+  ];
+  fileTests.forEach(t => {
+    Services.prefs.setBoolPref("security.fileuri.strict_origin_policy", t[0]);
+    var filePrin = ssm.createCodebasePrincipal(fileURI, {});
+    do_check_eq(filePrin.origin, t[1]);
+  });
+  Services.prefs.clearUserPref("security.fileuri.strict_origin_policy");
+
+  var aboutBlankURI = makeURI('about:blank');
+  var aboutBlankPrin = ssm.createCodebasePrincipal(aboutBlankURI, {});
+  var thrown = false;
+  try {
+    aboutBlankPrin.origin;
+  } catch (e) {
+    thrown = true;
+  }
+  do_check_true(thrown);
+
 }
--- a/devtools/client/aboutdebugging/test/browser_addons_debug_webextension.js
+++ b/devtools/client/aboutdebugging/test/browser_addons_debug_webextension.js
@@ -23,18 +23,17 @@ add_task(function* testWebExtensionsTool
     tab, document, debugBtn,
   } = yield setupTestAboutDebuggingWebExtension(ADDON_NAME, ADDON_MANIFEST_PATH);
 
   // Wait for a notification sent by a script evaluated the test addon via
   // the web console.
   let onCustomMessage = new Promise(done => {
     Services.obs.addObserver(function listener(message, topic) {
       let apiMessage = message.wrappedJSObject;
-      if (!apiMessage.originAttributes ||
-          apiMessage.originAttributes.addonId != ADDON_ID) {
+      if (apiMessage.addonId != ADDON_ID) {
         return;
       }
       Services.obs.removeObserver(listener, "console-api-log-event");
       done(apiMessage.arguments);
     }, "console-api-log-event", false);
   });
 
   // Be careful, this JS function is going to be executed in the addon toolbox,
--- a/devtools/client/aboutdebugging/test/browser_addons_debug_webextension_popup.js
+++ b/devtools/client/aboutdebugging/test/browser_addons_debug_webextension_popup.js
@@ -39,18 +39,17 @@ function makeWidgetId(id) {
 add_task(function* testWebExtensionsToolboxSwitchToPopup() {
   let {
     tab, document, debugBtn,
   } = yield setupTestAboutDebuggingWebExtension(ADDON_NAME, ADDON_MANIFEST_PATH);
 
   let onReadyForOpenPopup = new Promise(done => {
     Services.obs.addObserver(function listener(message, topic) {
       let apiMessage = message.wrappedJSObject;
-      if (!apiMessage.originAttributes ||
-          apiMessage.originAttributes.addonId != ADDON_ID) {
+      if (apiMessage.addonId != ADDON_ID) {
         return;
       }
 
       if (apiMessage.arguments[0] == "readyForOpenPopup") {
         Services.obs.removeObserver(listener, "console-api-log-event");
         done();
       }
     }, "console-api-log-event", false);
@@ -144,18 +143,17 @@ add_task(function* testWebExtensionsTool
     env.set("MOZ_TOOLBOX_TEST_SCRIPT", "");
   });
 
   // Wait for a notification sent by a script evaluated the test addon via
   // the web console.
   let onPopupCustomMessage = new Promise(done => {
     Services.obs.addObserver(function listener(message, topic) {
       let apiMessage = message.wrappedJSObject;
-      if (!apiMessage.originAttributes ||
-          apiMessage.originAttributes.addonId != ADDON_ID) {
+      if (apiMessage.addonId != ADDON_ID) {
         return;
       }
 
       if (apiMessage.arguments[0] == "Popup page function called") {
         Services.obs.removeObserver(listener, "console-api-log-event");
         done(apiMessage.arguments);
       }
     }, "console-api-log-event", false);
--- a/devtools/client/framework/test/browser_toolbox_textbox_context_menu.js
+++ b/devtools/client/framework/test/browser_toolbox_textbox_context_menu.js
@@ -31,25 +31,21 @@ add_task(function* () {
   let onContextMenuPopup = once(textboxContextMenu, "popupshowing");
   textboxContextMenu.openPopupAtScreen(0, 0, true);
   yield onContextMenuPopup;
 
   is(cmdUndo.getAttribute("disabled"), "true", "cmdUndo is disabled");
   is(cmdDelete.getAttribute("disabled"), "true", "cmdDelete is disabled");
   is(cmdSelectAll.getAttribute("disabled"), "true", "cmdSelectAll is disabled");
 
-  // Cut/Copy items are enabled in context menu even if there
-  // is no selection. See also Bug 1303033
+  // Cut/Copy/Paste items are enabled in context menu even if there
+  // is no selection. See also Bug 1303033, and 1317322
   is(cmdCut.getAttribute("disabled"), "", "cmdCut is enabled");
   is(cmdCopy.getAttribute("disabled"), "", "cmdCopy is enabled");
-
-  if (isWindows()) {
-    // emptyClipboard only works on Windows (666254), assert paste only for this OS.
-    is(cmdPaste.getAttribute("disabled"), "true", "cmdPaste is disabled");
-  }
+  is(cmdPaste.getAttribute("disabled"), "", "cmdPaste is enabled");
 
   yield cleanup(toolbox);
 });
 
 function* cleanup(toolbox) {
   yield toolbox.destroy();
   gBrowser.removeCurrentTab();
 }
--- a/devtools/client/inspector/computed/test/browser_computed_search-filter_context-menu.js
+++ b/devtools/client/inspector/computed/test/browser_computed_search-filter_context-menu.js
@@ -40,25 +40,21 @@ add_task(function* () {
   EventUtils.synthesizeMouse(searchField, 2, 2,
     {type: "contextmenu", button: 2}, win);
   yield onContextMenuPopup;
 
   is(cmdUndo.getAttribute("disabled"), "true", "cmdUndo is disabled");
   is(cmdDelete.getAttribute("disabled"), "true", "cmdDelete is disabled");
   is(cmdSelectAll.getAttribute("disabled"), "true", "cmdSelectAll is disabled");
 
-  // Cut/Copy items are enabled in context menu even if there
-  // is no selection. See also Bug 1303033
+  // Cut/Copy/Paste items are enabled in context menu even if there is no
+  // selection. See also Bug 1303033, and 1317322
   is(cmdCut.getAttribute("disabled"), "", "cmdCut is enabled");
   is(cmdCopy.getAttribute("disabled"), "", "cmdCopy is enabled");
-
-  if (isWindows()) {
-    // emptyClipboard only works on Windows (666254), assert paste only for this OS.
-    is(cmdPaste.getAttribute("disabled"), "true", "cmdPaste is disabled");
-  }
+  is(cmdPaste.getAttribute("disabled"), "", "cmdPaste is enabled");
 
   info("Closing context menu");
   let onContextMenuHidden = once(searchContextMenu, "popuphidden");
   searchContextMenu.hidePopup();
   yield onContextMenuHidden;
 
   info("Copy text in search field using the context menu");
   searchField.value = TEST_INPUT;
--- a/devtools/client/inspector/rules/test/browser_rules_search-filter_context-menu.js
+++ b/devtools/client/inspector/rules/test/browser_rules_search-filter_context-menu.js
@@ -39,25 +39,21 @@ add_task(function* () {
   EventUtils.synthesizeMouse(searchField, 2, 2,
     {type: "contextmenu", button: 2}, win);
   yield onContextMenuPopup;
 
   is(cmdUndo.getAttribute("disabled"), "true", "cmdUndo is disabled");
   is(cmdDelete.getAttribute("disabled"), "true", "cmdDelete is disabled");
   is(cmdSelectAll.getAttribute("disabled"), "true", "cmdSelectAll is disabled");
 
-  // Cut/Copy items are enabled in context menu even if there
-  // is no selection. See also Bug 1303033
+  // Cut/Copy/Paste items are enabled in context menu even if there is no
+  // selection. See also Bug 1303033, and 1317322
   is(cmdCut.getAttribute("disabled"), "", "cmdCut is enabled");
   is(cmdCopy.getAttribute("disabled"), "", "cmdCopy is enabled");
-
-  if (isWindows()) {
-    // emptyClipboard only works on Windows (666254), assert paste only for this OS.
-    is(cmdPaste.getAttribute("disabled"), "true", "cmdPaste is disabled");
-  }
+  is(cmdPaste.getAttribute("disabled"), "", "cmdPaste is enabled");
 
   info("Closing context menu");
   let onContextMenuHidden = once(searchContextMenu, "popuphidden");
   searchContextMenu.hidePopup();
   yield onContextMenuHidden;
 
   info("Copy text in search field using the context menu");
   searchField.value = TEST_INPUT;
--- a/devtools/client/inspector/test/browser_inspector_search-filter_context-menu.js
+++ b/devtools/client/inspector/test/browser_inspector_search-filter_context-menu.js
@@ -38,24 +38,20 @@ add_task(function* () {
     {type: "contextmenu", button: 2}, win);
   yield onContextMenuPopup;
 
   is(cmdUndo.getAttribute("disabled"), "true", "cmdUndo is disabled");
   is(cmdDelete.getAttribute("disabled"), "true", "cmdDelete is disabled");
   is(cmdSelectAll.getAttribute("disabled"), "true", "cmdSelectAll is disabled");
 
   // Cut/Copy items are enabled in context menu even if there
-  // is no selection. See also Bug 1303033
+  // is no selection. See also Bug 1303033, and 1317322
   is(cmdCut.getAttribute("disabled"), "", "cmdCut is enabled");
   is(cmdCopy.getAttribute("disabled"), "", "cmdCopy is enabled");
-
-  if (isWindows()) {
-    // emptyClipboard only works on Windows (666254), assert paste only for this OS.
-    is(cmdPaste.getAttribute("disabled"), "true", "cmdPaste is disabled");
-  }
+  is(cmdPaste.getAttribute("disabled"), "", "cmdPaste is enabled");
 
   info("Closing context menu");
   let onContextMenuHidden = once(searchContextMenu, "popuphidden");
   searchContextMenu.hidePopup();
   yield onContextMenuHidden;
 
   info("Copy text in search field using the context menu");
   searchBox.value = TEST_INPUT;
--- a/devtools/client/webconsole/new-console-output/test/fixtures/stubs/consoleApi.js
+++ b/devtools/client/webconsole/new-console-output/test/fixtures/stubs/consoleApi.js
@@ -683,16 +683,17 @@ stubPreparedMessages.set("console.log(%c
   ],
   "notes": null
 }));
 
 stubPackets.set("console.log('foobar', 'test')", {
   "from": "server1.conn0.child1/consoleActor2",
   "type": "consoleAPICall",
   "message": {
+    "addonId": "",
     "arguments": [
       "foobar",
       "test"
     ],
     "columnNumber": 27,
     "counter": null,
     "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
@@ -707,16 +708,17 @@ stubPackets.set("console.log('foobar', '
     "category": "webdev"
   }
 });
 
 stubPackets.set("console.log(undefined)", {
   "from": "server1.conn1.child1/consoleActor2",
   "type": "consoleAPICall",
   "message": {
+    "addonId": "",
     "arguments": [
       {
         "type": "undefined"
       }
     ],
     "columnNumber": 27,
     "counter": null,
     "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
@@ -732,16 +734,17 @@ stubPackets.set("console.log(undefined)"
     "category": "webdev"
   }
 });
 
 stubPackets.set("console.warn('danger, will robinson!')", {
   "from": "server1.conn2.child1/consoleActor2",
   "type": "consoleAPICall",
   "message": {
+    "addonId": "",
     "arguments": [
       "danger, will robinson!"
     ],
     "columnNumber": 27,
     "counter": null,
     "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
@@ -755,16 +758,17 @@ stubPackets.set("console.warn('danger, w
     "category": "webdev"
   }
 });
 
 stubPackets.set("console.log(NaN)", {
   "from": "server1.conn3.child1/consoleActor2",
   "type": "consoleAPICall",
   "message": {
+    "addonId": "",
     "arguments": [
       {
         "type": "NaN"
       }
     ],
     "columnNumber": 27,
     "counter": null,
     "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
@@ -780,16 +784,17 @@ stubPackets.set("console.log(NaN)", {
     "category": "webdev"
   }
 });
 
 stubPackets.set("console.log(null)", {
   "from": "server1.conn4.child1/consoleActor2",
   "type": "consoleAPICall",
   "message": {
+    "addonId": "",
     "arguments": [
       {
         "type": "null"
       }
     ],
     "columnNumber": 27,
     "counter": null,
     "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
@@ -805,16 +810,17 @@ stubPackets.set("console.log(null)", {
     "category": "webdev"
   }
 });
 
 stubPackets.set("console.log('鼬')", {
   "from": "server1.conn5.child1/consoleActor2",
   "type": "consoleAPICall",
   "message": {
+    "addonId": "",
     "arguments": [
       "鼬"
     ],
     "columnNumber": 27,
     "counter": null,
     "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
@@ -828,16 +834,17 @@ stubPackets.set("console.log('鼬')", {
     "category": "webdev"
   }
 });
 
 stubPackets.set("console.clear()", {
   "from": "server1.conn6.child1/consoleActor2",
   "type": "consoleAPICall",
   "message": {
+    "addonId": "",
     "arguments": [],
     "columnNumber": 27,
     "counter": null,
     "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
     "level": "clear",
     "lineNumber": 1,
@@ -849,16 +856,17 @@ stubPackets.set("console.clear()", {
     "category": "webdev"
   }
 });
 
 stubPackets.set("console.count('bar')", {
   "from": "server1.conn7.child1/consoleActor2",
   "type": "consoleAPICall",
   "message": {
+    "addonId": "",
     "arguments": [
       "bar"
     ],
     "columnNumber": 27,
     "counter": {
       "count": 1,
       "label": "bar"
     },
@@ -875,16 +883,17 @@ stubPackets.set("console.count('bar')", 
     "category": "webdev"
   }
 });
 
 stubPackets.set("console.assert(false, {message: 'foobar'})", {
   "from": "server1.conn8.child1/consoleActor2",
   "type": "consoleAPICall",
   "message": {
+    "addonId": "",
     "arguments": [
       {
         "type": "object",
         "actor": "server1.conn8.child1/obj31",
         "class": "Object",
         "extensible": true,
         "frozen": false,
         "sealed": false,
@@ -928,16 +937,17 @@ stubPackets.set("console.assert(false, {
     "category": "webdev"
   }
 });
 
 stubPackets.set("console.log('hello \nfrom \rthe \"string world!')", {
   "from": "server1.conn9.child1/consoleActor2",
   "type": "consoleAPICall",
   "message": {
+    "addonId": "",
     "arguments": [
       "hello \nfrom \rthe \"string world!"
     ],
     "columnNumber": 27,
     "counter": null,
     "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
@@ -951,16 +961,17 @@ stubPackets.set("console.log('hello \nfr
     "category": "webdev"
   }
 });
 
 stubPackets.set("console.log('úṇĩçödê țĕșť')", {
   "from": "server1.conn10.child1/consoleActor2",
   "type": "consoleAPICall",
   "message": {
+    "addonId": "",
     "arguments": [
       "úṇĩçödê țĕșť"
     ],
     "columnNumber": 27,
     "counter": null,
     "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
@@ -974,16 +985,17 @@ stubPackets.set("console.log('úṇĩçödê țĕșť')", {
     "category": "webdev"
   }
 });
 
 stubPackets.set("console.dirxml(window)", {
   "from": "server1.conn11.child1/consoleActor2",
   "type": "consoleAPICall",
   "message": {
+    "addonId": "",
     "arguments": [
       {
         "type": "object",
         "actor": "server1.conn11.child1/obj31",
         "class": "Window",
         "extensible": true,
         "frozen": false,
         "sealed": false,
@@ -1009,16 +1021,17 @@ stubPackets.set("console.dirxml(window)"
     "category": "webdev"
   }
 });
 
 stubPackets.set("console.trace()", {
   "from": "server1.conn12.child1/consoleActor2",
   "type": "consoleAPICall",
   "message": {
+    "addonId": "",
     "arguments": [],
     "columnNumber": 3,
     "counter": null,
     "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "testStacktraceFiltering",
     "groupName": "",
     "level": "trace",
     "lineNumber": 3,
@@ -1053,16 +1066,17 @@ stubPackets.set("console.trace()", {
     "category": "webdev"
   }
 });
 
 stubPackets.set("console.time('bar')", {
   "from": "server1.conn13.child1/consoleActor2",
   "type": "consoleAPICall",
   "message": {
+    "addonId": "",
     "arguments": [
       "bar"
     ],
     "columnNumber": 1,
     "counter": null,
     "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
@@ -1078,16 +1092,17 @@ stubPackets.set("console.time('bar')", {
     "category": "webdev"
   }
 });
 
 stubPackets.set("console.timeEnd('bar')", {
   "from": "server1.conn13.child1/consoleActor2",
   "type": "consoleAPICall",
   "message": {
+    "addonId": "",
     "arguments": [
       "bar"
     ],
     "columnNumber": 1,
     "counter": null,
     "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
@@ -1104,16 +1119,17 @@ stubPackets.set("console.timeEnd('bar')"
     "category": "webdev"
   }
 });
 
 stubPackets.set("console.table('bar')", {
   "from": "server1.conn14.child1/consoleActor2",
   "type": "consoleAPICall",
   "message": {
+    "addonId": "",
     "arguments": [
       "bar"
     ],
     "columnNumber": 1,
     "counter": null,
     "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
@@ -1127,16 +1143,17 @@ stubPackets.set("console.table('bar')", 
     "category": "webdev"
   }
 });
 
 stubPackets.set("console.table(['a', 'b', 'c'])", {
   "from": "server1.conn15.child1/consoleActor2",
   "type": "consoleAPICall",
   "message": {
+    "addonId": "",
     "arguments": [
       {
         "type": "object",
         "actor": "server1.conn15.child1/obj31",
         "class": "Array",
         "extensible": true,
         "frozen": false,
         "sealed": false,
@@ -1167,16 +1184,17 @@ stubPackets.set("console.table(['a', 'b'
     "category": "webdev"
   }
 });
 
 stubPackets.set("console.group('bar')", {
   "from": "server1.conn16.child1/consoleActor2",
   "type": "consoleAPICall",
   "message": {
+    "addonId": "",
     "arguments": [
       "bar"
     ],
     "columnNumber": 1,
     "counter": null,
     "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "bar",
@@ -1190,16 +1208,17 @@ stubPackets.set("console.group('bar')", 
     "category": "webdev"
   }
 });
 
 stubPackets.set("console.groupEnd('bar')", {
   "from": "server1.conn16.child1/consoleActor2",
   "type": "consoleAPICall",
   "message": {
+    "addonId": "",
     "arguments": [
       "bar"
     ],
     "columnNumber": 1,
     "counter": null,
     "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "bar",
@@ -1213,16 +1232,17 @@ stubPackets.set("console.groupEnd('bar')
     "category": "webdev"
   }
 });
 
 stubPackets.set("console.groupCollapsed('foo')", {
   "from": "server1.conn17.child1/consoleActor2",
   "type": "consoleAPICall",
   "message": {
+    "addonId": "",
     "arguments": [
       "foo"
     ],
     "columnNumber": 1,
     "counter": null,
     "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "foo",
@@ -1236,16 +1256,17 @@ stubPackets.set("console.groupCollapsed(
     "category": "webdev"
   }
 });
 
 stubPackets.set("console.groupEnd('foo')", {
   "from": "server1.conn17.child1/consoleActor2",
   "type": "consoleAPICall",
   "message": {
+    "addonId": "",
     "arguments": [
       "foo"
     ],
     "columnNumber": 1,
     "counter": null,
     "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "foo",
@@ -1259,16 +1280,17 @@ stubPackets.set("console.groupEnd('foo')
     "category": "webdev"
   }
 });
 
 stubPackets.set("console.group()", {
   "from": "server1.conn18.child1/consoleActor2",
   "type": "consoleAPICall",
   "message": {
+    "addonId": "",
     "arguments": [],
     "columnNumber": 1,
     "counter": null,
     "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
     "level": "group",
     "lineNumber": 2,
@@ -1280,16 +1302,17 @@ stubPackets.set("console.group()", {
     "category": "webdev"
   }
 });
 
 stubPackets.set("console.groupEnd()", {
   "from": "server1.conn18.child1/consoleActor2",
   "type": "consoleAPICall",
   "message": {
+    "addonId": "",
     "arguments": [],
     "columnNumber": 1,
     "counter": null,
     "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
     "groupName": "",
     "level": "groupEnd",
     "lineNumber": 3,
@@ -1301,16 +1324,17 @@ stubPackets.set("console.groupEnd()", {
     "category": "webdev"
   }
 });
 
 stubPackets.set("console.log(%cfoobar)", {
   "from": "server1.conn19.child1/consoleActor2",
   "type": "consoleAPICall",
   "message": {
+    "addonId": "",
     "arguments": [
       "foo",
       "bar"
     ],
     "columnNumber": 1,
     "counter": null,
     "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "functionName": "triggerPacket",
--- a/devtools/client/webconsole/new-console-output/test/fixtures/stubs/networkEvent.js
+++ b/devtools/client/webconsole/new-console-output/test/fixtures/stubs/networkEvent.js
@@ -156,24 +156,24 @@ stubPackets.set("GET request", {
       {
         "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-network-event.html",
         "lineNumber": 3,
         "columnNumber": 1,
         "functionName": "triggerPacket",
         "asyncCause": null
       },
       {
-        "filename": "chrome://mochikit/content/tests/BrowserTestUtils/content-task.js line 52 > eval",
+        "filename": "resource://testing-common/content-task.js line 52 > eval",
         "lineNumber": 7,
         "columnNumber": 9,
         "functionName": null,
         "asyncCause": null
       },
       {
-        "filename": "chrome://mochikit/content/tests/BrowserTestUtils/content-task.js",
+        "filename": "resource://testing-common/content-task.js",
         "lineNumber": 53,
         "columnNumber": 20,
         "functionName": null,
         "asyncCause": null
       }
     ]
   },
   "response": {},
@@ -211,24 +211,24 @@ stubPackets.set("GET request eventTiming
         {
           "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-network-event.html",
           "lineNumber": 3,
           "columnNumber": 1,
           "functionName": "triggerPacket",
           "asyncCause": null
         },
         {
-          "filename": "chrome://mochikit/content/tests/BrowserTestUtils/content-task.js line 52 > eval",
+          "filename": "resource://testing-common/content-task.js line 52 > eval",
           "lineNumber": 7,
           "columnNumber": 9,
           "functionName": null,
           "asyncCause": null
         },
         {
-          "filename": "chrome://mochikit/content/tests/BrowserTestUtils/content-task.js",
+          "filename": "resource://testing-common/content-task.js",
           "lineNumber": 53,
           "columnNumber": 20,
           "functionName": null,
           "asyncCause": null
         }
       ]
     },
     "response": {
@@ -275,24 +275,24 @@ stubPackets.set("XHR GET request", {
       {
         "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-network-event.html",
         "lineNumber": 4,
         "columnNumber": 1,
         "functionName": "triggerPacket",
         "asyncCause": null
       },
       {
-        "filename": "chrome://mochikit/content/tests/BrowserTestUtils/content-task.js line 52 > eval",
+        "filename": "resource://testing-common/content-task.js line 52 > eval",
         "lineNumber": 7,
         "columnNumber": 9,
         "functionName": null,
         "asyncCause": null
       },
       {
-        "filename": "chrome://mochikit/content/tests/BrowserTestUtils/content-task.js",
+        "filename": "resource://testing-common/content-task.js",
         "lineNumber": 53,
         "columnNumber": 20,
         "functionName": null,
         "asyncCause": null
       }
     ]
   },
   "response": {},
@@ -330,24 +330,24 @@ stubPackets.set("XHR GET request eventTi
         {
           "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-network-event.html",
           "lineNumber": 4,
           "columnNumber": 1,
           "functionName": "triggerPacket",
           "asyncCause": null
         },
         {
-          "filename": "chrome://mochikit/content/tests/BrowserTestUtils/content-task.js line 52 > eval",
+          "filename": "resource://testing-common/content-task.js line 52 > eval",
           "lineNumber": 7,
           "columnNumber": 9,
           "functionName": null,
           "asyncCause": null
         },
         {
-          "filename": "chrome://mochikit/content/tests/BrowserTestUtils/content-task.js",
+          "filename": "resource://testing-common/content-task.js",
           "lineNumber": 53,
           "columnNumber": 20,
           "functionName": null,
           "asyncCause": null
         }
       ]
     },
     "response": {
@@ -394,24 +394,24 @@ stubPackets.set("XHR POST request", {
       {
         "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-network-event.html",
         "lineNumber": 4,
         "columnNumber": 1,
         "functionName": "triggerPacket",
         "asyncCause": null
       },
       {
-        "filename": "chrome://mochikit/content/tests/BrowserTestUtils/content-task.js line 52 > eval",
+        "filename": "resource://testing-common/content-task.js line 52 > eval",
         "lineNumber": 7,
         "columnNumber": 9,
         "functionName": null,
         "asyncCause": null
       },
       {
-        "filename": "chrome://mochikit/content/tests/BrowserTestUtils/content-task.js",
+        "filename": "resource://testing-common/content-task.js",
         "lineNumber": 53,
         "columnNumber": 20,
         "functionName": null,
         "asyncCause": null
       }
     ]
   },
   "response": {},
@@ -449,24 +449,24 @@ stubPackets.set("XHR POST request eventT
         {
           "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-network-event.html",
           "lineNumber": 4,
           "columnNumber": 1,
           "functionName": "triggerPacket",
           "asyncCause": null
         },
         {
-          "filename": "chrome://mochikit/content/tests/BrowserTestUtils/content-task.js line 52 > eval",
+          "filename": "resource://testing-common/content-task.js line 52 > eval",
           "lineNumber": 7,
           "columnNumber": 9,
           "functionName": null,
           "asyncCause": null
         },
         {
-          "filename": "chrome://mochikit/content/tests/BrowserTestUtils/content-task.js",
+          "filename": "resource://testing-common/content-task.js",
           "lineNumber": 53,
           "columnNumber": 20,
           "functionName": null,
           "asyncCause": null
         }
       ]
     },
     "response": {
--- a/devtools/client/webconsole/new-console-output/test/fixtures/stubs/pageError.js
+++ b/devtools/client/webconsole/new-console-output/test/fixtures/stubs/pageError.js
@@ -18,17 +18,17 @@ stubPreparedMessages.set("ReferenceError
   "allowRepeating": true,
   "source": "javascript",
   "timeStamp": 1476573167137,
   "type": "log",
   "level": "error",
   "messageText": "ReferenceError: asdf is not defined",
   "parameters": null,
   "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"javascript\",\"timeStamp\":1476573167137,\"type\":\"log\",\"level\":\"error\",\"messageText\":\"ReferenceError: asdf is not defined\",\"parameters\":null,\"repeatId\":null,\"stacktrace\":[{\"filename\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"lineNumber\":3,\"columnNumber\":5,\"functionName\":\"bar\"},{\"filename\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"lineNumber\":6,\"columnNumber\":5,\"functionName\":\"foo\"},{\"filename\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"lineNumber\":9,\"columnNumber\":3,\"functionName\":null},{\"filename\":\"chrome://mochikit/content/tests/BrowserTestUtils/content-task.js line 52 > eval\",\"lineNumber\":6,\"columnNumber\":9,\"functionName\":null},{\"filename\":\"chrome://mochikit/content/tests/BrowserTestUtils/content-task.js\",\"lineNumber\":53,\"columnNumber\":20,\"functionName\":null}],\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":3,\"column\":5},\"groupId\":null,\"exceptionDocURL\":\"https://developer.mozilla.org/docs/Web/JavaScript/Reference/Errors/Not_defined?utm_source=mozilla&utm_medium=firefox-console-errors&utm_campaign=default\",\"userProvidedStyles\":null,\"notes\":null}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"javascript\",\"timeStamp\":1476573167137,\"type\":\"log\",\"level\":\"error\",\"messageText\":\"ReferenceError: asdf is not defined\",\"parameters\":null,\"repeatId\":null,\"stacktrace\":[{\"filename\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"lineNumber\":3,\"columnNumber\":5,\"functionName\":\"bar\"},{\"filename\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"lineNumber\":6,\"columnNumber\":5,\"functionName\":\"foo\"},{\"filename\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"lineNumber\":9,\"columnNumber\":3,\"functionName\":null},{\"filename\":\"resource://testing-common/content-task.js line 52 > eval\",\"lineNumber\":6,\"columnNumber\":9,\"functionName\":null},{\"filename\":\"resource://testing-common/content-task.js\",\"lineNumber\":53,\"columnNumber\":20,\"functionName\":null}],\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":3,\"column\":5},\"groupId\":null,\"exceptionDocURL\":\"https://developer.mozilla.org/docs/Web/JavaScript/Reference/Errors/Not_defined?utm_source=mozilla&utm_medium=firefox-console-errors&utm_campaign=default\",\"userProvidedStyles\":null,\"notes\":null}",
   "stacktrace": [
     {
       "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
       "lineNumber": 3,
       "columnNumber": 5,
       "functionName": "bar"
     },
     {
@@ -39,23 +39,23 @@ stubPreparedMessages.set("ReferenceError
     },
     {
       "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
       "lineNumber": 9,
       "columnNumber": 3,
       "functionName": null
     },
     {
-      "filename": "chrome://mochikit/content/tests/BrowserTestUtils/content-task.js line 52 > eval",
+      "filename": "resource://testing-common/content-task.js line 52 > eval",
       "lineNumber": 6,
       "columnNumber": 9,
       "functionName": null
     },
     {
-      "filename": "chrome://mochikit/content/tests/BrowserTestUtils/content-task.js",
+      "filename": "resource://testing-common/content-task.js",
       "lineNumber": 53,
       "columnNumber": 20,
       "functionName": null
     }
   ],
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "line": 3,
@@ -72,26 +72,26 @@ stubPreparedMessages.set("SyntaxError: r
   "allowRepeating": true,
   "source": "javascript",
   "timeStamp": 1487992945524,
   "type": "log",
   "level": "error",
   "messageText": "SyntaxError: redeclaration of let a",
   "parameters": null,
   "repeat": 1,
-  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"javascript\",\"timeStamp\":1487992945524,\"type\":\"log\",\"level\":\"error\",\"messageText\":\"SyntaxError: redeclaration of let a\",\"parameters\":null,\"repeatId\":null,\"stacktrace\":[{\"filename\":\"chrome://mochikit/content/tests/BrowserTestUtils/content-task.js line 52 > eval\",\"lineNumber\":6,\"columnNumber\":9,\"functionName\":null},{\"filename\":\"chrome://mochikit/content/tests/BrowserTestUtils/content-task.js\",\"lineNumber\":53,\"columnNumber\":20,\"functionName\":null}],\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":2,\"column\":9},\"groupId\":null,\"userProvidedStyles\":null,\"notes\":[{\"messageBody\":\"Previously declared at line 2, column 6\",\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":2,\"column\":6}}]}",
+  "repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"javascript\",\"timeStamp\":1487992945524,\"type\":\"log\",\"level\":\"error\",\"messageText\":\"SyntaxError: redeclaration of let a\",\"parameters\":null,\"repeatId\":null,\"stacktrace\":[{\"filename\":\"resource://testing-common/content-task.js line 52 > eval\",\"lineNumber\":6,\"columnNumber\":9,\"functionName\":null},{\"filename\":\"resource://testing-common/content-task.js\",\"lineNumber\":53,\"columnNumber\":20,\"functionName\":null}],\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":2,\"column\":9},\"groupId\":null,\"userProvidedStyles\":null,\"notes\":[{\"messageBody\":\"Previously declared at line 2, column 6\",\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html\",\"line\":2,\"column\":6}}]}",
   "stacktrace": [
     {
-      "filename": "chrome://mochikit/content/tests/BrowserTestUtils/content-task.js line 52 > eval",
+      "filename": "resource://testing-common/content-task.js line 52 > eval",
       "lineNumber": 6,
       "columnNumber": 9,
       "functionName": null
     },
     {
-      "filename": "chrome://mochikit/content/tests/BrowserTestUtils/content-task.js",
+      "filename": "resource://testing-common/content-task.js",
       "lineNumber": 53,
       "columnNumber": 20,
       "functionName": null
     }
   ],
   "frame": {
     "source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
     "line": 2,
@@ -145,23 +145,23 @@ stubPackets.set("ReferenceError: asdf is
       },
       {
         "filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html",
         "lineNumber": 9,
         "columnNumber": 3,
         "functionName": null
       },
       {
-        "filename": "chrome://mochikit/content/tests/BrowserTestUtils/content-task.js line 52 > eval",
+        "filename": "resource://testing-common/content-task.js line 52 > eval",
         "lineNumber": 6,
         "columnNumber": 9,
         "functionName": null
       },
       {
-        "filename": "chrome://mochikit/content/tests/BrowserTestUtils/content-task.js",
+        "filename": "resource://testing-common/content-task.js",
         "lineNumber": 53,
         "columnNumber": 20,
         "functionName": null
       }
     ],
     "notes": null
   }
 });
@@ -181,23 +181,23 @@ stubPackets.set("SyntaxError: redeclarat
     "warning": false,
     "error": false,
     "exception": true,
     "strict": false,
     "info": false,
     "private": false,
     "stacktrace": [
       {
-        "filename": "chrome://mochikit/content/tests/BrowserTestUtils/content-task.js line 52 > eval",
+        "filename": "resource://testing-common/content-task.js line 52 > eval",
         "lineNumber": 6,
         "columnNumber": 9,
         "functionName": null
       },
       {
-        "filename": "chrome://mochikit/content/tests/BrowserTestUtils/content-task.js",
+        "filename": "resource://testing-common/content-task.js",
         "lineNumber": 53,
         "columnNumber": 20,
         "functionName": null
       }
     ],
     "notes": [
       {
         "messageBody": "Previously declared at line 2, column 6",
--- a/devtools/server/actors/tab.js
+++ b/devtools/server/actors/tab.js
@@ -728,17 +728,17 @@ TabActor.prototype = {
       if (window.parent && window != this._originalWindow) {
         parentID = window.parent
                          .QueryInterface(Ci.nsIInterfaceRequestor)
                          .getInterface(Ci.nsIDOMWindowUtils)
                          .outerWindowID;
       }
 
       // Collect the addonID from the document origin attributes.
-      let addonID = window.document.nodePrincipal.originAttributes.addonId;
+      let addonID = window.document.nodePrincipal.addonId;
 
       return {
         id,
         parentID,
         addonID,
         url: window.location.href,
         title: window.document.title,
       };
--- a/devtools/server/actors/utils/webconsole-listeners.js
+++ b/devtools/server/actors/utils/webconsole-listeners.js
@@ -296,18 +296,17 @@ ConsoleAPIListener.prototype =
     if (this.addonId) {
       // ConsoleAPI.jsm messages contains a consoleID, (and it is currently
       // used in Addon SDK add-ons), the standard 'console' object
       // (which is used in regular webpages and in WebExtensions pages)
       // contains the originAttributes of the source document principal.
 
       // Filtering based on the originAttributes used by
       // the Console API object.
-      if (message.originAttributes &&
-          message.originAttributes.addonId == this.addonId) {
+      if (message.addonId == this.addonId) {
         return true;
       }
 
       // Filtering based on the old-style consoleID property used by
       // the legacy Console JSM module.
       if (message.consoleID && message.consoleID == `addon/${this.addonId}`) {
         return true;
       }
--- a/devtools/server/actors/webextension.js
+++ b/devtools/server/actors/webextension.js
@@ -304,17 +304,17 @@ WebExtensionActor.prototype._allowSource
 /**
  * Return true if the given global is associated with this addon and should be
  * added as a debuggee, false otherwise.
  */
 WebExtensionActor.prototype._shouldAddNewGlobalAsDebuggee = function (newGlobal) {
   const global = unwrapDebuggerObjectGlobal(newGlobal);
 
   if (global instanceof Ci.nsIDOMWindow) {
-    return global.document.nodePrincipal.originAttributes.addonId == this.id;
+    return global.document.nodePrincipal.addonId == this.id;
   }
 
   try {
     // This will fail for non-Sandbox objects, hence the try-catch block.
     let metadata = Cu.getSandboxMetadata(global);
     if (metadata) {
       return metadata.addonID === this.id;
     }
--- a/devtools/shared/tests/unit/test_console_filtering.js
+++ b/devtools/shared/tests/unit/test_console_filtering.js
@@ -2,58 +2,67 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 const { console, ConsoleAPI } = require("resource://gre/modules/Console.jsm");
 const { ConsoleAPIListener } = require("devtools/server/actors/utils/webconsole-listeners");
 const Services = require("Services");
 
+// FIXME: This test shouldn't need to rely on low-level internals.
+const {Service} = Cu.import("resource://gre/modules/ExtensionManagement.jsm", {});
+
 var seenMessages = 0;
 var seenTypes = 0;
 
 var callback = {
   onConsoleAPICall: function (message) {
     if (message.consoleID && message.consoleID == "addon/foo") {
       do_check_eq(message.level, "warn");
       do_check_eq(message.arguments[0], "Warning from foo");
       seenTypes |= 1;
-    } else if (message.originAttributes &&
-              message.originAttributes.addonId == "bar") {
+    } else if (message.addonId == "bar") {
       do_check_eq(message.level, "error");
       do_check_eq(message.arguments[0], "Error from bar");
       seenTypes |= 2;
     } else {
       do_check_eq(message.level, "log");
       do_check_eq(message.arguments[0], "Hello from default console");
       seenTypes |= 4;
     }
     seenMessages++;
   }
 };
 
 function createFakeAddonWindow({addonId} = {}) {
-  let baseURI = Services.io.newURI("about:blank");
-  let originAttributes = {addonId};
+  const uuidGen = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator);
+  const uuid = uuidGen.generateUUID().number.slice(1, -1);
+
+  const url = `moz-extension://${uuid}/`;
+  Service.uuidMap.set(uuid, {id: addonId});
+
+  let baseURI = Services.io.newURI(url);
   let principal = Services.scriptSecurityManager
-        .createCodebasePrincipal(baseURI, originAttributes);
+        .createCodebasePrincipal(baseURI, {});
   let chromeWebNav = Services.appShell.createWindowlessBrowser(true);
   let docShell = chromeWebNav.QueryInterface(Ci.nsIInterfaceRequestor)
                              .getInterface(Ci.nsIDocShell);
   docShell.createAboutBlankContentViewer(principal);
   let addonWindow = docShell.contentViewer.DOMDocument.defaultView;
 
   return {addonWindow, chromeWebNav};
 }
 
 /**
  * Tests that the consoleID property of the ConsoleAPI options gets passed
  * through to console messages.
  */
 function run_test() {
+  Service.init();
+
   // console1 Test Console.jsm messages tagged by the Addon SDK
   // are still filtered correctly.
   let console1 = new ConsoleAPI({
     consoleID: "addon/foo"
   });
 
   // console2 - WebExtension page's console messages tagged
   // by 'originAttributes.addonId' are filtered correctly.
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -9,17 +9,16 @@
 #include <algorithm>
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/AutoRestore.h"
 #include "mozilla/BasePrincipal.h"
 #include "mozilla/Casting.h"
 #include "mozilla/dom/ContentChild.h"
-#include "mozilla/dom/ChromeUtils.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/PendingGlobalHistoryEntry.h"
 #include "mozilla/dom/TabChild.h"
 #include "mozilla/dom/ProfileTimelineMarkerBinding.h"
 #include "mozilla/dom/ScreenOrientation.h"
 #include "mozilla/dom/ToJSValue.h"
 #include "mozilla/dom/PermissionMessageUtils.h"
 #include "mozilla/dom/workers/ServiceWorkerManager.h"
@@ -8032,19 +8031,17 @@ nsDocShell::CreateAboutBlankContentViewe
   // mContentViewer->PermitUnload may release |this| docshell.
   nsCOMPtr<nsIDocShell> kungFuDeathGrip(this);
 
   AutoRestore<bool> creatingDocument(mCreatingDocument);
   mCreatingDocument = true;
 
   if (aPrincipal && !nsContentUtils::IsSystemPrincipal(aPrincipal) &&
       mItemType != typeChrome) {
-    MOZ_ASSERT(ChromeUtils::IsOriginAttributesEqualIgnoringAddonId(
-      aPrincipal->OriginAttributesRef(),
-      mOriginAttributes));
+    MOZ_ASSERT(aPrincipal->OriginAttributesRef() == mOriginAttributes);
   }
 
   // Make sure timing is created.  But first record whether we had it
   // already, so we don't clobber the timing for an in-progress load.
   bool hadTiming = mTiming;
   bool toBeReset = MaybeInitTiming();
   if (mContentViewer) {
     if (aCheckPermitUnload) {
@@ -12370,17 +12367,16 @@ nsDocShell::AddToSessionHistory(nsIURI* 
         if (loadInfo->GetLoadingSandboxed()) {
           if (loadInfo->LoadingPrincipal()) {
             principalToInherit = nsNullPrincipal::CreateWithInheritedAttributes(
             loadInfo->LoadingPrincipal());
           } else {
             // get the OriginAttributes
             OriginAttributes attrs;
             loadInfo->GetOriginAttributes(&attrs);
-            attrs.StripAttributes(OriginAttributes::STRIP_ADDON_ID);
             principalToInherit = nsNullPrincipal::Create(attrs);
           }
         } else {
           principalToInherit = loadInfo->PrincipalToInherit();
         }
       }
     }
   }
--- a/dom/asmjscache/AsmJSCache.cpp
+++ b/dom/asmjscache/AsmJSCache.cpp
@@ -26,17 +26,16 @@
 #include "mozilla/ipc/BackgroundParent.h"
 #include "mozilla/ipc/BackgroundUtils.h"
 #include "mozilla/ipc/PBackgroundChild.h"
 #include "mozilla/Unused.h"
 #include "nsAutoPtr.h"
 #include "nsIAtom.h"
 #include "nsIFile.h"
 #include "nsIIPCBackgroundChildCreateCallback.h"
-#include "nsIPermissionManager.h"
 #include "nsIPrincipal.h"
 #include "nsIRunnable.h"
 #include "nsISimpleEnumerator.h"
 #include "nsIThread.h"
 #include "nsJSPrincipals.h"
 #include "nsThreadUtils.h"
 #include "nsXULAppAPI.h"
 #include "prio.h"
@@ -345,21 +344,19 @@ public:
 
   ParentRunnable(const PrincipalInfo& aPrincipalInfo,
                  OpenMode aOpenMode,
                  WriteParams aWriteParams)
   : mOwningThread(NS_GetCurrentThread()),
     mPrincipalInfo(aPrincipalInfo),
     mOpenMode(aOpenMode),
     mWriteParams(aWriteParams),
-    mPersistence(quota::PERSISTENCE_TYPE_INVALID),
     mState(eInitial),
     mResult(JS::AsmJSCache_InternalError),
-    mIsApp(false),
-    mEnforcingQuota(true),
+    mDeleteReceived(false),
     mActorDestroyed(false),
     mOpened(false)
   {
     MOZ_ASSERT(XRE_IsParentProcess());
     AssertIsOnOwningThread();
   }
 
 private:
@@ -388,41 +385,16 @@ private:
 
   void
   AssertIsOnNonOwningThread() const
   {
     MOZ_ASSERT(!IsOnBackgroundThread());
     MOZ_ASSERT(!IsOnOwningThread());
   }
 
-  // This method is called on the owning thread when no cache entry was found
-  // to open. If we just tried a lookup in persistent storage then we might
-  // still get a hit in temporary storage (for an asm.js module that wasn't
-  // compiled at install-time).
-  void
-  CacheMiss()
-  {
-    AssertIsOnOwningThread();
-    MOZ_ASSERT(mState == eFailedToReadMetadata ||
-               mState == eWaitingToOpenCacheFileForRead);
-    MOZ_ASSERT(mOpenMode == eOpenForRead);
-
-    if (mPersistence == quota::PERSISTENCE_TYPE_TEMPORARY) {
-      Fail();
-      return;
-    }
-
-    // Try again with a clean slate. InitOnMainThread will see that mPersistence
-    // is initialized and switch to temporary storage.
-    MOZ_ASSERT(mPersistence == quota::PERSISTENCE_TYPE_PERSISTENT);
-    FinishOnOwningThread();
-    mState = eInitial;
-    NS_DispatchToMainThread(this);
-  }
-
   // This method is called on the owning thread when the JS engine is finished
   // reading/writing the cache entry.
   void
   Close()
   {
     AssertIsOnOwningThread();
     MOZ_ASSERT(mState == eOpened);
 
@@ -442,17 +414,17 @@ private:
     MOZ_ASSERT(mState != eFinished);
 
     mState = eFinished;
 
     MOZ_ASSERT(!mOpened);
 
     FinishOnOwningThread();
 
-    if (!mActorDestroyed) {
+    if (!mDeleteReceived && !mActorDestroyed) {
       Unused << Send__delete__(this, mResult);
     }
   }
 
   // The same as method above but is intended to be called off the owning
   // thread.
   void
   FailOnNonOwningThread()
@@ -461,19 +433,16 @@ private:
     MOZ_ASSERT(mState != eOpened &&
                mState != eFailing &&
                mState != eFinished);
 
     mState = eFailing;
     MOZ_ALWAYS_SUCCEEDS(mOwningThread->Dispatch(this, NS_DISPATCH_NORMAL));
   }
 
-  void
-  InitPersistenceType();
-
   nsresult
   InitOnMainThread();
 
   void
   OpenDirectory();
 
   nsresult
   ReadMetadata();
@@ -514,16 +483,19 @@ private:
   DirectoryLockFailed() override;
 
   // IPDL methods.
   mozilla::ipc::IPCResult
   Recv__delete__(const JS::AsmJSCacheResult& aResult) override
   {
     AssertIsOnOwningThread();
     MOZ_ASSERT(mState != eFinished);
+    MOZ_ASSERT(!mDeleteReceived);
+
+    mDeleteReceived = true;
 
     if (mOpened) {
       Close();
     } else {
       Fail();
     }
 
     MOZ_ASSERT(mState == eFinished);
@@ -566,33 +538,22 @@ private:
 
     mModuleIndex = aModuleIndex;
     mState = eReadyToOpenCacheFileForRead;
     DispatchToIOThread();
 
     return IPC_OK();
   }
 
-  mozilla::ipc::IPCResult
-  RecvCacheMiss() override
-  {
-    AssertIsOnOwningThread();
-
-    CacheMiss();
-
-    return IPC_OK();
-  }
-
   nsCOMPtr<nsIEventTarget> mOwningThread;
   const PrincipalInfo mPrincipalInfo;
   const OpenMode mOpenMode;
   const WriteParams mWriteParams;
 
   // State initialized during eInitial:
-  quota::PersistenceType mPersistence;
   nsCString mSuffix;
   nsCString mGroup;
   nsCString mOrigin;
   RefPtr<DirectoryLock> mDirectoryLock;
 
   // State initialized during eReadyToReadMetadata
   nsCOMPtr<nsIFile> mDirectory;
   nsCOMPtr<nsIFile> mMetadataFile;
@@ -602,138 +563,84 @@ private:
   unsigned mModuleIndex;
 
   enum State {
     eInitial, // Just created, waiting to be dispatched to main thread
     eWaitingToFinishInit, // Waiting to finish initialization
     eWaitingToOpenDirectory, // Waiting to open directory
     eWaitingToOpenMetadata, // Waiting to be called back from OpenDirectory
     eReadyToReadMetadata, // Waiting to read the metadata file on the IO thread
-    eFailedToReadMetadata, // Waiting to be dispatched to owning thread after fail
     eSendingMetadataForRead, // Waiting to send OnOpenMetadataForRead
     eWaitingToOpenCacheFileForRead, // Waiting to hear back from child
     eReadyToOpenCacheFileForRead, // Waiting to open cache file for read
     eSendingCacheFile, // Waiting to send OnOpenCacheFile on the owning thread
     eOpened, // Finished calling OnOpenCacheFile, waiting to be closed
     eFailing, // Just failed, waiting to be dispatched to the owning thread
     eFinished, // Terminal state
   };
   State mState;
   JS::AsmJSCacheResult mResult;
 
-  bool mIsApp;
-  bool mEnforcingQuota;
+  bool mDeleteReceived;
   bool mActorDestroyed;
   bool mOpened;
 };
 
-void
-ParentRunnable::InitPersistenceType()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(mState == eInitial);
-
-  if (mOpenMode == eOpenForWrite) {
-    MOZ_ASSERT(mPersistence == quota::PERSISTENCE_TYPE_INVALID);
-
-    // If we are performing install-time caching of an app, we'd like to store
-    // the cache entry in persistent storage so the entry is never evicted,
-    // but we need to check that quota is not enforced for the app.
-    // That justifies us in skipping all quota checks when storing the cache
-    // entry and avoids all the issues around the persistent quota prompt.
-    // If quota is enforced for the app, then we can still cache in temporary
-    // for a likely good first-run experience.
-
-    MOZ_ASSERT_IF(mWriteParams.mInstalled, mIsApp);
-
-    if (mWriteParams.mInstalled &&
-        !QuotaManager::IsQuotaEnforced(quota::PERSISTENCE_TYPE_PERSISTENT,
-                                       mOrigin, mIsApp)) {
-      mPersistence = quota::PERSISTENCE_TYPE_PERSISTENT;
-    } else {
-      mPersistence = quota::PERSISTENCE_TYPE_TEMPORARY;
-    }
-
-    return;
-  }
-
-  // For the reasons described above, apps may have cache entries in both
-  // persistent and temporary storage. At lookup time we don't know how and
-  // where the given script was cached, so start the search in persistent
-  // storage and, if that fails, search in temporary storage. (Non-apps can
-  // only be stored in temporary storage.)
-
-  MOZ_ASSERT_IF(mPersistence != quota::PERSISTENCE_TYPE_INVALID,
-                mIsApp && mPersistence == quota::PERSISTENCE_TYPE_PERSISTENT);
-
-  if (mPersistence == quota::PERSISTENCE_TYPE_INVALID && mIsApp) {
-    mPersistence = quota::PERSISTENCE_TYPE_PERSISTENT;
-  } else {
-    mPersistence = quota::PERSISTENCE_TYPE_TEMPORARY;
-  }
-}
-
 nsresult
 ParentRunnable::InitOnMainThread()
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mState == eInitial);
   MOZ_ASSERT(mPrincipalInfo.type() != PrincipalInfo::TNullPrincipalInfo);
 
   nsresult rv;
   nsCOMPtr<nsIPrincipal> principal =
     PrincipalInfoToPrincipal(mPrincipalInfo, &rv);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   rv = QuotaManager::GetInfoFromPrincipal(principal, &mSuffix, &mGroup,
-                                          &mOrigin, &mIsApp);
+                                          &mOrigin);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  InitPersistenceType();
-
-  mEnforcingQuota =
-    QuotaManager::IsQuotaEnforced(mPersistence, mOrigin, mIsApp);
-
   return NS_OK;
 }
 
 void
 ParentRunnable::OpenDirectory()
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(mState == eWaitingToFinishInit ||
              mState == eWaitingToOpenDirectory);
   MOZ_ASSERT(QuotaManager::Get());
 
   mState = eWaitingToOpenMetadata;
 
   // XXX The exclusive lock shouldn't be needed for read operations.
-  QuotaManager::Get()->OpenDirectory(mPersistence,
+  QuotaManager::Get()->OpenDirectory(quota::PERSISTENCE_TYPE_TEMPORARY,
                                      mGroup,
                                      mOrigin,
-                                     mIsApp,
                                      quota::Client::ASMJS,
                                      /* aExclusive */ true,
                                      this);
 }
 
 nsresult
 ParentRunnable::ReadMetadata()
 {
   AssertIsOnIOThread();
   MOZ_ASSERT(mState == eReadyToReadMetadata);
 
   QuotaManager* qm = QuotaManager::Get();
   MOZ_ASSERT(qm, "We are on the QuotaManager's IO thread");
 
   nsresult rv =
-    qm->EnsureOriginIsInitialized(mPersistence, mSuffix, mGroup, mOrigin,
-                                  mIsApp, getter_AddRefs(mDirectory));
+    qm->EnsureOriginIsInitialized(quota::PERSISTENCE_TYPE_TEMPORARY, mSuffix,
+                                  mGroup, mOrigin, getter_AddRefs(mDirectory));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     mResult = JS::AsmJSCache_StorageInitFailure;
     return rv;
   }
 
   rv = mDirectory->Append(NS_LITERAL_STRING(ASMJSCACHE_DIRECTORY_NAME));
   NS_ENSURE_SUCCESS(rv, rv);
 
@@ -794,35 +701,34 @@ ParentRunnable::OpenCacheFileForWrite()
 
   nsCOMPtr<nsIFile> file;
   nsresult rv = GetCacheFile(mDirectory, mModuleIndex, getter_AddRefs(file));
   NS_ENSURE_SUCCESS(rv, rv);
 
   QuotaManager* qm = QuotaManager::Get();
   MOZ_ASSERT(qm, "We are on the QuotaManager's IO thread");
 
-  if (mEnforcingQuota) {
-    // Create the QuotaObject before all file IO and keep it alive until caching
-    // completes to get maximum assertion coverage in QuotaManager against
-    // concurrent removal, etc.
-    mQuotaObject = qm->GetQuotaObject(mPersistence, mGroup, mOrigin, file);
-    NS_ENSURE_STATE(mQuotaObject);
+  // Create the QuotaObject before all file IO and keep it alive until caching
+  // completes to get maximum assertion coverage in QuotaManager against
+  // concurrent removal, etc.
+  mQuotaObject = qm->GetQuotaObject(quota::PERSISTENCE_TYPE_TEMPORARY, mGroup,
+                                    mOrigin, file);
+  NS_ENSURE_STATE(mQuotaObject);
 
+  if (!mQuotaObject->MaybeUpdateSize(mWriteParams.mSize,
+                                     /* aTruncate */ false)) {
+    // If the request fails, it might be because mOrigin is using too much
+    // space (MaybeUpdateSize will not evict our own origin since it is
+    // active). Try to make some space by evicting LRU entries until there is
+    // enough space.
+    EvictEntries(mDirectory, mGroup, mOrigin, mWriteParams.mSize, mMetadata);
     if (!mQuotaObject->MaybeUpdateSize(mWriteParams.mSize,
                                        /* aTruncate */ false)) {
-      // If the request fails, it might be because mOrigin is using too much
-      // space (MaybeUpdateSize will not evict our own origin since it is
-      // active). Try to make some space by evicting LRU entries until there is
-      // enough space.
-      EvictEntries(mDirectory, mGroup, mOrigin, mWriteParams.mSize, mMetadata);
-      if (!mQuotaObject->MaybeUpdateSize(mWriteParams.mSize,
-                                         /* aTruncate */ false)) {
-        mResult = JS::AsmJSCache_QuotaExceeded;
-        return NS_ERROR_FAILURE;
-      }
+      mResult = JS::AsmJSCache_QuotaExceeded;
+      return NS_ERROR_FAILURE;
     }
   }
 
   int32_t openFlags = PR_RDWR | PR_TRUNCATE | PR_CREATE_FILE;
   rv = file->OpenNSPRFileDesc(openFlags, 0644, &mFileDesc);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Move the mModuleIndex's LRU entry to the recent end of the queue.
@@ -848,23 +754,22 @@ ParentRunnable::OpenCacheFileForRead()
 
   nsCOMPtr<nsIFile> file;
   nsresult rv = GetCacheFile(mDirectory, mModuleIndex, getter_AddRefs(file));
   NS_ENSURE_SUCCESS(rv, rv);
 
   QuotaManager* qm = QuotaManager::Get();
   MOZ_ASSERT(qm, "We are on the QuotaManager's IO thread");
 
-  if (mEnforcingQuota) {
-    // Even though it's not strictly necessary, create the QuotaObject before
-    // all file IO and keep it alive until caching completes to get maximum
-    // assertion coverage in QuotaManager against concurrent removal, etc.
-    mQuotaObject = qm->GetQuotaObject(mPersistence, mGroup, mOrigin, file);
-    NS_ENSURE_STATE(mQuotaObject);
-  }
+  // Even though it's not strictly necessary, create the QuotaObject before all
+  // file IO and keep it alive until caching completes to get maximum assertion
+  // coverage in QuotaManager against concurrent removal, etc.
+  mQuotaObject = qm->GetQuotaObject(quota::PERSISTENCE_TYPE_TEMPORARY, mGroup,
+                                    mOrigin, file);
+  NS_ENSURE_STATE(mQuotaObject);
 
   rv = file->GetFileSize(&mFileSize);
   NS_ENSURE_SUCCESS(rv, rv);
 
   int32_t openFlags = PR_RDONLY | nsIFile::OS_READAHEAD;
   rv = file->OpenNSPRFileDesc(openFlags, 0644, &mFileDesc);
   NS_ENSURE_SUCCESS(rv, rv);
 
@@ -951,18 +856,17 @@ ParentRunnable::Run()
       return NS_OK;
     }
 
     case eReadyToReadMetadata: {
       AssertIsOnIOThread();
 
       rv = ReadMetadata();
       if (NS_FAILED(rv)) {
-        mState = eFailedToReadMetadata;
-        MOZ_ALWAYS_SUCCEEDS(mOwningThread->Dispatch(this, NS_DISPATCH_NORMAL));
+        FailOnNonOwningThread();
         return NS_OK;
       }
 
       if (mOpenMode == eOpenForRead) {
         mState = eSendingMetadataForRead;
         MOZ_ALWAYS_SUCCEEDS(mOwningThread->Dispatch(this, NS_DISPATCH_NORMAL));
 
         return NS_OK;
@@ -974,28 +878,16 @@ ParentRunnable::Run()
         return NS_OK;
       }
 
       mState = eSendingCacheFile;
       MOZ_ALWAYS_SUCCEEDS(mOwningThread->Dispatch(this, NS_DISPATCH_NORMAL));
       return NS_OK;
     }
 
-    case eFailedToReadMetadata: {
-      AssertIsOnOwningThread();
-
-      if (mOpenMode == eOpenForRead) {
-        CacheMiss();
-        return NS_OK;
-      }
-
-      Fail();
-      return NS_OK;
-    }
-
     case eSendingMetadataForRead: {
       AssertIsOnOwningThread();
       MOZ_ASSERT(mOpenMode == eOpenForRead);
 
       mState = eWaitingToOpenCacheFileForRead;
 
       // Metadata is now open.
       if (!SendOnOpenMetadataForRead(mMetadata)) {
@@ -1284,24 +1176,23 @@ private:
   // IPDL methods.
   mozilla::ipc::IPCResult
   RecvOnOpenMetadataForRead(const Metadata& aMetadata) override
   {
     MOZ_ASSERT(NS_IsMainThread());
     MOZ_ASSERT(mState == eOpening);
 
     uint32_t moduleIndex;
-    if (FindHashMatch(aMetadata, mReadParams, &moduleIndex)) {
-      if (!SendSelectCacheFileToRead(moduleIndex)) {
-        return IPC_FAIL_NO_REASON(this);
-      }
+    if (!FindHashMatch(aMetadata, mReadParams, &moduleIndex)) {
+      Fail(JS::AsmJSCache_InternalError);
+      Send__delete__(this, JS::AsmJSCache_InternalError);
       return IPC_OK();
     }
 
-    if (!SendCacheMiss()) {
+    if (!SendSelectCacheFileToRead(moduleIndex)) {
       return IPC_FAIL_NO_REASON(this);
     }
     return IPC_OK();
   }
 
   mozilla::ipc::IPCResult
   RecvOnOpenCacheFile(const int64_t& aFileSize,
                       const FileDescriptor& aFileDesc) override
@@ -1646,34 +1537,32 @@ CloseEntryForRead(size_t aSize,
 
   MOZ_ASSERT(aSize + sizeof(AsmJSCookieType) == childRunnable->FileSize());
   MOZ_ASSERT(aMemory - sizeof(AsmJSCookieType) ==
                childRunnable->MappedMemory());
 }
 
 JS::AsmJSCacheResult
 OpenEntryForWrite(nsIPrincipal* aPrincipal,
-                  bool aInstalled,
                   const char16_t* aBegin,
                   const char16_t* aEnd,
                   size_t aSize,
                   uint8_t** aMemory,
                   intptr_t* aHandle)
 {
   if (size_t(aEnd - aBegin) < sMinCachedModuleLength) {
     return JS::AsmJSCache_ModuleTooSmall;
   }
 
   // Add extra space for the AsmJSCookieType (see OpenEntryForRead).
   aSize += sizeof(AsmJSCookieType);
 
   static_assert(sNumFastHashChars < sMinCachedModuleLength, "HashString safe");
 
   WriteParams writeParams;
-  writeParams.mInstalled = aInstalled;
   writeParams.mSize = aSize;
   writeParams.mFastHash = HashString(aBegin, sNumFastHashChars);
   writeParams.mNumChars = aEnd - aBegin;
   writeParams.mFullHash = HashString(aBegin, writeParams.mNumChars);
 
   ChildRunnable::AutoClose childRunnable;
   ReadParams notARead;
   JS::AsmJSCacheResult openResult =
@@ -1885,33 +1774,30 @@ ParamTraits<Metadata>::Log(const paramTy
 
 void
 ParamTraits<WriteParams>::Write(Message* aMsg, const paramType& aParam)
 {
   WriteParam(aMsg, aParam.mSize);
   WriteParam(aMsg, aParam.mFastHash);
   WriteParam(aMsg, aParam.mNumChars);
   WriteParam(aMsg, aParam.mFullHash);
-  WriteParam(aMsg, aParam.mInstalled);
 }
 
 bool
 ParamTraits<WriteParams>::Read(const Message* aMsg, PickleIterator* aIter,
                                paramType* aResult)
 {
   return ReadParam(aMsg, aIter, &aResult->mSize) &&
          ReadParam(aMsg, aIter, &aResult->mFastHash) &&
          ReadParam(aMsg, aIter, &aResult->mNumChars) &&
-         ReadParam(aMsg, aIter, &aResult->mFullHash) &&
-         ReadParam(aMsg, aIter, &aResult->mInstalled);
+         ReadParam(aMsg, aIter, &aResult->mFullHash);
 }
 
 void
 ParamTraits<WriteParams>::Log(const paramType& aParam, std::wstring* aLog)
 {
   LogParam(aParam.mSize, aLog);
   LogParam(aParam.mFastHash, aLog);
   LogParam(aParam.mNumChars, aLog);
   LogParam(aParam.mFullHash, aLog);
-  LogParam(aParam.mInstalled, aLog);
 }
 
 } // namespace IPC
--- a/dom/asmjscache/AsmJSCache.h
+++ b/dom/asmjscache/AsmJSCache.h
@@ -68,24 +68,22 @@ struct Metadata
 
 // Parameters specific to opening a cache entry for writing
 struct WriteParams
 {
   int64_t mSize;
   int64_t mFastHash;
   int64_t mNumChars;
   int64_t mFullHash;
-  bool mInstalled;
 
   WriteParams()
   : mSize(0),
     mFastHash(0),
     mNumChars(0),
-    mFullHash(0),
-    mInstalled(false)
+    mFullHash(0)
   { }
 };
 
 // Parameters specific to opening a cache entry for reading
 struct ReadParams
 {
   const char16_t* mBegin;
   const char16_t* mLimit;
@@ -116,17 +114,16 @@ OpenEntryForRead(nsIPrincipal* aPrincipa
                  const uint8_t** aMemory,
                  intptr_t *aHandle);
 void
 CloseEntryForRead(size_t aSize,
                   const uint8_t* aMemory,
                   intptr_t aHandle);
 JS::AsmJSCacheResult
 OpenEntryForWrite(nsIPrincipal* aPrincipal,
-                  bool aInstalled,
                   const char16_t* aBegin,
                   const char16_t* aEnd,
                   size_t aSize,
                   uint8_t** aMemory,
                   intptr_t* aHandle);
 void
 CloseEntryForWrite(size_t aSize,
                    uint8_t* aMemory,
--- a/dom/asmjscache/PAsmJSCacheEntry.ipdl
+++ b/dom/asmjscache/PAsmJSCacheEntry.ipdl
@@ -17,17 +17,16 @@ protocol PAsmJSCacheEntry
 
   // When the cache is opened to read, the parent process sends over the
   // origin's Metadata so the child process can select the cache entry to open
   // (based on hash) and notify the parent (via SelectCacheFileToRead).
 child:
   async OnOpenMetadataForRead(Metadata metadata);
 parent:
   async SelectCacheFileToRead(uint32_t moduleIndex);
-  async CacheMiss();
 
 child:
   // Once the cache file has been opened, the child is notified and sent an
   // open file descriptor.
   async OnOpenCacheFile(int64_t fileSize, FileDescriptor fileDesc);
 
 both:
   async __delete__(AsmJSCacheResult result);
--- a/dom/base/ChromeUtils.cpp
+++ b/dom/base/ChromeUtils.cpp
@@ -177,38 +177,16 @@ ChromeUtils::IsOriginAttributesEqual(dom
 {
   return IsOriginAttributesEqual(aA, aB);
 }
 
 /* static */ bool
 ChromeUtils::IsOriginAttributesEqual(const dom::OriginAttributesDictionary& aA,
                                      const dom::OriginAttributesDictionary& aB)
 {
-  return aA.mAddonId == aB.mAddonId &&
-         aA.mAppId == aB.mAppId &&
-         aA.mInIsolatedMozBrowser == aB.mInIsolatedMozBrowser &&
-         aA.mUserContextId == aB.mUserContextId &&
-         aA.mPrivateBrowsingId == aB.mPrivateBrowsingId;
-}
-
-/* static */ bool
-ChromeUtils::IsOriginAttributesEqualIgnoringAddonId(const dom::OriginAttributesDictionary& aA,
-                                                    const dom::OriginAttributesDictionary& aB)
-{
   return aA.mAppId == aB.mAppId &&
          aA.mInIsolatedMozBrowser == aB.mInIsolatedMozBrowser &&
          aA.mUserContextId == aB.mUserContextId &&
          aA.mPrivateBrowsingId == aB.mPrivateBrowsingId;
 }
 
-/* static */ bool
-ChromeUtils::IsOriginAttributesEqualIgnoringFPD(const dom::OriginAttributesDictionary& aA,
-                                                const dom::OriginAttributesDictionary& aB)
-{
-  return aA.mAddonId == aB.mAddonId &&
-         aA.mAppId == aB.mAppId &&
-         aA.mInIsolatedMozBrowser == aB.mInIsolatedMozBrowser &&
-         aA.mUserContextId == aB.mUserContextId &&
-         aA.mPrivateBrowsingId == aB.mPrivateBrowsingId;
-}
-
 } // namespace dom
 } // namespace mozilla
--- a/dom/base/ChromeUtils.h
+++ b/dom/base/ChromeUtils.h
@@ -88,20 +88,22 @@ public:
                           const dom::OriginAttributesDictionary& aA,
                           const dom::OriginAttributesDictionary& aB);
 
   static bool
   IsOriginAttributesEqual(const dom::OriginAttributesDictionary& aA,
                           const dom::OriginAttributesDictionary& aB);
 
   static bool
-  IsOriginAttributesEqualIgnoringAddonId(const dom::OriginAttributesDictionary& aA,
-                                         const dom::OriginAttributesDictionary& aB);
-
-  static bool
   IsOriginAttributesEqualIgnoringFPD(const dom::OriginAttributesDictionary& aA,
-                                     const dom::OriginAttributesDictionary& aB);
+                                     const dom::OriginAttributesDictionary& aB)
+  {
+    return aA.mAppId == aB.mAppId &&
+           aA.mInIsolatedMozBrowser == aB.mInIsolatedMozBrowser &&
+           aA.mUserContextId == aB.mUserContextId &&
+           aA.mPrivateBrowsingId == aB.mPrivateBrowsingId;
+  }
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_ChromeUtils__
--- a/dom/base/PostMessageEvent.cpp
+++ b/dom/base/PostMessageEvent.cpp
@@ -100,17 +100,17 @@ PostMessageEvent::Run()
     if (NS_WARN_IF(!targetPrin))
       return NS_OK;
 
     // Note: This is contrary to the spec with respect to file: URLs, which
     //       the spec groups into a single origin, but given we intentionally
     //       don't do that in other places it seems better to hold the line for
     //       now.  Long-term, we want HTML5 to address this so that we can
     //       be compliant while being safer.
-    if (!BasePrincipal::Cast(targetPrin)->EqualsIgnoringAddonId(mProvidedPrincipal)) {
+    if (!targetPrin->Equals(mProvidedPrincipal)) {
       nsAutoString providedOrigin, targetOrigin;
       nsresult rv = nsContentUtils::GetUTFOrigin(targetPrin, targetOrigin);
       NS_ENSURE_SUCCESS(rv, rv);
       rv = nsContentUtils::GetUTFOrigin(mProvidedPrincipal, providedOrigin);
       NS_ENSURE_SUCCESS(rv, rv);
 
       MOZ_DIAGNOSTIC_ASSERT(providedOrigin != targetOrigin ||
                             (mProvidedPrincipal->OriginAttributesRef() ==
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -3187,36 +3187,36 @@ nsContentUtils::GetOriginAttributes(nsID
 
   nsCOMPtr<nsILoadGroup> loadGroup = aDocument->GetDocumentLoadGroup();
   if (loadGroup) {
     return GetOriginAttributes(loadGroup);
   }
 
   mozilla::OriginAttributes attrs;
   nsCOMPtr<nsIChannel> channel = aDocument->GetChannel();
-  if (channel && NS_GetOriginAttributes(channel, attrs)) {
-    attrs.StripAttributes(OriginAttributes::STRIP_ADDON_ID);
+  if (channel) {
+    NS_GetOriginAttributes(channel, attrs);
   }
   return attrs;
 }
 
 // static
 mozilla::OriginAttributes
 nsContentUtils::GetOriginAttributes(nsILoadGroup* aLoadGroup)
 {
   if (!aLoadGroup) {
     return mozilla::OriginAttributes();
   }
   mozilla::OriginAttributes attrs;
   nsCOMPtr<nsIInterfaceRequestor> callbacks;
   aLoadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
   if (callbacks) {
     nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(callbacks);
-    if (loadContext && loadContext->GetOriginAttributes(attrs)) {
-      attrs.StripAttributes(OriginAttributes::STRIP_ADDON_ID);
+    if (loadContext) {
+      loadContext->GetOriginAttributes(attrs);
     }
   }
   return attrs;
 }
 
 // static
 bool
 nsContentUtils::IsInPrivateBrowsing(nsIDocument* aDoc)
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -3054,18 +3054,17 @@ nsDOMWindowUtils::GetFileReferences(cons
                                     int32_t* aSliceRefCnt, JSContext* aCx,
                                     bool* aResult)
 {
   nsCOMPtr<nsPIDOMWindowOuter> window = do_QueryReferent(mWindow);
   NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
 
   nsCString origin;
   nsresult rv =
-    quota::QuotaManager::GetInfoFromWindow(window, nullptr, nullptr, &origin,
-                                           nullptr);
+    quota::QuotaManager::GetInfoFromWindow(window, nullptr, nullptr, &origin);
   NS_ENSURE_SUCCESS(rv, rv);
 
   IDBOpenDBOptions options;
   JS::Rooted<JS::Value> optionsVal(aCx, aOptions);
   if (!options.Init(aCx, optionsVal)) {
     return NS_ERROR_TYPE_ERR;
   }
 
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -3170,17 +3170,16 @@ nsGlobalWindow::SetNewDocument(nsIDocume
   nsCOMPtr<nsIPrincipal> principal = mDoc->NodePrincipal();
   nsString addonId;
   principal->GetAddonId(addonId);
   if (GetDocGroup() && !nsContentUtils::IsSystemPrincipal(principal) && addonId.IsEmpty()) {
     js::SetCompartmentValidAccessPtr(cx, newInnerGlobal,
                                      newInnerWindow->GetDocGroup()->GetValidAccessPtr());
   }
 
-  nsJSContext::PokeGC(JS::gcreason::SET_NEW_DOCUMENT, GetWrapperPreserveColor());
   kungFuDeathGrip->DidInitializeContext();
 
   // We wait to fire the debugger hook until the window is all set up and hooked
   // up with the outer. See bug 969156.
   if (createdInnerWindow) {
     nsContentUtils::AddScriptRunner(
       NewRunnableMethod(newInnerWindow,
                         &nsGlobalWindow::FireOnNewGlobalObject));
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -1178,18 +1178,19 @@ FullGCTimerFired(nsITimer* aTimer, void*
 
 //static
 void
 nsJSContext::GarbageCollectNow(JS::gcreason::Reason aReason,
                                IsIncremental aIncremental,
                                IsShrinking aShrinking,
                                int64_t aSliceMillis)
 {
-  PROFILER_LABEL("nsJSContext", "GarbageCollectNow",
-    js::ProfileEntry::Category::GC);
+  PROFILER_LABEL_PRINTF("nsJSContext", "GarbageCollectNow",
+                        js::ProfileEntry::Category::GC,
+                        "%s", JS::gcreason::ExplainReason(aReason));
 
   MOZ_ASSERT_IF(aSliceMillis, aIncremental == IncrementalGC);
 
   KillGCTimer();
 
   // Reset sPendingLoadCount in case the timer that fired was a
   // timer we scheduled due to a normal GC timer firing while
   // documents were loading. If this happens we're waiting for a
@@ -2395,27 +2396,26 @@ AsmJSCacheOpenEntryForRead(JS::Handle<JS
   nsIPrincipal* principal =
     nsJSPrincipals::get(JS_GetCompartmentPrincipals(js::GetObjectCompartment(aGlobal)));
   return asmjscache::OpenEntryForRead(principal, aBegin, aLimit, aSize, aMemory,
                                       aHandle);
 }
 
 static JS::AsmJSCacheResult
 AsmJSCacheOpenEntryForWrite(JS::Handle<JSObject*> aGlobal,
-                            bool aInstalled,
                             const char16_t* aBegin,
                             const char16_t* aEnd,
                             size_t aSize,
                             uint8_t** aMemory,
                             intptr_t* aHandle)
 {
   nsIPrincipal* principal =
     nsJSPrincipals::get(JS_GetCompartmentPrincipals(js::GetObjectCompartment(aGlobal)));
-  return asmjscache::OpenEntryForWrite(principal, aInstalled, aBegin, aEnd,
-                                       aSize, aMemory, aHandle);
+  return asmjscache::OpenEntryForWrite(principal, aBegin, aEnd, aSize, aMemory,
+                                       aHandle);
 }
 
 class AsyncTaskRunnable final : public Runnable
 {
   ~AsyncTaskRunnable()
   {
     MOZ_ASSERT(!mTask);
   }
--- a/dom/cache/Context.cpp
+++ b/dom/cache/Context.cpp
@@ -255,17 +255,16 @@ Context::QuotaInitRunnable::OpenDirector
 
   // QuotaManager::OpenDirectory() will hold a reference to us as
   // a listener.  We will then get DirectoryLockAcquired() on the owning
   // thread when it is safe to access our storage directory.
   mState = STATE_WAIT_FOR_DIRECTORY_LOCK;
   QuotaManager::Get()->OpenDirectory(PERSISTENCE_TYPE_DEFAULT,
                                      mQuotaInfo.mGroup,
                                      mQuotaInfo.mOrigin,
-                                     mQuotaInfo.mIsApp,
                                      quota::Client::DOMCACHE,
                                      /* aExclusive */ false,
                                      this);
 }
 
 void
 Context::QuotaInitRunnable::DirectoryLockAcquired(DirectoryLock* aLock)
 {
@@ -372,18 +371,17 @@ Context::QuotaInitRunnable::Run()
         break;
       }
 
       RefPtr<ManagerId> managerId = mManager->GetManagerId();
       nsCOMPtr<nsIPrincipal> principal = managerId->Principal();
       nsresult rv = QuotaManager::GetInfoFromPrincipal(principal,
                                                        &mQuotaInfo.mSuffix,
                                                        &mQuotaInfo.mGroup,
-                                                       &mQuotaInfo.mOrigin,
-                                                       &mQuotaInfo.mIsApp);
+                                                       &mQuotaInfo.mOrigin);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         resolver->Resolve(rv);
         break;
       }
 
       mState = STATE_CREATE_QUOTA_MANAGER;
       MOZ_ALWAYS_SUCCEEDS(
         mInitiatingThread->Dispatch(this, nsIThread::DISPATCH_NORMAL));
@@ -432,17 +430,16 @@ Context::QuotaInitRunnable::Run()
       }
 
       QuotaManager* qm = QuotaManager::Get();
       MOZ_DIAGNOSTIC_ASSERT(qm);
       nsresult rv = qm->EnsureOriginIsInitialized(PERSISTENCE_TYPE_DEFAULT,
                                                   mQuotaInfo.mSuffix,
                                                   mQuotaInfo.mGroup,
                                                   mQuotaInfo.mOrigin,
-                                                  mQuotaInfo.mIsApp,
                                                   getter_AddRefs(mQuotaInfo.mDir));
       if (NS_FAILED(rv)) {
         resolver->Resolve(rv);
         break;
       }
 
       mState = STATE_RUN_ON_TARGET;
 
--- a/dom/cache/ManagerId.cpp
+++ b/dom/cache/ManagerId.cpp
@@ -25,18 +25,17 @@ ManagerId::Create(nsIPrincipal* aPrincip
 
   // The QuotaManager::GetInfoFromPrincipal() has special logic for system
   // and about: principals.  We need to use the same modified origin in
   // order to interpret calls from QM correctly.
   nsCString quotaOrigin;
   nsresult rv = QuotaManager::GetInfoFromPrincipal(aPrincipal,
                                                    nullptr,   // suffix
                                                    nullptr,   // group
-                                                   &quotaOrigin,
-                                                   nullptr);  // is app
+                                                   &quotaOrigin);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   RefPtr<ManagerId> ref = new ManagerId(aPrincipal, quotaOrigin);
   ref.forget(aManagerIdOut);
 
   return NS_OK;
 }
 
--- a/dom/cache/Types.h
+++ b/dom/cache/Types.h
@@ -24,21 +24,19 @@ enum Namespace
 };
 static const Namespace INVALID_NAMESPACE = NUMBER_OF_NAMESPACES;
 
 typedef int64_t CacheId;
 static const CacheId INVALID_CACHE_ID = -1;
 
 struct QuotaInfo
 {
-  QuotaInfo() : mIsApp(false) { }
   nsCOMPtr<nsIFile> mDir;
   nsCString mSuffix;
   nsCString mGroup;
   nsCString mOrigin;
-  bool mIsApp;
 };
 
 } // namespace cache
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_cache_Types_h
--- a/dom/cache/test/mochitest/mochitest.ini
+++ b/dom/cache/test/mochitest/mochitest.ini
@@ -38,10 +38,11 @@ support-files =
 [test_cache_delete.html]
 [test_cache_put_reorder.html]
 [test_cache_https.html]
 [test_cache_redirect.html]
 [test_cache_restart.html]
 [test_cache_shrink.html]
 [test_cache_orphaned_cache.html]
 [test_cache_orphaned_body.html]
+scheme=https
 [test_cache_untrusted.html]
 [test_chrome_constructor.html]
--- a/dom/console/Console.cpp
+++ b/dom/console/Console.cpp
@@ -152,16 +152,25 @@ public:
   }
 
   void
   SetOriginAttributes(const OriginAttributes& aOriginAttributes)
   {
     mOriginAttributes = aOriginAttributes;
   }
 
+  void
+  SetAddonId(nsIPrincipal* aPrincipal)
+  {
+    nsAutoString addonId;
+    aPrincipal->GetAddonId(addonId);
+
+    mAddonId = addonId;
+  }
+
   bool
   PopulateArgumentsSequence(Sequence<JS::Value>& aSequence) const
   {
     AssertIsOnOwningThread();
 
     for (uint32_t i = 0; i < mCopiedArguments.Length(); ++i) {
       if (NS_WARN_IF(!aSequence.AppendElement(mCopiedArguments[i],
                                               fallible))) {
@@ -245,16 +254,18 @@ public:
   uint64_t mOuterIDNumber;
   nsString mOuterIDString;
 
   uint64_t mInnerIDNumber;
   nsString mInnerIDString;
 
   OriginAttributes mOriginAttributes;
 
+  nsString mAddonId;
+
   nsString mMethodString;
 
   // Stack management is complicated, because we want to do it as
   // lazily as possible.  Therefore, we have the following behavior:
   // 1)  mTopStackFrame is initialized whenever we have any JS on the stack
   // 2)  mReifiedStack is initialized if we're created in a worker.
   // 3)  mStack is set (possibly to null if there is no JS on the stack) if
   //     we're created on main thread.
@@ -1212,16 +1223,17 @@ Console::MethodInternal(JSContext* aCx, 
     }
 
     nsCOMPtr<nsIPrincipal> principal = sop->GetPrincipal();
     if (NS_WARN_IF(!principal)) {
       return;
     }
 
     oa = principal->OriginAttributesRef();
+    callData->SetAddonId(principal);
 
 #ifdef DEBUG
     if (!nsContentUtils::IsSystemPrincipal(principal)) {
       nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(mWindow);
       if (webNav) {
         nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(webNav);
         MOZ_ASSERT(loadContext);
 
@@ -1495,16 +1507,18 @@ Console::PopulateConsoleNotificationInTh
 
   // Save the principal's OriginAttributes in the console event data
   // so that we will be able to filter messages by origin attributes.
   JS::Rooted<JS::Value> originAttributesValue(aCx);
   if (ToJSValue(aCx, aData->mOriginAttributes, &originAttributesValue)) {
     event.mOriginAttributes = originAttributesValue;
   }
 
+  event.mAddonId = aData->mAddonId;
+
   event.mID.Construct();
   event.mInnerID.Construct();
 
   if (aData->mIDType == ConsoleCallData::eString) {
     event.mID.Value().SetAsString() = aData->mOuterIDString;
     event.mInnerID.Value().SetAsString() = aData->mInnerIDString;
   } else if (aData->mIDType == ConsoleCallData::eNumber) {
     event.mID.Value().SetAsUnsignedLongLong() = aData->mOuterIDNumber;
--- a/dom/events/DataTransfer.cpp
+++ b/dom/events/DataTransfer.cpp
@@ -676,17 +676,17 @@ DataTransfer::SetDataAtInternal(const ns
   if (aIndex > 0 &&
       (mEventMessage == eCut || mEventMessage == eCopy ||
        mEventMessage == ePaste)) {
     return NS_ERROR_DOM_INDEX_SIZE_ERR;
   }
 
   // Don't allow the custom type to be assigned.
   if (aFormat.EqualsLiteral(kCustomTypesMime)) {
-    return NS_ERROR_TYPE_ERR;
+    return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
   }
 
   if (!PrincipalMaySetData(aFormat, aData, aSubjectPrincipal)) {
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 
   return SetDataWithPrincipal(aFormat, aData, aIndex, aSubjectPrincipal);
 }
--- a/dom/file/FileReader.cpp
+++ b/dom/file/FileReader.cpp
@@ -286,18 +286,23 @@ FileReader::DoReadData(uint64_t aCount)
     MOZ_ASSERT(mResult.Length() == mDataLen, "unexpected mResult length");
     if (uint64_t(oldLen) + aCount > UINT32_MAX)
       return NS_ERROR_OUT_OF_MEMORY;
     char16_t *buf = nullptr;
     mResult.GetMutableData(&buf, oldLen + aCount, fallible);
     NS_ENSURE_TRUE(buf, NS_ERROR_OUT_OF_MEMORY);
 
     uint32_t bytesRead = 0;
-    mAsyncStream->ReadSegments(ReadFuncBinaryString, buf + oldLen, aCount,
-                               &bytesRead);
+    nsresult rv =
+      mAsyncStream->ReadSegments(ReadFuncBinaryString, buf + oldLen, aCount,
+                                 &bytesRead);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
     MOZ_ASSERT(bytesRead == aCount, "failed to read data");
   }
   else {
     CheckedInt<uint64_t> size = mDataLen;
     size += aCount;
 
     //Update memory buffer to reflect the contents of the file
     if (!size.isValid() ||
@@ -309,17 +314,21 @@ FileReader::DoReadData(uint64_t aCount)
 
     if (mDataFormat != FILE_AS_ARRAYBUFFER) {
       mFileData = (char *) realloc(mFileData, mDataLen + aCount);
       NS_ENSURE_TRUE(mFileData, NS_ERROR_OUT_OF_MEMORY);
     }
 
     uint32_t bytesRead = 0;
     MOZ_DIAGNOSTIC_ASSERT(mFileData);
-    mAsyncStream->Read(mFileData + mDataLen, aCount, &bytesRead);
+    nsresult rv = mAsyncStream->Read(mFileData + mDataLen, aCount, &bytesRead);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
     MOZ_ASSERT(bytesRead == aCount, "failed to read data");
   }
 
   mDataLen += aCount;
   return NS_OK;
 }
 
 // Helper methods
@@ -608,35 +617,35 @@ FileReader::OnInputStreamReady(nsIAsyncI
     return NS_OK;
   }
 
   // We use this class to decrease the busy counter at the end of this method.
   // In theory we can do it immediatelly but, for debugging reasons, we want to
   // be 100% sure we have a workerHolder when OnLoadEnd() is called.
   FileReaderDecreaseBusyCounter RAII(this);
 
-  uint64_t aCount;
-  nsresult rv = aStream->Available(&aCount);
+  uint64_t count;
+  nsresult rv = aStream->Available(&count);
 
-  if (NS_SUCCEEDED(rv) && aCount) {
-    rv = DoReadData(aCount);
+  if (NS_SUCCEEDED(rv) && count) {
+    rv = DoReadData(count);
   }
 
   if (NS_SUCCEEDED(rv)) {
     rv = DoAsyncWait();
   }
 
-  if (NS_FAILED(rv) || !aCount) {
+  if (NS_FAILED(rv) || !count) {
     if (rv == NS_BASE_STREAM_CLOSED) {
       rv = NS_OK;
     }
     return OnLoadEnd(rv);
   }
 
-  mTransferred += aCount;
+  mTransferred += count;
 
   //Notify the timer is the appropriate timeframe has passed
   if (mTimerIsActive) {
     mProgressEventWasDelayed = true;
   } else {
     rv = DispatchProgressEvent(NS_LITERAL_STRING(PROGRESS_STR));
     NS_ENSURE_SUCCESS(rv, rv);
 
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -6610,24 +6610,16 @@ HTMLInputElement::GetFiles(nsIDOMFileLis
 NS_IMETHODIMP
 HTMLInputElement::GetSelectionRange(int32_t* aSelectionStart,
                                     int32_t* aSelectionEnd)
 {
   // Flush frames, because our editor state will want to work with the frame.
   if (IsInComposedDoc()) {
     GetComposedDoc()->FlushPendingNotifications(FlushType::Frames);
   }
-  if (!GetPrimaryFrame()) {
-    // Can we return a selection range anyway here, now that it lives on our
-    // state?  In fact, could we make this behave more like
-    // GetSelectionDirection, in the sense of working even when we have no
-    // frame, by just delegating entirely to mState?  And then, do we really
-    // need the flush?
-    return NS_ERROR_FAILURE;
-  }
 
   nsTextEditorState* state = GetEditorState();
   if (!state) {
     // Not a text control.
     return NS_ERROR_FAILURE;
   }
 
   return state->GetSelectionRange(aSelectionStart, aSelectionEnd);
--- a/dom/html/HTMLTextAreaElement.cpp
+++ b/dom/html/HTMLTextAreaElement.cpp
@@ -843,24 +843,16 @@ HTMLTextAreaElement::SetSelectionEnd(con
 NS_IMETHODIMP
 HTMLTextAreaElement::GetSelectionRange(int32_t* aSelectionStart,
                                        int32_t* aSelectionEnd)
 {
   // Flush frames, because our editor state will want to work with the frame.
   if (IsInComposedDoc()) {
     GetComposedDoc()->FlushPendingNotifications(FlushType::Frames);
   }
-  if (!GetPrimaryFrame()) {
-    // Can we return a selection range anyway here, now that it lives on our
-    // state?  In fact, could we make this behave more like
-    // GetSelectionDirection, in the sense of working even when we have no
-    // frame, by just delegating entirely to mState?  And then, do we really
-    // need the flush?
-    return NS_ERROR_FAILURE;
-  }
 
   return mState.GetSelectionRange(aSelectionStart, aSelectionEnd);
 }
 
 static void
 DirectionToName(nsITextControlFrame::SelectionDirection dir, nsAString& aDirection)
 {
   if (dir == nsITextControlFrame::eNone) {
new file mode 100644
--- /dev/null
+++ b/dom/html/crashtests/1343886-1.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <script>
+            document.documentElement.scrollTop = "500";
+            o1 = document.createRange();
+            o2 = document.createElement('input'); 
+            o1.selectNode(document.documentElement);
+            o1.surroundContents(o2);
+            o2.selectionStart;
+        </script>
+    </head>
+    <body></body>
+</html>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/html/crashtests/1343886-2.xml
@@ -0,0 +1,3 @@
+<input xmlns="http://www.w3.org/1999/xhtml">
+  <script>document.documentElement.selectionStart</script>
+</input>
new file mode 100644
--- /dev/null
+++ b/dom/html/crashtests/1343886-3.xml
@@ -0,0 +1,3 @@
+<textarea xmlns="http://www.w3.org/1999/xhtml">
+  <script>document.documentElement.selectionStart</script>
+</textarea>
--- a/dom/html/crashtests/crashtests.list
+++ b/dom/html/crashtests/crashtests.list
@@ -73,8 +73,11 @@ load 916322-2.html
 load 1032654.html
 load 1141260.html
 load 1228876.html
 load 1230110.html
 load 1237633.html
 load 1281972-1.html
 load 1282894.html
 load 1290904.html
+load 1343886-1.html
+load 1343886-2.xml
+load 1343886-3.xml
--- a/dom/html/nsTextEditorState.cpp
+++ b/dom/html/nsTextEditorState.cpp
@@ -1548,21 +1548,23 @@ nsTextEditorState::SetSelectionPropertie
     mSelectionProperties = aProps;
   }
 }
 
 nsresult
 nsTextEditorState::GetSelectionRange(int32_t* aSelectionStart,
                                      int32_t* aSelectionEnd)
 {
-  MOZ_ASSERT(mBoundFrame,
-             "Caller didn't flush out frames and check for a frame?");
   MOZ_ASSERT(aSelectionStart);
   MOZ_ASSERT(aSelectionEnd);
 
+  if (!mBoundFrame) {
+    return NS_ERROR_FAILURE;
+  }
+
   // It's not clear that all the checks here are needed, but the previous
   // version of this code in nsTextControlFrame was doing them, so we keep them
   // for now.
 
   nsresult rv = mBoundFrame->EnsureEditorInitialized();
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsISelectionController* selCon = GetSelectionController();
--- a/dom/indexedDB/ActorsParent.cpp
+++ b/dom/indexedDB/ActorsParent.cpp
@@ -161,17 +161,17 @@ class VersionChangeTransaction;
  ******************************************************************************/
 
 // If JS_STRUCTURED_CLONE_VERSION changes then we need to update our major
 // schema version.
 static_assert(JS_STRUCTURED_CLONE_VERSION == 8,
               "Need to update the major schema version.");
 
 // Major schema version. Bump for almost everything.
-const uint32_t kMajorSchemaVersion = 25;
+const uint32_t kMajorSchemaVersion = 26;
 
 // Minor schema version. Should almost always be 0 (maybe bump on release
 // branches if we have to).
 const uint32_t kMinorSchemaVersion = 0;
 
 // The schema version we store in the SQLite database is a (signed) 32-bit
 // integer. The major version is left-shifted 4 bits so the max value is
 // 0xFFFFFFF. The minor version occupies the lower 4 bits and its max is 0xF.
@@ -239,16 +239,17 @@ const uint32_t kConnectionThreadIdleMS =
 
 #define SAVEPOINT_CLAUSE "SAVEPOINT sp;"
 
 const uint32_t kFileCopyBufferSize = 32768;
 
 #define JOURNAL_DIRECTORY_NAME "journals"
 
 const char kFileManagerDirectoryNameSuffix[] = ".files";
+const char kSQLiteSuffix[] = ".sqlite";
 const char kSQLiteJournalSuffix[] = ".sqlite-journal";
 const char kSQLiteSHMSuffix[] = ".sqlite-shm";
 const char kSQLiteWALSuffix[] = ".sqlite-wal";
 
 const char kPrefIndexedDBEnabled[] = "dom.indexedDB.enabled";
 
 const char kPrefFileHandleEnabled[] = "dom.fileHandle.enabled";
 
@@ -4129,16 +4130,120 @@ UpgradeSchemaFrom24_0To25_0(mozIStorageC
   nsresult rv = aConnection->SetSchemaVersion(MakeSchemaVersion(25, 0));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   return NS_OK;
 }
 
+class StripObsoleteOriginAttributesFunction final
+  : public mozIStorageFunction
+{
+public:
+  NS_DECL_ISUPPORTS
+
+private:
+  ~StripObsoleteOriginAttributesFunction()
+  { }
+
+  NS_IMETHOD
+  OnFunctionCall(mozIStorageValueArray* aArguments,
+                 nsIVariant** aResult) override
+  {
+    MOZ_ASSERT(aArguments);
+    MOZ_ASSERT(aResult);
+
+    PROFILER_LABEL("IndexedDB",
+                   "StripObsoleteOriginAttributesFunction::OnFunctionCall",
+                   js::ProfileEntry::Category::STORAGE);
+
+#ifdef DEBUG
+  {
+    uint32_t argCount;
+    MOZ_ALWAYS_SUCCEEDS(aArguments->GetNumEntries(&argCount));
+    MOZ_ASSERT(argCount == 1);
+
+    int32_t type;
+    MOZ_ALWAYS_SUCCEEDS(aArguments->GetTypeOfIndex(0, &type));
+    MOZ_ASSERT(type == mozIStorageValueArray::VALUE_TYPE_TEXT);
+  }
+#endif
+
+    nsCString origin;
+    nsresult rv = aArguments->GetUTF8String(0, origin);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    // Deserialize and re-serialize to automatically drop any obsolete origin
+    // attributes.
+    OriginAttributes oa;
+
+    nsCString originNoSuffix;
+    bool ok = oa.PopulateFromOrigin(origin, originNoSuffix);
+    if (NS_WARN_IF(!ok)) {
+      return NS_ERROR_FAILURE;
+    }
+
+    nsCString suffix;
+    oa.CreateSuffix(suffix);
+
+    nsCOMPtr<nsIVariant> result =
+      new mozilla::storage::UTF8TextVariant(originNoSuffix + suffix);
+
+    result.forget(aResult);
+    return NS_OK;
+  }
+};
+
+nsresult
+UpgradeSchemaFrom25_0To26_0(mozIStorageConnection* aConnection)
+{
+  AssertIsOnIOThread();
+  MOZ_ASSERT(aConnection);
+
+  PROFILER_LABEL("IndexedDB",
+                 "UpgradeSchemaFrom25_0To26_0",
+                 js::ProfileEntry::Category::STORAGE);
+
+  NS_NAMED_LITERAL_CSTRING(functionName, "strip_obsolete_attributes");
+
+  nsCOMPtr<mozIStorageFunction> stripObsoleteAttributes =
+    new StripObsoleteOriginAttributesFunction();
+
+  nsresult rv = aConnection->CreateFunction(functionName,
+                                            /* aNumArguments */ 1,
+                                            stripObsoleteAttributes);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+    "UPDATE DATABASE "
+      "SET origin = strip_obsolete_attributes(origin) "
+      "WHERE origin LIKE '%^%';"
+  ));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->RemoveFunction(functionName);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = aConnection->SetSchemaVersion(MakeSchemaVersion(26, 0));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
 nsresult
 GetDatabaseFileURL(nsIFile* aDatabaseFile,
                    PersistenceType aPersistenceType,
                    const nsACString& aGroup,
                    const nsACString& aOrigin,
                    uint32_t aTelemetryId,
                    nsIFileURL** aResult)
 {
@@ -4635,17 +4740,17 @@ CreateStorageConnection(nsIFile* aDBFile
       }
 
       rv = stmt->Execute();
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
     } else  {
       // This logic needs to change next time we change the schema!
-      static_assert(kSQLiteSchemaVersion == int32_t((25 << 4) + 0),
+      static_assert(kSQLiteSchemaVersion == int32_t((26 << 4) + 0),
                     "Upgrade function needed due to schema version increase.");
 
       while (schemaVersion != kSQLiteSchemaVersion) {
         if (schemaVersion == 4) {
           rv = UpgradeSchemaFrom4To5(connection);
         } else if (schemaVersion == 5) {
           rv = UpgradeSchemaFrom5To6(connection);
         } else if (schemaVersion == 6) {
@@ -4683,16 +4788,18 @@ CreateStorageConnection(nsIFile* aDBFile
         } else if (schemaVersion == MakeSchemaVersion(21, 0)) {
           rv = UpgradeSchemaFrom21_0To22_0(connection);
         } else if (schemaVersion == MakeSchemaVersion(22, 0)) {
           rv = UpgradeSchemaFrom22_0To23_0(connection, aOrigin);
         } else if (schemaVersion == MakeSchemaVersion(23, 0)) {
           rv = UpgradeSchemaFrom23_0To24_0(connection);
         } else if (schemaVersion == MakeSchemaVersion(24, 0)) {
           rv = UpgradeSchemaFrom24_0To25_0(connection);
+        } else if (schemaVersion == MakeSchemaVersion(25, 0)) {
+          rv = UpgradeSchemaFrom25_0To26_0(connection);
         } else {
           IDB_WARNING("Unable to open IndexedDB database, no upgrade path is "
                       "available!");
           return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
         }
 
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return rv;
@@ -7477,17 +7584,16 @@ protected:
 
   const CommonFactoryRequestParams mCommonParams;
   nsCString mSuffix;
   nsCString mGroup;
   nsCString mOrigin;
   nsCString mDatabaseId;
   nsString mDatabaseFilePath;
   State mState;
-  bool mIsApp;
   bool mEnforcingQuota;
   const bool mDeleting;
   bool mBlockedDatabaseOpen;
   bool mChromeWriteAccessAllowed;
   bool mFileHandleDisabled;
 
 public:
   void
@@ -9230,16 +9336,19 @@ public:
   GetOrCreateThreadPool();
 
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(QuotaClient, override)
 
   mozilla::dom::quota::Client::Type
   GetType() override;
 
   nsresult
+  UpgradeStorageFrom1_0To2_0(nsIFile* aDirectory) override;
+
+  nsresult
   InitOrigin(PersistenceType aPersistenceType,
              const nsACString& aGroup,
              const nsACString& aOrigin,
              UsageInfo* aUsageInfo) override;
 
   nsresult
   GetUsageForOrigin(PersistenceType aPersistenceType,
                     const nsACString& aGroup,
@@ -9279,16 +9388,23 @@ private:
   ~QuotaClient() override;
 
   nsresult
   GetDirectory(PersistenceType aPersistenceType,
                const nsACString& aOrigin,
                nsIFile** aDirectory);
 
   nsresult
+  GetDatabaseFilenames(nsIFile* aDirectory,
+                       UsageInfo* aUsageInfo,
+                       bool aForUpgrade,
+                       nsTArray<nsString>& aSubdirsToProcess,
+                       nsTHashtable<nsStringHashKey>& aDatabaseFilename);
+
+  nsresult
   GetUsageForDirectoryInternal(nsIFile* aDirectory,
                                UsageInfo* aUsageInfo,
                                bool aDatabaseFiles);
 
   // Runs on the PBackground thread. Checks to see if there's a queued
   // Maintenance to run.
   void
   ProcessMaintenanceQueue();
@@ -10036,34 +10152,31 @@ DeserializeStructuredCloneFiles(FileMana
       *aHasPreprocessInfo = true;
     }
   }
 
   return NS_OK;
 }
 
 bool
-GetDatabaseBaseFilename(const nsAString& aFilename,
-                        nsDependentSubstring& aDatabaseBaseFilename)
+GetBaseFilename(const nsAString& aFilename,
+                const nsAString& aSuffix,
+                nsDependentSubstring& aBaseFilename)
 {
   MOZ_ASSERT(!aFilename.IsEmpty());
-  MOZ_ASSERT(aDatabaseBaseFilename.IsEmpty());
-
-  NS_NAMED_LITERAL_STRING(sqlite, ".sqlite");
-
-  if (!StringEndsWith(aFilename, sqlite) ||
-      aFilename.Length() == sqlite.Length()) {
+  MOZ_ASSERT(aBaseFilename.IsEmpty());
+
+  if (!StringEndsWith(aFilename, aSuffix) ||
+      aFilename.Length() == aSuffix.Length()) {
     return false;
   }
 
-  MOZ_ASSERT(aFilename.Length() > sqlite.Length());
-
-  aDatabaseBaseFilename.Rebind(aFilename,
-                               0,
-                               aFilename.Length() - sqlite.Length());
+  MOZ_ASSERT(aFilename.Length() > aSuffix.Length());
+
+  aBaseFilename.Rebind(aFilename, 0, aFilename.Length() - aSuffix.Length());
   return true;
 }
 
 nsresult
 SerializeStructuredCloneFiles(
                          PBackgroundParent* aBackgroundActor,
                          Database* aDatabase,
                          const nsTArray<StructuredCloneFile>& aFiles,
@@ -17082,25 +17195,23 @@ Cursor::RecvContinue(const CursorRequest
 
 /*******************************************************************************
  * FileManager
  ******************************************************************************/
 
 FileManager::FileManager(PersistenceType aPersistenceType,
                          const nsACString& aGroup,
                          const nsACString& aOrigin,
-                         bool aIsApp,
                          const nsAString& aDatabaseName,
                          bool aEnforcingQuota)
   : mPersistenceType(aPersistenceType)
   , mGroup(aGroup)
   , mOrigin(aOrigin)
   , mDatabaseName(aDatabaseName)
   , mLastFileId(0)
-  , mIsApp(aIsApp)
   , mEnforcingQuota(aEnforcingQuota)
   , mInvalidated(false)
 { }
 
 nsresult
 FileManager::Init(nsIFile* aDirectory,
                   mozIStorageConnection* aConnection)
 {
@@ -17706,22 +17817,113 @@ QuotaClient::GetOrCreateThreadPool()
 }
 
 mozilla::dom::quota::Client::Type
 QuotaClient::GetType()
 {
   return QuotaClient::IDB;
 }
 
-struct FileManagerInitInfo
-{
-  nsCOMPtr<nsIFile> mDirectory;
-  nsCOMPtr<nsIFile> mDatabaseFile;
-  nsCOMPtr<nsIFile> mDatabaseWALFile;
-};
+nsresult
+QuotaClient::UpgradeStorageFrom1_0To2_0(nsIFile* aDirectory)
+{
+  AssertIsOnIOThread();
+  MOZ_ASSERT(aDirectory);
+
+  AutoTArray<nsString, 20> subdirsToProcess;
+  nsTHashtable<nsStringHashKey> databaseFilenames(20);
+  nsresult rv = GetDatabaseFilenames(aDirectory,
+                                     nullptr,
+                                     /* aForUpgrade */ true,
+                                     subdirsToProcess,
+                                     databaseFilenames);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  const NS_ConvertASCIItoUTF16 filesSuffix(
+    kFileManagerDirectoryNameSuffix,
+    LiteralStringLength(kFileManagerDirectoryNameSuffix));
+
+  for (uint32_t count = subdirsToProcess.Length(), i = 0; i < count; i++) {
+    const nsString& subdirName = subdirsToProcess[i];
+
+    // If the directory has the correct suffix then it should exist in
+    // databaseFilenames.
+    nsDependentSubstring subdirNameBase;
+    if (GetBaseFilename(subdirName, filesSuffix, subdirNameBase)) {
+      Unused << NS_WARN_IF(!databaseFilenames.GetEntry(subdirNameBase));
+
+      continue;
+    }
+
+    // The directory didn't have the right suffix but we might need to rename
+    // it. Check to see if we have a database that references this directory.
+    nsString subdirNameWithSuffix;
+    if (databaseFilenames.GetEntry(subdirName)) {
+      subdirNameWithSuffix = subdirName + filesSuffix;
+    } else {
+      // Windows doesn't allow a directory to end with a dot ('.'), so we have
+      // to check that possibility here too.
+      // We do this on all platforms, because the origin directory may have
+      // been created on Windows and now accessed on different OS.
+      nsString subdirNameWithDot = subdirName + NS_LITERAL_STRING(".");
+      if (NS_WARN_IF(!databaseFilenames.GetEntry(subdirNameWithDot))) {
+        continue;
+      }
+      subdirNameWithSuffix = subdirNameWithDot + filesSuffix;
+    }
+
+    // We do have a database that uses this directory so we should rename it
+    // now. However, first check to make sure that we're not overwriting
+    // something else.
+    nsCOMPtr<nsIFile> subdir;
+    rv = aDirectory->Clone(getter_AddRefs(subdir));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    rv = subdir->Append(subdirNameWithSuffix);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    bool exists;
+    rv = subdir->Exists(&exists);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    if (exists) {
+      IDB_REPORT_INTERNAL_ERR();
+      return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+    }
+
+    rv = aDirectory->Clone(getter_AddRefs(subdir));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    rv = subdir->Append(subdirName);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    DebugOnly<bool> isDirectory;
+    MOZ_ASSERT(NS_SUCCEEDED(subdir->IsDirectory(&isDirectory)));
+    MOZ_ASSERT(isDirectory);
+
+    rv = subdir->RenameTo(nullptr, subdirNameWithSuffix);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+  }
+
+  return NS_OK;
+}
 
 nsresult
 QuotaClient::InitOrigin(PersistenceType aPersistenceType,
                         const nsACString& aGroup,
                         const nsACString& aOrigin,
                         UsageInfo* aUsageInfo)
 {
   AssertIsOnIOThread();
@@ -17733,282 +17935,128 @@ QuotaClient::InitOrigin(PersistenceType 
     return rv;
   }
 
   // We need to see if there are any files in the directory already. If they
   // are database files then we need to cleanup stored files (if it's needed)
   // and also get the usage.
 
   AutoTArray<nsString, 20> subdirsToProcess;
-  nsTArray<nsCOMPtr<nsIFile>> unknownFiles;
-  nsTHashtable<nsStringHashKey> validSubdirs(20);
-  AutoTArray<FileManagerInitInfo, 20> initInfos;
-
-  nsCOMPtr<nsISimpleEnumerator> entries;
-  rv = directory->GetDirectoryEntries(getter_AddRefs(entries));
+  nsTHashtable<nsStringHashKey> databaseFilenames(20);
+  rv = GetDatabaseFilenames(directory,
+                            aUsageInfo,
+                            /* aForUpgrade */ false,
+                            subdirsToProcess,
+                            databaseFilenames);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   const NS_ConvertASCIItoUTF16 filesSuffix(
     kFileManagerDirectoryNameSuffix,
     LiteralStringLength(kFileManagerDirectoryNameSuffix));
 
-  const NS_ConvertASCIItoUTF16 journalSuffix(
-    kSQLiteJournalSuffix,
-    LiteralStringLength(kSQLiteJournalSuffix));
-  const NS_ConvertASCIItoUTF16 shmSuffix(kSQLiteSHMSuffix,
-                                         LiteralStringLength(kSQLiteSHMSuffix));
+  for (uint32_t count = subdirsToProcess.Length(), i = 0; i < count; i++) {
+    const nsString& subdirName = subdirsToProcess[i];
+
+    // The directory must have the correct suffix.
+    nsDependentSubstring subdirNameBase;
+    if (NS_WARN_IF(!GetBaseFilename(subdirName, filesSuffix, subdirNameBase))) {
+      return NS_ERROR_UNEXPECTED;
+    }
+
+    // The directory base must exist in databaseFilenames.
+    if (NS_WARN_IF(!databaseFilenames.GetEntry(subdirNameBase))) {
+      return NS_ERROR_UNEXPECTED;
+    }
+  }
+
+  const NS_ConvertASCIItoUTF16 sqliteSuffix(kSQLiteSuffix,
+                                            LiteralStringLength(kSQLiteSuffix));
   const NS_ConvertASCIItoUTF16 walSuffix(kSQLiteWALSuffix,
                                          LiteralStringLength(kSQLiteWALSuffix));
 
-  bool hasMore;
-  while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) &&
-         hasMore &&
-         (!aUsageInfo || !aUsageInfo->Canceled())) {
-    nsCOMPtr<nsISupports> entry;
-    rv = entries->GetNext(getter_AddRefs(entry));
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-
-    nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
-    MOZ_ASSERT(file);
-
-    nsString leafName;
-    rv = file->GetLeafName(leafName);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-
-    bool isDirectory;
-    rv = file->IsDirectory(&isDirectory);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-
-    if (isDirectory) {
-      if (!StringEndsWith(leafName, filesSuffix) ||
-          !validSubdirs.GetEntry(leafName)) {
-        subdirsToProcess.AppendElement(leafName);
-      }
-      continue;
-    }
-
-    // Skip Desktop Service Store (.DS_Store) files. These files are only used
-    // on Mac OS X, but the profile can be shared across different operating
-    // systems, so we check it on all platforms.
-    if (leafName.EqualsLiteral(DSSTORE_FILE_NAME)) {
-      continue;
-    }
-
-    // Skip SQLite temporary files. These files take up space on disk but will
-    // be deleted as soon as the database is opened, so we don't count them
-    // towards quota.
-    if (StringEndsWith(leafName, journalSuffix) ||
-        StringEndsWith(leafName, shmSuffix)) {
-      continue;
-    }
-
-    // The SQLite WAL file does count towards quota, but it is handled below
-    // once we find the actual database file.
-    if (StringEndsWith(leafName, walSuffix)) {
-      continue;
-    }
-
-    nsDependentSubstring dbBaseFilename;
-    if (!GetDatabaseBaseFilename(leafName, dbBaseFilename)) {
-      unknownFiles.AppendElement(file);
-      continue;
-    }
+  for (auto iter = databaseFilenames.ConstIter(); !iter.Done(); iter.Next()) {
+    auto& databaseFilename = iter.Get()->GetKey();
 
     nsCOMPtr<nsIFile> fmDirectory;
     rv = directory->Clone(getter_AddRefs(fmDirectory));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
-    nsString fmDirectoryBaseName = dbBaseFilename + filesSuffix;
-
-    rv = fmDirectory->Append(fmDirectoryBaseName);
+    rv = fmDirectory->Append(databaseFilename + filesSuffix);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    nsCOMPtr<nsIFile> databaseFile;
+    rv = directory->Clone(getter_AddRefs(databaseFile));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    rv = databaseFile->Append(databaseFilename + sqliteSuffix);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     nsCOMPtr<nsIFile> walFile;
     if (aUsageInfo) {
       rv = directory->Clone(getter_AddRefs(walFile));
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
 
-      rv = walFile->Append(dbBaseFilename + walSuffix);
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return rv;
-      }
-    }
-
-    FileManagerInitInfo* initInfo = initInfos.AppendElement();
-    initInfo->mDirectory.swap(fmDirectory);
-    initInfo->mDatabaseFile.swap(file);
-    initInfo->mDatabaseWALFile.swap(walFile);
-
-    validSubdirs.PutEntry(fmDirectoryBaseName);
-  }
-
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  for (uint32_t count = subdirsToProcess.Length(), i = 0; i < count; i++) {
-    const nsString& subdirName = subdirsToProcess[i];
-
-    // If the directory has the correct suffix then it must exist in
-    // validSubdirs.
-    if (StringEndsWith(subdirName, filesSuffix)) {
-      if (NS_WARN_IF(!validSubdirs.GetEntry(subdirName))) {
-        return NS_ERROR_UNEXPECTED;
-      }
-
-      continue;
-    }
-
-    // The directory didn't have the right suffix but we might need to rename
-    // it. Check to see if we have a database that references this directory.
-    nsString subdirNameWithSuffix = subdirName + filesSuffix;
-    if (!validSubdirs.GetEntry(subdirNameWithSuffix)) {
-      // Windows doesn't allow a directory to end with a dot ('.'), so we have
-      // to check that possibility here too.
-      // We do this on all platforms, because the origin directory may have
-      // been created on Windows and now accessed on different OS.
-      subdirNameWithSuffix = subdirName + NS_LITERAL_STRING(".") + filesSuffix;
-      if (NS_WARN_IF(!validSubdirs.GetEntry(subdirNameWithSuffix))) {
-        return NS_ERROR_UNEXPECTED;
-      }
-    }
-
-    // We do have a database that uses this directory so we should rename it
-    // now. However, first check to make sure that we're not overwriting
-    // something else.
-    nsCOMPtr<nsIFile> subdir;
-    rv = directory->Clone(getter_AddRefs(subdir));
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-
-    rv = subdir->Append(subdirNameWithSuffix);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-
-    bool exists;
-    rv = subdir->Exists(&exists);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-
-    if (exists) {
-      IDB_REPORT_INTERNAL_ERR();
-      return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
-    }
-
-    rv = directory->Clone(getter_AddRefs(subdir));
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-
-    rv = subdir->Append(subdirName);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-
-    DebugOnly<bool> isDirectory;
-    MOZ_ASSERT(NS_SUCCEEDED(subdir->IsDirectory(&isDirectory)));
-    MOZ_ASSERT(isDirectory);
-
-    rv = subdir->RenameTo(nullptr, subdirNameWithSuffix);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-  }
-
-  for (uint32_t count = initInfos.Length(), i = 0; i < count; i++) {
-    FileManagerInitInfo& initInfo = initInfos[i];
-    MOZ_ASSERT(initInfo.mDirectory);
-    MOZ_ASSERT(initInfo.mDatabaseFile);
-    MOZ_ASSERT_IF(aUsageInfo, initInfo.mDatabaseWALFile);
-
-    rv = FileManager::InitDirectory(initInfo.mDirectory,
-                                    initInfo.mDatabaseFile,
+      rv = walFile->Append(databaseFilename + walSuffix);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+    }
+
+    rv = FileManager::InitDirectory(fmDirectory,
+                                    databaseFile,
                                     aPersistenceType,
                                     aGroup,
                                     aOrigin,
-                                    TelemetryIdForFile(initInfo.mDatabaseFile));
+                                    TelemetryIdForFile(databaseFile));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     if (aUsageInfo && !aUsageInfo->Canceled()) {
       int64_t fileSize;
-      rv = initInfo.mDatabaseFile->GetFileSize(&fileSize);
+      rv = databaseFile->GetFileSize(&fileSize);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
 
       MOZ_ASSERT(fileSize >= 0);
 
       aUsageInfo->AppendToDatabaseUsage(uint64_t(fileSize));
 
-      rv = initInfo.mDatabaseWALFile->GetFileSize(&fileSize);
+      rv = walFile->GetFileSize(&fileSize);
       if (NS_SUCCEEDED(rv)) {
         MOZ_ASSERT(fileSize >= 0);
         aUsageInfo->AppendToDatabaseUsage(uint64_t(fileSize));
       } else if (NS_WARN_IF(rv != NS_ERROR_FILE_NOT_FOUND &&
                             rv != NS_ERROR_FILE_TARGET_DOES_NOT_EXIST)) {
         return rv;
       }
 
       uint64_t usage;
-      rv = FileManager::GetUsage(initInfo.mDirectory, &usage);
+      rv = FileManager::GetUsage(fmDirectory, &usage);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
 
       aUsageInfo->AppendToFileUsage(usage);
     }
   }
 
-  // We have to do this after file manager initialization.
-  if (!unknownFiles.IsEmpty()) {
-#ifdef DEBUG
-    for (uint32_t count = unknownFiles.Length(), i = 0; i < count; i++) {
-      nsCOMPtr<nsIFile>& unknownFile = unknownFiles[i];
-
-      nsString leafName;
-      MOZ_ALWAYS_SUCCEEDS(unknownFile->GetLeafName(leafName));
-
-      MOZ_ASSERT(!StringEndsWith(leafName, journalSuffix));
-      MOZ_ASSERT(!StringEndsWith(leafName, shmSuffix));
-      MOZ_ASSERT(!StringEndsWith(leafName, walSuffix));
-
-      nsString path;
-      MOZ_ALWAYS_SUCCEEDS(unknownFile->GetPath(path));
-      MOZ_ASSERT(!path.IsEmpty());
-
-      nsPrintfCString warning(R"(Refusing to open databases for "%s" because )"
-                              R"(an unexpected file exists in the storage )"
-                              R"(area: "%s")",
-                              PromiseFlatCString(aOrigin).get(),
-                              NS_ConvertUTF16toUTF8(path).get());
-      NS_WARNING(warning.get());
-    }
-#endif
-    return NS_ERROR_UNEXPECTED;
-  }
-
   return NS_OK;
 }
 
 nsresult
 QuotaClient::GetUsageForOrigin(PersistenceType aPersistenceType,
                                const nsACString& aGroup,
                                const nsACString& aOrigin,
                                UsageInfo* aUsageInfo)
@@ -18205,16 +18253,119 @@ QuotaClient::GetDirectory(PersistenceTyp
     return rv;
   }
 
   directory.forget(aDirectory);
   return NS_OK;
 }
 
 nsresult
+QuotaClient::GetDatabaseFilenames(
+                              nsIFile* aDirectory,
+                              UsageInfo* aUsageInfo,
+                              bool aForUpgrade,
+                              nsTArray<nsString>& aSubdirsToProcess,
+                              nsTHashtable<nsStringHashKey>& aDatabaseFilenames)
+{
+  AssertIsOnIOThread();
+
+  nsCOMPtr<nsISimpleEnumerator> entries;
+  nsresult rv = aDirectory->GetDirectoryEntries(getter_AddRefs(entries));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  const NS_ConvertASCIItoUTF16 sqliteSuffix(kSQLiteSuffix,
+                                            LiteralStringLength(kSQLiteSuffix));
+  const NS_ConvertASCIItoUTF16 journalSuffix(
+    kSQLiteJournalSuffix,
+    LiteralStringLength(kSQLiteJournalSuffix));
+  const NS_ConvertASCIItoUTF16 shmSuffix(kSQLiteSHMSuffix,
+                                         LiteralStringLength(kSQLiteSHMSuffix));
+  const NS_ConvertASCIItoUTF16 walSuffix(kSQLiteWALSuffix,
+                                         LiteralStringLength(kSQLiteWALSuffix));
+
+  bool hasMore;
+  while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) &&
+         hasMore &&
+         (!aUsageInfo || !aUsageInfo->Canceled())) {
+    nsCOMPtr<nsISupports> entry;
+    rv = entries->GetNext(getter_AddRefs(entry));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
+    MOZ_ASSERT(file);
+
+    nsString leafName;
+    rv = file->GetLeafName(leafName);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    bool isDirectory;
+    rv = file->IsDirectory(&isDirectory);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    if (isDirectory) {
+      aSubdirsToProcess.AppendElement(leafName);
+      continue;
+    }
+
+    // Skip Desktop Service Store (.DS_Store) files. These files are only used
+    // on Mac OS X, but the profile can be shared across different operating
+    // systems, so we check it on all platforms.
+    if (leafName.EqualsLiteral(DSSTORE_FILE_NAME)) {
+      continue;
+    }
+
+    // Skip SQLite temporary files. These files take up space on disk but will
+    // be deleted as soon as the database is opened, so we don't count them
+    // towards quota.
+    if (StringEndsWith(leafName, journalSuffix) ||
+        StringEndsWith(leafName, shmSuffix)) {
+      continue;
+    }
+
+    // The SQLite WAL file does count towards quota, but it is handled below
+    // once we find the actual database file.
+    if (StringEndsWith(leafName, walSuffix)) {
+      continue;
+    }
+
+    nsDependentSubstring leafNameBase;
+    if (!GetBaseFilename(leafName, sqliteSuffix, leafNameBase)) {
+      nsString path;
+      MOZ_ALWAYS_SUCCEEDS(file->GetPath(path));
+      MOZ_ASSERT(!path.IsEmpty());
+
+      nsPrintfCString warning(R"(An unexpected file exists in the storage )"
+                              R"(area: "%s")",
+                              NS_ConvertUTF16toUTF8(path).get());
+      NS_WARNING(warning.get());
+      if (!aForUpgrade) {
+        return NS_ERROR_UNEXPECTED;
+      }
+      continue;
+    }
+
+    aDatabaseFilenames.PutEntry(leafNameBase);
+  }
+
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+nsresult
 QuotaClient::GetUsageForDirectoryInternal(nsIFile* aDirectory,
                                           UsageInfo* aUsageInfo,
                                           bool aDatabaseFiles)
 {
   AssertIsOnIOThread();
   MOZ_ASSERT(aDirectory);
   MOZ_ASSERT(aUsageInfo);
 
@@ -18641,24 +18792,22 @@ Maintenance::DirectoryWork()
 
         // Found a database.
         if (databasePaths.IsEmpty()) {
           MOZ_ASSERT(group.IsEmpty());
           MOZ_ASSERT(origin.IsEmpty());
 
           int64_t dummyTimeStamp;
           nsCString dummySuffix;
-          bool dummyIsApp;
           if (NS_WARN_IF(NS_FAILED(
                 quotaManager->GetDirectoryMetadata2(originDir,
                                                     &dummyTimeStamp,
                                                     dummySuffix,
                                                     group,
-                                                    origin,
-                                                    &dummyIsApp)))) {
+                                                    origin)))) {
             // Not much we can do here...
             continue;
           }
         }
 
         MOZ_ASSERT(!databasePaths.Contains(idbFilePath));
 
         databasePaths.AppendElement(idbFilePath);
@@ -19468,30 +19617,30 @@ AutoProgressHandler::OnProgress(mozIStor
 }
 
 /*******************************************************************************
  * Local class implementations
  ******************************************************************************/
 
 NS_IMPL_ISUPPORTS(CompressDataBlobsFunction, mozIStorageFunction)
 NS_IMPL_ISUPPORTS(EncodeKeysFunction, mozIStorageFunction)
+NS_IMPL_ISUPPORTS(StripObsoleteOriginAttributesFunction, mozIStorageFunction);
 
 #if !defined(MOZ_B2G)
 
 nsresult
 UpgradeFileIdsFunction::Init(nsIFile* aFMDirectory,
                              mozIStorageConnection* aConnection)
 {
   // This file manager doesn't need real origin info, etc. The only purpose is
   // to store file ids without adding more complexity or code duplication.
   RefPtr<FileManager> fileManager =
     new FileManager(PERSISTENCE_TYPE_INVALID,
                     EmptyCString(),
                     EmptyCString(),
-                    false,
                     EmptyString(),
                     false);
 
   nsresult rv = fileManager->Init(aFMDirectory, aConnection);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
@@ -20730,17 +20879,16 @@ FactoryOp::FactoryOp(Factory* aFactory,
                      const CommonFactoryRequestParams& aCommonParams,
                      bool aDeleting)
   : DatabaseOperationBase(aFactory->GetLoggingInfo()->Id(),
                           aFactory->GetLoggingInfo()->NextRequestSN())
   , mFactory(aFactory)
   , mContentParent(Move(aContentParent))
   , mCommonParams(aCommonParams)
   , mState(State::Initial)
-  , mIsApp(false)
   , mEnforcingQuota(true)
   , mDeleting(aDeleting)
   , mBlockedDatabaseOpen(false)
   , mChromeWriteAccessAllowed(false)
   , mFileHandleDisabled(false)
 {
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aFactory);
@@ -21079,23 +21227,22 @@ FactoryOp::CheckPermission(ContentParent
       }
 
       mChromeWriteAccessAllowed = canWrite;
     } else {
       mChromeWriteAccessAllowed = true;
     }
 
     if (State::Initial == mState) {
-      QuotaManager::GetInfoForChrome(&mSuffix, &mGroup, &mOrigin, &mIsApp);
-
-      MOZ_ASSERT(!QuotaManager::IsFirstPromptRequired(persistenceType, mOrigin,
-                                                      mIsApp));
-
-      mEnforcingQuota =
-        QuotaManager::IsQuotaEnforced(persistenceType, mOrigin, mIsApp);
+      QuotaManager::GetInfoForChrome(&mSuffix, &mGroup, &mOrigin);
+
+      MOZ_ASSERT(
+        QuotaManager::IsOriginWhitelistedForPersistentStorage(mOrigin));
+
+      mEnforcingQuota = false;
     }
 
     *aPermission = PermissionRequestBase::kPermissionAllowed;
     return NS_OK;
   }
 
   MOZ_ASSERT(principalInfo.type() == PrincipalInfo::TContentPrincipalInfo);
 
@@ -21104,54 +21251,50 @@ FactoryOp::CheckPermission(ContentParent
     PrincipalInfoToPrincipal(principalInfo, &rv);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   nsCString suffix;
   nsCString group;
   nsCString origin;
-  bool isApp;
   rv = QuotaManager::GetInfoFromPrincipal(principal,
                                           &suffix,
                                           &group,
-                                          &origin,
-                                          &isApp);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-#ifdef IDB_MOBILE
-  if (persistenceType == PERSISTENCE_TYPE_PERSISTENT &&
-      !QuotaManager::IsOriginWhitelistedForPersistentStorage(origin) &&
-      !isApp) {
-    return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
-  }
-#endif
+                                          &origin);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
 
   PermissionRequestBase::PermissionValue permission;
 
-  if (QuotaManager::IsFirstPromptRequired(persistenceType, origin, isApp)) {
-    rv = PermissionRequestBase::GetCurrentPermission(principal, &permission);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
+  if (persistenceType == PERSISTENCE_TYPE_PERSISTENT) {
+    if (QuotaManager::IsOriginWhitelistedForPersistentStorage(origin)) {
+      permission = PermissionRequestBase::kPermissionAllowed;
+    } else {
+#ifdef IDB_MOBILE
+      return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
+#else
+      rv = PermissionRequestBase::GetCurrentPermission(principal, &permission);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+#endif
     }
   } else {
     permission = PermissionRequestBase::kPermissionAllowed;
   }
 
   if (permission != PermissionRequestBase::kPermissionDenied &&
       State::Initial == mState) {
     mSuffix = suffix;
     mGroup = group;
     mOrigin = origin;
-    mIsApp = isApp;
-
-    mEnforcingQuota =
-      QuotaManager::IsQuotaEnforced(persistenceType, mOrigin, mIsApp);
+
+    mEnforcingQuota = persistenceType != PERSISTENCE_TYPE_PERSISTENT;
   }
 
   *aPermission = permission;
   return NS_OK;
 }
 
 nsresult
 FactoryOp::SendVersionChangeMessages(DatabaseActorInfo* aDatabaseActorInfo,
@@ -21298,17 +21441,16 @@ FactoryOp::OpenDirectory()
     return rv;
   }
 
   mState = State::DirectoryOpenPending;
 
   quotaManager->OpenDirectory(persistenceType,
                               mGroup,
                               mOrigin,
-                              mIsApp,
                               Client::IDB,
                               /* aExclusive */ false,
                               this);
 
   return NS_OK;
 }
 
 bool
@@ -21553,17 +21695,16 @@ OpenDatabaseOp::DoDatabaseWork()
 
   nsCOMPtr<nsIFile> dbDirectory;
 
   nsresult rv =
     quotaManager->EnsureOriginIsInitialized(persistenceType,
                                             mSuffix,
                                             mGroup,
                                             mOrigin,
-                                            mIsApp,
                                             getter_AddRefs(dbDirectory));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   rv = dbDirectory->Append(NS_LITERAL_STRING(IDB_DIRECTORY_NAME));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
@@ -21677,17 +21818,16 @@ OpenDatabaseOp::DoDatabaseWork()
   MOZ_ASSERT(mgr);
 
   RefPtr<FileManager> fileManager =
     mgr->GetFileManager(persistenceType, mOrigin, databaseName);
   if (!fileManager) {
     fileManager = new FileManager(persistenceType,
                                   mGroup,
                                   mOrigin,
-                                  mIsApp,
                                   databaseName,
                                   mEnforcingQuota);
 
     rv = fileManager->Init(fmDirectory, connection);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
@@ -21748,18 +21888,19 @@ OpenDatabaseOp::LoadDatabaseInformation(
   }
 
   nsCString origin;
   rv = stmt->GetUTF8String(1, origin);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
-  if (mOrigin != origin) {
-    NS_WARNING("Origins don't match!");
+  // We can't just compare these strings directly. See bug 1339081 comment 69.
+  if (NS_WARN_IF(!QuotaManager::AreOriginsEqualOnDisk(mOrigin, origin))) {
+    return NS_ERROR_FILE_CORRUPTED;
   }
 
   int64_t version;
   rv = stmt->GetInt64(2, &version);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
--- a/dom/indexedDB/FileManager.h
+++ b/dom/indexedDB/FileManager.h
@@ -37,17 +37,16 @@ class FileManager final
   nsString mDirectoryPath;
   nsString mJournalDirectoryPath;
 
   int64_t mLastFileId;
 
   // Protected by IndexedDatabaseManager::FileMutex()
   nsDataHashtable<nsUint64HashKey, FileInfo*> mFileInfos;
 
-  const bool mIsApp;
   const bool mEnforcingQuota;
   bool mInvalidated;
 
 public:
   static already_AddRefed<nsIFile>
   GetFileForId(nsIFile* aDirectory, int64_t aId);
 
   static already_AddRefed<nsIFile>
@@ -62,17 +61,16 @@ public:
                 uint32_t aTelemetryId);
 
   static nsresult
   GetUsage(nsIFile* aDirectory, uint64_t* aUsage);
 
   FileManager(PersistenceType aPersistenceType,
               const nsACString& aGroup,
               const nsACString& aOrigin,
-              bool aIsApp,
               const nsAString& aDatabaseName,
               bool aEnforcingQuota);
 
   PersistenceType
   Type() const
   {
     return mPersistenceType;
   }
@@ -84,22 +82,16 @@ public:
   }
 
   const nsACString&
   Origin() const
   {
     return mOrigin;
   }
 
-  bool
-  IsApp() const
-  {
-    return mIsApp;
-  }
-
   const nsAString&
   DatabaseName() const
   {
     return mDatabaseName;
   }
 
   bool
   EnforcingQuota() const
--- a/dom/indexedDB/IDBDatabase.cpp
+++ b/dom/indexedDB/IDBDatabase.cpp
@@ -1087,32 +1087,31 @@ IDBDatabase::GetQuotaInfo(nsACString& aO
   PrincipalInfo* principalInfo = mFactory->GetPrincipalInfo();
   MOZ_ASSERT(principalInfo);
 
   switch (principalInfo->type()) {
     case PrincipalInfo::TNullPrincipalInfo:
       MOZ_CRASH("Is this needed?!");
 
     case PrincipalInfo::TSystemPrincipalInfo:
-      QuotaManager::GetInfoForChrome(nullptr, nullptr, &aOrigin, nullptr);
+      QuotaManager::GetInfoForChrome(nullptr, nullptr, &aOrigin);
       return NS_OK;
 
     case PrincipalInfo::TContentPrincipalInfo: {
       nsresult rv;
       nsCOMPtr<nsIPrincipal> principal =
         PrincipalInfoToPrincipal(*principalInfo, &rv);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
 
       rv = QuotaManager::GetInfoFromPrincipal(principal,
                                               nullptr,
                                               nullptr,
-                                              &aOrigin,
-                                              nullptr);
+                                              &aOrigin);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
 
       return NS_OK;
     }
 
     default:
--- a/dom/indexedDB/IndexedDatabaseManager.cpp
+++ b/dom/indexedDB/IndexedDatabaseManager.cpp
@@ -1361,17 +1361,16 @@ DeleteFilesRunnable::Open()
     return NS_ERROR_FAILURE;
   }
 
   mState = State_DirectoryOpenPending;
 
   quotaManager->OpenDirectory(mFileManager->Type(),
                               mFileManager->Group(),
                               mFileManager->Origin(),
-                              mFileManager->IsApp(),
                               Client::IDB,
                               /* aExclusive */ false,
                               this);
 
   return NS_OK;
 }
 
 nsresult
--- a/dom/indexedDB/test/mochitest.ini
+++ b/dom/indexedDB/test/mochitest.ini
@@ -8,18 +8,16 @@ support-files =
   bfcache_iframe2.html
   blob_worker_crash_iframe.html
   error_events_abort_transactions_iframe.html
   event_propagation_iframe.html
   exceptions_in_events_iframe.html
   file.js
   helpers.js
   leaving_page_iframe.html
-  service_worker.js
-  service_worker_client.html
   third_party_iframe1.html
   third_party_iframe2.html
   unit/test_abort_deleted_index.js
   unit/test_abort_deleted_objectStore.js
   unit/test_add_put.js
   unit/test_add_twice_failure.js
   unit/test_advance.js
   unit/test_autoIncrement.js
@@ -237,23 +235,23 @@ skip-if = e10s
 [test_remove_index.html]
 [test_rename_index.html]
 [test_rename_index_errors.html]
 [test_remove_objectStore.html]
 [test_rename_objectStore.html]
 [test_rename_objectStore_errors.html]
 [test_request_readyState.html]
 [test_sandbox.html]
-[test_serviceworker.html]
 [test_setVersion.html]
 [test_setVersion_abort.html]
 [test_setVersion_events.html]
 [test_setVersion_exclusion.html]
 [test_setVersion_throw.html]
 [test_storage_manager_estimate.html]
+scheme=https
 [test_success_events_after_abort.html]
 [test_table_locks.html]
 [test_table_rollback.html]
 [test_third_party.html]
 [test_traffic_jam.html]
 [test_transaction_abort.html]
 [test_transaction_abort_hang.html]
 [test_transaction_duplicate_store_names.html]
index 68bb4749f527fbf71c5b509b6614c69ea8b37284..2dcc10ffe0cb547330c47ce7690b6b115b08a329
GIT binary patch
literal 65706
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
O&oh1+n(<uRKm9-Y>sev|
index b7e434171df3d80dc91cf7ddf684188b8723d468..77f26035bcffc5cbde21d00056512fa41411c043
GIT binary patch
literal 3055
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
aSMv7~{{)z&a+TkwD9>LD4@cH4%>D!OP9C8E
index 264c68d866d55dfc3739fc8e85302677e0b57a74..faa95ee843bdddd8e0aa15260bf04898e9084ece
GIT binary patch
literal 3276
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
Vrlnp$@ok*a{1rC8I=8oF_%9QiTyp>b
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..01b9cfe11801a4e6ff772af78d350ddeda3ff56f
GIT binary patch
literal 4308
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
index 888d24434525d9cf041733de62061b853c773899..114d72e6cf8718b42539ed7ef1717809cce916f5
GIT binary patch
literal 3188
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
b{!jK7wXd|?f<oLcTo_<6U!U|33wM75NhW<!
index b1082106b218c990ecabc23a4d1dc1637f5b2085..74c84ecbfe84cc997f4d598d5b88aa18527a4989
GIT binary patch
literal 3490
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
--- a/dom/indexedDB/test/unit/test_defaultStorageUpgrade.js
+++ b/dom/indexedDB/test/unit/test_defaultStorageUpgrade.js
@@ -41,20 +41,16 @@ function* testSteps()
     { url: "file:///c:/Users/joe/", dbName: "dbJ", dbVersion: 1 },
 
     // This one lives in storage/default/file++++c++Users+joe+index.html
     { url: "file:///c:/Users/joe/index.html", dbName: "dbK", dbVersion: 1 },
 
     // This one lives in storage/permanent/chrome
     { dbName: "dbL", dbVersion: 1 },
 
-    // This one lives in storage/default/1007+t+https+++developer.cdn.mozilla.net
-    { appId: 1007, inIsolatedMozBrowser: true, url: "https://developer.cdn.mozilla.net",
-      dbName: "dbN", dbVersion: 1 },
-
     // This one lives in storage/default/http+++127.0.0.1
     { url: "http://127.0.0.1", dbName: "dbO", dbVersion: 1 },
 
     // This one lives in storage/default/file++++
     { url: "file:///", dbName: "dbP", dbVersion: 1 },
 
     // This one lives in storage/default/file++++c++
     { url: "file:///c:/", dbName: "dbQ", dbVersion: 1 },
@@ -82,39 +78,25 @@ function* testSteps()
       dbOptions: { version: 1, storage: "temporary" } },
 
     // This one lives in storage/temporary/http+++localhost+82
     // The .metadata file was intentionally truncated for this origin directory
     // to test restoring during upgrade.
     { url: "http://localhost:82", dbName: "dbW",
       dbOptions: { version: 1, storage: "temporary" } },
 
-    // This one lives in storage/temporary/1007+t+https+++developer.cdn.mozilla.net
-    { appId: 1007, inIsolatedMozBrowser: true, url: "https://developer.cdn.mozilla.net",
-      dbName: "dbY", dbOptions: { version: 1, storage: "temporary" } },
-
     // This one lives in storage/temporary/http+++localhost
     { url: "http://localhost", dbName: "dbZ",
       dbOptions: { version: 1, storage: "temporary" } }
   ];
 
-  let ios = SpecialPowers.Cc["@mozilla.org/network/io-service;1"]
-                         .getService(SpecialPowers.Ci.nsIIOService);
-
-  let ssm = SpecialPowers.Cc["@mozilla.org/scriptsecuritymanager;1"]
-                         .getService(SpecialPowers.Ci.nsIScriptSecurityManager);
-
   function openDatabase(params) {
     let request;
     if ("url" in params) {
-      let uri = ios.newURI(params.url);
-      let principal =
-        ssm.createCodebasePrincipal(uri,
-                                    {appId: params.appId || ssm.NO_APPID,
-                                     inIsolatedMozBrowser: params.inIsolatedMozBrowser});
+      let principal = getPrincipal(params.url);
       if ("dbVersion" in params) {
         request = indexedDB.openForPrincipal(principal, params.dbName,
                                              params.dbVersion);
       } else {
         request = indexedDB.openForPrincipal(principal, params.dbName,
                                              params.dbOptions);
       }
     } else {
--- a/dom/indexedDB/test/unit/test_idbSubdirUpgrade.js
+++ b/dom/indexedDB/test/unit/test_idbSubdirUpgrade.js
@@ -4,61 +4,44 @@
  */
 
 var testGenerator = testSteps();
 
 function* testSteps()
 {
   const openParams = [
     // This one lives in storage/default/http+++www.mozilla.org
-    { url: "http://www.mozilla.org", dbName: "dbB", dbVersion: 1 },
-
-    // This one lives in storage/default/1007+t+https+++developer.cdn.mozilla.net
-    { appId: 1007, inIsolatedMozBrowser: true, url: "https://developer.cdn.mozilla.net",
-      dbName: "dbN", dbVersion: 1 },
+    { url: "http://www.mozilla.org", dbName: "dbB", dbVersion: 1 }
   ];
 
-  let ios = SpecialPowers.Cc["@mozilla.org/network/io-service;1"]
-                         .getService(SpecialPowers.Ci.nsIIOService);
-
-  let ssm = SpecialPowers.Cc["@mozilla.org/scriptsecuritymanager;1"]
-                         .getService(SpecialPowers.Ci.nsIScriptSecurityManager);
-
-  function openDatabase(params) {
-    let uri = ios.newURI(params.url);
-    let principal =
-      ssm.createCodebasePrincipal(uri,
-                                  {appId: params.appId || ssm.NO_APPID,
-                                   inIsolatedMozBrowser: params.inIsolatedMozBrowser});
-    let request = indexedDB.openForPrincipal(principal, params.dbName,
-                                             params.dbVersion);
-    return request;
-  }
-
   for (let i = 1; i <= 2; i++) {
     clearAllDatabases(continueToNextStepSync);
     yield undefined;
 
     installPackagedProfile("idbSubdirUpgrade" + i + "_profile");
 
     for (let params of openParams) {
-      let request = openDatabase(params);
+      let request = indexedDB.openForPrincipal(getPrincipal(params.url),
+                                               params.dbName,
+                                               params.dbVersion);
       request.onerror = errorHandler;
       request.onupgradeneeded = unexpectedSuccessHandler;
       request.onsuccess = grabEventAndContinueHandler;
       let event = yield undefined;
 
       is(event.type, "success", "Correct event type");
     }
 
     resetAllDatabases(continueToNextStepSync);
     yield undefined;
 
     for (let params of openParams) {
-      let request = openDatabase(params);
+      let request = indexedDB.openForPrincipal(getPrincipal(params.url),
+                                               params.dbName,
+                                               params.dbVersion);
       request.onerror = errorHandler;
       request.onupgradeneeded = unexpectedSuccessHandler;
       request.onsuccess = grabEventAndContinueHandler;
       let event = yield undefined;
 
       is(event.type, "success", "Correct event type");
     }
   }
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/test/unit/test_obsoleteOriginAttributesUpgrade.js
@@ -0,0 +1,43 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+var testGenerator = testSteps();
+
+function* testSteps()
+{
+  const url = "moz-extension://8ea6d31b-917c-431f-a204-15b95e904d4f";
+  const dbName = "Hello.";
+  const dbVersion = 1;
+
+  clearAllDatabases(continueToNextStepSync);
+  yield;
+
+  // The origin directory contained in the package is:
+  // "moz-extension+++8ea6d31b-917c-431f-a204-15b95e904d4f^addonId=indexedDB-test%40kmaglione.mozilla.com"
+  installPackagedProfile("obsoleteOriginAttributes_profile");
+
+  let request =
+    indexedDB.openForPrincipal(getPrincipal(url), dbName, dbVersion);
+  request.onerror = errorHandler;
+  request.onupgradeneeded = unexpectedSuccessHandler;
+  request.onsuccess = grabEventAndContinueHandler;
+  let event = yield;
+
+  is(event.type, "success", "Correct event type");
+
+  resetAllDatabases(continueToNextStepSync);
+  yield;
+
+  request = indexedDB.openForPrincipal(getPrincipal(url), dbName, dbVersion);
+  request.onerror = errorHandler;
+  request.onupgradeneeded = unexpectedSuccessHandler;
+  request.onsuccess = grabEventAndContinueHandler;
+  event = yield;
+
+  is(event.type, "success", "Correct event type");
+
+  finishTest();
+}
+
--- a/dom/indexedDB/test/unit/test_schema23upgrade.js
+++ b/dom/indexedDB/test/unit/test_schema23upgrade.js
@@ -4,60 +4,43 @@
  */
 
 var testGenerator = testSteps();
 
 function* testSteps()
 {
   const openParams = [
     // This one lives in storage/default/http+++www.mozilla.org
-    { url: "http://www.mozilla.org", dbName: "dbB", dbVersion: 1 },
-
-    // This one lives in storage/default/1007+t+https+++developer.cdn.mozilla.net
-    { appId: 1007, inIsolatedMozBrowser: true, url: "https://developer.cdn.mozilla.net",
-      dbName: "dbN", dbVersion: 1 },
+    { url: "http://www.mozilla.org", dbName: "dbB", dbVersion: 1 }
   ];
 
-  let ios = SpecialPowers.Cc["@mozilla.org/network/io-service;1"]
-                         .getService(SpecialPowers.Ci.nsIIOService);
-
-  let ssm = SpecialPowers.Cc["@mozilla.org/scriptsecuritymanager;1"]
-                         .getService(SpecialPowers.Ci.nsIScriptSecurityManager);
-
-  function openDatabase(params) {
-    let uri = ios.newURI(params.url);
-    let principal =
-      ssm.createCodebasePrincipal(uri,
-                                  {appId: params.appId || ssm.NO_APPID,
-                                   inIsolatedMozBrowser: params.inIsolatedMozBrowser});
-    let request = indexedDB.openForPrincipal(principal, params.dbName,
-                                             params.dbVersion);
-    return request;
-  }
-
   clearAllDatabases(continueToNextStepSync);
   yield undefined;
 
   installPackagedProfile("schema23upgrade_profile");
 
   for (let params of openParams) {
-    let request = openDatabase(params);
+    let request = indexedDB.openForPrincipal(getPrincipal(params.url),
+                                             params.dbName,
+                                             params.dbVersion);
     request.onerror = errorHandler;
     request.onupgradeneeded = unexpectedSuccessHandler;
     request.onsuccess = grabEventAndContinueHandler;
     let event = yield undefined;
 
     is(event.type, "success", "Correct event type");
   }
 
   resetAllDatabases(continueToNextStepSync);
   yield undefined;
 
   for (let params of openParams) {
-    let request = openDatabase(params);
+    let request = indexedDB.openForPrincipal(getPrincipal(params.url),
+                                             params.dbName,
+                                             params.dbVersion);
     request.onerror = errorHandler;
     request.onupgradeneeded = unexpectedSuccessHandler;
     request.onsuccess = grabEventAndContinueHandler;
     let event = yield undefined;
 
     is(event.type, "success", "Correct event type");
   }
 
--- a/dom/indexedDB/test/unit/test_storagePersistentUpgrade.js
+++ b/dom/indexedDB/test/unit/test_storagePersistentUpgrade.js
@@ -4,60 +4,43 @@
  */
 
 var testGenerator = testSteps();
 
 function* testSteps()
 {
   const openParams = [
     // This one lives in storage/default/http+++www.mozilla.org
-    { url: "http://www.mozilla.org", dbName: "dbB", dbVersion: 1 },
-
-    // This one lives in storage/default/1007+t+https+++developer.cdn.mozilla.net
-    { appId: 1007, inIsolatedMozBrowser: true, url: "https://developer.cdn.mozilla.net",
-      dbName: "dbN", dbVersion: 1 },
+    { url: "http://www.mozilla.org", dbName: "dbB", dbVersion: 1 }
   ];
 
-  let ios = SpecialPowers.Cc["@mozilla.org/network/io-service;1"]
-                         .getService(SpecialPowers.Ci.nsIIOService);
-
-  let ssm = SpecialPowers.Cc["@mozilla.org/scriptsecuritymanager;1"]
-                         .getService(SpecialPowers.Ci.nsIScriptSecurityManager);
-
-  function openDatabase(params) {
-    let uri = ios.newURI(params.url);
-    let principal =
-      ssm.createCodebasePrincipal(uri,
-                                  {appId: params.appId || ssm.NO_APPID,
-                                   inIsolatedMozBrowser: params.inIsolatedMozBrowser});
-    let request = indexedDB.openForPrincipal(principal, params.dbName,
-                                             params.dbVersion);
-    return request;
-  }
-
   clearAllDatabases(continueToNextStepSync);
   yield undefined;
 
   installPackagedProfile("storagePersistentUpgrade_profile");
 
   for (let params of openParams) {
-    let request = openDatabase(params);
+    let request = indexedDB.openForPrincipal(getPrincipal(params.url),
+                                             params.dbName,
+                                             params.dbVersion);
     request.onerror = errorHandler;
     request.onupgradeneeded = unexpectedSuccessHandler;
     request.onsuccess = grabEventAndContinueHandler;
     let event = yield undefined;
 
     is(event.type, "success", "Correct event type");
   }
 
   resetAllDatabases(continueToNextStepSync);
   yield undefined;
 
   for (let params of openParams) {
-    let request = openDatabase(params);
+    let request = indexedDB.openForPrincipal(getPrincipal(params.url),
+                                             params.dbName,
+                                             params.dbVersion);
     request.onerror = errorHandler;
     request.onupgradeneeded = unexpectedSuccessHandler;
     request.onsuccess = grabEventAndContinueHandler;
     let event = yield undefined;
 
     is(event.type, "success", "Correct event type");
   }
 
--- a/dom/indexedDB/test/unit/test_wasm_recompile.js
+++ b/dom/indexedDB/test/unit/test_wasm_recompile.js
@@ -41,23 +41,21 @@ function* testSteps()
 
   let filesDir = getChromeFilesDir();
 
   let file = filesDir.clone();
   file.append("2");
 
   info("Reading out contents of compiled blob");
 
+  File.createFromNsIFile(file).then(grabEventAndContinueHandler);
+  let domFile = yield undefined;
+
   let fileReader = new FileReader();
   fileReader.onload = continueToNextStepSync;
-
-  let domFile;
-  File.createFromNsIFile(file).then(file => { domFile = file; }).then(continueToNextStepSync);
-  yield undefined;
-
   fileReader.readAsArrayBuffer(domFile);
 
   yield undefined;
 
   let compiledBuffer = fileReader.result;
 
   info("Opening database");
 
@@ -80,22 +78,21 @@ function* testSteps()
 
   info("Verifying wasm module");
 
   verifyWasmModule(request.result, wasmData.wasm);
   yield undefined;
 
   info("Reading out contents of new compiled blob");
 
+  File.createFromNsIFile(file).then(grabEventAndContinueHandler);
+  domFile = yield undefined;
+
   fileReader = new FileReader();
   fileReader.onload = continueToNextStepSync;
-
-  File.createFromNsIFile(file).then(file => { domFile = file; }).then(continueToNextStepSync);
-  yield undefined;
-
   fileReader.readAsArrayBuffer(domFile);
 
   yield undefined;
 
   let newCompiledBuffer = fileReader.result;
 
   info("Verifying blobs differ");
 
@@ -108,24 +105,23 @@ function* testSteps()
   request.onsuccess = continueToNextStepSync;
   yield undefined;
 
   info("Verifying wasm module");
 
   verifyWasmModule(request.result, wasmData.wasm);
   yield undefined;
 
-  info("Reading contents of new compiled blob again");
+  info("Reading out contents of new compiled blob again");
+
+  File.createFromNsIFile(file).then(grabEventAndContinueHandler);
+  domFile = yield undefined;
 
   fileReader = new FileReader();
   fileReader.onload = continueToNextStepSync;
-
-  File.createFromNsIFile(file).then(file => { domFile = file; }).then(continueToNextStepSync);
-  yield undefined;
-
   fileReader.readAsArrayBuffer(domFile);
 
   yield undefined;
 
   let newCompiledBuffer2 = fileReader.result;
 
   info("Verifying blob didn't change");
 
--- a/dom/indexedDB/test/unit/xpcshell-parent-process.ini
+++ b/dom/indexedDB/test/unit/xpcshell-parent-process.ini
@@ -8,16 +8,17 @@ head = xpcshell-head-parent-process.js
 tail =
 skip-if = toolkit == 'gonk'
 support-files =
   bug1056939_profile.zip
   defaultStorageUpgrade_profile.zip
   idbSubdirUpgrade1_profile.zip
   idbSubdirUpgrade2_profile.zip
   mutableFileUpgrade_profile.zip
+  obsoleteOriginAttributes_profile.zip
   oldDirectories_profile.zip
   GlobalObjectsChild.js
   GlobalObjectsComponent.js
   GlobalObjectsComponent.manifest
   GlobalObjectsModule.jsm
   GlobalObjectsSandbox.js
   metadata2Restore_profile.zip
   metadataRestore_profile.zip
@@ -28,16 +29,17 @@ support-files =
   storagePersistentUpgrade_profile.zip
   wasm_recompile_profile.zip
   xpcshell-shared.ini
 
 [include:xpcshell-shared.ini]
 
 [test_bad_origin_directory.js]
 skip-if = release_or_beta
+[test_obsoleteOriginAttributesUpgrade.js]
 [test_blob_file_backed.js]
 [test_bug1056939.js]
 [test_cleanup_transaction.js]
 [test_database_close_without_onclose.js]
 [test_database_onclose.js]
 [test_defaultStorageUpgrade.js]
 [test_file_copy_failure.js]
 [test_idbSubdirUpgrade.js]
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -5090,16 +5090,23 @@ ContentParent::RecvUpdateChildScalars(
 mozilla::ipc::IPCResult
 ContentParent::RecvUpdateChildKeyedScalars(
                 InfallibleTArray<KeyedScalarAction>&& aScalarActions)
 {
   TelemetryIPC::UpdateChildKeyedScalars(GeckoProcessType_Content, aScalarActions);
   return IPC_OK();
 }
 
+mozilla::ipc::IPCResult
+ContentParent::RecvRecordChildEvents(nsTArray<mozilla::Telemetry::ChildEventData>&& aEvents)
+{
+  TelemetryIPC::RecordChildEvents(GeckoProcessType_Content, aEvents);
+  return IPC_OK();
+}
+
 PURLClassifierParent*
 ContentParent::AllocPURLClassifierParent(const Principal& aPrincipal,
                                          const bool& aUseTrackingProtection,
                                          bool* aSuccess)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   *aSuccess = true;
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -1120,16 +1120,18 @@ private:
   virtual mozilla::ipc::IPCResult RecvAccumulateChildHistograms(
     InfallibleTArray<Accumulation>&& aAccumulations) override;
   virtual mozilla::ipc::IPCResult RecvAccumulateChildKeyedHistograms(
     InfallibleTArray<KeyedAccumulation>&& aAccumulations) override;
   virtual mozilla::ipc::IPCResult RecvUpdateChildScalars(
     InfallibleTArray<ScalarAction>&& aScalarActions) override;
   virtual mozilla::ipc::IPCResult RecvUpdateChildKeyedScalars(
     InfallibleTArray<KeyedScalarAction>&& aScalarActions) override;
+  virtual mozilla::ipc::IPCResult RecvRecordChildEvents(
+    nsTArray<ChildEventData>&& events) override;
 public:
   void SendGetFilesResponseAndForget(const nsID& aID,
                                      const GetFilesResponseResult& aResult);
 
   bool SendRequestMemoryReport(const uint32_t& aGeneration,
                                const bool& aAnonymize,
                                const bool& aMinimizeMemoryUsage,
                                const MaybeFileDesc& aDMDFile) override;
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -85,16 +85,17 @@ using class mozilla::dom::ipc::Structure
 using mozilla::DataStorageType from "ipc/DataStorageIPCUtils.h";
 using mozilla::OriginAttributes from "mozilla/ipc/BackgroundUtils.h";
 using struct mozilla::layers::TextureFactoryIdentifier from "mozilla/layers/CompositorTypes.h";
 using struct mozilla::dom::FlyWebPublishOptions from "mozilla/dom/FlyWebPublishOptionsIPCSerializer.h";
 using mozilla::Telemetry::Accumulation from "mozilla/TelemetryComms.h";
 using mozilla::Telemetry::KeyedAccumulation from "mozilla/TelemetryComms.h";
 using mozilla::Telemetry::ScalarAction from "mozilla/TelemetryComms.h";
 using mozilla::Telemetry::KeyedScalarAction from "mozilla/TelemetryComms.h";
+using mozilla::Telemetry::ChildEventData from "mozilla/TelemetryComms.h";
 
 union ChromeRegistryItem
 {
     ChromePackage;
     OverrideMapping;
     SubstitutionMapping;
 };
 
@@ -1197,16 +1198,17 @@ parent:
 
     /**
      * Messages for communicating child Telemetry to the parent process
      */
     async AccumulateChildHistograms(Accumulation[] accumulations);
     async AccumulateChildKeyedHistograms(KeyedAccumulation[] accumulations);
     async UpdateChildScalars(ScalarAction[] updates);
     async UpdateChildKeyedScalars(KeyedScalarAction[] updates);
+    async RecordChildEvents(ChildEventData[] events);
 
     sync GetA11yContentId() returns (uint32_t aContentId);
 
     async AddMemoryReport(MemoryReport aReport);
     async FinishMemoryReport(uint32_t aGeneration);
 
 both:
      async AsyncMessage(nsString aMessage, CpowEntry[] aCpows,
--- a/dom/media/gmp/widevine-adapter/WidevineVideoDecoder.cpp
+++ b/dom/media/gmp/widevine-adapter/WidevineVideoDecoder.cpp
@@ -73,17 +73,17 @@ WidevineVideoDecoder::InitDecode(const G
   } else if (mCodecType == kGMPVideoCodecVP9) {
     config.codec = VideoDecoderConfig::kCodecVp9;
     config.profile = VideoDecoderConfig::kProfileNotNeeded;
   } else {
     mCallback->Error(GMPInvalidArgErr);
     return;
   }
   config.format = kYv12;
-  config.coded_size = Size(aCodecSettings.mWidth, aCodecSettings.mHeight);
+  config.coded_size = mCodedSize = Size(aCodecSettings.mWidth, aCodecSettings.mHeight);
   nsTArray<uint8_t> extraData;
   if (aCodecSpecificLength > 0) {
     // The first byte is the WebRTC packetization mode, which can be ignored.
     extraData.AppendElements(aCodecSpecific + 1, aCodecSpecificLength - 1);
     config.extra_data = extraData.Elements();
     config.extra_data_size = extraData.Length();
   }
   Status rv = CDM()->InitializeVideoDecoder(config);
@@ -123,17 +123,28 @@ WidevineVideoDecoder::Decode(GMPVideoEnc
   CDM_LOG("WidevineVideoDecoder::Decode(timestamp=%" PRId64 ") rv=%d",
           sample.timestamp, rv);
 
   // Destroy frame, so that the shmem is now free to be used to return
   // output to the Gecko process.
   aInputFrame->Destroy();
   aInputFrame = nullptr;
 
-  if (rv == kSuccess) {
+  if (rv == kSuccess || rv == kNoKey) {
+    if (rv == kNoKey) {
+      CDM_LOG("NoKey for sample at time=%" PRId64 "!", sample.timestamp);
+      // Somehow our key became unusable. Typically this would happen when
+      // a stream requires output protection, and the configuration changed
+      // such that output protection is no longer available. For example, a
+      // non-compliant monitor was attached. The JS player should notice the
+      // key status changing to "output-restricted", and is supposed to switch
+      // to a stream that doesn't require OP. In order to keep the playback
+      // pipeline rolling, just output a black frame. See bug 1343140.
+      frame.InitToBlack(mCodedSize.width, mCodedSize.height, sample.timestamp);
+    }
     if (!ReturnOutput(frame)) {
       CDM_LOG("WidevineVideoDecoder::Decode() Failed in ReturnOutput()");
       mCallback->Error(GMPDecodeErr);
       return;
     }
     // A reset should only be started at most at level mReturnOutputCallDepth 1,
     // and if it's started it should be finished by that call by the time
     // the it returns, so it should always be false by this point.
--- a/dom/media/gmp/widevine-adapter/WidevineVideoDecoder.h
+++ b/dom/media/gmp/widevine-adapter/WidevineVideoDecoder.h
@@ -68,13 +68,14 @@ private:
   // Number of calls of ReturnOutput currently in progress.
   int32_t mReturnOutputCallDepth;
   // If we're waiting to drain. Used to prevent drain completing while
   // ReturnOutput calls are still on the stack.
   bool mDrainPending;
   // If a reset is being performed. Used to track if ReturnOutput should
   // dump current frame.
   bool mResetInProgress;
+  cdm::Size mCodedSize;
 };
 
 } // namespace mozilla
 
 #endif // WidevineVideoDecoder_h_
--- a/dom/media/gmp/widevine-adapter/WidevineVideoFrame.cpp
+++ b/dom/media/gmp/widevine-adapter/WidevineVideoFrame.cpp
@@ -119,9 +119,36 @@ WidevineVideoFrame::SetTimestamp(int64_t
 }
 
 int64_t
 WidevineVideoFrame::Timestamp() const
 {
   return mTimestamp;
 }
 
+void
+WidevineVideoFrame::InitToBlack(uint32_t aWidth, uint32_t aHeight, int64_t aTimeStamp)
+{
+  SetFormat(VideoFormat::kI420);
+  SetSize(cdm::Size(aWidth, aHeight));
+  size_t ySize = aWidth * aHeight;
+  size_t uSize = ((aWidth + 1) / 2) * ((aHeight + 1) / 2);
+  WidevineBuffer* buffer = new WidevineBuffer(ySize + uSize);
+  // Black in YCbCr is (0,128,128).
+  memset(buffer->Data(), 0, ySize);
+  memset(buffer->Data() + ySize, 128, uSize);
+  if (mBuffer) {
+    mBuffer->Destroy();
+    mBuffer = nullptr;
+  }
+  SetFrameBuffer(buffer);
+  SetPlaneOffset(VideoFrame::kYPlane, 0);
+  SetStride(VideoFrame::kYPlane, aWidth);
+  // Note: U and V planes are stored at the same place in order to
+  // save memory since their contents are the same.
+  SetPlaneOffset(VideoFrame::kUPlane, ySize);
+  SetStride(VideoFrame::kUPlane, (aWidth + 1) / 2);
+  SetPlaneOffset(VideoFrame::kVPlane, ySize);
+  SetStride(VideoFrame::kVPlane, (aWidth + 1) / 2);
+  SetTimestamp(aTimeStamp);
+}
+
 } // namespace mozilla
--- a/dom/media/gmp/widevine-adapter/WidevineVideoFrame.h
+++ b/dom/media/gmp/widevine-adapter/WidevineVideoFrame.h
@@ -31,16 +31,18 @@ public:
   uint32_t PlaneOffset(cdm::VideoFrame::VideoPlane aPlane) override;
 
   void SetStride(cdm::VideoFrame::VideoPlane aPlane, uint32_t aStride) override;
   uint32_t Stride(cdm::VideoFrame::VideoPlane aPlane) override;
 
   void SetTimestamp(int64_t aTimestamp) override;
   int64_t Timestamp() const override;
 
+  void InitToBlack(uint32_t aWidth, uint32_t aHeight, int64_t aTimeStamp);
+
 protected:
   cdm::VideoFormat mFormat;
   cdm::Size mSize;
   cdm::Buffer* mBuffer;
   uint32_t mPlaneOffsets[kMaxPlanes];
   uint32_t mPlaneStrides[kMaxPlanes];
   int64_t mTimestamp;
 };
--- a/dom/performance/Performance.cpp
+++ b/dom/performance/Performance.cpp
@@ -511,33 +511,16 @@ Performance::QueueEntry(PerformanceEntry
                                            QueueEntry, (aEntry));
 
   if (!mPendingNotificationObserversTask) {
     RunNotificationObserversTask();
   }
 }
 
 /* static */ bool
-Performance::IsEnabled(JSContext* aCx, JSObject* aGlobal)
-{
-  if (NS_IsMainThread()) {
-    return Preferences::GetBool("dom.enable_user_timing", false);
-  }
-
-  WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
-  MOZ_ASSERT(workerPrivate);
-  workerPrivate->AssertIsOnWorkerThread();
-
-  RefPtr<PrefEnabledRunnable> runnable =
-    new PrefEnabledRunnable(workerPrivate,
-                            NS_LITERAL_CSTRING("dom.enable_user_timing"));
-  return runnable->Dispatch() && runnable->IsEnabled();
-}
-
-/* static */ bool
 Performance::IsObserverEnabled(JSContext* aCx, JSObject* aGlobal)
 {
   if (NS_IsMainThread()) {
     return Preferences::GetBool("dom.enable_performance_observer", false);
   }
 
   WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
   MOZ_ASSERT(workerPrivate);
--- a/dom/performance/Performance.h
+++ b/dom/performance/Performance.h
@@ -34,18 +34,16 @@ class WorkerPrivate;
 // Base class for main-thread and worker Performance API
 class Performance : public DOMEventTargetHelper
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(Performance,
                                            DOMEventTargetHelper)
 
-  static bool IsEnabled(JSContext* aCx, JSObject* aGlobal);
-
   static bool IsObserverEnabled(JSContext* aCx, JSObject* aGlobal);
 
   static already_AddRefed<Performance>
   CreateForMainThread(nsPIDOMWindowInner* aWindow,
                       nsDOMNavigationTiming* aDOMTiming,
                       nsITimedChannel* aChannel);
 
   static already_AddRefed<Performance>
--- a/dom/plugins/ipc/PluginModuleParent.h
+++ b/dom/plugins/ipc/PluginModuleParent.h
@@ -19,18 +19,20 @@
 #include "mozilla/TimeStamp.h"
 #include "npapi.h"
 #include "npfunctions.h"
 #include "nsDataHashtable.h"
 #include "nsHashKeys.h"
 #include "nsIObserver.h"
 #ifdef XP_WIN
 #include "nsWindowsHelpers.h"
+#if defined(MOZ_SANDBOX)
 #include "sandboxPermissions.h"
 #endif
+#endif
 
 #ifdef MOZ_CRASHREPORTER
 #include "nsExceptionHandler.h"
 #endif
 
 class nsIProfileSaveEvent;
 class nsPluginTag;
 
--- a/dom/plugins/test/unit/test_plugin_default_state_xpi.js
+++ b/dom/plugins/test/unit/test_plugin_default_state_xpi.js
@@ -73,16 +73,17 @@ add_task(function* test_state() {
         hasMoreElements: () => result.length > 0,
         getNext: () => result.shift(),
       };
     },
   };
 
   let dirSvc = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties);
   dirSvc.QueryInterface(Ci.nsIDirectoryService).registerProvider(provider);
+  dirSvc = null;
 
   // We installed a non-restartless addon, need to restart the manager.
   restartManager();
   gPluginHost.reloadPlugins();
 
   Assert.ok(addonDir.exists(), "Addon path should exist: " + addonDir.path);
   Assert.ok(addonDir.isDirectory(), "Addon path should be a directory: " + addonDir.path);
   let pluginDir = addonDir.clone();
--- a/dom/presentation/tests/xpcshell/test_multicast_dns_device_provider.js
+++ b/dom/presentation/tests/xpcshell/test_multicast_dns_device_provider.js
@@ -473,16 +473,17 @@ function filterDevice() {
         { requestedUrl: "unknowSchem://example.com", supported: false },
       ];
 
       for (let test of tests) {
         Assert.equal(device.isRequestedUrlSupported(test.requestedUrl), test.supported);
       }
 
       provider.listener = null;
+      provider = null;
       run_next_test();
     },
     updateDevice: function() {},
     removeDevice: function() {},
     onSessionRequest: function() {},
   };
 
   provider.listener = listener;
@@ -830,16 +831,17 @@ function ignoreIncompatibleDevice() {
   deferred.promise.then(function() {
     Assert.equal(mockServerObj.id, mockDevice.host);
 
     // Start discovery
     Services.prefs.setBoolPref(PREF_DISCOVERY, true);
     Assert.equal(listener.count(), 0);
 
     provider.listener = null;
+    provider = null;
 
     run_next_test();
   });
 }
 
 function ignoreSelfDevice() {
   Services.prefs.setBoolPref(PREF_DISCOVERY, false);
   Services.prefs.setBoolPref(PREF_DISCOVERABLE, true);
@@ -913,16 +915,17 @@ function ignoreSelfDevice() {
   deferred.promise.then(() => {
     Assert.equal(mockServerObj.id, mockDevice.host);
 
     // Start discovery
     Services.prefs.setBoolPref(PREF_DISCOVERY, true);
     Assert.equal(listener.count(), 0);
 
     provider.listener = null;
+    provider = null;
 
     run_next_test();
   });
 }
 
 function addDeviceDynamically() {
   Services.prefs.setBoolPref(PREF_DISCOVERY, false);
 
--- a/dom/quota/ActorsChild.cpp
+++ b/dom/quota/ActorsChild.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 "ActorsChild.h"
 
+#include "nsVariant.h"
 #include "QuotaManagerService.h"
 #include "QuotaRequests.h"
 
 namespace mozilla {
 namespace dom {
 namespace quota {
 
 /*******************************************************************************
@@ -226,17 +227,32 @@ QuotaRequestChild::HandleResponse(nsresu
 }
 
 void
 QuotaRequestChild::HandleResponse()
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(mRequest);
 
-  mRequest->SetResult();
+  RefPtr<nsVariant> variant = new nsVariant();
+  variant->SetAsVoid();
+
+  mRequest->SetResult(variant);
+}
+
+void
+QuotaRequestChild::HandleResponse(bool aResponse)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mRequest);
+
+  RefPtr<nsVariant> variant = new nsVariant();
+  variant->SetAsBool(aResponse);
+
+  mRequest->SetResult(variant);
 }
 
 void
 QuotaRequestChild::ActorDestroy(ActorDestroyReason aWhy)
 {
   AssertIsOnOwningThread();
 }
 
@@ -246,23 +262,28 @@ QuotaRequestChild::Recv__delete__(const 
   AssertIsOnOwningThread();
   MOZ_ASSERT(mRequest);
 
   switch (aResponse.type()) {
     case RequestResponse::Tnsresult:
       HandleResponse(aResponse.get_nsresult());
       break;
 
+    case RequestResponse::TInitResponse:
     case RequestResponse::TClearOriginResponse:
-    case RequestResponse::TClearOriginsResponse:
+    case RequestResponse::TClearDataResponse:
     case RequestResponse::TClearAllResponse:
     case RequestResponse::TResetAllResponse:
       HandleResponse();
       break;
 
+    case RequestResponse::TInitOriginResponse:
+      HandleResponse(aResponse.get_InitOriginResponse().created());
+      break;
+
     default:
       MOZ_CRASH("Unknown response type!");
   }
 
   return IPC_OK();
 }
 
 } // namespace quota
--- a/dom/quota/ActorsChild.h
+++ b/dom/quota/ActorsChild.h
@@ -133,16 +133,19 @@ private:
   ~QuotaRequestChild();
 
   void
   HandleResponse(nsresult aResponse);
 
   void
   HandleResponse();
 
+  void
+  HandleResponse(bool aResponse);
+
   // IPDL methods are only called by IPDL.
   virtual void
   ActorDestroy(ActorDestroyReason aWhy) override;
 
   virtual mozilla::ipc::IPCResult
   Recv__delete__(const RequestResponse& aResponse) override;
 };
 
--- a/dom/quota/ActorsParent.cpp
+++ b/dom/quota/ActorsParent.cpp
@@ -72,16 +72,20 @@
 #define DISABLE_ASSERTS_FOR_FUZZING 0
 
 #if DISABLE_ASSERTS_FOR_FUZZING
 #define ASSERT_UNLESS_FUZZING(...) do { } while (0)
 #else
 #define ASSERT_UNLESS_FUZZING(...) MOZ_ASSERT(false, __VA_ARGS__)
 #endif
 
+#define UNKNOWN_FILE_WARNING(_leafName) \
+  QM_WARNING("Something (%s) in the directory that doesn't belong!", \
+             NS_ConvertUTF16toUTF8(leafName).get())
+
 // The amount of time, in milliseconds, that our IO thread will stay alive
 // after the last event it processes.
 #define DEFAULT_THREAD_TIMEOUT_MS 30000
 
 // The amount of time, in milliseconds, that we will wait for active storage
 // transactions on shutdown before aborting them.
 #define DEFAULT_SHUTDOWN_TIMER_MS 30000
 
@@ -116,17 +120,17 @@ namespace {
 
 /*******************************************************************************
  * Constants
  ******************************************************************************/
 
 const uint32_t kSQLitePageSizeOverride = 512;
 
 // Major storage version. Bump for backwards-incompatible changes.
-const uint32_t kMajorStorageVersion = 1;
+const uint32_t kMajorStorageVersion = 2;
 
 // Minor storage version. Bump for backwards-compatible changes.
 const uint32_t kMinorStorageVersion = 0;
 
 // The storage version we store in the SQLite database is a (signed) 32-bit
 // integer. The major version is left-shifted 16 bits so the max value is
 // 0xFFFF. The minor version occupies the lower 16 bits and its max is 0xFFFF.
 static_assert(kMajorStorageVersion <= 0xFFFF,
@@ -168,31 +172,32 @@ enum AppId {
   kNoAppId = nsIScriptSecurityManager::NO_APP_ID,
   kUnknownAppId = nsIScriptSecurityManager::UNKNOWN_APP_ID
 };
 
 #define STORAGE_FILE_NAME "storage.sqlite"
 
 // The name of the file that we use to load/save the last access time of an
 // origin.
+// XXX We should get rid of old metadata files at some point, bug 1343576.
 #define METADATA_FILE_NAME ".metadata"
+#define METADATA_TMP_FILE_NAME ".metadata-tmp"
 #define METADATA_V2_FILE_NAME ".metadata-v2"
+#define METADATA_V2_TMP_FILE_NAME ".metadata-v2-tmp"
 
 /******************************************************************************
  * SQLite functions
  ******************************************************************************/
 
-#if 0
 int32_t
 MakeStorageVersion(uint32_t aMajorStorageVersion,
                    uint32_t aMinorStorageVersion)
 {
   return int32_t((aMajorStorageVersion << 16) + aMinorStorageVersion);
 }
-#endif
 
 uint32_t
 GetMajorStorageVersion(int32_t aStorageVersion)
 {
   return uint32_t(aStorageVersion >> 16);
 
 }
 
@@ -237,17 +242,16 @@ CreateTables(mozIStorageConnection* aCon
 class DirectoryLockImpl final
   : public DirectoryLock
 {
   RefPtr<QuotaManager> mQuotaManager;
 
   const Nullable<PersistenceType> mPersistenceType;
   const nsCString mGroup;
   const OriginScope mOriginScope;
-  const Nullable<bool> mIsApp;
   const Nullable<Client::Type> mClientType;
   RefPtr<OpenDirectoryListener> mOpenListener;
 
   nsTArray<DirectoryLockImpl*> mBlocking;
   nsTArray<DirectoryLockImpl*> mBlockedOn;
 
   const bool mExclusive;
 
@@ -257,17 +261,16 @@ class DirectoryLockImpl final
 
   bool mInvalidated;
 
 public:
   DirectoryLockImpl(QuotaManager* aQuotaManager,
                     const Nullable<PersistenceType>& aPersistenceType,
                     const nsACString& aGroup,
                     const OriginScope& aOriginScope,
-                    const Nullable<bool>& aIsApp,
                     const Nullable<Client::Type>& aClientType,
                     bool aExclusive,
                     bool aInternal,
                     OpenDirectoryListener* aOpenListener);
 
   void
   AssertIsOnOwningThread() const
 #ifdef DEBUG
@@ -289,22 +292,16 @@ public:
   }
 
   const OriginScope&
   GetOriginScope() const
   {
     return mOriginScope;
   }
 
-  const Nullable<bool>&
-  GetIsApp() const
-  {
-    return mIsApp;
-  }
-
   const Nullable<Client::Type>&
   GetClientType() const
   {
     return mClientType;
   }
 
   bool
   IsInternal() const
@@ -484,20 +481,20 @@ namespace {
 
 class OriginInfo final
 {
   friend class GroupInfo;
   friend class QuotaManager;
   friend class QuotaObject;
 
 public:
-  OriginInfo(GroupInfo* aGroupInfo, const nsACString& aOrigin, bool aIsApp,
+  OriginInfo(GroupInfo* aGroupInfo, const nsACString& aOrigin,
              uint64_t aUsage, int64_t aAccessTime)
   : mGroupInfo(aGroupInfo), mOrigin(aOrigin), mUsage(aUsage),
-    mAccessTime(aAccessTime), mIsApp(aIsApp)
+    mAccessTime(aAccessTime)
   {
     MOZ_COUNT_CTOR(OriginInfo);
   }
 
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(OriginInfo)
 
   int64_t
   AccessTime() const
@@ -526,17 +523,16 @@ private:
   }
 
   nsDataHashtable<nsStringHashKey, QuotaObject*> mQuotaObjects;
 
   GroupInfo* mGroupInfo;
   const nsCString mOrigin;
   uint64_t mUsage;
   int64_t mAccessTime;
-  const bool mIsApp;
 };
 
 class OriginInfoLRUComparator
 {
 public:
   bool
   Equals(const OriginInfo* a, const OriginInfo* b) const
   {
@@ -1042,17 +1038,16 @@ class GetUsageOp final
   // If mGetGroupUsage is false, we use mUsageInfo to record the origin usage
   // and the file usage. Otherwise, we use it to record the group usage and the
   // limit.
   UsageInfo mUsageInfo;
 
   const UsageParams mParams;
   nsCString mSuffix;
   nsCString mGroup;
-  bool mIsApp;
   bool mGetGroupUsage;
 
 public:
   explicit GetUsageOp(const UsageRequestParams& aParams);
 
   MOZ_IS_CLASS_INIT bool
   Init(Quota* aQuota);
 
@@ -1106,16 +1101,65 @@ private:
   virtual void
   SendResults() override;
 
   // IPDL methods.
   virtual void
   ActorDestroy(ActorDestroyReason aWhy) override;
 };
 
+class InitOp final
+  : public QuotaRequestBase
+{
+public:
+  InitOp()
+    : QuotaRequestBase(/* aExclusive */ false)
+  {
+    AssertIsOnOwningThread();
+  }
+
+private:
+  ~InitOp()
+  { }
+
+  nsresult
+  DoDirectoryWork(QuotaManager* aQuotaManager) override;
+
+  void
+  GetResponse(RequestResponse& aResponse) override;
+};
+
+class InitOriginOp final
+  : public QuotaRequestBase
+{
+  const InitOriginParams mParams;
+  nsCString mSuffix;
+  nsCString mGroup;
+  bool mCreated;
+
+public:
+  explicit InitOriginOp(const RequestParams& aParams);
+
+  bool
+  Init(Quota* aQuota) override;
+
+private:
+  ~InitOriginOp()
+  { }
+
+  nsresult
+  DoInitOnMainThread() override;
+
+  nsresult
+  DoDirectoryWork(QuotaManager* aQuotaManager) override;
+
+  void
+  GetResponse(RequestResponse& aResponse) override;
+};
+
 class ResetOrClearOp final
   : public QuotaRequestBase
 {
   const bool mClear;
 
 public:
   explicit ResetOrClearOp(bool aClear)
     : QuotaRequestBase(/* aExclusive */ true)
@@ -1133,43 +1177,75 @@ private:
 
   virtual nsresult
   DoDirectoryWork(QuotaManager* aQuotaManager) override;
 
   virtual void
   GetResponse(RequestResponse& aResponse) override;
 };
 
-class OriginClearOp final
+class ClearRequestBase
   : public QuotaRequestBase
 {
-  const RequestParams mParams;
-  const bool mMultiple;
-
-public:
-  explicit OriginClearOp(const RequestParams& aParams);
-
-  virtual bool
-  Init(Quota* aQuota) override;
-
-private:
-  ~OriginClearOp()
-  { }
-
-  virtual nsresult
-  DoInitOnMainThread() override;
+protected:
+  explicit ClearRequestBase(bool aExclusive)
+    : QuotaRequestBase(aExclusive)
+  {
+    AssertIsOnOwningThread();
+  }
 
   void
   DeleteFiles(QuotaManager* aQuotaManager,
               PersistenceType aPersistenceType);
 
-  virtual nsresult
+  nsresult
   DoDirectoryWork(QuotaManager* aQuotaManager) override;
-
-  virtual void
+};
+
+class ClearOriginOp final
+  : public ClearRequestBase
+{
+  const ClearOriginParams mParams;
+
+public:
+  explicit ClearOriginOp(const RequestParams& aParams);
+
+  bool
+  Init(Quota* aQuota) override;
+
+private:
+  ~ClearOriginOp()
+  { }
+
+  nsresult
+  DoInitOnMainThread() override;
+
+  void
+  GetResponse(RequestResponse& aResponse) override;
+};
+
+class ClearDataOp final
+  : public ClearRequestBase
+{
+  const ClearDataParams mParams;
+
+public:
+  explicit ClearDataOp(const RequestParams& aParams);
+
+  bool
+  Init(Quota* aQuota) override;
+
+private:
+  ~ClearDataOp()
+  { }
+
+  nsresult
+  DoInitOnMainThread() override;
+
+  void
   GetResponse(RequestResponse& aResponse) override;
 };
 
 /*******************************************************************************
  * Helper Functions
  ******************************************************************************/
 
 template <typename T, bool = mozilla::IsUnsigned<T>::value>
@@ -1206,16 +1282,37 @@ template <typename T, typename U>
 void
 AssertNoUnderflow(T aDest, U aArg)
 {
   IntChecker<T>::Assert(aDest);
   IntChecker<T>::Assert(aArg);
   MOZ_ASSERT(uint64_t(aDest) >= uint64_t(aArg));
 }
 
+bool
+IsOSMetadata(const nsAString& aFileName)
+{
+  return aFileName.EqualsLiteral(DSSTORE_FILE_NAME);
+}
+
+bool
+IsOriginMetadata(const nsAString& aFileName)
+{
+  return aFileName.EqualsLiteral(METADATA_FILE_NAME) ||
+         aFileName.EqualsLiteral(METADATA_V2_FILE_NAME) ||
+         IsOSMetadata(aFileName);
+}
+
+bool
+IsTempMetadata(const nsAString& aFileName)
+{
+  return aFileName.EqualsLiteral(METADATA_TMP_FILE_NAME) ||
+         aFileName.EqualsLiteral(METADATA_V2_TMP_FILE_NAME);
+}
+
 } // namespace
 
 BackgroundThreadObject::BackgroundThreadObject()
   : mOwningThread(NS_GetCurrentThread())
 {
   AssertIsOnOwningThread();
 }
 
@@ -1314,286 +1411,299 @@ class StorageDirectoryHelper
 
 protected:
   struct OriginProps;
 
   nsTArray<OriginProps> mOriginProps;
 
   nsCOMPtr<nsIFile> mDirectory;
 
+  const bool mPersistent;
+
 public:
-  StorageDirectoryHelper(nsIFile* aDirectory)
+  StorageDirectoryHelper(nsIFile* aDirectory,
+                         bool aPersistent)
     : mMutex("StorageDirectoryHelper::mMutex")
     , mCondVar(mMutex, "StorageDirectoryHelper::mCondVar")
     , mMainThreadResultCode(NS_OK)
     , mWaiting(true)
     , mDirectory(aDirectory)
+    , mPersistent(aPersistent)
   {
     AssertIsOnIOThread();
   }
 
 protected:
   ~StorageDirectoryHelper()
   { }
 
   nsresult
-  AddOriginDirectory(nsIFile* aDirectory,
-                     OriginProps** aOriginProps);
+  GetDirectoryMetadata(nsIFile* aDirectory,
+                       int64_t& aTimestamp,
+                       nsACString& aGroup,
+                       nsACString& aOrigin,
+                       Nullable<bool>& aIsApp);
+
+  // Upgrade helper to load the contents of ".metadata-v2" files from previous
+  // schema versions.  Although QuotaManager has a similar GetDirectoryMetadata2
+  // method, it is only intended to read current version ".metadata-v2" files.
+  // And unlike the old ".metadata" files, the ".metadata-v2" format can evolve
+  // because our "storage.sqlite" lets us track the overall version of the
+  // storage directory.
+  nsresult
+  GetDirectoryMetadata2(nsIFile* aDirectory,
+                        int64_t& aTimestamp,
+                        nsACString& aSuffix,
+                        nsACString& aGroup,
+                        nsACString& aOrigin,
+                        bool& aIsApp);
+
+  nsresult
+  RemoveObsoleteOrigin(const OriginProps& aOriginProps);
 
   nsresult
   ProcessOriginDirectories();
 
   virtual nsresult
-  DoProcessOriginDirectories() = 0;
+  ProcessOriginDirectory(const OriginProps& aOriginProps) = 0;
 
 private:
   nsresult
   RunOnMainThread();
 
   NS_IMETHOD
   Run() override;
 };
 
 struct StorageDirectoryHelper::OriginProps
 {
   enum Type
   {
     eChrome,
-    eContent
+    eContent,
+    eObsolete
   };
 
   nsCOMPtr<nsIFile> mDirectory;
+  nsString mLeafName;
   nsCString mSpec;
   OriginAttributes mAttrs;
   int64_t mTimestamp;
   nsCString mSuffix;
   nsCString mGroup;
   nsCString mOrigin;
 
   Type mType;
-  bool mIsApp;
   bool mNeedsRestore;
+  bool mNeedsRestore2;
   bool mIgnore;
 
 public:
   explicit OriginProps()
     : mTimestamp(0)
     , mType(eContent)
-    , mIsApp(false)
     , mNeedsRestore(false)
+    , mNeedsRestore2(false)
     , mIgnore(false)
   { }
+
+  nsresult
+  Init(nsIFile* aDirectory);
 };
 
 class MOZ_STACK_CLASS OriginParser final
 {
+public:
+  enum ResultType {
+    InvalidOrigin,
+    ObsoleteOrigin,
+    ValidOrigin
+  };
+
+private:
   static bool
   IgnoreWhitespace(char16_t /* aChar */)
   {
     return false;
   }
 
   typedef nsCCharSeparatedTokenizerTemplate<IgnoreWhitespace> Tokenizer;
 
-  enum SchemaType {
+  enum SchemeType {
     eNone,
     eFile,
     eAbout
   };
 
   enum State {
-    eExpectingAppIdOrSchema,
+    eExpectingAppIdOrScheme,
     eExpectingInMozBrowser,
-    eExpectingSchema,
+    eExpectingScheme,
     eExpectingEmptyToken1,
     eExpectingEmptyToken2,
     eExpectingEmptyToken3,
     eExpectingHost,
     eExpectingPort,
     eExpectingEmptyTokenOrDriveLetterOrPathnameComponent,
     eExpectingEmptyTokenOrPathnameComponent,
     eComplete,
     eHandledTrailingSeparator
   };
 
   const nsCString mOrigin;
   const OriginAttributes mOriginAttributes;
   Tokenizer mTokenizer;
 
   uint32_t mAppId;
-  nsCString mSchema;
+  nsCString mScheme;
   nsCString mHost;
   Nullable<uint32_t> mPort;
   nsTArray<nsCString> mPathnameComponents;
   nsCString mHandledTokens;
 
-  SchemaType mSchemaType;
+  SchemeType mSchemeType;
   State mState;
   bool mInIsolatedMozBrowser;
   bool mMaybeDriveLetter;
   bool mError;
 
 public:
   OriginParser(const nsACString& aOrigin,
                const OriginAttributes& aOriginAttributes)
     : mOrigin(aOrigin)
     , mOriginAttributes(aOriginAttributes)
     , mTokenizer(aOrigin, '+')
     , mAppId(kNoAppId)
     , mPort()
-    , mSchemaType(eNone)
-    , mState(eExpectingAppIdOrSchema)
+    , mSchemeType(eNone)
+    , mState(eExpectingAppIdOrScheme)
     , mInIsolatedMozBrowser(false)
     , mMaybeDriveLetter(false)
     , mError(false)
   { }
 
-  static bool
+  static ResultType
   ParseOrigin(const nsACString& aOrigin,
               nsCString& aSpec,
               OriginAttributes* aAttrs);
 
-  bool
+  ResultType
   Parse(nsACString& aSpec, OriginAttributes* aAttrs);
 
 private:
   void
-  HandleSchema(const nsDependentCSubstring& aSchema);
+  HandleScheme(const nsDependentCSubstring& aToken);
 
   void
-  HandlePathnameComponent(const nsDependentCSubstring& aSchema);
+  HandlePathnameComponent(const nsDependentCSubstring& aToken);
 
   void
   HandleToken(const nsDependentCSubstring& aToken);
 
   void
   HandleTrailingSeparator();
 };
 
 class CreateOrUpgradeDirectoryMetadataHelper final
   : public StorageDirectoryHelper
 {
-  const bool mPersistent;
+  nsCOMPtr<nsIFile> mPermanentStorageDir;
 
 public:
   CreateOrUpgradeDirectoryMetadataHelper(nsIFile* aDirectory,
                                          bool aPersistent)
-    : StorageDirectoryHelper(aDirectory)
-    , mPersistent(aPersistent)
+    : StorageDirectoryHelper(aDirectory, aPersistent)
   { }
 
   nsresult
   CreateOrUpgradeMetadataFiles();
 
 private:
   nsresult
   MaybeUpgradeOriginDirectory(nsIFile* aDirectory);
 
   nsresult
-  GetDirectoryMetadata(nsIFile* aDirectory,
-                       int64_t* aTimestamp,
-                       nsACString& aGroup,
-                       nsACString& aOrigin,
-                       bool* aHasIsApp);
-
-  virtual nsresult
-  DoProcessOriginDirectories();
+  ProcessOriginDirectory(const OriginProps& aOriginProps) override;
 };
 
-class UpgradeDirectoryMetadataFrom1To2Helper final
+class UpgradeStorageFrom0_0To1_0Helper final
   : public StorageDirectoryHelper
 {
-  const bool mPersistent;
-
 public:
-  UpgradeDirectoryMetadataFrom1To2Helper(nsIFile* aDirectory,
-                                         bool aPersistent)
-    : StorageDirectoryHelper(aDirectory)
-    , mPersistent(aPersistent)
+  UpgradeStorageFrom0_0To1_0Helper(nsIFile* aDirectory,
+                                   bool aPersistent)
+    : StorageDirectoryHelper(aDirectory, aPersistent)
   { }
 
   nsresult
-  UpgradeMetadataFiles();
+  DoUpgrade();
 
 private:
   nsresult
-  GetDirectoryMetadata(nsIFile* aDirectory,
-                       int64_t* aTimestamp,
-                       nsACString& aGroup,
-                       nsACString& aOrigin,
-                       bool* aIsApp);
-
-  virtual nsresult
-  DoProcessOriginDirectories() override;
+  ProcessOriginDirectory(const OriginProps& aOriginProps) override;
+};
+
+class UpgradeStorageFrom1_0To2_0Helper final
+  : public StorageDirectoryHelper
+{
+public:
+  UpgradeStorageFrom1_0To2_0Helper(nsIFile* aDirectory,
+                                   bool aPersistent)
+    : StorageDirectoryHelper(aDirectory, aPersistent)
+  { }
+
+  nsresult
+  DoUpgrade();
+
+private:
+  nsresult
+  MaybeUpgradeClients(const OriginProps& aOriginProps);
+
+  nsresult
+  MaybeRemoveAppsData(const OriginProps& aOriginProps,
+                      bool* aRemoved);
+
+  nsresult
+  MaybeStripObsoleteOriginAttributes(const OriginProps& aOriginProps,
+                                     bool* aStripped);
+
+  nsresult
+  ProcessOriginDirectory(const OriginProps& aOriginProps) override;
 };
 
 class RestoreDirectoryMetadata2Helper final
   : public StorageDirectoryHelper
 {
-  const bool mPersistent;
-
 public:
   RestoreDirectoryMetadata2Helper(nsIFile* aDirectory,
                                   bool aPersistent)
-    : StorageDirectoryHelper(aDirectory)
-    , mPersistent(aPersistent)
+    : StorageDirectoryHelper(aDirectory, aPersistent)
   { }
 
   nsresult
   RestoreMetadata2File();
 
 private:
-  virtual nsresult
-  DoProcessOriginDirectories();
-};
-
-class OriginKey : public nsAutoCString
-{
-public:
-  OriginKey(PersistenceType aPersistenceType,
-            const nsACString& aOrigin)
-  {
-    PersistenceTypeToText(aPersistenceType, *this);
-    Append(':');
-    Append(aOrigin);
-  }
+  nsresult
+  ProcessOriginDirectory(const OriginProps& aOriginProps) override;
 };
 
 void
 SanitizeOriginString(nsCString& aOrigin)
 {
 
 #ifdef XP_WIN
   NS_ASSERTION(!strcmp(QuotaManager::kReplaceChars,
                        FILE_ILLEGAL_CHARACTERS FILE_PATH_SEPARATOR),
                "Illegal file characters have changed!");
 #endif
 
   aOrigin.ReplaceChar(QuotaManager::kReplaceChars, '+');
 }
 
-bool
-IsTreatedAsPersistent(PersistenceType aPersistenceType,
-                      bool aIsApp)
-{
-  if (aPersistenceType == PERSISTENCE_TYPE_PERSISTENT ||
-      (aPersistenceType == PERSISTENCE_TYPE_DEFAULT && aIsApp)) {
-    return true;
-  }
-
-  return false;
-}
-
-bool
-IsTreatedAsTemporary(PersistenceType aPersistenceType,
-                     bool aIsApp)
-{
-  return !IsTreatedAsPersistent(aPersistenceType, aIsApp);
-}
-
 nsresult
 CloneStoragePath(nsIFile* aBaseDir,
                  const nsAString& aStorageName,
                  nsAString& aStoragePath)
 {
   nsresult rv;
 
   nsCOMPtr<nsIFile> storageDir;
@@ -1610,22 +1720,21 @@ CloneStoragePath(nsIFile* aBaseDir,
   rv = storageDir->GetPath(aStoragePath);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   return NS_OK;
 }
 
-nsresult
-GetLastModifiedTime(nsIFile* aFile, int64_t* aTimestamp)
+int64_t
+GetLastModifiedTime(nsIFile* aFile, bool aPersistent)
 {
   AssertIsOnIOThread();
   MOZ_ASSERT(aFile);
-  MOZ_ASSERT(aTimestamp);
 
   class MOZ_STACK_CLASS Helper final
   {
   public:
     static nsresult
     GetLastModifiedTime(nsIFile* aFile, int64_t* aTimestamp)
     {
       AssertIsOnIOThread();
@@ -1640,19 +1749,18 @@ GetLastModifiedTime(nsIFile* aFile, int6
 
       if (!isDirectory) {
         nsString leafName;
         rv = aFile->GetLeafName(leafName);
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return rv;
         }
 
-        if (leafName.EqualsLiteral(METADATA_FILE_NAME) ||
-            leafName.EqualsLiteral(METADATA_V2_FILE_NAME) ||
-            leafName.EqualsLiteral(DSSTORE_FILE_NAME)) {
+        if (IsOriginMetadata(leafName) ||
+            IsTempMetadata(leafName)) {
           return NS_OK;
         }
 
         int64_t timestamp;
         rv = aFile->GetLastModifiedTime(&timestamp);
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return rv;
         }
@@ -1692,24 +1800,27 @@ GetLastModifiedTime(nsIFile* aFile, int6
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
 
       return NS_OK;
     }
   };
 
+  if (aPersistent) {
+    return PR_Now();
+  }
+
   int64_t timestamp = INT64_MIN;
   nsresult rv = Helper::GetLastModifiedTime(aFile, &timestamp);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  *aTimestamp = timestamp;
-  return NS_OK;
+  if (NS_FAILED(rv)) {
+    timestamp = PR_Now();
+  }
+
+  return timestamp;
 }
 
 nsresult
 EnsureDirectory(nsIFile* aDirectory, bool* aCreated)
 {
   AssertIsOnIOThread();
 
   nsresult rv = aDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755);
@@ -1732,75 +1843,65 @@ EnsureDirectory(nsIFile* aDirectory, boo
 
 enum FileFlag {
   kTruncateFileFlag,
   kUpdateFileFlag,
   kAppendFileFlag
 };
 
 nsresult
-GetOutputStream(nsIFile* aDirectory,
-                const nsAString& aFilename,
+GetOutputStream(nsIFile* aFile,
                 FileFlag aFileFlag,
                 nsIOutputStream** aStream)
 {
   AssertIsOnIOThread();
 
-  nsCOMPtr<nsIFile> file;
-  nsresult rv = aDirectory->Clone(getter_AddRefs(file));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  rv = file->Append(aFilename);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
+  nsresult rv;
 
   nsCOMPtr<nsIOutputStream> outputStream;
   switch (aFileFlag) {
     case kTruncateFileFlag: {
       rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream),
-                                       file);
+                                       aFile);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
 
       break;
     }
 
     case kUpdateFileFlag: {
       bool exists;
-      rv = file->Exists(&exists);
+      rv = aFile->Exists(&exists);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
 
       if (!exists) {
         *aStream = nullptr;
         return NS_OK;
       }
 
       nsCOMPtr<nsIFileStream> stream;
-      rv = NS_NewLocalFileStream(getter_AddRefs(stream), file);
+      rv = NS_NewLocalFileStream(getter_AddRefs(stream), aFile);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
 
       outputStream = do_QueryInterface(stream);
       if (NS_WARN_IF(!outputStream)) {
         return NS_ERROR_FAILURE;
       }
 
       break;
     }
 
     case kAppendFileFlag: {
       rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream),
-                                       file,
+                                       aFile,
                                        PR_WRONLY | PR_CREATE_FILE | PR_APPEND);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
 
       break;
     }
 
@@ -1808,31 +1909,28 @@ GetOutputStream(nsIFile* aDirectory,
       MOZ_CRASH("Should never get here!");
   }
 
   outputStream.forget(aStream);
   return NS_OK;
 }
 
 nsresult
-GetBinaryOutputStream(nsIFile* aDirectory,
-                      const nsAString& aFilename,
+GetBinaryOutputStream(nsIFile* aFile,
                       FileFlag aFileFlag,
                       nsIBinaryOutputStream** aStream)
 {
   nsCOMPtr<nsIOutputStream> outputStream;
-  nsresult rv = GetOutputStream(aDirectory,
-                                aFilename,
+  nsresult rv = GetOutputStream(aFile,
                                 aFileFlag,
                                 getter_AddRefs(outputStream));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
-
   nsCOMPtr<nsIBinaryOutputStream> binaryStream =
     do_CreateInstance("@mozilla.org/binaryoutputstream;1");
   if (NS_WARN_IF(!binaryStream)) {
     return NS_ERROR_FAILURE;
   }
 
   rv = binaryStream->SetOutputStream(outputStream);
   if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -1866,17 +1964,17 @@ GetJarPrefix(uint32_t aAppId,
   aJarPrefix.Append('+');
   aJarPrefix.Append(aInIsolatedMozBrowser ? 't' : 'f');
   aJarPrefix.Append('+');
 }
 
 nsresult
 CreateDirectoryMetadata(nsIFile* aDirectory, int64_t aTimestamp,
                         const nsACString& aSuffix, const nsACString& aGroup,
-                        const nsACString& aOrigin, bool aIsApp)
+                        const nsACString& aOrigin)
 {
   AssertIsOnIOThread();
 
   OriginAttributes groupAttributes;
 
   nsCString groupNoSuffix;
   bool ok = groupAttributes.PopulateFromOrigin(aGroup, groupNoSuffix);
   if (!ok) {
@@ -1902,21 +2000,29 @@ CreateDirectoryMetadata(nsIFile* aDirect
   GetJarPrefix(originAttributes.mAppId,
                originAttributes.mInIsolatedMozBrowser,
                originPrefix);
 
   nsCString origin = originPrefix + originNoSuffix;
 
   MOZ_ASSERT(groupPrefix == originPrefix);
 
+  nsCOMPtr<nsIFile> file;
+  nsresult rv = aDirectory->Clone(getter_AddRefs(file));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = file->Append(NS_LITERAL_STRING(METADATA_TMP_FILE_NAME));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
   nsCOMPtr<nsIBinaryOutputStream> stream;
-  nsresult rv = GetBinaryOutputStream(aDirectory,
-                                      NS_LITERAL_STRING(METADATA_FILE_NAME),
-                                      kTruncateFileFlag,
-                                      getter_AddRefs(stream));
+  rv = GetBinaryOutputStream(file, kTruncateFileFlag, getter_AddRefs(stream));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   MOZ_ASSERT(stream);
 
   rv = stream->Write64(aTimestamp);
   if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -1928,36 +2034,63 @@ CreateDirectoryMetadata(nsIFile* aDirect
     return rv;
   }
 
   rv = stream->WriteStringZ(origin.get());
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
-  rv = stream->WriteBoolean(aIsApp);
+  // Currently unused (used to be isApp).
+  rv = stream->WriteBoolean(false);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = stream->Flush();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = stream->Close();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = file->RenameTo(nullptr, NS_LITERAL_STRING(METADATA_FILE_NAME));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   return NS_OK;
 }
 
 nsresult
-CreateDirectoryMetadata2(nsIFile* aDirectory, int64_t aTimestamp,
-                         const nsACString& aSuffix, const nsACString& aGroup,
-                         const nsACString& aOrigin, bool aIsApp)
+CreateDirectoryMetadata2(nsIFile* aDirectory,
+                         int64_t aTimestamp,
+                         const nsACString& aSuffix,
+                         const nsACString& aGroup,
+                         const nsACString& aOrigin)
 {
   AssertIsOnIOThread();
+  MOZ_ASSERT(aDirectory);
+
+  nsCOMPtr<nsIFile> file;
+  nsresult rv = aDirectory->Clone(getter_AddRefs(file));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = file->Append(NS_LITERAL_STRING(METADATA_V2_TMP_FILE_NAME));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
 
   nsCOMPtr<nsIBinaryOutputStream> stream;
-  nsresult rv = GetBinaryOutputStream(aDirectory,
-                                      NS_LITERAL_STRING(METADATA_V2_FILE_NAME),
-                                      kTruncateFileFlag,
-                                      getter_AddRefs(stream));
+  rv = GetBinaryOutputStream(file, kTruncateFileFlag, getter_AddRefs(stream));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   MOZ_ASSERT(stream);
 
   rv = stream->Write64(aTimestamp);
   if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -1977,32 +2110,51 @@ CreateDirectoryMetadata2(nsIFile* aDirec
   }
 
   // Reserved data 2
   rv = stream->Write32(0);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
+  // The suffix isn't used right now, but we might need it in future. It's
+  // a bit of redundancy we can live with given how painful is to upgrade
+  // metadata files.
   rv = stream->WriteStringZ(PromiseFlatCString(aSuffix).get());
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   rv = stream->WriteStringZ(PromiseFlatCString(aGroup).get());
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   rv = stream->WriteStringZ(PromiseFlatCString(aOrigin).get());
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
-  rv = stream->WriteBoolean(aIsApp);
+  // Currently unused (used to be isApp).
+  rv = stream->WriteBoolean(false);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = stream->Flush();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = stream->Close();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = file->RenameTo(nullptr, NS_LITERAL_STRING(METADATA_V2_FILE_NAME));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   return NS_OK;
 }
 
 nsresult
@@ -2114,41 +2266,38 @@ DeallocPQuotaParent(PQuotaParent* aActor
 /*******************************************************************************
  * Directory lock
  ******************************************************************************/
 
 DirectoryLockImpl::DirectoryLockImpl(QuotaManager* aQuotaManager,
                                      const Nullable<PersistenceType>& aPersistenceType,
                                      const nsACString& aGroup,
                                      const OriginScope& aOriginScope,
-                                     const Nullable<bool>& aIsApp,
                                      const Nullable<Client::Type>& aClientType,
                                      bool aExclusive,
                                      bool aInternal,
                                      OpenDirectoryListener* aOpenListener)
   : mQuotaManager(aQuotaManager)
   , mPersistenceType(aPersistenceType)
   , mGroup(aGroup)
   , mOriginScope(aOriginScope)
-  , mIsApp(aIsApp)
   , mClientType(aClientType)
   , mOpenListener(aOpenListener)
   , mExclusive(aExclusive)
   , mInternal(aInternal)
   , mInvalidated(false)
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(aQuotaManager);
   MOZ_ASSERT_IF(aOriginScope.IsOrigin(), !aOriginScope.GetOrigin().IsEmpty());
   MOZ_ASSERT_IF(!aInternal, !aPersistenceType.IsNull());
   MOZ_ASSERT_IF(!aInternal,
                 aPersistenceType.Value() != PERSISTENCE_TYPE_INVALID);
   MOZ_ASSERT_IF(!aInternal, !aGroup.IsEmpty());
   MOZ_ASSERT_IF(!aInternal, aOriginScope.IsOrigin());
-  MOZ_ASSERT_IF(!aInternal, !aIsApp.IsNull());
   MOZ_ASSERT_IF(!aInternal, !aClientType.IsNull());
   MOZ_ASSERT_IF(!aInternal, aClientType.Value() != Client::TYPE_MAX);
   MOZ_ASSERT_IF(!aInternal, aOpenListener);
 }
 
 DirectoryLockImpl::~DirectoryLockImpl()
 {
   AssertIsOnOwningThread();
@@ -2806,40 +2955,37 @@ QuotaManager::IsShuttingDown()
 {
   return gShutdown;
 }
 
 auto
 QuotaManager::CreateDirectoryLock(const Nullable<PersistenceType>& aPersistenceType,
                                   const nsACString& aGroup,
                                   const OriginScope& aOriginScope,
-                                  const Nullable<bool>& aIsApp,
                                   const Nullable<Client::Type>& aClientType,
                                   bool aExclusive,
                                   bool aInternal,
                                   OpenDirectoryListener* aOpenListener)
   -> already_AddRefed<DirectoryLockImpl>
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT_IF(aOriginScope.IsOrigin(), !aOriginScope.GetOrigin().IsEmpty());
   MOZ_ASSERT_IF(!aInternal, !aPersistenceType.IsNull());
   MOZ_ASSERT_IF(!aInternal,
                 aPersistenceType.Value() != PERSISTENCE_TYPE_INVALID);
   MOZ_ASSERT_IF(!aInternal, !aGroup.IsEmpty());
   MOZ_ASSERT_IF(!aInternal, aOriginScope.IsOrigin());
-  MOZ_ASSERT_IF(!aInternal, !aIsApp.IsNull());
   MOZ_ASSERT_IF(!aInternal, !aClientType.IsNull());
   MOZ_ASSERT_IF(!aInternal, aClientType.Value() != Client::TYPE_MAX);
   MOZ_ASSERT_IF(!aInternal, aOpenListener);
 
   RefPtr<DirectoryLockImpl> lock = new DirectoryLockImpl(this,
                                                            aPersistenceType,
                                                            aGroup,
                                                            aOriginScope,
-                                                           aIsApp,
                                                            aClientType,
                                                            aExclusive,
                                                            aInternal,
                                                            aOpenListener);
 
   mPendingDirectoryLocks.AppendElement(lock);
 
   // See if this lock needs to wait.
@@ -2861,30 +3007,28 @@ QuotaManager::CreateDirectoryLock(const 
   }
 
   return lock.forget();
 }
 
 auto
 QuotaManager::CreateDirectoryLockForEviction(PersistenceType aPersistenceType,
                                              const nsACString& aGroup,
-                                             const nsACString& aOrigin,
-                                             bool aIsApp)
+                                             const nsACString& aOrigin)
   -> already_AddRefed<DirectoryLockImpl>
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(aPersistenceType != PERSISTENCE_TYPE_INVALID);
   MOZ_ASSERT(!aOrigin.IsEmpty());
 
   RefPtr<DirectoryLockImpl> lock =
     new DirectoryLockImpl(this,
                           Nullable<PersistenceType>(aPersistenceType),
                           aGroup,
                           OriginScope::FromOrigin(aOrigin),
-                          Nullable<bool>(aIsApp),
                           Nullable<Client::Type>(),
                           /* aExclusive */ true,
                           /* aInternal */ true,
                           nullptr);
 
 #ifdef DEBUG
   for (uint32_t index = mDirectoryLocks.Length(); index > 0; index--) {
     DirectoryLockImpl* existingLock = mDirectoryLocks[index - 1];
@@ -2989,18 +3133,18 @@ QuotaManager::CollectOriginsForEviction(
   struct MOZ_STACK_CLASS Helper final
   {
     static void
     GetInactiveOriginInfos(nsTArray<RefPtr<OriginInfo>>& aOriginInfos,
                            nsTArray<DirectoryLockImpl*>& aLocks,
                            nsTArray<OriginInfo*>& aInactiveOriginInfos)
     {
       for (OriginInfo* originInfo : aOriginInfos) {
-        MOZ_ASSERT(IsTreatedAsTemporary(originInfo->mGroupInfo->mPersistenceType,
-                                        originInfo->mIsApp));
+        MOZ_ASSERT(originInfo->mGroupInfo->mPersistenceType !=
+                     PERSISTENCE_TYPE_PERSISTENT);
 
         OriginScope originScope = OriginScope::FromOrigin(originInfo->mOrigin);
 
         bool match = false;
         for (uint32_t j = aLocks.Length(); j > 0; j--) {
           DirectoryLockImpl* lock = aLocks[j - 1];
           if (originScope.Matches(lock->GetOriginScope())) {
             match = true;
@@ -3093,18 +3237,17 @@ QuotaManager::CollectOriginsForEviction(
   if (sizeToBeFreed >= aMinSizeToBeFreed) {
     // Success, add directory locks for these origins, so any other
     // operations for them will be delayed (until origin eviction is finalized).
 
     for (OriginInfo* originInfo : inactiveOrigins) {
       RefPtr<DirectoryLockImpl> lock =
         CreateDirectoryLockForEviction(originInfo->mGroupInfo->mPersistenceType,
                                        originInfo->mGroupInfo->mGroup,
-                                       originInfo->mOrigin,
-                                       originInfo->mIsApp);
+                                       originInfo->mOrigin);
       aLocks.AppendElement(lock.forget());
     }
 
     return sizeToBeFreed;
   }
 
   return 0;
 }
@@ -3244,22 +3387,21 @@ QuotaManager::Shutdown()
     lock->Invalidate();
   }
 }
 
 void
 QuotaManager::InitQuotaForOrigin(PersistenceType aPersistenceType,
                                  const nsACString& aGroup,
                                  const nsACString& aOrigin,
-                                 bool aIsApp,
                                  uint64_t aUsageBytes,
                                  int64_t aAccessTime)
 {
   AssertIsOnIOThread();
-  MOZ_ASSERT(IsTreatedAsTemporary(aPersistenceType, aIsApp));
+  MOZ_ASSERT(aPersistenceType != PERSISTENCE_TYPE_PERSISTENT);
 
   MutexAutoLock lock(mQuotaMutex);
 
   GroupInfoPair* pair;
   if (!mGroupInfoPairs.Get(aGroup, &pair)) {
     pair = new GroupInfoPair();
     mGroupInfoPairs.Put(aGroup, pair);
     // The hashtable is now responsible to delete the GroupInfoPair.
@@ -3267,17 +3409,17 @@ QuotaManager::InitQuotaForOrigin(Persist
 
   RefPtr<GroupInfo> groupInfo = pair->LockedGetGroupInfo(aPersistenceType);
   if (!groupInfo) {
     groupInfo = new GroupInfo(pair, aPersistenceType, aGroup);
     pair->LockedSetGroupInfo(aPersistenceType, groupInfo);
   }
 
   RefPtr<OriginInfo> originInfo =
-    new OriginInfo(groupInfo, aOrigin, aIsApp, aUsageBytes, aAccessTime);
+    new OriginInfo(groupInfo, aOrigin, aUsageBytes, aAccessTime);
   groupInfo->LockedAddOriginInfo(originInfo);
 }
 
 void
 QuotaManager::DecreaseUsageForOrigin(PersistenceType aPersistenceType,
                                      const nsACString& aGroup,
                                      const nsACString& aOrigin,
                                      int64_t aSize)
@@ -3516,23 +3658,21 @@ QuotaManager::RestoreDirectoryMetadata2(
   return NS_OK;
 }
 
 nsresult
 QuotaManager::GetDirectoryMetadata2(nsIFile* aDirectory,
                                     int64_t* aTimestamp,
                                     nsACString& aSuffix,
                                     nsACString& aGroup,
-                                    nsACString& aOrigin,
-                                    bool* aIsApp)
+                                    nsACString& aOrigin)
 {
   MOZ_ASSERT(!NS_IsMainThread());
   MOZ_ASSERT(aDirectory);
   MOZ_ASSERT(aTimestamp);
-  MOZ_ASSERT(aIsApp);
   MOZ_ASSERT(mStorageInitialized);
 
   nsCOMPtr<nsIBinaryInputStream> binaryStream;
   nsresult rv = GetBinaryInputStream(aDirectory,
                                      NS_LITERAL_STRING(METADATA_V2_FILE_NAME),
                                      getter_AddRefs(binaryStream));
   NS_ENSURE_SUCCESS(rv, rv);
 
@@ -3567,57 +3707,54 @@ QuotaManager::GetDirectoryMetadata2(nsIF
   nsCString group;
   rv = binaryStream->ReadCString(group);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCString origin;
   rv = binaryStream->ReadCString(origin);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  bool isApp;
-  rv = binaryStream->ReadBoolean(&isApp);
+  // Currently unused (used to be isApp).
+  bool dummy;
+  rv = binaryStream->ReadBoolean(&dummy);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   *aTimestamp = timestamp;
   aSuffix = suffix;
   aGroup = group;
   aOrigin = origin;
-  *aIsApp = isApp;
   return NS_OK;
 }
 
 nsresult
 QuotaManager::GetDirectoryMetadata2WithRestore(nsIFile* aDirectory,
                                                bool aPersistent,
                                                int64_t* aTimestamp,
                                                nsACString& aSuffix,
                                                nsACString& aGroup,
-                                               nsACString& aOrigin,
-                                               bool* aIsApp)
+                                               nsACString& aOrigin)
 {
   nsresult rv = GetDirectoryMetadata2(aDirectory,
                                       aTimestamp,
                                       aSuffix,
                                       aGroup,
-                                      aOrigin,
-                                      aIsApp);
+                                      aOrigin);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     rv = RestoreDirectoryMetadata2(aDirectory, aPersistent);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     rv = GetDirectoryMetadata2(aDirectory,
                                aTimestamp,
                                aSuffix,
                                aGroup,
-                               aOrigin,
-                               aIsApp);
+                               aOrigin);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
 
   return NS_OK;
 }
 
@@ -3718,102 +3855,63 @@ QuotaManager::InitializeRepository(Persi
 
     if (!isDirectory) {
       nsString leafName;
       rv = childDirectory->GetLeafName(leafName);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
 
-      if (leafName.EqualsLiteral(DSSTORE_FILE_NAME)) {
+      if (IsOSMetadata(leafName)) {
         continue;
       }
 
-      QM_WARNING("Something (%s) in the repository that doesn't belong!",
-                 NS_ConvertUTF16toUTF8(leafName).get());
+      UNKNOWN_FILE_WARNING(leafName);
       return NS_ERROR_UNEXPECTED;
     }
 
     int64_t timestamp;
     nsCString suffix;
     nsCString group;
     nsCString origin;
-    bool isApp;
     rv = GetDirectoryMetadata2WithRestore(childDirectory,
                                           /* aPersistent */ false,
                                           &timestamp,
                                           suffix,
                                           group,
-                                          origin,
-                                          &isApp);
+                                          origin);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
-    if (IsTreatedAsPersistent(aPersistenceType, isApp)) {
-      continue;
-    }
-
-    rv = InitializeOrigin(aPersistenceType, group, origin, isApp, timestamp,
+    rv = InitializeOrigin(aPersistenceType, group, origin, timestamp,
                           childDirectory);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   return NS_OK;
 }
 
-namespace {
-
-// The Cache API was creating top level morgue directories by accident for
-// a short time in nightly.  This unfortunately prevents all storage from
-// working.  So recover these profiles by removing these corrupt directories.
-// This should be removed at some point in the future.
-bool
-MaybeRemoveCorruptDirectory(const nsAString& aLeafName, nsIFile* aDir)
-{
-#ifdef NIGHTLY_BUILD
-  MOZ_ASSERT(aDir);
-
-  if (aLeafName != NS_LITERAL_STRING("morgue")) {
-    return false;
-  }
-
-  NS_WARNING("QuotaManager removing corrupt morgue directory!");
-
-  nsresult rv = aDir->Remove(true /* recursive */);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return false;
-  }
-
-  return true;
-#else
-  return false;
-#endif // NIGHTLY_BUILD
-}
-
-} // namespace
-
 nsresult
 QuotaManager::InitializeOrigin(PersistenceType aPersistenceType,
                                const nsACString& aGroup,
                                const nsACString& aOrigin,
-                               bool aIsApp,
                                int64_t aAccessTime,
                                nsIFile* aDirectory)
 {
   AssertIsOnIOThread();
 
   nsresult rv;
 
-  bool trackQuota = IsQuotaEnforced(aPersistenceType, aOrigin, aIsApp);
+  bool trackQuota = aPersistenceType != PERSISTENCE_TYPE_PERSISTENT;
 
   // We need to initialize directories of all clients if they exists and also
   // get the total usage to initialize the quota.
   nsAutoPtr<UsageInfo> usageInfo;
   if (trackQuota) {
     usageInfo = new UsageInfo();
   }
 
@@ -3825,53 +3923,60 @@ QuotaManager::InitializeOrigin(Persisten
   while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && hasMore) {
     nsCOMPtr<nsISupports> entry;
     rv = entries->GetNext(getter_AddRefs(entry));
     NS_ENSURE_SUCCESS(rv, rv);
 
     nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
     NS_ENSURE_TRUE(file, NS_NOINTERFACE);
 
+    bool isDirectory;
+    rv = file->IsDirectory(&isDirectory);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
     nsString leafName;
     rv = file->GetLeafName(leafName);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    if (leafName.EqualsLiteral(METADATA_FILE_NAME) ||
-        leafName.EqualsLiteral(METADATA_V2_FILE_NAME) ||
-        leafName.EqualsLiteral(DSSTORE_FILE_NAME)) {
-      continue;
-    }
-
-    bool isDirectory;
-    rv = file->IsDirectory(&isDirectory);
-    NS_ENSURE_SUCCESS(rv, rv);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
 
     if (!isDirectory) {
-      NS_WARNING("Unknown file found!");
+      if (IsOriginMetadata(leafName)) {
+        continue;
+      }
+
+      if (IsTempMetadata(leafName)) {
+        rv = file->Remove(/* recursive */ false);
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          return rv;
+        }
+
+        continue;
+      }
+
+      UNKNOWN_FILE_WARNING(leafName);
       return NS_ERROR_UNEXPECTED;
     }
 
-    if (MaybeRemoveCorruptDirectory(leafName, file)) {
-      continue;
-    }
-
     Client::Type clientType;
     rv = Client::TypeFromText(leafName, clientType);
     if (NS_FAILED(rv)) {
-      NS_WARNING("Unknown directory found!");
+      UNKNOWN_FILE_WARNING(leafName);
       return NS_ERROR_UNEXPECTED;
     }
 
     rv = mClients[clientType]->InitOrigin(aPersistenceType, aGroup, aOrigin,
                                           usageInfo);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   if (trackQuota) {
-    InitQuotaForOrigin(aPersistenceType, aGroup, aOrigin, aIsApp,
+    InitQuotaForOrigin(aPersistenceType, aGroup, aOrigin,
                        usageInfo->TotalUsage(), aAccessTime);
   }
 
   return NS_OK;
 }
 
 nsresult
 QuotaManager::MaybeUpgradeIndexedDBDirectory()
@@ -4157,20 +4262,20 @@ QuotaManager::UpgradeStorageFrom0_0To1_0
     }
 
     rv = directory->InitWithPath(GetStoragePath(persistenceType));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     bool persistent = persistenceType == PERSISTENCE_TYPE_PERSISTENT;
-    RefPtr<UpgradeDirectoryMetadataFrom1To2Helper> helper =
-      new UpgradeDirectoryMetadataFrom1To2Helper(directory, persistent);
-
-    rv = helper->UpgradeMetadataFiles();
+    RefPtr<UpgradeStorageFrom0_0To1_0Helper> helper =
+      new UpgradeStorageFrom0_0To1_0Helper(directory, persistent);
+
+    rv = helper->DoUpgrade();
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
 
 #ifdef DEBUG
   {
     int32_t storageVersion;
@@ -4178,33 +4283,131 @@ QuotaManager::UpgradeStorageFrom0_0To1_0
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     MOZ_ASSERT(storageVersion == 0);
   }
 #endif
 
-  rv = aConnection->SetSchemaVersion(kStorageVersion);
+  rv = aConnection->SetSchemaVersion(MakeStorageVersion(1, 0));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   return NS_OK;
 }
 
-#if 0
 nsresult
 QuotaManager::UpgradeStorageFrom1_0To2_0(mozIStorageConnection* aConnection)
 {
   AssertIsOnIOThread();
   MOZ_ASSERT(aConnection);
 
+  // The upgrade consists of a number of logically distinct bugs that
+  // intentionally got fixed at the same time to trigger just one major
+  // version bump.
+  //
+  //
+  // Morgue directory cleanup
+  // [Feature/Bug]:
+  // The original bug that added "on demand" morgue cleanup is 1165119.
+  //
+  // [Mutations]:
+  // Morgue directories are removed from all origin directories during the
+  // upgrade process. Origin initialization and usage calculation doesn't try
+  // to remove morgue directories anymore.
+  //
+  // [Downgrade-incompatible changes]:
+  // Morgue directories can reappear if user runs an already upgraded profile
+  // in an older version of Firefox. Morgue directories then prevent current
+  // Firefox from initializing and using the storage.
+  //
+  //
+  // App data removal
+  // [Feature/Bug]:
+  // The bug that removes isApp flags is 1311057.
+  //
+  // [Mutations]:
+  // Origin directories with appIds are removed during the upgrade process.
+  //
+  // [Downgrade-incompatible changes]:
+  // Origin directories with appIds can reappear if user runs an already
+  // upgraded profile in an older version of Firefox. Origin directories with
+  // appIds don't prevent current Firefox from initializing and using the
+  // storage, but they wouldn't ever be removed again, potentially causing
+  // problems once appId is removed from origin attributes.
+  //
+  //
+  // Strip obsolete origin attributes
+  // [Feature/Bug]:
+  // The bug that strips obsolete origin attributes is 1314361.
+  //
+  // [Mutations]:
+  // Origin directories with obsolete origin attributes are renamed and their
+  // metadata files are updated during the upgrade process.
+  //
+  // [Downgrade-incompatible changes]:
+  // Origin directories with obsolete origin attributes can reappear if user
+  // runs an already upgraded profile in an older version of Firefox. Origin
+  // directories with obsolete origin attributes don't prevent current Firefox
+  // from initializing and using the storage, but they wouldn't ever be upgraded
+  // again, potentially causing problems in future.
+  //
+  //
+  // File manager directory renaming (client specific)
+  // [Feature/Bug]:
+  // The original bug that added "on demand" file manager directory renaming is
+  // 1056939.
+  //
+  // [Mutations]:
+  // All file manager directories are renamed to contain the ".files" suffix.
+  //
+  // [Downgrade-incompatible changes]:
+  // File manager directories with the ".files" suffix prevent older versions of
+  // Firefox from initializing and using the storage.
+  // File manager directories without the ".files" suffix can appear if user
+  // runs an already upgraded profile in an older version of Firefox. File
+  // manager directories without the ".files" suffix then prevent current
+  // Firefox from initializing and using the storage.
+
   nsresult rv;
 
+  for (const PersistenceType persistenceType : kAllPersistenceTypes) {
+    nsCOMPtr<nsIFile> directory =
+      do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    rv = directory->InitWithPath(GetStoragePath(persistenceType));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    bool exists;
+    rv = directory->Exists(&exists);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    if (!exists) {
+      continue;
+    }
+
+    bool persistent = persistenceType == PERSISTENCE_TYPE_PERSISTENT;
+    RefPtr<UpgradeStorageFrom1_0To2_0Helper> helper =
+      new UpgradeStorageFrom1_0To2_0Helper(directory, persistent);
+
+    rv = helper->DoUpgrade();
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+  }
+
 #ifdef DEBUG
   {
     int32_t storageVersion;
     rv = aConnection->GetSchemaVersion(&storageVersion);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
@@ -4214,17 +4417,27 @@ QuotaManager::UpgradeStorageFrom1_0To2_0
 
   rv = aConnection->SetSchemaVersion(MakeStorageVersion(2, 0));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   return NS_OK;
 }
-#endif
+
+#ifdef DEBUG
+
+void
+QuotaManager::AssertStorageIsInitialized() const
+{
+  AssertIsOnIOThread();
+  MOZ_ASSERT(mStorageInitialized);
+}
+
+#endif // DEBUG
 
 nsresult
 QuotaManager::EnsureStorageIsInitialized()
 {
   AssertIsOnIOThread();
 
   if (mStorageInitialized) {
     return NS_OK;
@@ -4353,26 +4566,24 @@ QuotaManager::EnsureStorageIsInitialized
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
 
       MOZ_ASSERT(NS_SUCCEEDED(connection->GetSchemaVersion(&storageVersion)));
       MOZ_ASSERT(storageVersion == kStorageVersion);
     } else {
       // This logic needs to change next time we change the storage!
-      static_assert(kStorageVersion == int32_t((1 << 16) + 0),
+      static_assert(kStorageVersion == int32_t((2 << 16) + 0),
                     "Upgrade function needed due to storage version increase.");
 
       while (storageVersion != kStorageVersion) {
         if (storageVersion == 0) {
           rv = UpgradeStorageFrom0_0To1_0(connection);
-#if 0
         } else if (storageVersion == MakeStorageVersion(1, 0)) {
           rv = UpgradeStorageFrom1_0To2_0(connection);
-#endif
         } else {
           NS_WARNING("Unable to initialize storage, no upgrade path is "
                      "available!");
           return NS_ERROR_FAILURE;
         }
 
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return rv;
@@ -4397,28 +4608,26 @@ QuotaManager::EnsureStorageIsInitialized
 
   return NS_OK;
 }
 
 void
 QuotaManager::OpenDirectory(PersistenceType aPersistenceType,
                             const nsACString& aGroup,
                             const nsACString& aOrigin,
-                            bool aIsApp,
                             Client::Type aClientType,
                             bool aExclusive,
                             OpenDirectoryListener* aOpenListener)
 {
   AssertIsOnOwningThread();
 
   RefPtr<DirectoryLockImpl> lock =
     CreateDirectoryLock(Nullable<PersistenceType>(aPersistenceType),
                         aGroup,
                         OriginScope::FromOrigin(aOrigin),
-                        Nullable<bool>(aIsApp),
                         Nullable<Client::Type>(aClientType),
                         aExclusive,
                         false,
                         aOpenListener);
   MOZ_ASSERT(lock);
 }
 
 void
@@ -4429,17 +4638,16 @@ QuotaManager::OpenDirectoryInternal(cons
                                     OpenDirectoryListener* aOpenListener)
 {
   AssertIsOnOwningThread();
 
   RefPtr<DirectoryLockImpl> lock =
     CreateDirectoryLock(aPersistenceType,
                         EmptyCString(),
                         aOriginScope,
-                        Nullable<bool>(),
                         Nullable<Client::Type>(aClientType),
                         aExclusive,
                         true,
                         aOpenListener);
   MOZ_ASSERT(lock);
 
   if (!aExclusive) {
     return;
@@ -4485,33 +4693,63 @@ QuotaManager::OpenDirectoryInternal(cons
   }
 }
 
 nsresult
 QuotaManager::EnsureOriginIsInitialized(PersistenceType aPersistenceType,
                                         const nsACString& aSuffix,
                                         const nsACString& aGroup,
                                         const nsACString& aOrigin,
-                                        bool aIsApp,
                                         nsIFile** aDirectory)
 {
   AssertIsOnIOThread();
+  MOZ_ASSERT(aDirectory);
+
+  nsCOMPtr<nsIFile> directory;
+  bool created;
+  nsresult rv = EnsureOriginIsInitializedInternal(aPersistenceType,
+                                                  aSuffix,
+                                                  aGroup,
+                                                  aOrigin,
+                                                  getter_AddRefs(directory),
+                                                  &created);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  directory.forget(aDirectory);
+  return NS_OK;
+}
+
+nsresult
+QuotaManager::EnsureOriginIsInitializedInternal(
+                                               PersistenceType aPersistenceType,
+                                               const nsACString& aSuffix,
+                                               const nsACString& aGroup,
+                                               const nsACString& aOrigin,
+                                               nsIFile** aDirectory,
+                                               bool* aCreated)
+{
+  AssertIsOnIOThread();
+  MOZ_ASSERT(aDirectory);
+  MOZ_ASSERT(aCreated);
 
   nsresult rv = EnsureStorageIsInitialized();
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Get directory for this origin and persistence type.
   nsCOMPtr<nsIFile> directory;
   rv = GetDirectoryForOrigin(aPersistenceType, aOrigin,
                              getter_AddRefs(directory));
   NS_ENSURE_SUCCESS(rv, rv);
 
-  if (IsTreatedAsPersistent(aPersistenceType, aIsApp)) {
-    if (mInitializedOrigins.Contains(OriginKey(aPersistenceType, aOrigin))) {
+  if (aPersistenceType == PERSISTENCE_TYPE_PERSISTENT) {
+    if (mInitializedOrigins.Contains(aOrigin)) {
       directory.forget(aDirectory);
+      *aCreated = false;
       return NS_OK;
     }
   } else if (!mTemporaryStorageInitialized) {
     rv = InitializeRepository(aPersistenceType);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       // We have to cleanup partially initialized quota.
       RemoveQuota();
 
@@ -4565,109 +4803,106 @@ QuotaManager::EnsureOriginIsInitialized(
     nsresult rv = directory->GetLeafName(leafName);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     if (!leafName.EqualsLiteral(kChromeOrigin)) {
       nsCString spec;
       OriginAttributes attrs;
-      bool result = OriginParser::ParseOrigin(NS_ConvertUTF16toUTF8(leafName),
-                                              spec, &attrs);
-      if (NS_WARN_IF(!result)) {
+      OriginParser::ResultType result =
+        OriginParser::ParseOrigin(NS_ConvertUTF16toUTF8(leafName),
+                                  spec,
+                                  &attrs);
+      if (NS_WARN_IF(result != OriginParser::ValidOrigin)) {
         QM_WARNING("Preventing creation of a new origin directory which is not "
-                   "supported by our origin parser!");
+                   "supported by our origin parser or is obsolete!");
 
         return NS_ERROR_FAILURE;
       }
     }
   }
 #endif
 
   bool created;
   rv = EnsureDirectory(directory, &created);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  if (IsTreatedAsPersistent(aPersistenceType, aIsApp)) {
+  if (aPersistenceType == PERSISTENCE_TYPE_PERSISTENT) {
     if (created) {
       timestamp = PR_Now();
 
       rv = CreateDirectoryMetadata(directory,
                                    timestamp,
                                    aSuffix,
                                    aGroup,
-                                   aOrigin,
-                                   aIsApp);
+                                   aOrigin);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
 
       rv = CreateDirectoryMetadata2(directory,
                                     timestamp,
                                     aSuffix,
                                     aGroup,
-                                    aOrigin,
-                                    aIsApp);
+                                    aOrigin);
       NS_ENSURE_SUCCESS(rv, rv);
     } else {
-      bool persistent = aPersistenceType == PERSISTENCE_TYPE_PERSISTENT;
       rv = GetDirectoryMetadata2WithRestore(directory,
-                                            persistent,
+                                            /* aPersistent */ true,
                                             &timestamp);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
 
       MOZ_ASSERT(timestamp <= PR_Now());
     }
 
-    rv = InitializeOrigin(aPersistenceType, aGroup, aOrigin, aIsApp, timestamp,
+    rv = InitializeOrigin(aPersistenceType, aGroup, aOrigin, timestamp,
                           directory);
     NS_ENSURE_SUCCESS(rv, rv);
 
-    mInitializedOrigins.AppendElement(OriginKey(aPersistenceType, aOrigin));
+    mInitializedOrigins.AppendElement(aOrigin);
   } else if (created) {
     timestamp = PR_Now();
 
     rv = CreateDirectoryMetadata(directory,
                                  timestamp,
                                  aSuffix,
                                  aGroup,
-                                 aOrigin,
-                                 aIsApp);
+                                 aOrigin);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     rv = CreateDirectoryMetadata2(directory,
                                   timestamp,
                                   aSuffix,
                                   aGroup,
-                                  aOrigin,
-                                  aIsApp);
+                                  aOrigin);
     NS_ENSURE_SUCCESS(rv, rv);
 
-    rv = InitializeOrigin(aPersistenceType, aGroup, aOrigin, aIsApp, timestamp,
+    rv = InitializeOrigin(aPersistenceType, aGroup, aOrigin, timestamp,
                           directory);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   directory.forget(aDirectory);
+  *aCreated = created;
   return NS_OK;
 }
 
 void
 QuotaManager::OriginClearCompleted(PersistenceType aPersistenceType,
-                                   const nsACString& aOrigin,
-                                   bool aIsApp)
+                                   const nsACString& aOrigin)
 {
   AssertIsOnIOThread();
 
-  if (IsTreatedAsPersistent(aPersistenceType, aIsApp)) {
-    mInitializedOrigins.RemoveElement(OriginKey(aPersistenceType, aOrigin));
+  if (aPersistenceType == PERSISTENCE_TYPE_PERSISTENT) {
+    mInitializedOrigins.RemoveElement(aOrigin);
   }
 
   for (uint32_t index = 0; index < Client::TYPE_MAX; index++) {
     mClients[index]->OnOriginClearCompleted(aPersistenceType, aOrigin);
   }
 }
 
 void
@@ -4758,24 +4993,23 @@ QuotaManager::GetStorageId(PersistenceTy
   aDatabaseId = str;
 }
 
 // static
 nsresult
 QuotaManager::GetInfoFromPrincipal(nsIPrincipal* aPrincipal,
                                    nsACString* aSuffix,
                                    nsACString* aGroup,
-                                   nsACString* aOrigin,
-                                   bool* aIsApp)
+                                   nsACString* aOrigin)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aPrincipal);
 
   if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
-    GetInfoForChrome(aSuffix, aGroup, aOrigin, aIsApp);
+    GetInfoForChrome(aSuffix, aGroup, aOrigin);
     return NS_OK;
   }
 
 
   if (aPrincipal->GetIsNullPrincipal()) {
     NS_WARNING("IndexedDB not supported from this principal!");
     return NS_ERROR_FAILURE;
   }
@@ -4823,70 +5057,60 @@ QuotaManager::GetInfoFromPrincipal(nsIPr
       aGroup->Assign(baseDomain + suffix);
     }
   }
 
   if (aOrigin) {
     aOrigin->Assign(origin);
   }
 
-  if (aIsApp) {
-    *aIsApp = aPrincipal->GetAppStatus() !=
-                nsIPrincipal::APP_STATUS_NOT_INSTALLED;
-  }
-
   return NS_OK;
 }
 
 // static
 nsresult
 QuotaManager::GetInfoFromWindow(nsPIDOMWindowOuter* aWindow,
                                 nsACString* aSuffix,
                                 nsACString* aGroup,
-                                nsACString* aOrigin,
-                                bool* aIsApp)
+                                nsACString* aOrigin)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aWindow);
 
   nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(aWindow);
   NS_ENSURE_TRUE(sop, NS_ERROR_FAILURE);
 
   nsCOMPtr<nsIPrincipal> principal = sop->GetPrincipal();
   NS_ENSURE_TRUE(principal, NS_ERROR_FAILURE);
 
   nsresult rv =
-    GetInfoFromPrincipal(principal, aSuffix, aGroup, aOrigin, aIsApp);
+    GetInfoFromPrincipal(principal, aSuffix, aGroup, aOrigin);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 // static
 void
 QuotaManager::GetInfoForChrome(nsACString* aSuffix,
                                nsACString* aGroup,
-                               nsACString* aOrigin,
-                               bool* aIsApp)
+                               nsACString* aOrigin)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(nsContentUtils::LegacyIsCallerChromeOrNativeCode());
 
   if (aSuffix) {
     aSuffix->Assign(EmptyCString());
   }
   if (aGroup) {
     ChromeOrigin(*aGroup);
   }
   if (aOrigin) {
     ChromeOrigin(*aOrigin);
   }
-  if (aIsApp) {
-    *aIsApp = false;
-  }
 }
 
 // static
 bool
 QuotaManager::IsOriginWhitelistedForPersistentStorage(const nsACString& aOrigin)
 {
   // The first prompt and quota tracking is not required for these origins in
   // persistent storage.
@@ -4896,42 +5120,34 @@ QuotaManager::IsOriginWhitelistedForPers
       StringBeginsWith(aOrigin, nsDependentCString(kResourceOriginPrefix))) {
     return true;
   }
 
   return false;
 }
 
 // static
-bool
-QuotaManager::IsFirstPromptRequired(PersistenceType aPersistenceType,
-                                    const nsACString& aOrigin,
-                                    bool aIsApp)
-{
-  if (IsTreatedAsTemporary(aPersistenceType, aIsApp)) {
-    return false;
-  }
-
-  return !IsOriginWhitelistedForPersistentStorage(aOrigin);
+void
+QuotaManager::ChromeOrigin(nsACString& aOrigin)
+{
+  aOrigin.AssignLiteral(kChromeOrigin);
 }
 
 // static
 bool
-QuotaManager::IsQuotaEnforced(PersistenceType aPersistenceType,
-                              const nsACString& aOrigin,
-                              bool aIsApp)
-{
-  return IsTreatedAsTemporary(aPersistenceType, aIsApp);
-}
-
-// static
-void
-QuotaManager::ChromeOrigin(nsACString& aOrigin)
-{
-  aOrigin.AssignLiteral(kChromeOrigin);
+QuotaManager::AreOriginsEqualOnDisk(nsACString& aOrigin1,
+                                    nsACString& aOrigin2)
+{
+  nsCString origin1Sanitized(aOrigin1);
+  SanitizeOriginString(origin1Sanitized);
+
+  nsCString origin2Sanitized(aOrigin2);
+  SanitizeOriginString(origin2Sanitized);
+
+  return origin1Sanitized == origin2Sanitized;
 }
 
 uint64_t
 QuotaManager::LockedCollectOriginsForEviction(
                                   uint64_t aMinSizeToBeFreed,
                                   nsTArray<RefPtr<DirectoryLockImpl>>& aLocks)
 {
   mQuotaMutex.AssertCurrentThreadOwns();
@@ -5098,31 +5314,29 @@ QuotaManager::CheckTemporaryStorageLimit
 
     for (uint32_t index = 0; index < doomedOriginInfos.Length(); index++) {
       OriginInfo* doomedOriginInfo = doomedOriginInfos[index];
 
       PersistenceType persistenceType =
         doomedOriginInfo->mGroupInfo->mPersistenceType;
       nsCString group = doomedOriginInfo->mGroupInfo->mGroup;
       nsCString origin = doomedOriginInfo->mOrigin;
-      bool isApp = doomedOriginInfo->mIsApp;
       LockedRemoveQuotaForOrigin(persistenceType, group, origin);
 
 #ifdef DEBUG
       doomedOriginInfos[index] = nullptr;
 #endif
 
-      doomedOrigins.AppendElement(OriginParams(persistenceType, origin, isApp));
+      doomedOrigins.AppendElement(OriginParams(persistenceType, origin));
     }
   }
 
   for (const OriginParams& doomedOrigin : doomedOrigins) {
     OriginClearCompleted(doomedOrigin.mPersistenceType,
-                         doomedOrigin.mOrigin,
-                         doomedOrigin.mIsApp);
+                         doomedOrigin.mOrigin);
   }
 }
 
 void
 QuotaManager::DeleteFilesForOrigin(PersistenceType aPersistenceType,
                                    const nsACString& aOrigin)
 {
   nsCOMPtr<nsIFile> directory;
@@ -5590,18 +5804,17 @@ FinalizeOriginEvictionOp::DoDirectoryWor
 {
   AssertIsOnIOThread();
 
   PROFILER_LABEL("Quota", "FinalizeOriginEvictionOp::DoDirectoryWork",
                  js::ProfileEntry::Category::OTHER);
 
   for (RefPtr<DirectoryLockImpl>& lock : mLocks) {
     aQuotaManager->OriginClearCompleted(lock->GetPersistenceType().Value(),
-                                        lock->GetOriginScope().GetOrigin(),
-                                        lock->GetIsApp().Value());
+                                        lock->GetOriginScope().GetOrigin());
   }
 
   return NS_OK;
 }
 
 void
 FinalizeOriginEvictionOp::UnblockOpen()
 {
@@ -5680,30 +5893,32 @@ SaveOriginAccessTimeOp::DoDirectoryWork(
 {
   AssertIsOnIOThread();
   MOZ_ASSERT(!mPersistenceType.IsNull());
   MOZ_ASSERT(mOriginScope.IsOrigin());
 
   PROFILER_LABEL("Quota", "SaveOriginAccessTimeOp::DoDirectoryWork",
                  js::ProfileEntry::Category::OTHER);
 
-  nsCOMPtr<nsIFile> directory;
+  nsCOMPtr<nsIFile> file;
   nsresult rv =
     aQuotaManager->GetDirectoryForOrigin(mPersistenceType.Value(),
                                          mOriginScope.GetOrigin(),
-                                         getter_AddRefs(directory));
+                                         getter_AddRefs(file));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = file->Append(NS_LITERAL_STRING(METADATA_V2_FILE_NAME));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   nsCOMPtr<nsIBinaryOutputStream> stream;
-  rv = GetBinaryOutputStream(directory,
-                             NS_LITERAL_STRING(METADATA_V2_FILE_NAME),
-                             kUpdateFileFlag,
-                             getter_AddRefs(stream));
+  rv = GetBinaryOutputStream(file, kUpdateFileFlag, getter_AddRefs(stream));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   // The origin directory may not exist anymore.
   if (stream) {
     rv = stream->Write64(mTimestamp);
     if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -5802,32 +6017,43 @@ Quota::DeallocPQuotaUsageRequestParent(P
 }
 
 PQuotaRequestParent*
 Quota::AllocPQuotaRequestParent(const RequestParams& aParams)
 {
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aParams.type() != RequestParams::T__None);
 
-  if (aParams.type() == RequestParams::TClearOriginsParams) {
+  if (aParams.type() == RequestParams::TClearDataParams) {
     PBackgroundParent* actor = Manager();
     MOZ_ASSERT(actor);
 
     if (BackgroundParent::IsOtherProcessActor(actor)) {
       ASSERT_UNLESS_FUZZING();
       return nullptr;
     }
   }
 
   RefPtr<QuotaRequestBase> actor;
 
   switch (aParams.type()) {
+    case RequestParams::TInitParams:
+      actor = new InitOp();
+      break;
+
+    case RequestParams::TInitOriginParams:
+      actor = new InitOriginOp(aParams);
+      break;
+
     case RequestParams::TClearOriginParams:
-    case RequestParams::TClearOriginsParams:
-      actor = new OriginClearOp(aParams);
+      actor = new ClearOriginOp(aParams);
+      break;
+
+    case RequestParams::TClearDataParams:
+      actor = new ClearDataOp(aParams);
       break;
 
     case RequestParams::TClearAllParams:
       actor = new ResetOrClearOp(/* aClear */ true);
       break;
 
     case RequestParams::TResetAllParams:
       actor = new ResetOrClearOp(/* aClear */ false);
@@ -5966,18 +6192,18 @@ GetUsageOp::DoInitOnMainThread()
   nsCOMPtr<nsIPrincipal> principal =
     PrincipalInfoToPrincipal(principalInfo, &rv);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   // Figure out which origin we're dealing with.
   nsCString origin;
-  rv = QuotaManager::GetInfoFromPrincipal(principal, &mSuffix, &mGroup, &origin,
-                                          &mIsApp);
+  rv = QuotaManager::GetInfoFromPrincipal(principal, &mSuffix, &mGroup,
+                                          &origin);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   mOriginScope.SetFromOrigin(origin);
 
   return NS_OK;
 }
@@ -5998,20 +6224,19 @@ GetUsageOp::AddToUsage(QuotaManager* aQu
   rv = directory->Exists(&exists);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // If the directory exists then enumerate all the files inside, adding up
   // the sizes to get the final usage statistic.
   if (exists && !mUsageInfo.Canceled()) {
     bool initialized;
 
-    if (IsTreatedAsPersistent(aPersistenceType, mIsApp)) {
-      nsCString originKey =
-        OriginKey(aPersistenceType, mOriginScope.GetOrigin());
-      initialized = aQuotaManager->IsOriginInitialized(originKey);
+    if (aPersistenceType == PERSISTENCE_TYPE_PERSISTENT) {
+      initialized =
+        aQuotaManager->IsOriginInitialized(mOriginScope.GetOrigin());
     } else {
       initialized = aQuotaManager->IsTemporaryStorageInitialized();
     }
 
     nsCOMPtr<nsISimpleEnumerator> entries;
     rv = directory->GetDirectoryEntries(getter_AddRefs(entries));
     NS_ENSURE_SUCCESS(rv, rv);
 
@@ -6020,45 +6245,59 @@ GetUsageOp::AddToUsage(QuotaManager* aQu
            hasMore && !mUsageInfo.Canceled()) {
       nsCOMPtr<nsISupports> entry;
       rv = entries->GetNext(getter_AddRefs(entry));
       NS_ENSURE_SUCCESS(rv, rv);
 
       nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
       NS_ENSURE_TRUE(file, NS_NOINTERFACE);
 
+      bool isDirectory;
+      rv = file->IsDirectory(&isDirectory);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+
       nsString leafName;
       rv = file->GetLeafName(leafName);
-      NS_ENSURE_SUCCESS(rv, rv);
-
-      if (leafName.EqualsLiteral(METADATA_FILE_NAME) ||
-          leafName.EqualsLiteral(METADATA_V2_FILE_NAME) ||
-          leafName.EqualsLiteral(DSSTORE_FILE_NAME)) {
-        continue;
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
       }
 
-      if (!initialized) {
-        bool isDirectory;
-        rv = file->IsDirectory(&isDirectory);
-        NS_ENSURE_SUCCESS(rv, rv);
-
-        if (!isDirectory) {
-          NS_WARNING("Unknown file found!");
+      if (!isDirectory) {
+        // We are maintaining existing behavior here (failing if the origin is
+        // not yet initialized or just continuing otherwise).
+        // This can possibly be used by developers to add temporary backups into
+        // origin directories without losing get usage functionality.
+        if (IsOriginMetadata(leafName)) {
+          continue;
+        }
+
+        if (IsTempMetadata(leafName)) {
+          if (!initialized) {
+            rv = file->Remove(/* recursive */ false);
+            if (NS_WARN_IF(NS_FAILED(rv))) {
+              return rv;
+            }
+          }
+
+          continue;
+        }
+
+        UNKNOWN_FILE_WARNING(leafName);
+        if (!initialized) {
           return NS_ERROR_UNEXPECTED;
         }
-      }
-
-      if (MaybeRemoveCorruptDirectory(leafName, file)) {
         continue;
       }
 
       Client::Type clientType;
       rv = Client::TypeFromText(leafName, clientType);
       if (NS_FAILED(rv)) {
-        NS_WARNING("Unknown directory found!");
+        UNKNOWN_FILE_WARNING(leafName);
         if (!initialized) {
           return NS_ERROR_UNEXPECTED;
         }
         continue;
       }
 
       Client* client = aQuotaManager->GetClient(clientType);
       MOZ_ASSERT(client);
@@ -6096,17 +6335,16 @@ GetUsageOp::DoDirectoryWork(QuotaManager
   if (mGetGroupUsage) {
     nsCOMPtr<nsIFile> directory;
 
     // Ensure origin is initialized first. It will initialize all origins for
     // temporary storage including origins belonging to our group.
     rv = aQuotaManager->EnsureOriginIsInitialized(PERSISTENCE_TYPE_TEMPORARY,
                                                   mSuffix, mGroup,
                                                   mOriginScope.GetOrigin(),
-                                                  mIsApp,
                                                   getter_AddRefs(directory));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     // Get cached usage and limit (the method doesn't have to stat any files).
     aQuotaManager->GetGroupUsageAndLimit(mGroup, &mUsageInfo);
 
@@ -6219,16 +6457,133 @@ QuotaRequestBase::SendResults()
 void
 QuotaRequestBase::ActorDestroy(ActorDestroyReason aWhy)
 {
   AssertIsOnOwningThread();
 
   NoteActorDestroyed();
 }
 
+nsresult
+InitOp::DoDirectoryWork(QuotaManager* aQuotaManager)
+{
+  AssertIsOnIOThread();
+
+  PROFILER_LABEL("Quota", "InitOp::DoDirectoryWork",
+                 js::ProfileEntry::Category::OTHER);
+
+  aQuotaManager->AssertStorageIsInitialized();
+
+  return NS_OK;
+}
+
+void
+InitOp::GetResponse(RequestResponse& aResponse)
+{
+  AssertIsOnOwningThread();
+
+  aResponse = InitResponse();
+}
+
+InitOriginOp::InitOriginOp(const RequestParams& aParams)
+  : QuotaRequestBase(/* aExclusive */ false)
+  , mParams(aParams.get_InitOriginParams())
+  , mCreated(false)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(aParams.type() == RequestParams::TInitOriginParams);
+}
+
+bool
+InitOriginOp::Init(Quota* aQuota)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(aQuota);
+
+  if (NS_WARN_IF(!QuotaRequestBase::Init(aQuota))) {
+    return false;
+  }
+
+  MOZ_ASSERT(mParams.persistenceType() != PERSISTENCE_TYPE_INVALID);
+
+  mPersistenceType.SetValue(mParams.persistenceType());
+
+  mNeedsMainThreadInit = true;
+
+  return true;
+}
+
+nsresult
+InitOriginOp::DoInitOnMainThread()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(GetState() == State_Initializing);
+  MOZ_ASSERT(mNeedsMainThreadInit);
+
+  const PrincipalInfo& principalInfo = mParams.principalInfo();
+
+  nsresult rv;
+  nsCOMPtr<nsIPrincipal> principal =
+    PrincipalInfoToPrincipal(principalInfo, &rv);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  // Figure out which origin we're dealing with.
+  nsCString origin;
+  rv = QuotaManager::GetInfoFromPrincipal(principal, &mSuffix, &mGroup,
+                                          &origin);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  mOriginScope.SetFromOrigin(origin);
+
+  return NS_OK;
+}
+
+nsresult
+InitOriginOp::DoDirectoryWork(QuotaManager* aQuotaManager)
+{
+  AssertIsOnIOThread();
+  MOZ_ASSERT(!mPersistenceType.IsNull());
+
+  PROFILER_LABEL("Quota", "InitOriginOp::DoDirectoryWork",
+                 js::ProfileEntry::Category::OTHER);
+
+  nsCOMPtr<nsIFile> directory;
+  bool created;
+  nsresult rv =
+    aQuotaManager->EnsureOriginIsInitializedInternal(mPersistenceType.Value(),
+                                                     mSuffix,
+                                                     mGroup,
+                                                     mOriginScope.GetOrigin(),
+                                                     getter_AddRefs(directory),
+                                                     &created);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  mCreated = created;
+
+  return NS_OK;
+}
+
+void
+InitOriginOp::GetResponse(RequestResponse& aResponse)
+{
+  AssertIsOnOwningThread();
+
+  InitOriginResponse response;
+
+  response.created() = mCreated;
+
+  aResponse = response;
+}
+
 void
 ResetOrClearOp::DeleteFiles(QuotaManager* aQuotaManager)
 {
   AssertIsOnIOThread();
   MOZ_ASSERT(aQuotaManager);
 
   nsresult rv;
 
@@ -6301,93 +6656,19 @@ ResetOrClearOp::GetResponse(RequestRespo
   AssertIsOnOwningThread();
   if (mClear) {
     aResponse = ClearAllResponse();
   } else {
     aResponse = ResetAllResponse();
   }
 }
 
-OriginClearOp::OriginClearOp(const RequestParams& aParams)
-  : QuotaRequestBase(/* aExclusive */ true)
-  , mParams(aParams)
-  , mMultiple(aParams.type() == RequestParams::TClearOriginsParams)
-{
-  MOZ_ASSERT(aParams.type() == RequestParams::TClearOriginParams ||
-             aParams.type() == RequestParams::TClearOriginsParams);
-}
-
-bool
-OriginClearOp::Init(Quota* aQuota)
-{
-  AssertIsOnOwningThread();
-  MOZ_ASSERT(aQuota);
-
-  if (NS_WARN_IF(!QuotaRequestBase::Init(aQuota))) {
-    return false;
-  }
-
-  if (!mMultiple) {
-    const ClearOriginParams& params = mParams.get_ClearOriginParams();
-
-    if (params.persistenceTypeIsExplicit()) {
-      MOZ_ASSERT(params.persistenceType() != PERSISTENCE_TYPE_INVALID);
-
-      mPersistenceType.SetValue(params.persistenceType());
-    }
-  }
-
-  mNeedsMainThreadInit = true;
-
-  return true;
-}
-
-nsresult
-OriginClearOp::DoInitOnMainThread()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(GetState() == State_Initializing);
-  MOZ_ASSERT(mNeedsMainThreadInit);
-
-  if (mMultiple) {
-    const ClearOriginsParams& params = mParams.get_ClearOriginsParams();
-
-    mOriginScope.SetFromJSONPattern(params.pattern());
-  } else {
-    const ClearOriginParams& params = mParams.get_ClearOriginParams();
-
-    const PrincipalInfo& principalInfo = params.principalInfo();
-
-    nsresult rv;
-    nsCOMPtr<nsIPrincipal> principal =
-      PrincipalInfoToPrincipal(principalInfo, &rv);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-
-    // Figure out which origin we're dealing with.
-    nsCString origin;
-    rv = QuotaManager::GetInfoFromPrincipal(principal, nullptr, nullptr, &origin,
-                                            nullptr);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-    if (params.clearAll()) {
-      mOriginScope.SetFromPrefix(origin);
-    } else {
-      mOriginScope.SetFromOrigin(origin);
-    }
-  }
-
-  return NS_OK;
-}
-
 void
-OriginClearOp::DeleteFiles(QuotaManager* aQuotaManager,
-                           PersistenceType aPersistenceType)
+ClearRequestBase::DeleteFiles(QuotaManager* aQuotaManager,
+                              PersistenceType aPersistenceType)
 {
   AssertIsOnIOThread();
   MOZ_ASSERT(aQuotaManager);
 
   nsresult rv;
 
   nsCOMPtr<nsIFile> directory =
     do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv);
@@ -6436,43 +6717,41 @@ OriginClearOp::DeleteFiles(QuotaManager*
 
     nsString leafName;
     rv = file->GetLeafName(leafName);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return;
     }
 
     if (!isDirectory) {
-      if (!leafName.EqualsLiteral(DSSTORE_FILE_NAME)) {
-        QM_WARNING("Something (%s) in the repository that doesn't belong!",
-                   NS_ConvertUTF16toUTF8(leafName).get());
+      // Unknown files during clearing are allowed. Just warn if we find them.
+      if (!IsOSMetadata(leafName)) {
+        UNKNOWN_FILE_WARNING(leafName);
       }
       continue;
     }
 
     // Skip the origin directory if it doesn't match the pattern.
     if (!originScope.MatchesOrigin(OriginScope::FromOrigin(
                                      NS_ConvertUTF16toUTF8(leafName)))) {
       continue;
     }
 
     bool persistent = aPersistenceType == PERSISTENCE_TYPE_PERSISTENT;
 
     int64_t timestamp;
     nsCString suffix;
     nsCString group;
     nsCString origin;
-    bool isApp;
     rv = aQuotaManager->GetDirectoryMetadata2WithRestore(file,
                                                          persistent,
                                                          &timestamp,
                                                          suffix,
                                                          group,
-                                                         origin,
-                                                         &isApp);
+                                                         origin);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return;
     }
 
     for (uint32_t index = 0; index < 10; index++) {
       // We can't guarantee that this will always succeed on Windows...
       if (NS_SUCCEEDED((rv = file->Remove(true)))) {
         break;
@@ -6486,23 +6765,23 @@ OriginClearOp::DeleteFiles(QuotaManager*
     if (NS_FAILED(rv)) {
       NS_WARNING("Failed to remove directory, giving up!");
     }
 
     if (aPersistenceType != PERSISTENCE_TYPE_PERSISTENT) {
       aQuotaManager->RemoveQuotaForOrigin(aPersistenceType, group, origin);
     }
 
-    aQuotaManager->OriginClearCompleted(aPersistenceType, origin, isApp);
+    aQuotaManager->OriginClearCompleted(aPersistenceType, origin);
   }
 
 }
 
 nsresult
-OriginClearOp::DoDirectoryWork(QuotaManager* aQuotaManager)
+ClearRequestBase::DoDirectoryWork(QuotaManager* aQuotaManager)
 {
   AssertIsOnIOThread();
 
   PROFILER_LABEL("Quota", "OriginClearOp::DoDirectoryWork",
                  js::ProfileEntry::Category::OTHER);
 
   if (mPersistenceType.IsNull()) {
     for (const PersistenceType type : kAllPersistenceTypes) {
@@ -6510,66 +6789,264 @@ OriginClearOp::DoDirectoryWork(QuotaMana
     }
   } else {
     DeleteFiles(aQuotaManager, mPersistenceType.Value());
   }
 
   return NS_OK;
 }
 
-void
-OriginClearOp::GetResponse(RequestResponse& aResponse)
+ClearOriginOp::ClearOriginOp(const RequestParams& aParams)
+  : ClearRequestBase(/* aExclusive */ true)
+  , mParams(aParams)
+{
+  MOZ_ASSERT(aParams.type() == RequestParams::TClearOriginParams);
+}
+
+bool
+ClearOriginOp::Init(Quota* aQuota)
 {
   AssertIsOnOwningThread();
-
-  if (mMultiple) {
-    aResponse = ClearOriginsResponse();
-  } else {
-    aResponse = ClearOriginResponse();
-  }
+  MOZ_ASSERT(aQuota);
+
+  if (NS_WARN_IF(!QuotaRequestBase::Init(aQuota))) {
+    return false;
+  }
+
+  if (mParams.persistenceTypeIsExplicit()) {
+    MOZ_ASSERT(mParams.persistenceType() != PERSISTENCE_TYPE_INVALID);
+
+    mPersistenceType.SetValue(mParams.persistenceType());
+  }
+
+  mNeedsMainThreadInit = true;
+
+  return true;
 }
 
 nsresult
-StorageDirectoryHelper::AddOriginDirectory(nsIFile* aDirectory,
-                                           OriginProps** aOriginProps)
+ClearOriginOp::DoInitOnMainThread()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(GetState() == State_Initializing);
+  MOZ_ASSERT(mNeedsMainThreadInit);
+
+  const PrincipalInfo& principalInfo = mParams.principalInfo();
+
+  nsresult rv;
+  nsCOMPtr<nsIPrincipal> principal =
+    PrincipalInfoToPrincipal(principalInfo, &rv);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  // Figure out which origin we're dealing with.
+  nsCString origin;
+  rv = QuotaManager::GetInfoFromPrincipal(principal, nullptr, nullptr,
+                                          &origin);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  if (mParams.clearAll()) {
+    mOriginScope.SetFromPrefix(origin);
+  } else {
+    mOriginScope.SetFromOrigin(origin);
+  }
+
+  return NS_OK;
+}
+
+void
+ClearOriginOp::GetResponse(RequestResponse& aResponse)
+{
+  AssertIsOnOwningThread();
+
+  aResponse = ClearOriginResponse();
+}
+
+ClearDataOp::ClearDataOp(const RequestParams& aParams)
+  : ClearRequestBase(/* aExclusive */ true)
+  , mParams(aParams)
+{
+  MOZ_ASSERT(aParams.type() == RequestParams::TClearDataParams);
+}
+
+bool
+ClearDataOp::Init(Quota* aQuota)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(aQuota);
+
+  if (NS_WARN_IF(!QuotaRequestBase::Init(aQuota))) {
+    return false;
+  }
+
+  mNeedsMainThreadInit = true;
+
+  return true;
+}
+
+nsresult
+ClearDataOp::DoInitOnMainThread()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(GetState() == State_Initializing);
+  MOZ_ASSERT(mNeedsMainThreadInit);
+
+  mOriginScope.SetFromJSONPattern(mParams.pattern());
+
+  return NS_OK;
+}
+
+void
+ClearDataOp::GetResponse(RequestResponse& aResponse)
+{
+  AssertIsOnOwningThread();
+
+  aResponse = ClearDataResponse();
+}
+
+nsresult
+StorageDirectoryHelper::GetDirectoryMetadata(nsIFile* aDirectory,
+                                             int64_t& aTimestamp,
+                                             nsACString& aGroup,
+                                             nsACString& aOrigin,
+                                             Nullable<bool>& aIsApp)
 {
   AssertIsOnIOThread();
   MOZ_ASSERT(aDirectory);
 
-  OriginProps* originProps;
-
-  nsString leafName;
-  nsresult rv = aDirectory->GetLeafName(leafName);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  if (leafName.EqualsLiteral(kChromeOrigin)) {
-    originProps = mOriginProps.AppendElement();
-    originProps->mDirectory = aDirectory;
-    originProps->mSpec = kChromeOrigin;
-    originProps->mType = OriginProps::eChrome;
-  } else {
-    nsCString spec;
-    OriginAttributes attrs;
-    bool result = OriginParser::ParseOrigin(NS_ConvertUTF16toUTF8(leafName),
-                                            spec, &attrs);
-    if (NS_WARN_IF(!result)) {
-      return NS_ERROR_FAILURE;
-    }
-
-    originProps = mOriginProps.AppendElement();
-    originProps->mDirectory = aDirectory;
-    originProps->mSpec = spec;
-    originProps->mAttrs = attrs;
-    originProps->mType = OriginProps::eContent;
-  }
-
-  if (aOriginProps) {
-    *aOriginProps = originProps;
+  nsCOMPtr<nsIBinaryInputStream> binaryStream;
+  nsresult rv = GetBinaryInputStream(aDirectory,
+                                     NS_LITERAL_STRING(METADATA_FILE_NAME),
+                                     getter_AddRefs(binaryStream));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  uint64_t timestamp;
+  rv = binaryStream->Read64(&timestamp);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsCString group;
+  rv = binaryStream->ReadCString(group);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsCString origin;
+  rv = binaryStream->ReadCString(origin);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  Nullable<bool> isApp;
+  bool value;
+  if (NS_SUCCEEDED(binaryStream->ReadBoolean(&value))) {
+    isApp.SetValue(value);
+  }
+
+  aTimestamp = timestamp;
+  aGroup = group;
+  aOrigin = origin;
+  aIsApp = Move(isApp);
+  return NS_OK;
+}
+
+nsresult
+StorageDirectoryHelper::GetDirectoryMetadata2(nsIFile* aDirectory,
+                                              int64_t& aTimestamp,
+                                              nsACString& aSuffix,
+                                              nsACString& aGroup,
+                                              nsACString& aOrigin,
+                                              bool& aIsApp)
+{
+  AssertIsOnIOThread();
+  MOZ_ASSERT(aDirectory);
+  MOZ_ASSERT(aTimestamp);
+
+  nsCOMPtr<nsIBinaryInputStream> binaryStream;
+  nsresult rv = GetBinaryInputStream(aDirectory,
+                                     NS_LITERAL_STRING(METADATA_V2_FILE_NAME),
+                                     getter_AddRefs(binaryStream));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  uint64_t timestamp;
+  rv = binaryStream->Read64(&timestamp);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  bool persisted;
+  rv = binaryStream->ReadBoolean(&persisted);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  uint32_t reservedData1;
+  rv = binaryStream->Read32(&reservedData1);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  uint32_t reservedData2;
+  rv = binaryStream->Read32(&reservedData2);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsCString suffix;
+  rv = binaryStream->ReadCString(suffix);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsCString group;
+  rv = binaryStream->ReadCString(group);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsCString origin;
+  rv = binaryStream->ReadCString(origin);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  bool isApp;
+  rv = binaryStream->ReadBoolean(&isApp);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  aTimestamp = timestamp;
+  aSuffix = suffix;
+  aGroup = group;
+  aOrigin = origin;
+  aIsApp = isApp;
+  return NS_OK;
+}
+
+nsresult
+StorageDirectoryHelper::RemoveObsoleteOrigin(const OriginProps& aOriginProps)
+{
+  AssertIsOnIOThread();
+  MOZ_ASSERT(aOriginProps.mDirectory);
+
+  QM_WARNING("Deleting obsolete %s directory that is no longer a legal "
+             "origin!", NS_ConvertUTF16toUTF8(aOriginProps.mLeafName).get());
+
+  nsresult rv = aOriginProps.mDirectory->Remove(/* recursive */ true);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
   }
 
   return NS_OK;
 }
 
 nsresult
 StorageDirectoryHelper::ProcessOriginDirectories()
 {
@@ -6590,19 +7067,36 @@ StorageDirectoryHelper::ProcessOriginDir
   }
 
   // Verify that the bounce to the main thread didn't start the shutdown
   // sequence.
   if (NS_WARN_IF(QuotaManager::IsShuttingDown())) {
     return NS_ERROR_FAILURE;
   }
 
-  nsresult rv = DoProcessOriginDirectories();
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
+  nsresult rv;
+
+  // Don't try to upgrade obsolete origins, remove them right after we detect
+  // them.
+  for (auto& originProps : mOriginProps) {
+    if (originProps.mType == OriginProps::eObsolete) {
+      MOZ_ASSERT(originProps.mSuffix.IsEmpty());
+      MOZ_ASSERT(originProps.mGroup.IsEmpty());
+      MOZ_ASSERT(originProps.mOrigin.IsEmpty());
+
+      rv = RemoveObsoleteOrigin(originProps);
+    } else {
+      MOZ_ASSERT(!originProps.mGroup.IsEmpty());
+      MOZ_ASSERT(!originProps.mOrigin.IsEmpty());
+
+      rv = ProcessOriginDirectory(originProps);
+    }
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
   }
 
   return NS_OK;
 }
 
 nsresult
 StorageDirectoryHelper::RunOnMainThread()
 {
@@ -6621,18 +7115,17 @@ StorageDirectoryHelper::RunOnMainThread(
        index < count;
        index++) {
     OriginProps& originProps = mOriginProps[index];
 
     switch (originProps.mType) {
       case OriginProps::eChrome: {
         QuotaManager::GetInfoForChrome(&originProps.mSuffix,
                                        &originProps.mGroup,
-                                       &originProps.mOrigin,
-                                       &originProps.mIsApp);
+                                       &originProps.mOrigin);
         break;
       }
 
       case OriginProps::eContent: {
         nsCOMPtr<nsIURI> uri;
         rv = NS_NewURI(getter_AddRefs(uri), originProps.mSpec);
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return rv;
@@ -6642,25 +7135,29 @@ StorageDirectoryHelper::RunOnMainThread(
           BasePrincipal::CreateCodebasePrincipal(uri, originProps.mAttrs);
         if (NS_WARN_IF(!principal)) {
           return NS_ERROR_FAILURE;
         }
 
         rv = QuotaManager::GetInfoFromPrincipal(principal,
                                                 &originProps.mSuffix,
                                                 &originProps.mGroup,
-                                                &originProps.mOrigin,
-                                                &originProps.mIsApp);
+                                                &originProps.mOrigin);
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return rv;
         }
 
         break;
       }
 
+      case OriginProps::eObsolete: {
+        // There's no way to get info for obsolete origins.
+        break;
+      }
+
       default:
         MOZ_CRASH("Bad type!");
     }
   }
 
   return NS_OK;
 }
 
@@ -6678,39 +7175,76 @@ StorageDirectoryHelper::Run()
   MOZ_ASSERT(mWaiting);
 
   mWaiting = false;
   mCondVar.Notify();
 
   return NS_OK;
 }
 
+nsresult
+StorageDirectoryHelper::
+OriginProps::Init(nsIFile* aDirectory)
+{
+  AssertIsOnIOThread();
+  MOZ_ASSERT(aDirectory);
+
+  nsString leafName;
+  nsresult rv = aDirectory->GetLeafName(leafName);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  if (leafName.EqualsLiteral(kChromeOrigin)) {
+    mDirectory = aDirectory;
+    mLeafName = leafName;
+    mSpec = kChromeOrigin;
+    mType = eChrome;
+  } else {
+    nsCString spec;
+    OriginAttributes attrs;
+    OriginParser::ResultType result =
+      OriginParser::ParseOrigin(NS_ConvertUTF16toUTF8(leafName), spec, &attrs);
+    if (NS_WARN_IF(result == OriginParser::InvalidOrigin)) {
+      return NS_ERROR_FAILURE;
+    }
+
+    mDirectory = aDirectory;
+    mLeafName = leafName;
+    mSpec = spec;
+    mAttrs = attrs;
+    mType = result == OriginParser::ObsoleteOrigin ? eObsolete : eContent;
+  }
+
+  return NS_OK;
+}
+
 // static
-bool
+auto
 OriginParser::ParseOrigin(const nsACString& aOrigin,
                           nsCString& aSpec,
-                          OriginAttributes* aAttrs)
+                          OriginAttributes* aAttrs) -> ResultType
 {
   MOZ_ASSERT(!aOrigin.IsEmpty());
   MOZ_ASSERT(aAttrs);
 
   OriginAttributes originAttributes;
 
   nsCString originNoSuffix;
   bool ok = originAttributes.PopulateFromOrigin(aOrigin, originNoSuffix);
   if (!ok) {
-    return false;
+    return InvalidOrigin;
   }
 
   OriginParser parser(originNoSuffix, originAttributes);
   return parser.Parse(aSpec, aAttrs);
 }
 
-bool
-OriginParser::Parse(nsACString& aSpec, OriginAttributes* aAttrs)
+auto
+OriginParser::Parse(nsACString& aSpec, OriginAttributes* aAttrs) -> ResultType
 {
   MOZ_ASSERT(aAttrs);
 
   while (mTokenizer.hasMoreTokens()) {
     const nsDependentCSubstring& token = mTokenizer.nextToken();
 
     HandleToken(token);
 
@@ -6729,122 +7263,122 @@ OriginParser::Parse(nsACString& aSpec, O
   if (!mError && mTokenizer.separatorAfterCurrentToken()) {
     HandleTrailingSeparator();
   }
 
   if (mError) {
     QM_WARNING("Origin '%s' failed to parse, handled tokens: %s", mOrigin.get(),
                mHandledTokens.get());
 
-    return false;
+    return InvalidOrigin;
   }
 
   MOZ_ASSERT(mState == eComplete || mState == eHandledTrailingSeparator);
 
   if (mAppId == kNoAppId) {
     *aAttrs = mOriginAttributes;
   } else {
     MOZ_ASSERT(mOriginAttributes.mAppId == kNoAppId);
 
     *aAttrs = OriginAttributes(mAppId, mInIsolatedMozBrowser);
   }
 
-  nsAutoCString spec(mSchema);
-
-  if (mSchemaType == eFile) {
+  nsAutoCString spec(mScheme);
+
+  if (mSchemeType == eFile) {
     spec.AppendLiteral("://");
 
     for (uint32_t count = mPathnameComponents.Length(), index = 0;
          index < count;
          index++) {
       spec.Append('/');
       spec.Append(mPathnameComponents[index]);
     }
 
     aSpec = spec;
 
-    return true;
-  }
-
-  if (mSchemaType == eAbout) {
+    return ValidOrigin;
+  }
+
+  if (mSchemeType == eAbout) {
     spec.Append(':');
   } else {
     spec.AppendLiteral("://");
   }
 
   spec.Append(mHost);
 
   if (!mPort.IsNull()) {
     spec.Append(':');
     spec.AppendInt(mPort.Value());
   }
 
   aSpec = spec;
 
-  return true;
+  return mScheme.EqualsLiteral("app") ? ObsoleteOrigin : ValidOrigin;
 }
 
 void
-OriginParser::HandleSchema(const nsDependentCSubstring& aToken)
+OriginParser::HandleScheme(const nsDependentCSubstring& aToken)
 {
   MOZ_ASSERT(!aToken.IsEmpty());
-  MOZ_ASSERT(mState == eExpectingAppIdOrSchema || mState == eExpectingSchema);
+  MOZ_ASSERT(mState == eExpectingAppIdOrScheme || mState == eExpectingScheme);
 
   bool isAbout = false;
   bool isFile = false;
   if (aToken.EqualsLiteral("http") ||
       aToken.EqualsLiteral("https") ||
       (isAbout = aToken.EqualsLiteral("about") ||
                  aToken.EqualsLiteral("moz-safe-about")) ||
       aToken.EqualsLiteral("indexeddb") ||
       (isFile = aToken.EqualsLiteral("file")) ||
       aToken.EqualsLiteral("app") ||
       aToken.EqualsLiteral("resource") ||
       aToken.EqualsLiteral("moz-extension")) {
-    mSchema = aToken;
+    mScheme = aToken;
 
     if (isAbout) {
-      mSchemaType = eAbout;
+      mSchemeType = eAbout;
       mState = eExpectingHost;
     } else {
       if (isFile) {
-        mSchemaType = eFile;
+        mSchemeType = eFile;
       }
       mState = eExpectingEmptyToken1;
     }
 
     return;
   }
 
-  QM_WARNING("'%s' is not a valid schema!", nsCString(aToken).get());
+  QM_WARNING("'%s' is not a valid scheme!", nsCString(aToken).get());
 
   mError = true;
 }
 
 void
 OriginParser::HandlePathnameComponent(const nsDependentCSubstring& aToken)
 {
   MOZ_ASSERT(!aToken.IsEmpty());
   MOZ_ASSERT(mState == eExpectingEmptyTokenOrDriveLetterOrPathnameComponent ||
              mState == eExpectingEmptyTokenOrPathnameComponent);
-  MOZ_ASSERT(mSchemaType == eFile);
+  MOZ_ASSERT(mSchemeType == eFile);
 
   mPathnameComponents.AppendElement(aToken);
 
   mState = mTokenizer.hasMoreTokens() ? eExpectingEmptyTokenOrPathnameComponent
                                       : eComplete;
 }
 
 void
 OriginParser::HandleToken(const nsDependentCSubstring& aToken)
 {
   switch (mState) {
-    case eExpectingAppIdOrSchema: {
+    case eExpectingAppIdOrScheme: {
       if (aToken.IsEmpty()) {
-        QM_WARNING("Expected an app id or schema (not an empty string)!");
+        QM_WARNING("Expected an app id or scheme (not an empty string)!");
 
         mError = true;
         return;
       }
 
       if (NS_IsAsciiDigit(aToken.First())) {
         // nsDependentCSubstring doesn't provice ToInteger()
         nsCString token(aToken);
@@ -6853,17 +7387,17 @@ OriginParser::HandleToken(const nsDepend
         uint32_t appId = token.ToInteger(&rv);
         if (NS_SUCCEEDED(rv)) {
           mAppId = appId;
           mState = eExpectingInMozBrowser;
           return;
         }
       }
 
-      HandleSchema(aToken);
+      HandleScheme(aToken);
 
       return;
     }
 
     case eExpectingInMozBrowser: {
       if (aToken.Length() != 1) {
         QM_WARNING("'%d' is not a valid length for the inMozBrowser flag!",
                    aToken.Length());
@@ -6879,30 +7413,30 @@ OriginParser::HandleToken(const nsDepend
       } else {
         QM_WARNING("'%s' is not a valid value for the inMozBrowser flag!",
                    nsCString(aToken).get());
 
         mError = true;
         return;
       }
 
-      mState = eExpectingSchema;
+      mState = eExpectingScheme;
 
       return;
     }
 
-    case eExpectingSchema: {
+    case eExpectingScheme: {
       if (aToken.IsEmpty()) {
-        QM_WARNING("Expected a schema (not an empty string)!");
+        QM_WARNING("Expected a scheme (not an empty string)!");
 
         mError = true;
         return;
       }
 
-      HandleSchema(aToken);
+      HandleScheme(aToken);
 
       return;
     }
 
     case eExpectingEmptyToken1: {
       if (!aToken.IsEmpty()) {
         QM_WARNING("Expected the first empty token!");
 
@@ -6918,27 +7452,27 @@ OriginParser::HandleToken(const nsDepend
     case eExpectingEmptyToken2: {
       if (!aToken.IsEmpty()) {
         QM_WARNING("Expected the second empty token!");
 
         mError = true;
         return;
       }
 
-      if (mSchemaType == eFile) {
+      if (mSchemeType == eFile) {
         mState = eExpectingEmptyToken3;
       } else {
         mState = eExpectingHost;
       }
 
       return;
     }
 
     case eExpectingEmptyToken3: {
-      MOZ_ASSERT(mSchemaType == eFile);
+      MOZ_ASSERT(mSchemeType == eFile);
 
       if (!aToken.IsEmpty()) {
         QM_WARNING("Expected the third empty token!");
 
         mError = true;
         return;
       }
 
@@ -6960,17 +7494,17 @@ OriginParser::HandleToken(const nsDepend
       mHost = aToken;
 
       mState = mTokenizer.hasMoreTokens() ? eExpectingPort : eComplete;
 
       return;
     }
 
     case eExpectingPort: {
-      MOZ_ASSERT(mSchemaType == eNone);
+      MOZ_ASSERT(mSchemeType == eNone);
 
       if (aToken.IsEmpty()) {
         QM_WARNING("Expected a port (not an empty string)!");
 
         mError = true;
         return;
       }
 
@@ -6989,17 +7523,17 @@ OriginParser::HandleToken(const nsDepend
       }
 
       mState = eComplete;
 
       return;
     }
 
     case eExpectingEmptyTokenOrDriveLetterOrPathnameComponent: {
-      MOZ_ASSERT(mSchemaType == eFile);
+      MOZ_ASSERT(mSchemeType == eFile);
 
       if (aToken.IsEmpty()) {
         mPathnameComponents.AppendElement(EmptyCString());
 
         mState =
           mTokenizer.hasMoreTokens() ? eExpectingEmptyTokenOrPathnameComponent
                                      : eComplete;
 
@@ -7019,17 +7553,17 @@ OriginParser::HandleToken(const nsDepend
       }
 
       HandlePathnameComponent(aToken);
 
       return;
     }
 
     case eExpectingEmptyTokenOrPathnameComponent: {
-      MOZ_ASSERT(mSchemaType == eFile);
+      MOZ_ASSERT(mSchemeType == eFile);
 
       if (aToken.IsEmpty()) {
         if (mMaybeDriveLetter) {
           MOZ_ASSERT(mPathnameComponents.Length() == 1);
 
           nsCString& pathnameComponent = mPathnameComponents[0];
           pathnameComponent.Append(':');
 
@@ -7054,17 +7588,17 @@ OriginParser::HandleToken(const nsDepend
       MOZ_CRASH("Should never get here!");
   }
 }
 
 void
 OriginParser::HandleTrailingSeparator()
 {
   MOZ_ASSERT(mState == eComplete);
-  MOZ_ASSERT(mSchemaType == eFile);
+  MOZ_ASSERT(mSchemeType == eFile);
 
   mPathnameComponents.AppendElement(EmptyCString());
 
   mState = eHandledTrailingSeparator;
 }
 
 nsresult
 CreateOrUpgradeDirectoryMetadataHelper::CreateOrUpgradeMetadataFiles()
@@ -7120,70 +7654,61 @@ CreateOrUpgradeDirectoryMetadataHelper::
         rv = originDir->Remove(/* aRecursive */ true);
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return rv;
         }
 
         continue;
       }
     } else {
-      if (!leafName.EqualsLiteral(DSSTORE_FILE_NAME)) {
-        QM_WARNING("Something (%s) in the storage directory that doesn't belong!",
-                   NS_ConvertUTF16toUTF8(leafName).get());
-
+      // Unknown files during upgrade are allowed. Just warn if we find them.
+      if (!IsOSMetadata(leafName)) {
+        UNKNOWN_FILE_WARNING(leafName);
       }
       continue;
     }
 
     if (mPersistent) {
       rv = MaybeUpgradeOriginDirectory(originDir);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
     }
 
-    OriginProps* originProps;
-    rv = AddOriginDirectory(originDir, &originProps);
+    OriginProps originProps;
+    rv = originProps.Init(originDir);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     if (!mPersistent) {
       int64_t timestamp;
       nsCString group;
       nsCString origin;
-      bool hasIsApp;
+      Nullable<bool> isApp;
       rv = GetDirectoryMetadata(originDir,
-                                &timestamp,
+                                timestamp,
                                 group,
                                 origin,
-                                &hasIsApp);
+                                isApp);
       if (NS_FAILED(rv)) {
-        timestamp = INT64_MIN;
-        rv = GetLastModifiedTime(originDir, &timestamp);
-        if (NS_WARN_IF(NS_FAILED(rv))) {
-          return rv;
-        }