Merge mozilla-central to fx-team
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Tue, 11 Oct 2016 13:03:45 +0200
changeset 360353 8654fba1417d44e510b8f2791f5ccf06c0496744
parent 360352 cf0fc7add23cf008568653ce03f4c511506458b1 (current diff)
parent 360252 7ae377917236b7e6111146aa9fb4c073c0efc7f4 (diff)
child 360354 687572ea20cb84f1842ad00dcc8ca1712868a361
push id6795
push userjlund@mozilla.com
push dateMon, 23 Jan 2017 14:19:46 +0000
treeherdermozilla-beta@76101b503191 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone52.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to fx-team
dom/fmradio/FMRadio.cpp
dom/fmradio/FMRadio.h
dom/fmradio/FMRadioCommon.h
dom/fmradio/FMRadioService.cpp
dom/fmradio/FMRadioService.h
dom/fmradio/ipc/FMRadioChild.cpp
dom/fmradio/ipc/FMRadioChild.h
dom/fmradio/ipc/FMRadioParent.cpp
dom/fmradio/ipc/FMRadioParent.h
dom/fmradio/ipc/FMRadioRequestChild.cpp
dom/fmradio/ipc/FMRadioRequestChild.h
dom/fmradio/ipc/FMRadioRequestParent.cpp
dom/fmradio/ipc/FMRadioRequestParent.h
dom/fmradio/ipc/PFMRadio.ipdl
dom/fmradio/ipc/PFMRadioRequest.ipdl
dom/fmradio/ipc/moz.build
dom/fmradio/moz.build
dom/fmradio/test/marionette/manifest.ini
dom/fmradio/test/marionette/test_bug862672.js
dom/fmradio/test/marionette/test_bug876597.js
dom/fmradio/test/marionette/test_cancel_seek.js
dom/fmradio/test/marionette/test_enable_disable.js
dom/fmradio/test/marionette/test_one_seek_at_once.js
dom/fmradio/test/marionette/test_seek_up_and_down.js
dom/fmradio/test/marionette/test_set_frequency.js
dom/permission/tests/mochitest-fm.ini
dom/permission/tests/test_fmradio.html
dom/u2f/tests/test_frame.html
dom/u2f/tests/test_frame_appid_facet.html
dom/u2f/tests/test_frame_appid_facet_insecure.html
dom/u2f/tests/test_frame_appid_facet_subdomain.html
dom/u2f/tests/test_frame_register.html
dom/u2f/tests/test_frame_register_sign.html
dom/webidl/FMRadio.webidl
netwerk/protocol/about/nsAboutBloat.cpp
netwerk/protocol/about/nsAboutBloat.h
--- a/browser/base/content/newtab/dragDataHelper.js
+++ b/browser/base/content/newtab/dragDataHelper.js
@@ -6,17 +6,17 @@
 
 var gDragDataHelper = {
   get mimeType() {
     return "text/x-moz-url";
   },
 
   getLinkFromDragEvent: function DragDataHelper_getLinkFromDragEvent(aEvent) {
     let dt = aEvent.dataTransfer;
-    if (!dt || !dt.types.contains(this.mimeType)) {
+    if (!dt || !dt.types.includes(this.mimeType)) {
       return null;
     }
 
     let data = dt.getData(this.mimeType) || "";
     let [url, title] = data.split(/[\r\n]+/);
     return {url: url, title: title};
   }
 };
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -4968,28 +4968,21 @@
         ]]>
       </handler>
       <handler event="oop-browser-crashed">
         <![CDATA[
           if (!event.isTrusted)
             return;
 
           let browser = event.originalTarget;
-          let title = browser.contentTitle;
-          let uri = browser.currentURI;
           let icon = browser.mIconURL;
-
           let tab = this.getTabForBrowser(browser);
 
           if (this.selectedBrowser == browser) {
-            this.updateBrowserRemotenessByURL(browser, "about:tabcrashed");
-            browser.setAttribute("crashedPageTitle", title);
-            browser.docShell.displayLoadError(Cr.NS_ERROR_CONTENT_CRASHED, uri, null);
-            browser.removeAttribute("crashedPageTitle");
-            tab.setAttribute("crashed", true);
+            TabCrashHandler.onSelectedBrowserCrash(browser);
           } else {
             this.updateBrowserRemoteness(browser, false);
             SessionStore.reviveCrashedTab(tab);
           }
 
           tab.removeAttribute("soundplaying");
           this.setIcon(tab, icon, browser.contentPrincipal);
         ]]>
--- a/browser/base/content/test/general/browser_clipboard_pastefile.js
+++ b/browser/base/content/test/general/browser_clipboard_pastefile.js
@@ -39,17 +39,17 @@ add_task(function*() {
   searchbar.focus();
 
   yield new Promise((resolve, reject) => {
     searchbar.addEventListener("paste", function copyEvent(event) {
       searchbar.removeEventListener("paste", copyEvent, true);
 
       let dt = event.clipboardData;
       is(dt.types.length, 3, "number of types");
-      ok(dt.types.contains("text/plain"), "text/plain exists in types");
+      ok(dt.types.includes("text/plain"), "text/plain exists in types");
       ok(dt.mozTypesAt(0).contains("text/plain"), "text/plain exists in mozTypesAt");
       is(dt.getData("text/plain"), "Alternate", "text/plain returned in getData");
       is(dt.mozGetDataAt("text/plain", 0), "Alternate", "text/plain returned in mozGetDataAt");
 
       resolve();
     }, true);
 
     EventUtils.synthesizeKey("v", { accelKey: true });
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -651,20 +651,20 @@ file, You can obtain one at http://mozil
           this.inputField.removeAttribute("tooltiptext");
         ]]></body>
       </method>
 
       <method name="onDragOver">
         <parameter name="aEvent"/>
         <body>
           var types = aEvent.dataTransfer.types;
-          if (types.contains("application/x-moz-file") ||
-              types.contains("text/x-moz-url") ||
-              types.contains("text/uri-list") ||
-              types.contains("text/unicode"))
+          if (types.includes("application/x-moz-file") ||
+              types.includes("text/x-moz-url") ||
+              types.includes("text/uri-list") ||
+              types.includes("text/unicode"))
             aEvent.preventDefault();
         </body>
       </method>
 
       <method name="onDrop">
         <parameter name="aEvent"/>
         <body><![CDATA[
           let links = browserDragAndDrop.dropLinks(aEvent);
--- a/browser/components/downloads/content/allDownloadsViewOverlay.js
+++ b/browser/components/downloads/content/allDownloadsViewOverlay.js
@@ -1386,19 +1386,19 @@ DownloadsPlacesView.prototype = {
     dt.setData("text/uri-list", url);
     dt.setData("text/plain", url);
     dt.effectAllowed = "copyMove";
     dt.addElement(selectedItem);
   },
 
   onDragOver(aEvent) {
     let types = aEvent.dataTransfer.types;
-    if (types.contains("text/uri-list") ||
-        types.contains("text/x-moz-url") ||
-        types.contains("text/plain")) {
+    if (types.includes("text/uri-list") ||
+        types.includes("text/x-moz-url") ||
+        types.includes("text/plain")) {
       aEvent.preventDefault();
     }
   },
 
   onDrop(aEvent) {
     let dt = aEvent.dataTransfer;
     // If dragged item is from our source, do not try to
     // redownload already downloaded file.
--- a/browser/components/originattributes/test/browser/test.html
+++ b/browser/components/originattributes/test/browser/test.html
@@ -7,19 +7,14 @@
   <script>
   window.onmessage = function (evt) {
     if (evt.data != "HI") {
       return;
     }
 
     window.parent.postMessage("OK", "http://mochi.test:8888");
   };
-
-  setTimeout(function() {
-    window.parent.postMessage("KO", "http://mochi.test:8888");
-  }, 1000);
-
   </script>
 </head>
 <body>
   Hello World.
 </body>
 </html>
--- a/browser/components/search/content/search.xml
+++ b/browser/components/search/content/search.xml
@@ -851,17 +851,17 @@
 
       <handler event="keypress" keycode="VK_UP" modifiers="alt"
                phase="capturing"
                action="return this.openSearch();"/>
 
       <handler event="dragover">
       <![CDATA[
         var types = event.dataTransfer.types;
-        if (types.contains("text/plain") || types.contains("text/x-moz-text-internal"))
+        if (types.includes("text/plain") || types.includes("text/x-moz-text-internal"))
           event.preventDefault();
       ]]>
       </handler>
 
       <handler event="drop">
       <![CDATA[
         var dataTransfer = event.dataTransfer;
         var data = dataTransfer.getData("text/plain");
--- a/browser/components/sessionstore/SessionStore.jsm
+++ b/browser/components/sessionstore/SessionStore.jsm
@@ -165,16 +165,18 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 XPCOMUtils.defineLazyModuleGetter(this, "SessionSaver",
   "resource:///modules/sessionstore/SessionSaver.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "SessionCookies",
   "resource:///modules/sessionstore/SessionCookies.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "SessionFile",
   "resource:///modules/sessionstore/SessionFile.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "TabAttributes",
   "resource:///modules/sessionstore/TabAttributes.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "TabCrashHandler",
+  "resource:///modules/ContentCrashHandlers.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "TabState",
   "resource:///modules/sessionstore/TabState.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "TabStateCache",
   "resource:///modules/sessionstore/TabStateCache.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "TabStateFlusher",
   "resource:///modules/sessionstore/TabStateFlusher.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Utils",
   "resource:///modules/sessionstore/Utils.jsm");
@@ -914,17 +916,17 @@ var SessionStoreInternal = {
         this.onTabHide(win, target);
         break;
       case "TabPinned":
       case "TabUnpinned":
       case "SwapDocShells":
         this.saveStateDelayed(win);
         break;
       case "oop-browser-crashed":
-        this.onBrowserCrashed(win, target);
+        this.onBrowserCrashed(target);
         break;
       case "XULFrameLoaderCreated":
         if (target.namespaceURI == NS_XUL &&
             target.localName == "browser" &&
             target.frameLoader &&
             target.permanentKey) {
           this._lastKnownFrameLoader.set(target.permanentKey, target.frameLoader);
           this.resetEpoch(target);
@@ -1862,22 +1864,35 @@ var SessionStoreInternal = {
    * @param aWindow
    *        Window reference
    */
   onTabSelect: function ssi_onTabSelect(aWindow) {
     if (RunState.isRunning) {
       this._windows[aWindow.__SSi].selected = aWindow.gBrowser.tabContainer.selectedIndex;
 
       let tab = aWindow.gBrowser.selectedTab;
-      // If __SS_restoreState is still on the browser and it is
-      // TAB_STATE_NEEDS_RESTORE, then then we haven't restored
-      // this tab yet. Explicitly call restoreTabContent to kick off the restore.
-      if (tab.linkedBrowser.__SS_restoreState &&
-          tab.linkedBrowser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE)
-        this.restoreTabContent(tab);
+      let browser = tab.linkedBrowser;
+
+      if (browser.__SS_restoreState &&
+          browser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE) {
+        // If __SS_restoreState is still on the browser and it is
+        // TAB_STATE_NEEDS_RESTORE, then then we haven't restored
+        // this tab yet.
+        //
+        // It's possible that this tab was recently revived, and that
+        // we've deferred showing the tab crashed page for it (if the
+        // tab crashed in the background). If so, we need to re-enter
+        // the crashed state, since we'll be showing the tab crashed
+        // page.
+        if (TabCrashHandler.willShowCrashedTab(browser)) {
+          this.enterCrashedState(browser);
+        } else {
+          this.restoreTabContent(tab);
+        }
+      }
     }
   },
 
   onTabShow: function ssi_onTabShow(aWindow, aTab) {
     // If the tab hasn't been restored yet, move it into the right bucket
     if (aTab.linkedBrowser.__SS_restoreState &&
         aTab.linkedBrowser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE) {
       TabRestoreQueue.hiddenToVisible(aTab);
@@ -1909,33 +1924,47 @@ var SessionStoreInternal = {
   /**
    * Handler for the event that is fired when a <xul:browser> crashes.
    *
    * @param aWindow
    *        The window that the crashed browser belongs to.
    * @param aBrowser
    *        The <xul:browser> that is now in the crashed state.
    */
-  onBrowserCrashed: function(aWindow, aBrowser) {
+  onBrowserCrashed: function(aBrowser) {
     NS_ASSERT(aBrowser.isRemoteBrowser,
               "Only remote browsers should be able to crash");
-    this._crashedBrowsers.add(aBrowser.permanentKey);
+
+    this.enterCrashedState(aBrowser);
+    // The browser crashed so we might never receive flush responses.
+    // Resolve all pending flush requests for the crashed browser.
+    TabStateFlusher.resolveAll(aBrowser);
+  },
+
+  /**
+   * Called when a browser is showing or is about to show the tab
+   * crashed page. This method causes SessionStore to ignore the
+   * tab until it's restored.
+   *
+   * @param browser
+   *        The <xul:browser> that is about to show the crashed page.
+   */
+  enterCrashedState(browser) {
+    this._crashedBrowsers.add(browser.permanentKey);
+
+    let win = browser.ownerGlobal;
 
     // If we hadn't yet restored, or were still in the midst of
     // restoring this browser at the time of the crash, we need
     // to reset its state so that we can try to restore it again
     // when the user revives the tab from the crash.
-    if (aBrowser.__SS_restoreState) {
-      let tab = aWindow.gBrowser.getTabForBrowser(aBrowser);
+    if (browser.__SS_restoreState) {
+      let tab = win.gBrowser.getTabForBrowser(browser);
       this._resetLocalTabRestoringState(tab);
     }
-
-    // The browser crashed so we might never receive flush responses.
-    // Resolve all pending flush requests for the crashed browser.
-    TabStateFlusher.resolveAll(aBrowser);
   },
 
   // Clean up data that has been closed a long time ago.
   // Do not reschedule a save. This will wait for the next regular
   // save.
   onIdleDaily: function() {
     // Remove old closed windows
     this._cleanupOldData([this._closedWindows]);
--- a/browser/components/sessionstore/test/browser.ini
+++ b/browser/components/sessionstore/test/browser.ini
@@ -227,8 +227,10 @@ run-if = e10s
 [browser_newtab_userTypedValue.js]
 [browser_parentProcessRestoreHash.js]
 run-if = e10s
 [browser_sessionStoreContainer.js]
 [browser_windowStateContainer.js]
 [browser_1234021.js]
 [browser_remoteness_flip_on_restore.js]
 run-if = e10s
+[browser_background_tab_crash.js]
+run-if = e10s && crashreporter
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/browser/components/sessionstore/test/browser_background_tab_crash.js
@@ -0,0 +1,221 @@
+"use strict";
+
+/**
+ * These tests the behaviour of the browser when background tabs crash,
+ * while the foreground tab remains.
+ *
+ * The current behavioural rule is this: if only background tabs crash,
+ * then only the first tab shown of that group should show the tab crash
+ * page, and subsequent ones should restore on demand.
+ */
+
+/**
+ * Makes the current browser tab non-remote, and then sets up two remote
+ * background tabs, ensuring that both belong to the same content process.
+ * Callers should pass in a testing function that will execute (and possibly
+ * yield Promises) taking the created background tabs as arguments. Once
+ * the testing function completes, this function will take care of closing
+ * the opened tabs.
+ *
+ * @param testFn (function)
+ *        A Promise-generating function that will be called once the tabs
+ *        are opened and ready.
+ * @return Promise
+ *        Resolves once the testing function completes and the opened tabs
+ *        have been completely closed.
+ */
+function* setupBackgroundTabs(testFn) {
+  const REMOTE_PAGE = "http://www.example.com";
+  const NON_REMOTE_PAGE = "about:robots";
+
+  // Browse the initial tab to a non-remote page, which we'll have in the
+  // foreground.
+  let initialTab = gBrowser.selectedTab;
+  let initialBrowser = initialTab.linkedBrowser;
+  initialBrowser.loadURI(NON_REMOTE_PAGE);
+  yield BrowserTestUtils.browserLoaded(initialBrowser);
+
+  // Open some tabs that should be running in the content process.
+  let tab1 =
+    yield BrowserTestUtils.openNewForegroundTab(gBrowser, REMOTE_PAGE);
+  let remoteBrowser1 = tab1.linkedBrowser;
+  yield TabStateFlusher.flush(remoteBrowser1);
+
+  let tab2 =
+    yield BrowserTestUtils.openNewForegroundTab(gBrowser, REMOTE_PAGE);
+  let remoteBrowser2 = tab2.linkedBrowser;
+  yield TabStateFlusher.flush(remoteBrowser2);
+
+  // Quick sanity check - the two browsers should be remote and share the
+  // same childID, or else this test is not going to work.
+  Assert.ok(remoteBrowser1.isRemoteBrowser,
+            "Browser should be remote in order to crash.");
+  Assert.ok(remoteBrowser2.isRemoteBrowser,
+            "Browser should be remote in order to crash.");
+  Assert.equal(remoteBrowser1.frameLoader.childID,
+               remoteBrowser2.frameLoader.childID,
+               "Both remote browsers should share the same content process.");
+
+  // Now switch back to the non-remote browser...
+  yield BrowserTestUtils.switchTab(gBrowser, initialTab);
+
+  yield testFn([tab1, tab2]);
+
+  yield BrowserTestUtils.removeTab(tab1);
+  yield BrowserTestUtils.removeTab(tab2);
+}
+
+/**
+ * Takes some set of background tabs that are assumed to all belong to
+ * the same content process, and crashes them.
+ *
+ * @param tabs (Array(<xul:tab>))
+ *        The tabs to crash.
+ * @return Promise
+ *        Resolves once the tabs have crashed and entered the pending
+ *        background state.
+ */
+function* crashBackgroundTabs(tabs) {
+  Assert.ok(tabs.length > 0, "Need to crash at least one tab.");
+  for (let tab of tabs) {
+    Assert.ok(tab.linkedBrowser.isRemoteBrowser, "tab is remote");
+  }
+
+  let remotenessChangePromises = tabs.map((t) => {
+    return BrowserTestUtils.waitForEvent(t, "TabRemotenessChange");
+  });
+
+  let tabsRevived = tabs.map((t) => {
+    return promiseTabRestoring(t);
+  });
+
+  yield BrowserTestUtils.crashBrowser(tabs[0].linkedBrowser, false);
+  yield Promise.all(remotenessChangePromises);
+  yield Promise.all(tabsRevived);
+
+  // Both background tabs should now be in the pending restore
+  // state.
+  for (let tab of tabs) {
+    Assert.ok(!tab.linkedBrowser.isRemoteBrowser, "tab is not remote");
+    Assert.ok(!tab.linkedBrowser.hasAttribute("crashed"), "tab is not crashed");
+    Assert.ok(tab.linkedBrowser.hasAttribute("pending"), "tab is pending");
+  }
+}
+
+add_task(function* setup() {
+  // We'll simplify by making sure we only ever one content process for this
+  // test.
+  yield SpecialPowers.pushPrefEnv({ set: [[ "dom.ipc.processCount", 1 ]] });
+
+  // On debug builds, crashing tabs results in much thinking, which
+  // slows down the test and results in intermittent test timeouts,
+  // so we'll pump up the expected timeout for this test.
+  requestLongerTimeout(5);
+});
+
+/**
+ * Tests that if a content process crashes taking down only
+ * background tabs, then the first of those tabs that the user
+ * selects will show the tab crash page, but the rest will restore
+ * on demand.
+ */
+add_task(function* test_background_crash_simple() {
+  yield setupBackgroundTabs(function*([tab1, tab2]) {
+    // Let's crash one of those background tabs now...
+    yield crashBackgroundTabs([tab1, tab2]);
+
+    // Selecting the first tab should now send it to the tab crashed page.
+    let tabCrashedPagePromise =
+      BrowserTestUtils.waitForContentEvent(tab1.linkedBrowser,
+                                           "AboutTabCrashedReady",
+                                           false, null, true);
+    yield BrowserTestUtils.switchTab(gBrowser, tab1);
+    yield tabCrashedPagePromise;
+
+    // Selecting the second tab should restore it.
+    let tabRestored = promiseTabRestored(tab2);
+    yield BrowserTestUtils.switchTab(gBrowser, tab2);
+    yield tabRestored;
+  });
+});
+
+/**
+ * Tests that if a content process crashes taking down only
+ * background tabs, and the user is configured to send backlogged
+ * crash reports automatically, that the tab crashed page is not
+ * shown.
+ */
+add_task(function* test_background_crash_autosubmit_backlogged() {
+  yield SpecialPowers.pushPrefEnv({
+    set: [["browser.crashReports.unsubmittedCheck.autoSubmit", true]],
+  });
+
+  yield setupBackgroundTabs(function*([tab1, tab2]) {
+    // Let's crash one of those background tabs now...
+    yield crashBackgroundTabs([tab1, tab2]);
+
+    // Selecting the first tab should restore it.
+    let tabRestored = promiseTabRestored(tab1);
+    yield BrowserTestUtils.switchTab(gBrowser, tab1);
+    yield tabRestored;
+
+    // Selecting the second tab should restore it.
+    tabRestored = promiseTabRestored(tab2);
+    yield BrowserTestUtils.switchTab(gBrowser, tab2);
+    yield tabRestored;
+  });
+
+  yield SpecialPowers.popPrefEnv();
+});
+
+/**
+ * Tests that if there are two background tab crashes in a row, that
+ * the two sets of background crashes don't interfere with one another.
+ *
+ * Specifically, if we start with two background tabs (1, 2) which crash,
+ * and we visit 1, 1 should go to the tab crashed page. If we then have
+ * two new background tabs (3, 4) crash, visiting 2 should still restore.
+ * Visiting 4 should show us the tab crashed page, and then visiting 3
+ * should restore.
+ */
+add_task(function* test_background_crash_multiple() {
+  let initialTab = gBrowser.selectedTab;
+
+  yield setupBackgroundTabs(function*([tab1, tab2]) {
+    // Let's crash one of those background tabs now...
+    yield crashBackgroundTabs([tab1, tab2]);
+
+    // Selecting the first tab should now send it to the tab crashed page.
+    let tabCrashedPagePromise =
+      BrowserTestUtils.waitForContentEvent(tab1.linkedBrowser,
+                                           "AboutTabCrashedReady",
+                                           false, null, true);
+    yield BrowserTestUtils.switchTab(gBrowser, tab1);
+    yield tabCrashedPagePromise;
+
+    // Now switch back to the original non-remote tab...
+    yield BrowserTestUtils.switchTab(gBrowser, initialTab);
+
+    yield setupBackgroundTabs(function*([tab3, tab4]) {
+      yield crashBackgroundTabs([tab3, tab4]);
+
+      // Selecting the second tab should restore it.
+      let tabRestored = promiseTabRestored(tab2);
+      yield BrowserTestUtils.switchTab(gBrowser, tab2);
+      yield tabRestored;
+
+      // Selecting the fourth tab should now send it to the tab crashed page.
+      let tabCrashedPagePromise =
+        BrowserTestUtils.waitForContentEvent(tab4.linkedBrowser,
+                                             "AboutTabCrashedReady",
+                                             false, null, true);
+      yield BrowserTestUtils.switchTab(gBrowser, tab4);
+      yield tabCrashedPagePromise;
+
+      // Selecting the third tab should restore it.
+      tabRestored = promiseTabRestored(tab3);
+      yield BrowserTestUtils.switchTab(gBrowser, tab3);
+      yield tabRestored;
+    });
+  });
+});
--- a/browser/modules/ContentCrashHandlers.jsm
+++ b/browser/modules/ContentCrashHandlers.jsm
@@ -36,69 +36,102 @@ XPCOMUtils.defineLazyGetter(this, "gNavi
   return Services.strings.createBundle(url);
 });
 
 // We don't process crash reports older than 28 days, so don't bother
 // submitting them
 const PENDING_CRASH_REPORT_DAYS = 28;
 const DAY = 24 * 60 * 60 * 1000; // milliseconds
 const DAYS_TO_SUPPRESS = 30;
+const MAX_UNSEEN_CRASHED_CHILD_IDS = 20;
 
 this.TabCrashHandler = {
   _crashedTabCount: 0,
+  childMap: new Map(),
+  browserMap: new WeakMap(),
+  unseenCrashedChildIDs: [],
+  crashedBrowserQueues: new Map(),
 
   get prefs() {
     delete this.prefs;
     return this.prefs = Services.prefs.getBranch("browser.tabs.crashReporting.");
   },
 
   init: function () {
     if (this.initialized)
       return;
     this.initialized = true;
 
-    if (AppConstants.MOZ_CRASHREPORTER) {
-      Services.obs.addObserver(this, "ipc:content-shutdown", false);
-      Services.obs.addObserver(this, "oop-frameloader-crashed", false);
-
-      this.childMap = new Map();
-      this.browserMap = new WeakMap();
-    }
+    Services.obs.addObserver(this, "ipc:content-shutdown", false);
+    Services.obs.addObserver(this, "oop-frameloader-crashed", false);
 
     this.pageListener = new RemotePages("about:tabcrashed");
     // LOAD_BACKGROUND pages don't fire load events, so the about:tabcrashed
     // content will fire up its own message when its initial scripts have
     // finished running.
     this.pageListener.addMessageListener("Load", this.receiveMessage.bind(this));
     this.pageListener.addMessageListener("RemotePage:Unload", this.receiveMessage.bind(this));
     this.pageListener.addMessageListener("closeTab", this.receiveMessage.bind(this));
     this.pageListener.addMessageListener("restoreTab", this.receiveMessage.bind(this));
     this.pageListener.addMessageListener("restoreAll", this.receiveMessage.bind(this));
   },
 
   observe: function (aSubject, aTopic, aData) {
     switch (aTopic) {
-      case "ipc:content-shutdown":
+      case "ipc:content-shutdown": {
         aSubject.QueryInterface(Ci.nsIPropertyBag2);
 
-        if (!aSubject.get("abnormal"))
+        if (!aSubject.get("abnormal")) {
           return;
+        }
+
+        let childID = aSubject.get("childID");
+        let dumpID = aSubject.get("dumpID");
+
+        if (!dumpID) {
+          Services.telemetry
+                  .getHistogramById("FX_CONTENT_CRASH_DUMP_UNAVAILABLE")
+                  .add(1);
+        } else if (AppConstants.MOZ_CRASHREPORTER) {
+          this.childMap.set(childID, dumpID);
+        }
 
-        this.childMap.set(aSubject.get("childID"), aSubject.get("dumpID"));
+        if (!this.flushCrashedBrowserQueue(childID)) {
+          this.unseenCrashedChildIDs.push(childID);
+          // The elements in unseenCrashedChildIDs will only be removed if
+          // the tab crash page is shown. However, ipc:content-shutdown might
+          // be fired for processes for which we'll never show the tab crash
+          // page - for example, the thumbnailing process. Another case to
+          // consider is if the user is configured to submit backlogged crash
+          // reports automatically, and a background tab crashes. In that case,
+          // we will never show the tab crash page, and never remove the element
+          // from the list.
+          //
+          // Instead of trying to account for all of those cases, we prevent
+          // this list from getting too large by putting a reasonable upper
+          // limit on how many childIDs we track. It's unlikely that this
+          // array would ever get so large as to be unwieldy (that'd be a lot
+          // or crashes!), but a leak is a leak.
+          if (this.unseenCrashedChildIDs.length > MAX_UNSEEN_CRASHED_CHILD_IDS) {
+            this.unseenCrashedChildIDs.shift();
+          }
+        }
         break;
-
-      case "oop-frameloader-crashed":
+      }
+      case "oop-frameloader-crashed": {
         aSubject.QueryInterface(Ci.nsIFrameLoader);
 
         let browser = aSubject.ownerElement;
-        if (!browser)
+        if (!browser) {
           return;
+        }
 
         this.browserMap.set(browser.permanentKey, aSubject.childID);
         break;
+      }
     }
   },
 
   receiveMessage: function(message) {
     let browser = message.target.browser;
     let gBrowser = browser.ownerGlobal.gBrowser;
     let tab = gBrowser.getTabForBrowser(browser);
 
@@ -129,16 +162,143 @@ this.TabCrashHandler = {
         this.maybeSendCrashReport(message);
         SessionStore.reviveAllCrashedTabs();
         break;
       }
     }
   },
 
   /**
+   * This should be called once a content process has finished
+   * shutting down abnormally. Any tabbrowser browsers that were
+   * selected at the time of the crash will then be sent to
+   * the crashed tab page.
+   *
+   * @param childID (int)
+   *        The childID of the content process that just crashed.
+   * @returns boolean
+   *        True if one or more browsers were sent to the tab crashed
+   *        page.
+   */
+  flushCrashedBrowserQueue(childID) {
+    let browserQueue = this.crashedBrowserQueues.get(childID);
+    if (!browserQueue) {
+      return false;
+    }
+
+    this.crashedBrowserQueues.delete(childID);
+
+    let sentBrowser = false;
+    for (let weakBrowser of browserQueue) {
+      let browser = weakBrowser.get();
+      if (browser) {
+        this.sendToTabCrashedPage(browser);
+        sentBrowser = true;
+      }
+    }
+
+    return sentBrowser;
+  },
+
+  /**
+   * Called by a tabbrowser when it notices that its selected browser
+   * has crashed. This will queue the browser to show the tab crash
+   * page once the content process has finished tearing down.
+   *
+   * @param browser (<xul:browser>)
+   *        The selected browser that just crashed.
+   */
+  onSelectedBrowserCrash(browser) {
+    if (!browser.isRemoteBrowser) {
+      Cu.reportError("Selected crashed browser is not remote.")
+      return;
+    }
+    if (!browser.frameLoader) {
+      Cu.reportError("Selected crashed browser has no frameloader.");
+      return;
+    }
+
+    let childID = browser.frameLoader.childID;
+    let browserQueue = this.crashedBrowserQueues.get(childID);
+    if (!browserQueue) {
+      browserQueue = [];
+      this.crashedBrowserQueues.set(childID, browserQueue);
+    }
+    // It's probably unnecessary to store this browser as a
+    // weak reference, since the content process should complete
+    // its teardown in the same tick of the event loop, and then
+    // this queue will be flushed. The weak reference is to avoid
+    // leaking browsers in case anything goes wrong during this
+    // teardown process.
+    browserQueue.push(Cu.getWeakReference(browser));
+  },
+
+  /**
+   * This method is exposed for SessionStore to call if the user selects
+   * a tab which will restore on demand. It's possible that the tab
+   * is in this state because it recently crashed. If that's the case, then
+   * it's also possible that the user has not seen the tab crash page for
+   * that particular crash, in which case, we might show it to them instead
+   * of restoring the tab.
+   *
+   * @param browser (<xul:browser>)
+   *        A browser from a browser tab that the user has just selected
+   *        to restore on demand.
+   * @returns (boolean)
+   *        True if TabCrashHandler will send the user to the tab crash
+   *        page instead.
+   */
+  willShowCrashedTab(browser) {
+    let childID = this.browserMap.get(browser.permanentKey);
+    // We will only show the tab crash page if:
+    // 1) We are aware that this browser crashed
+    // 2) We know we've never shown the tab crash page for the
+    //    crash yet
+    // 3) The user is not configured to automatically submit backlogged
+    //    crash reports. If they are, we'll send the crash report
+    //    immediately.
+    if (childID &&
+        this.unseenCrashedChildIDs.indexOf(childID) != -1) {
+      if (UnsubmittedCrashHandler.autoSubmit) {
+        let dumpID = this.childMap.get(childID);
+        if (dumpID) {
+          UnsubmittedCrashHandler.submitReports([dumpID]);
+        }
+      } else {
+        this.sendToTabCrashedPage(browser);
+        return true;
+      }
+    }
+
+    return false;
+  },
+
+  /**
+   * We show a special page to users when a normal browser tab has crashed.
+   * This method should be called to send a browser to that page once the
+   * process has completely closed.
+   *
+   * @param browser (<xul:browser>)
+   *        The browser that has recently crashed.
+   */
+  sendToTabCrashedPage(browser) {
+    let title = browser.contentTitle;
+    let uri = browser.currentURI;
+    let gBrowser = browser.ownerGlobal.gBrowser;
+    let tab = gBrowser.getTabForBrowser(browser);
+    // The tab crashed page is non-remote by default.
+    gBrowser.updateBrowserRemoteness(browser, false);
+
+    browser.setAttribute("crashedPageTitle", title);
+    browser.docShell.displayLoadError(Cr.NS_ERROR_CONTENT_CRASHED, uri, null);
+    browser.removeAttribute("crashedPageTitle");
+    tab.setAttribute("crashed", true);
+  },
+
+  /**
    * Submits a crash report from about:tabcrashed, if the crash
    * reporter is enabled and a crash report can be found.
    *
    * @param aBrowser
    *        The <xul:browser> that the report was sent from.
    * @param aFormData
    *        An Object with the following properties:
    *
@@ -260,24 +420,24 @@ this.TabCrashHandler = {
     // can decide whether or not to display the "Restore All
     // Crashed Tabs" button.
     this.pageListener.sendAsyncMessage("UpdateCount", {
       count: this._crashedTabCount,
     });
 
     let browser = message.target.browser;
 
+    let childID = this.browserMap.get(browser.permanentKey);
+    let index = this.unseenCrashedChildIDs.indexOf(childID);
+    if (index != -1) {
+      this.unseenCrashedChildIDs.splice(index, 1);
+    }
+
     let dumpID = this.getDumpID(browser);
     if (!dumpID) {
-      // Make sure to only count once even if there are multiple windows
-      // that will all show about:tabcrashed.
-      if (this._crashedTabCount == 1) {
-        Services.telemetry.getHistogramById("FX_CONTENT_CRASH_DUMP_UNAVAILABLE").add(1);
-      }
-
       message.target.sendAsyncMessage("SetCrashReportAvailable", {
         hasReport: false,
       });
       return;
     }
 
     let sendReport = this.prefs.getBoolPref("sendReport");
     let includeURL = this.prefs.getBoolPref("includeURL");
@@ -315,28 +475,28 @@ this.TabCrashHandler = {
     let browser = message.target.browser;
     let childID = this.browserMap.get(browser.permanentKey);
 
     // Make sure to only count once even if there are multiple windows
     // that will all show about:tabcrashed.
     if (this._crashedTabCount == 0 && childID) {
       Services.telemetry.getHistogramById("FX_CONTENT_CRASH_NOT_SUBMITTED").add(1);
     }
-},
+  },
 
   /**
    * For some <xul:browser>, return a crash report dump ID for that browser
    * if we have been informed of one. Otherwise, return null.
    *
    * @param browser (<xul:browser)
    *        The browser to try to get the dump ID for
    * @returns dumpID (String)
    */
   getDumpID(browser) {
-    if (!this.childMap) {
+    if (!AppConstants.MOZ_CRASHREPORTER) {
       return null;
     }
 
     return this.childMap.get(this.browserMap.get(browser.permanentKey));
   },
 }
 
 /**
@@ -468,18 +628,18 @@ this.UnsubmittedCrashHandler = {
     try {
       reportIDs = yield CrashSubmit.pendingIDsAsync(dateLimit);
     } catch (e) {
       Cu.reportError(e);
       return null;
     }
 
     if (reportIDs.length) {
-      if (CrashNotificationBar.autoSubmit) {
-        CrashNotificationBar.submitReports(reportIDs);
+      if (this.autoSubmit) {
+        this.submitReports(reportIDs);
       } else if (this.shouldShowPendingSubmissionsNotification()) {
         return this.showPendingSubmissionsNotification(reportIDs);
       }
     }
     return null;
   }),
 
   /**
@@ -544,17 +704,17 @@ this.UnsubmittedCrashHandler = {
       return null;
     }
 
     let messageTemplate =
       gNavigatorBundle.GetStringFromName("pendingCrashReports2.label");
 
     let message = PluralForm.get(count, messageTemplate).replace("#1", count);
 
-    let notification = CrashNotificationBar.show({
+    let notification = this.show({
       notificationID: "pending-crash-reports",
       message,
       reportIDs,
       onAction: () => {
         this.showingNotification = false;
       },
     });
 
@@ -573,19 +733,17 @@ this.UnsubmittedCrashHandler = {
    * @param someDate (Date, optional)
    *        The Date to convert to the string. If not provided,
    *        defaults to today's date.
    * @returns String
    */
   dateString(someDate = new Date()) {
     return someDate.toLocaleFormat("%Y%m%d");
   },
-};
 
-this.CrashNotificationBar = {
   /**
    * Attempts to show a notification bar to the user in the most
    * recent browser window asking them to submit some crash report
    * IDs. If a notification cannot be shown (for example, there
    * is no browser window), this method exits silently.
    *
    * The notification will allow the user to submit their crash
    * reports. If the user dismissed the notification, the crash
--- a/devtools/client/webconsole/new-console-output/components/message-types/console-api-call.js
+++ b/devtools/client/webconsole/new-console-output/components/message-types/console-api-call.js
@@ -78,17 +78,18 @@ function ConsoleApiCall(props) {
     });
   }
 
   let collapseTitle = null;
   if (isGroupType(type)) {
     collapseTitle = l10n.getStr("groupToggle");
   }
 
-  const collapsible = attachment !== null || isGroupType(type);
+  const collapsible = isGroupType(type)
+    || (type === "error" && Array.isArray(stacktrace));
   const topLevelClasses = ["cm-s-mozilla"];
 
   return Message({
     messageId,
     open,
     collapsible,
     collapseTitle,
     source,
--- a/devtools/client/webconsole/new-console-output/components/message-types/page-error.js
+++ b/devtools/client/webconsole/new-console-output/components/message-types/page-error.js
@@ -23,16 +23,17 @@ PageError.propTypes = {
 
 PageError.defaultProps = {
   open: false,
   indent: 0,
 };
 
 function PageError(props) {
   const {
+    dispatch,
     message,
     open,
     serviceContainer,
     indent,
   } = props;
   const {
     id: messageId,
     source,
@@ -40,19 +41,20 @@ function PageError(props) {
     level,
     messageText: messageBody,
     repeat,
     stacktrace,
     frame
   } = message;
 
   const childProps = {
+    dispatch,
     messageId,
     open,
-    collapsible: true,
+    collapsible: Array.isArray(stacktrace),
     source,
     type,
     level,
     topLevelClasses: [],
     indent,
     messageBody,
     repeat,
     frame,
--- a/devtools/client/webconsole/new-console-output/test/components/console-api-call.test.js
+++ b/devtools/client/webconsole/new-console-output/test/components/console-api-call.test.js
@@ -118,16 +118,19 @@ describe("ConsoleAPICall component:", ()
       expect(frameLinks.eq(0).find(".frame-link-function-display-name").text()).toBe("testStacktraceFiltering");
       expect(frameLinks.eq(0).find(".frame-link-filename").text()).toBe(filepath);
 
       expect(frameLinks.eq(1).find(".frame-link-function-display-name").text()).toBe("foo");
       expect(frameLinks.eq(1).find(".frame-link-filename").text()).toBe(filepath);
 
       expect(frameLinks.eq(2).find(".frame-link-function-display-name").text()).toBe("triggerPacket");
       expect(frameLinks.eq(2).find(".frame-link-filename").text()).toBe(filepath);
+
+      //it should not be collapsible.
+      expect(wrapper.find(`.theme-twisty`).length).toBe(0);
     });
   });
 
   describe("console.group", () => {
     it("renders", () => {
       const message = stubPreparedMessages.get("console.group('bar')");
       const wrapper = render(ConsoleApiCall({ message, serviceContainer, open: true }));
 
--- a/devtools/client/webconsole/new-console-output/test/components/page-error.test.js
+++ b/devtools/client/webconsole/new-console-output/test/components/page-error.test.js
@@ -1,18 +1,28 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 "use strict";
 
 // Test utils.
 const expect = require("expect");
-const { render } = require("enzyme");
+const { render, mount } = require("enzyme");
+const sinon = require("sinon");
+
+// React
+const { createFactory } = require("devtools/client/shared/vendor/react");
+const Provider = createFactory(require("react-redux").Provider);
+const { setupStore } = require("devtools/client/webconsole/new-console-output/test/helpers");
 
 // Components under test.
 const PageError = require("devtools/client/webconsole/new-console-output/components/message-types/page-error");
+const {
+  MESSAGE_OPEN,
+  MESSAGE_CLOSE,
+} = require("devtools/client/webconsole/new-console-output/constants");
 const { INDENT_WIDTH } = require("devtools/client/webconsole/new-console-output/components/message-indent");
 
 // Test fakes.
 const { stubPreparedMessages } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs/index");
 const serviceContainer = require("devtools/client/webconsole/new-console-output/test/fixtures/serviceContainer");
 
 describe("PageError component:", () => {
   it("renders", () => {
@@ -32,21 +42,60 @@ describe("PageError component:", () => {
     // @TODO Will likely change. See https://github.com/devtools-html/gecko-dev/issues/285
     expect(locationLink.text()).toBe("test-tempfile.js:3:5");
   });
 
   it("has a stacktrace which can be openned", () => {
     const message = stubPreparedMessages.get("ReferenceError: asdf is not defined");
     const wrapper = render(PageError({ message, serviceContainer, open: true }));
 
+    // There should be a collapse button.
+    expect(wrapper.find(".theme-twisty.open").length).toBe(1);
+
     // There should be three stacktrace items.
     const frameLinks = wrapper.find(`.stack-trace span.frame-link`);
     expect(frameLinks.length).toBe(3);
   });
 
+  it("toggle the stacktrace when the collapse button is clicked", () => {
+    const store = setupStore([]);
+    store.dispatch = sinon.spy();
+    const message = stubPreparedMessages.get("ReferenceError: asdf is not defined");
+
+    let wrapper = mount(Provider({store},
+      PageError({
+        message,
+        open: true,
+        dispatch: store.dispatch,
+        serviceContainer,
+      })
+    ));
+    wrapper.find(".theme-twisty.open").simulate("click");
+    let call = store.dispatch.getCall(0);
+    expect(call.args[0]).toEqual({
+      id: message.id,
+      type: MESSAGE_CLOSE
+    });
+
+    wrapper = mount(Provider({store},
+      PageError({
+        message,
+        open: false,
+        dispatch: store.dispatch,
+        serviceContainer,
+      })
+    ));
+    wrapper.find(".theme-twisty").simulate("click");
+    call = store.dispatch.getCall(1);
+    expect(call.args[0]).toEqual({
+      id: message.id,
+      type: MESSAGE_OPEN
+    });
+  });
+
   it("has the expected indent", () => {
     const message = stubPreparedMessages.get("ReferenceError: asdf is not defined");
     const indent = 10;
     let wrapper = render(PageError({ message, serviceContainer, indent}));
     expect(wrapper.find(".indent").prop("style").width)
         .toBe(`${indent * INDENT_WIDTH}px`);
 
     wrapper = render(PageError({ message, serviceContainer}));
--- a/devtools/client/webconsole/test/browser_webconsole_bug_762593_insecure_passwords_about_blank_web_console_warning.js
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_762593_insecure_passwords_about_blank_web_console_warning.js
@@ -5,17 +5,17 @@
 
 // Tests that errors about insecure passwords are logged to the web console.
 
 "use strict";
 
 const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
                  "test/test-bug-762593-insecure-passwords-about-blank-web-console-warning.html";
 const INSECURE_PASSWORD_MSG = "Password fields present on an insecure " +
-  "(http://) page. This is a security risk that allows user login " +
+  "(http://) iframe. This is a security risk that allows user login " +
   "credentials to be stolen.";
 
 add_task(function* () {
   yield loadTab(TEST_URI);
 
   let hud = yield openConsole();
 
   yield waitForMessages({
--- a/dom/base/Location.h
+++ b/dom/base/Location.h
@@ -39,184 +39,183 @@ public:
 
   void SetDocShell(nsIDocShell *aDocShell);
   nsIDocShell *GetDocShell();
 
   // nsIDOMLocation
   NS_DECL_NSIDOMLOCATION
 
   #define THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME() { \
-    MOZ_ASSERT(aSubjectPrincipal.isSome()); \
-    if (!CallerSubsumes(aSubjectPrincipal.value())) { \
+    if (!CallerSubsumes(&aSubjectPrincipal)) { \
       aError.Throw(NS_ERROR_DOM_SECURITY_ERR); \
       return; \
     } \
   }
 
   // WebIDL API:
   void Assign(const nsAString& aUrl,
-              const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+              nsIPrincipal& aSubjectPrincipal,
               ErrorResult& aError)
   {
     THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = Assign(aUrl);
   }
 
   void Replace(const nsAString& aUrl,
-               const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+               nsIPrincipal& aSubjectPrincipal,
                ErrorResult& aError)
   {
     aError = Replace(aUrl);
   }
 
   void Reload(bool aForceget,
-              const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+              nsIPrincipal& aSubjectPrincipal,
               ErrorResult& aError)
   {
     THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = Reload(aForceget);
   }
 
   void GetHref(nsAString& aHref,
-               const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+               nsIPrincipal& aSubjectPrincipal,
                ErrorResult& aError)
   {
     THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = GetHref(aHref);
   }
 
   void SetHref(const nsAString& aHref,
-               const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+               nsIPrincipal& aSubjectPrincipal,
                ErrorResult& aError)
   {
     aError = SetHref(aHref);
   }
 
   void GetOrigin(nsAString& aOrigin,
-                 const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                 nsIPrincipal& aSubjectPrincipal,
                  ErrorResult& aError)
   {
     THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = GetOrigin(aOrigin);
   }
 
   void GetProtocol(nsAString& aProtocol,
-                   const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                   nsIPrincipal& aSubjectPrincipal,
                    ErrorResult& aError)
   {
     THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = GetProtocol(aProtocol);
   }
 
   void SetProtocol(const nsAString& aProtocol,
-                   const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                   nsIPrincipal& aSubjectPrincipal,
                    ErrorResult& aError)
   {
     THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = SetProtocol(aProtocol);
   }
 
   void GetHost(nsAString& aHost,
-               const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+               nsIPrincipal& aSubjectPrincipal,
                ErrorResult& aError)
   {
     THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = GetHost(aHost);
   }
 
   void SetHost(const nsAString& aHost,
-               const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+               nsIPrincipal& aSubjectPrincipal,
                ErrorResult& aError)
   {
     THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = SetHost(aHost);
   }
 
   void GetHostname(nsAString& aHostname,
-                   const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                   nsIPrincipal& aSubjectPrincipal,
                    ErrorResult& aError)
   {
     THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = GetHostname(aHostname);
   }
 
   void SetHostname(const nsAString& aHostname,
-                   const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                   nsIPrincipal& aSubjectPrincipal,
                    ErrorResult& aError)
   {
     THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = SetHostname(aHostname);
   }
 
   void GetPort(nsAString& aPort,
-               const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+               nsIPrincipal& aSubjectPrincipal,
                ErrorResult& aError)
   {
     THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = GetPort(aPort);
   }
 
   void SetPort(const nsAString& aPort,
-               const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+               nsIPrincipal& aSubjectPrincipal,
                ErrorResult& aError)
   {
     THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = SetPort(aPort);
   }
 
   void GetPathname(nsAString& aPathname,
-                   const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                   nsIPrincipal& aSubjectPrincipal,
                    ErrorResult& aError)
   {
     THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = GetPathname(aPathname);
   }
 
   void SetPathname(const nsAString& aPathname,
-                   const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                   nsIPrincipal& aSubjectPrincipal,
                    ErrorResult& aError)
   {
     THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = SetPathname(aPathname);
   }
 
   void GetSearch(nsAString& aSeach,
-                 const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                 nsIPrincipal& aSubjectPrincipal,
                  ErrorResult& aError)
   {
     THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = GetSearch(aSeach);
   }
 
   void SetSearch(const nsAString& aSeach,
-                 const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                 nsIPrincipal& aSubjectPrincipal,
                  ErrorResult& aError)
   {
     THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = SetSearch(aSeach);
   }
 
   void GetHash(nsAString& aHash,
-               const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+               nsIPrincipal& aSubjectPrincipal,
                ErrorResult& aError)
   {
     THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = GetHash(aHash);
   }
 
   void SetHash(const nsAString& aHash,
-               const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+               nsIPrincipal& aSubjectPrincipal,
                ErrorResult& aError)
   {
     THROW_AND_RETURN_IF_CALLER_DOESNT_SUBSUME();
     aError = SetHash(aHash);
   }
 
   void Stringify(nsAString& aRetval,
-                 const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                 nsIPrincipal& aSubjectPrincipal,
                  ErrorResult& aError)
   {
     // GetHref checks CallerSubsumes.
     GetHref(aRetval, aSubjectPrincipal, aError);
   }
 
   nsPIDOMWindowInner* GetParentObject() const
   {
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -86,20 +86,16 @@
 #include "BluetoothManager.h"
 #endif
 #include "DOMCameraManager.h"
 
 #ifdef MOZ_AUDIO_CHANNEL_MANAGER
 #include "AudioChannelManager.h"
 #endif
 
-#ifdef MOZ_B2G_FM
-#include "mozilla/dom/FMRadio.h"
-#endif
-
 #include "nsIDOMGlobalPropertyInitializer.h"
 #include "nsJSUtils.h"
 
 #include "nsScriptNameSpaceManager.h"
 
 #include "mozilla/dom/NavigatorBinding.h"
 #include "mozilla/dom/Promise.h"
 
@@ -287,23 +283,16 @@ Navigator::Invalidate()
 
   if (mBatteryManager) {
     mBatteryManager->Shutdown();
     mBatteryManager = nullptr;
   }
 
   mBatteryPromise = nullptr;
 
-#ifdef MOZ_B2G_FM
-  if (mFMRadio) {
-    mFMRadio->Shutdown();
-    mFMRadio = nullptr;
-  }
-#endif
-
   if (mPowerManager) {
     mPowerManager->Shutdown();
     mPowerManager = nullptr;
   }
 
   if (mIccManager) {
     mIccManager->Shutdown();
     mIccManager = nullptr;
@@ -1541,40 +1530,16 @@ Navigator::GetMozNotification(ErrorResul
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   mNotification = new DesktopNotificationCenter(mWindow);
   return mNotification;
 }
 
-#ifdef MOZ_B2G_FM
-
-using mozilla::dom::FMRadio;
-
-FMRadio*
-Navigator::GetMozFMRadio(ErrorResult& aRv)
-{
-  if (!mFMRadio) {
-    if (!mWindow) {
-      aRv.Throw(NS_ERROR_UNEXPECTED);
-      return nullptr;
-    }
-
-    NS_ENSURE_TRUE(mWindow->GetDocShell(), nullptr);
-
-    mFMRadio = new FMRadio();
-    mFMRadio->Init(mWindow);
-  }
-
-  return mFMRadio;
-}
-
-#endif  // MOZ_B2G_FM
-
 //*****************************************************************************
 //    Navigator::nsINavigatorBattery
 //*****************************************************************************
 
 Promise*
 Navigator::GetBattery(ErrorResult& aRv)
 {
   if (mBatteryPromise) {
--- a/dom/base/Navigator.h
+++ b/dom/base/Navigator.h
@@ -52,20 +52,16 @@ namespace mozilla {
 namespace dom {
 
 class Permissions;
 
 namespace battery {
 class BatteryManager;
 } // namespace battery
 
-#ifdef MOZ_B2G_FM
-class FMRadio;
-#endif
-
 class Promise;
 
 class DesktopNotificationCenter;
 class MobileMessageManager;
 class MozIdleObserver;
 #ifdef MOZ_GAMEPAD
 class Gamepad;
 class GamepadServiceTest;
@@ -243,19 +239,16 @@ public:
   MobileConnectionArray* GetMozMobileConnections(ErrorResult& aRv);
 #endif // MOZ_B2G_RIL
 #ifdef MOZ_GAMEPAD
   void GetGamepads(nsTArray<RefPtr<Gamepad> >& aGamepads, ErrorResult& aRv);
   GamepadServiceTest* RequestGamepadServiceTest();
 #endif // MOZ_GAMEPAD
   already_AddRefed<Promise> GetVRDisplays(ErrorResult& aRv);
   void GetActiveVRDisplays(nsTArray<RefPtr<VRDisplay>>& aDisplays) const;
-#ifdef MOZ_B2G_FM
-  FMRadio* GetMozFMRadio(ErrorResult& aRv);
-#endif
 #ifdef MOZ_B2G_BT
   bluetooth::BluetoothManager* GetMozBluetooth(ErrorResult& aRv);
 #endif // MOZ_B2G_BT
 #ifdef MOZ_TIME_MANAGER
   time::TimeManager* GetMozTime(ErrorResult& aRv);
 #endif // MOZ_TIME_MANAGER
 #ifdef MOZ_AUDIO_CHANNEL_MANAGER
   system::AudioChannelManager* GetMozAudioChannelManager(ErrorResult& aRv);
@@ -335,19 +328,16 @@ private:
 
   RefPtr<nsMimeTypeArray> mMimeTypes;
   RefPtr<nsPluginArray> mPlugins;
   RefPtr<Permissions> mPermissions;
   RefPtr<Geolocation> mGeolocation;
   RefPtr<DesktopNotificationCenter> mNotification;
   RefPtr<battery::BatteryManager> mBatteryManager;
   RefPtr<Promise> mBatteryPromise;
-#ifdef MOZ_B2G_FM
-  RefPtr<FMRadio> mFMRadio;
-#endif
   RefPtr<PowerManager> mPowerManager;
   RefPtr<IccManager> mIccManager;
   RefPtr<MobileMessageManager> mMobileMessageManager;
   RefPtr<Telephony> mTelephony;
   RefPtr<Voicemail> mVoicemail;
   RefPtr<TVManager> mTVManager;
   RefPtr<InputPortManager> mInputPortManager;
   RefPtr<network::Connection> mConnection;
--- a/dom/base/contentAreaDropListener.js
+++ b/dom/base/contentAreaDropListener.js
@@ -133,21 +133,21 @@ ContentAreaDropListener.prototype =
 
   canDropLink: function(aEvent, aAllowSameDocument)
   {
     if (this._eventTargetIsDisabled(aEvent))
       return false;
 
     let dataTransfer = aEvent.dataTransfer;
     let types = dataTransfer.types;
-    if (!types.contains("application/x-moz-file") &&
-        !types.contains("text/x-moz-url") &&
-        !types.contains("text/uri-list") &&
-        !types.contains("text/x-moz-text-internal") &&
-        !types.contains("text/plain"))
+    if (!types.includes("application/x-moz-file") &&
+        !types.includes("text/x-moz-url") &&
+        !types.includes("text/uri-list") &&
+        !types.includes("text/x-moz-text-internal") &&
+        !types.includes("text/plain"))
       return false;
 
     if (aAllowSameDocument)
       return true;
 
     let sourceNode = dataTransfer.mozSourceNode;
     if (!sourceNode)
       return true;
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -432,30 +432,29 @@ LOCAL_INCLUDES += [
     '/netwerk/base',
     '/security/manager/ssl',
     '/widget',
     '/xpcom/ds',
 ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
     LOCAL_INCLUDES += [
-        '../fmradio',
         '../system/gonk',
     ]
 
 if CONFIG['MOZ_WEBRTC']:
     LOCAL_INCLUDES += [
         '/netwerk/sctp/datachannel',
     ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 
-for var in ('MOZ_B2G_RIL', 'MOZ_B2G_FM'):
+for var in ('MOZ_B2G_RIL'):
     if CONFIG[var]:
         DEFINES[var] = True
 
 if CONFIG['MOZ_BUILD_APP'] in ['browser', 'mobile/android', 'xulrunner']:
     DEFINES['HAVE_SIDEBAR'] = True
 
 if CONFIG['MOZ_X11']:
     CXXFLAGS += CONFIG['TK_CFLAGS']
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -95,20 +95,16 @@
 #include "nsIEventListenerService.h"
 #include "nsIMessageManager.h"
 
 #include "mozilla/dom/TouchEvent.h"
 
 #include "nsWrapperCacheInlines.h"
 #include "mozilla/dom/HTMLCollectionBinding.h"
 
-#ifdef MOZ_B2G_FM
-#include "FMRadio.h"
-#endif
-
 #include "nsDebug.h"
 
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/Likely.h"
 #include "nsIInterfaceInfoManager.h"
 
 #ifdef MOZ_TIME_MANAGER
 #include "TimeManager.h"
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -6238,16 +6238,24 @@ nsDocument::LoadBindingDocument(const ns
                                      ? Some(nsContentUtils::SubjectPrincipal())
                                      : Nothing(),
                                    rv);
   return rv.StealNSResult();
 }
 
 void
 nsIDocument::LoadBindingDocument(const nsAString& aURI,
+                                 nsIPrincipal& aSubjectPrincipal,
+                                 ErrorResult& rv)
+{
+  LoadBindingDocument(aURI, Some(&aSubjectPrincipal), rv);
+}
+
+void
+nsIDocument::LoadBindingDocument(const nsAString& aURI,
                                  const Maybe<nsIPrincipal*>& aSubjectPrincipal,
                                  ErrorResult& rv)
 {
   nsCOMPtr<nsIURI> uri;
   rv = NS_NewURI(getter_AddRefs(uri), aURI,
                  mCharacterSet.get(),
                  GetDocBaseURI());
   if (rv.Failed()) {
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -6812,23 +6812,22 @@ nsGlobalWindow::CanMoveResizeWindows(boo
     }
   }
   return true;
 }
 
 bool
 nsGlobalWindow::AlertOrConfirm(bool aAlert,
                                const nsAString& aMessage,
-                               const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                               nsIPrincipal& aSubjectPrincipal,
                                ErrorResult& aError)
 {
   // XXX This method is very similar to nsGlobalWindow::Prompt, make
   // sure any modifications here don't need to happen over there!
   MOZ_ASSERT(IsOuterWindow());
-  MOZ_ASSERT(aSubjectPrincipal.isSome());
 
   if (!AreDialogsEnabled()) {
     // Just silently return.  In the case of alert(), the return value is
     // ignored.  In the case of confirm(), returning false is the same thing as
     // would happen if the user cancels.
     return false;
   }
 
@@ -6837,17 +6836,17 @@ nsGlobalWindow::AlertOrConfirm(bool aAle
   // the whole time a modal dialog is open.
   nsAutoPopupStatePusher popupStatePusher(openAbused, true);
 
   // Before bringing up the window, unsuppress painting and flush
   // pending reflows.
   EnsureReflowFlushAndPaint();
 
   nsAutoString title;
-  MakeScriptDialogTitle(title, aSubjectPrincipal.value());
+  MakeScriptDialogTitle(title, &aSubjectPrincipal);
 
   // Remove non-terminating null characters from the
   // string. See bug #310037.
   nsAutoString final;
   nsContentUtils::StripNullChars(aMessage, final);
 
   nsresult rv;
   nsCOMPtr<nsIPromptFactory> promptFac =
@@ -6890,55 +6889,55 @@ nsGlobalWindow::AlertOrConfirm(bool aAle
                prompt->Alert(title.get(), final.get()) :
                prompt->Confirm(title.get(), final.get(), &result);
   }
 
   return result;
 }
 
 void
-nsGlobalWindow::Alert(const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+nsGlobalWindow::Alert(nsIPrincipal& aSubjectPrincipal,
                       ErrorResult& aError)
 {
   MOZ_ASSERT(IsInnerWindow());
   Alert(EmptyString(), aSubjectPrincipal, aError);
 }
 
 void
 nsGlobalWindow::AlertOuter(const nsAString& aMessage,
-                           const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                           nsIPrincipal& aSubjectPrincipal,
                            ErrorResult& aError)
 {
   MOZ_RELEASE_ASSERT(IsOuterWindow());
   AlertOrConfirm(/* aAlert = */ true, aMessage, aSubjectPrincipal, aError);
 }
 
 void
 nsGlobalWindow::Alert(const nsAString& aMessage,
-                      const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                      nsIPrincipal& aSubjectPrincipal,
                       ErrorResult& aError)
 {
   FORWARD_TO_OUTER_OR_THROW(AlertOuter, (aMessage, aSubjectPrincipal, aError),
                             aError, );
 }
 
 bool
 nsGlobalWindow::ConfirmOuter(const nsAString& aMessage,
-                             const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                             nsIPrincipal& aSubjectPrincipal,
                              ErrorResult& aError)
 {
   MOZ_RELEASE_ASSERT(IsOuterWindow());
 
   return AlertOrConfirm(/* aAlert = */ false, aMessage, aSubjectPrincipal,
                         aError);
 }
 
 bool
 nsGlobalWindow::Confirm(const nsAString& aMessage,
-                        const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                        nsIPrincipal& aSubjectPrincipal,
                         ErrorResult& aError)
 {
   FORWARD_TO_OUTER_OR_THROW(ConfirmOuter, (aMessage, aSubjectPrincipal, aError),
                             aError, false);
 }
 
 already_AddRefed<Promise>
 nsGlobalWindow::Fetch(const RequestOrUSVString& aInput,
@@ -6946,23 +6945,22 @@ nsGlobalWindow::Fetch(const RequestOrUSV
 {
   return FetchRequest(this, aInput, aInit, aRv);
 }
 
 void
 nsGlobalWindow::PromptOuter(const nsAString& aMessage,
                             const nsAString& aInitial,
                             nsAString& aReturn,
-                            const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                            nsIPrincipal& aSubjectPrincipal,
                             ErrorResult& aError)
 {
   // XXX This method is very similar to nsGlobalWindow::AlertOrConfirm, make
   // sure any modifications here don't need to happen over there!
   MOZ_RELEASE_ASSERT(IsOuterWindow());
-  MOZ_ASSERT(aSubjectPrincipal.isSome());
 
   SetDOMStringToNull(aReturn);
 
   if (!AreDialogsEnabled()) {
     // Return null, as if the user just canceled the prompt.
     return;
   }
 
@@ -6971,17 +6969,17 @@ nsGlobalWindow::PromptOuter(const nsAStr
   // the whole time a modal dialog is open.
   nsAutoPopupStatePusher popupStatePusher(openAbused, true);
 
   // Before bringing up the window, unsuppress painting and flush
   // pending reflows.
   EnsureReflowFlushAndPaint();
 
   nsAutoString title;
-  MakeScriptDialogTitle(title, aSubjectPrincipal.value());
+  MakeScriptDialogTitle(title, &aSubjectPrincipal);
 
   // Remove non-terminating null characters from the
   // string. See bug #310037.
   nsAutoString fixedMessage, fixedInitial;
   nsContentUtils::StripNullChars(aMessage, fixedMessage);
   nsContentUtils::StripNullChars(aInitial, fixedInitial);
 
   nsresult rv;
@@ -7032,17 +7030,17 @@ nsGlobalWindow::PromptOuter(const nsAStr
   if (ok && outValue) {
     aReturn.Assign(outValue);
   }
 }
 
 void
 nsGlobalWindow::Prompt(const nsAString& aMessage, const nsAString& aInitial,
                        nsAString& aReturn,
-                       const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                       nsIPrincipal& aSubjectPrincipal,
                        ErrorResult& aError)
 {
   FORWARD_TO_OUTER_OR_THROW(PromptOuter,
                             (aMessage, aInitial, aReturn, aSubjectPrincipal,
                              aError),
                             aError, );
 }
 
@@ -8278,21 +8276,20 @@ nsGlobalWindow::CallerInnerWindow()
   nsCOMPtr<nsPIDOMWindowInner> win = do_QueryInterface(global);
   return nsGlobalWindow::Cast(win);
 }
 
 void
 nsGlobalWindow::PostMessageMozOuter(JSContext* aCx, JS::Handle<JS::Value> aMessage,
                                     const nsAString& aTargetOrigin,
                                     JS::Handle<JS::Value> aTransfer,
-                                    const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                                    nsIPrincipal& aSubjectPrincipal,
                                     ErrorResult& aError)
 {
   MOZ_RELEASE_ASSERT(IsOuterWindow());
-  MOZ_ASSERT(aSubjectPrincipal.isSome());
 
   //
   // Window.postMessage is an intentional subversion of the same-origin policy.
   // As such, this code must be particularly careful in the information it
   // exposes to calling code.
   //
   // http://www.whatwg.org/specs/web-apps/current-work/multipage/section-crossDocumentMessages.html
   //
@@ -8365,17 +8362,17 @@ nsGlobalWindow::PostMessageMozOuter(JSCo
     }
 
     if (NS_FAILED(originURI->SetUserPass(EmptyCString())) ||
         NS_FAILED(originURI->SetPath(EmptyCString()))) {
       return;
     }
 
     PrincipalOriginAttributes attrs =
-      BasePrincipal::Cast(aSubjectPrincipal.value())->OriginAttributesRef();
+      BasePrincipal::Cast(&aSubjectPrincipal)->OriginAttributesRef();
     // Create a nsIPrincipal inheriting the app/browser attributes from the
     // caller.
     providedPrincipal = BasePrincipal::CreateCodebasePrincipal(originURI, attrs);
     if (NS_WARN_IF(!providedPrincipal)) {
       return;
     }
   }
 
@@ -8403,30 +8400,30 @@ nsGlobalWindow::PostMessageMozOuter(JSCo
 
   aError = NS_DispatchToCurrentThread(event);
 }
 
 void
 nsGlobalWindow::PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage,
                                const nsAString& aTargetOrigin,
                                JS::Handle<JS::Value> aTransfer,
-                               const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                               nsIPrincipal& aSubjectPrincipal,
                                ErrorResult& aError)
 {
   FORWARD_TO_OUTER_OR_THROW(PostMessageMozOuter,
                             (aCx, aMessage, aTargetOrigin, aTransfer,
                              aSubjectPrincipal, aError),
                             aError, );
 }
 
 void
 nsGlobalWindow::PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage,
                                const nsAString& aTargetOrigin,
                                const Optional<Sequence<JS::Value>>& aTransfer,
-                               const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                               nsIPrincipal& aSubjectPrincipal,
                                ErrorResult& aError)
 {
   JS::Rooted<JS::Value> transferArray(aCx, JS::UndefinedValue());
   if (aTransfer.WasPassed()) {
     const Sequence<JS::Value >& values = aTransfer.Value();
 
     // The input sequence only comes from the generated bindings code, which
     // ensures it is rooted.
@@ -9044,41 +9041,39 @@ nsGlobalWindow::CacheXBLPrototypeHandler
     mCachedXBLPrototypeHandlers = new nsJSThingHashtable<nsPtrHashKey<nsXBLPrototypeHandler>, JSObject*>();
     PreserveWrapper(ToSupports(this));
   }
 
   mCachedXBLPrototypeHandlers->Put(aKey, aHandler);
 }
 
 Element*
-nsGlobalWindow::GetFrameElementOuter(const Maybe<nsIPrincipal*>& aSubjectPrincipal)
+nsGlobalWindow::GetFrameElementOuter(nsIPrincipal& aSubjectPrincipal)
 {
   MOZ_RELEASE_ASSERT(IsOuterWindow());
-  MOZ_ASSERT(aSubjectPrincipal.isSome());
 
   if (!mDocShell || mDocShell->GetIsMozBrowserOrApp()) {
     return nullptr;
   }
 
   // Per HTML5, the frameElement getter returns null in cross-origin situations.
   Element* element = GetRealFrameElementOuter();
   if (!element) {
     return nullptr;
   }
 
-  if (!aSubjectPrincipal.value()->
-         SubsumesConsideringDomain(element->NodePrincipal())) {
+  if (!aSubjectPrincipal.SubsumesConsideringDomain(element->NodePrincipal())) {
     return nullptr;
   }
 
   return element;
 }
 
 Element*
-nsGlobalWindow::GetFrameElement(const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+nsGlobalWindow::GetFrameElement(nsIPrincipal& aSubjectPrincipal,
                                 ErrorResult& aError)
 {
   FORWARD_TO_OUTER_OR_THROW(GetFrameElementOuter, (aSubjectPrincipal), aError,
                             nullptr);
 }
 
 Element*
 nsGlobalWindow::GetRealFrameElementOuter()
@@ -9247,33 +9242,32 @@ nsGlobalWindow::ConvertDialogOptions(con
     }
   }
 }
 
 already_AddRefed<nsIVariant>
 nsGlobalWindow::ShowModalDialogOuter(const nsAString& aUrl,
                                      nsIVariant* aArgument,
                                      const nsAString& aOptions,
-                                     const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                                     nsIPrincipal& aSubjectPrincipal,
                                      ErrorResult& aError)
 {
   MOZ_RELEASE_ASSERT(IsOuterWindow());
-  MOZ_ASSERT(aSubjectPrincipal.isSome());
 
   if (mDoc) {
     mDoc->WarnOnceAbout(nsIDocument::eShowModalDialog);
   }
 
   if (!IsShowModalDialogEnabled()) {
     aError.Throw(NS_ERROR_NOT_AVAILABLE);
     return nullptr;
   }
 
   RefPtr<DialogValueHolder> argHolder =
-    new DialogValueHolder(aSubjectPrincipal.value(), aArgument);
+    new DialogValueHolder(&aSubjectPrincipal, aArgument);
 
   // Before bringing up the window/dialog, unsuppress painting and flush
   // pending reflows.
   EnsureReflowFlushAndPaint();
 
   if (!AreDialogsEnabled()) {
     // We probably want to keep throwing here; silently doing nothing is a bit
     // weird given the typical use cases of showModalDialog().
@@ -9320,30 +9314,30 @@ nsGlobalWindow::ShowModalDialogOuter(con
   MOZ_ASSERT(!aError.Failed());
 
   return retVal.forget();
 }
 
 already_AddRefed<nsIVariant>
 nsGlobalWindow::ShowModalDialog(const nsAString& aUrl, nsIVariant* aArgument,
                                 const nsAString& aOptions,
-                                const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                                nsIPrincipal& aSubjectPrincipal,
                                 ErrorResult& aError)
 {
   FORWARD_TO_OUTER_OR_THROW(ShowModalDialogOuter,
                             (aUrl, aArgument, aOptions, aSubjectPrincipal,
                              aError), aError, nullptr);
 }
 
 void
 nsGlobalWindow::ShowModalDialog(JSContext* aCx, const nsAString& aUrl,
                                 JS::Handle<JS::Value> aArgument,
                                 const nsAString& aOptions,
                                 JS::MutableHandle<JS::Value> aRetval,
-                                const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                                nsIPrincipal& aSubjectPrincipal,
                                 ErrorResult& aError)
 {
   MOZ_ASSERT(IsInnerWindow());
 
   nsCOMPtr<nsIVariant> args;
   aError = nsContentUtils::XPConnect()->JSToVariant(aCx,
                                                     aArgument,
                                                     getter_AddRefs(args));
@@ -10582,27 +10576,27 @@ nsGlobalWindow::GetSessionStorage(ErrorR
   if (MOZ_LOG_TEST(gDOMLeakPRLog, LogLevel::Debug)) {
     PR_LogPrint("nsGlobalWindow %p returns %p sessionStorage", this, mSessionStorage.get());
   }
 
   return mSessionStorage;
 }
 
 DOMStorage*
-nsGlobalWindow::GetLocalStorage(const Maybe<nsIPrincipal*>& aSubjectPrincipal,
-                                ErrorResult& aError)
+nsGlobalWindow::GetLocalStorage(ErrorResult& aError)
 {
   MOZ_RELEASE_ASSERT(IsInnerWindow());
 
   if (!Preferences::GetBool(kStorageEnabled)) {
     return nullptr;
   }
 
   if (!mLocalStorage) {
-    if (!DOMStorage::CanUseStorage(AsInner(), aSubjectPrincipal)) {
+    if (nsContentUtils::StorageAllowedForWindow(AsInner()) ==
+          nsContentUtils::StorageAccess::eDeny) {
       aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
       return nullptr;
     }
 
     nsIPrincipal *principal = GetPrincipal();
     if (!principal) {
       return nullptr;
     }
@@ -11658,20 +11652,17 @@ nsGlobalWindow::CloneStorageEvent(const 
   aEvent->GetNewValue(dict.mNewValue);
   aEvent->GetUrl(dict.mUrl);
 
   RefPtr<DOMStorage> storageArea = aEvent->GetStorageArea();
   MOZ_ASSERT(storageArea);
 
   RefPtr<DOMStorage> storage;
   if (storageArea->GetType() == DOMStorage::LocalStorage) {
-    storage = GetLocalStorage(nsContentUtils::GetCurrentJSContext()
-                                ? Some(nsContentUtils::SubjectPrincipal())
-                                : Nothing(),
-                              aRv);
+    storage = GetLocalStorage(aRv);
   } else {
     MOZ_ASSERT(storageArea->GetType() == DOMStorage::SessionStorage);
     storage = GetSessionStorage(aRv);
   }
 
   if (aRv.Failed() || !storage) {
     return nullptr;
   }
@@ -13996,42 +13987,40 @@ NS_INTERFACE_MAP_END_INHERITING(nsGlobal
 
 NS_IMPL_ADDREF_INHERITED(nsGlobalModalWindow, nsGlobalWindow)
 NS_IMPL_RELEASE_INHERITED(nsGlobalModalWindow, nsGlobalWindow)
 
 
 void
 nsGlobalWindow::GetDialogArgumentsOuter(JSContext* aCx,
                                         JS::MutableHandle<JS::Value> aRetval,
-                                        const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                                        nsIPrincipal& aSubjectPrincipal,
                                         ErrorResult& aError)
 {
   MOZ_RELEASE_ASSERT(IsOuterWindow());
-  MOZ_ASSERT(aSubjectPrincipal.isSome());
   MOZ_ASSERT(IsModalContentWindow(),
              "This should only be called on modal windows!");
 
   if (!mDialogArguments) {
     MOZ_ASSERT(mIsClosed, "This window should be closed!");
     aRetval.setUndefined();
     return;
   }
 
   // This does an internal origin check, and returns undefined if the subject
   // does not subsumes the origin of the arguments.
   JS::Rooted<JSObject*> wrapper(aCx, GetWrapper());
   JSAutoCompartment ac(aCx, wrapper);
-  mDialogArguments->Get(aCx, wrapper, aSubjectPrincipal.value(),
-                        aRetval, aError);
+  mDialogArguments->Get(aCx, wrapper, &aSubjectPrincipal, aRetval, aError);
 }
 
 void
 nsGlobalWindow::GetDialogArguments(JSContext* aCx,
                                    JS::MutableHandle<JS::Value> aRetval,
-                                   const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                                   nsIPrincipal& aSubjectPrincipal,
                                    ErrorResult& aError)
 {
   FORWARD_TO_OUTER_OR_THROW(GetDialogArgumentsOuter,
                             (aCx, aRetval, aSubjectPrincipal, aError),
                             aError, );
 }
 
 /* static */ already_AddRefed<nsGlobalModalWindow>
@@ -14065,38 +14054,36 @@ void
 nsGlobalWindow::InitWasOffline()
 {
   mWasOffline = NS_IsOffline() || NS_IsAppOffline(GetPrincipal());
 }
 
 void
 nsGlobalWindow::GetReturnValueOuter(JSContext* aCx,
                                     JS::MutableHandle<JS::Value> aReturnValue,
-                                    const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                                    nsIPrincipal& aSubjectPrincipal,
                                     ErrorResult& aError)
 {
   MOZ_RELEASE_ASSERT(IsOuterWindow());
-  MOZ_ASSERT(aSubjectPrincipal.isSome());
   MOZ_ASSERT(IsModalContentWindow(),
              "This should only be called on modal windows!");
 
   if (mReturnValue) {
     JS::Rooted<JSObject*> wrapper(aCx, GetWrapper());
     JSAutoCompartment ac(aCx, wrapper);
-    mReturnValue->Get(aCx, wrapper, aSubjectPrincipal.value(),
-                      aReturnValue, aError);
+    mReturnValue->Get(aCx, wrapper, &aSubjectPrincipal, aReturnValue, aError);
   } else {
     aReturnValue.setUndefined();
   }
 }
 
 void
 nsGlobalWindow::GetReturnValue(JSContext* aCx,
                                JS::MutableHandle<JS::Value> aReturnValue,
-                               const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                               nsIPrincipal& aSubjectPrincipal,
                                ErrorResult& aError)
 {
   FORWARD_TO_OUTER_OR_THROW(GetReturnValueOuter,
                             (aCx, aReturnValue, aSubjectPrincipal, aError),
                             aError, );
 }
 
 NS_IMETHODIMP
@@ -14110,38 +14097,36 @@ nsGlobalModalWindow::GetReturnValue(nsIV
     return NS_OK;
   }
   return mReturnValue->Get(nsContentUtils::SubjectPrincipal(), aRetVal);
 }
 
 void
 nsGlobalWindow::SetReturnValueOuter(JSContext* aCx,
                                     JS::Handle<JS::Value> aReturnValue,
-                                    const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                                    nsIPrincipal& aSubjectPrincipal,
                                     ErrorResult& aError)
 {
   MOZ_RELEASE_ASSERT(IsOuterWindow());
-  MOZ_ASSERT(aSubjectPrincipal.isSome());
   MOZ_ASSERT(IsModalContentWindow(),
              "This should only be called on modal windows!");
 
   nsCOMPtr<nsIVariant> returnValue;
   aError =
     nsContentUtils::XPConnect()->JSToVariant(aCx, aReturnValue,
                                              getter_AddRefs(returnValue));
   if (!aError.Failed()) {
-    mReturnValue = new DialogValueHolder(aSubjectPrincipal.value(),
-                                         returnValue);
+    mReturnValue = new DialogValueHolder(&aSubjectPrincipal, returnValue);
   }
 }
 
 void
 nsGlobalWindow::SetReturnValue(JSContext* aCx,
                                JS::Handle<JS::Value> aReturnValue,
-                               const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                               nsIPrincipal& aSubjectPrincipal,
                                ErrorResult& aError)
 {
   FORWARD_TO_OUTER_OR_THROW(SetReturnValueOuter,
                             (aCx, aReturnValue, aSubjectPrincipal, aError),
                             aError, );
 }
 
 NS_IMETHODIMP
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -932,19 +932,19 @@ public:
   void SetOpener(JSContext* aCx, JS::Handle<JS::Value> aOpener,
                  mozilla::ErrorResult& aError);
   already_AddRefed<nsPIDOMWindowOuter> GetParentOuter();
   already_AddRefed<nsPIDOMWindowOuter> GetParent(mozilla::ErrorResult& aError);
   already_AddRefed<nsPIDOMWindowOuter> GetParent() override;
   nsPIDOMWindowOuter* GetScriptableParent() override;
   nsPIDOMWindowOuter* GetScriptableParentOrNull() override;
   mozilla::dom::Element*
-  GetFrameElementOuter(const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal);
+  GetFrameElementOuter(nsIPrincipal& aSubjectPrincipal);
   mozilla::dom::Element*
-  GetFrameElement(const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal,
+  GetFrameElement(nsIPrincipal& aSubjectPrincipal,
                   mozilla::ErrorResult& aError);
   already_AddRefed<nsIDOMElement> GetFrameElement() override;
   already_AddRefed<nsPIDOMWindowOuter>
   OpenOuter(const nsAString& aUrl,
             const nsAString& aName,
             const nsAString& aOptions,
             mozilla::ErrorResult& aError);
   already_AddRefed<nsPIDOMWindowOuter>
@@ -977,58 +977,58 @@ public:
   static bool
   TokenizeDialogOptions(nsAString& aToken, nsAString::const_iterator& aIter,
                         nsAString::const_iterator aEnd);
   static void
   ConvertDialogOptions(const nsAString& aOptions, nsAString& aResult);
 
 protected:
   bool AlertOrConfirm(bool aAlert, const nsAString& aMessage,
-                      const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                      nsIPrincipal& aSubjectPrincipal,
                       mozilla::ErrorResult& aError);
 
 public:
-  void Alert(const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal,
+  void Alert(nsIPrincipal& aSubjectPrincipal,
              mozilla::ErrorResult& aError);
   void AlertOuter(const nsAString& aMessage,
-                  const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                  nsIPrincipal& aSubjectPrincipal,
                   mozilla::ErrorResult& aError);
   void Alert(const nsAString& aMessage,
-             const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal,
+             nsIPrincipal& aSubjectPrincipal,
              mozilla::ErrorResult& aError);
   bool ConfirmOuter(const nsAString& aMessage,
-                    const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                    nsIPrincipal& aSubjectPrincipal,
                     mozilla::ErrorResult& aError);
   bool Confirm(const nsAString& aMessage,
-               const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal,
+               nsIPrincipal& aSubjectPrincipal,
                mozilla::ErrorResult& aError);
   void PromptOuter(const nsAString& aMessage, const nsAString& aInitial,
                    nsAString& aReturn,
-                   const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                   nsIPrincipal& aSubjectPrincipal,
                    mozilla::ErrorResult& aError);
   void Prompt(const nsAString& aMessage, const nsAString& aInitial,
               nsAString& aReturn,
-              const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal,
+              nsIPrincipal& aSubjectPrincipal,
               mozilla::ErrorResult& aError);
   already_AddRefed<mozilla::dom::cache::CacheStorage> GetCaches(mozilla::ErrorResult& aRv);
   already_AddRefed<mozilla::dom::Promise> Fetch(const mozilla::dom::RequestOrUSVString& aInput,
                                                 const mozilla::dom::RequestInit& aInit,
                                                 mozilla::ErrorResult& aRv);
   void PrintOuter(mozilla::ErrorResult& aError);
   void Print(mozilla::ErrorResult& aError);
   void ShowModalDialog(JSContext* aCx, const nsAString& aUrl,
                        JS::Handle<JS::Value> aArgument,
                        const nsAString& aOptions,
                        JS::MutableHandle<JS::Value> aRetval,
-                       const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                       nsIPrincipal& aSubjectPrincipal,
                        mozilla::ErrorResult& aError);
   void PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage,
                       const nsAString& aTargetOrigin,
                       const mozilla::dom::Optional<mozilla::dom::Sequence<JS::Value > >& aTransfer,
-                      const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                      nsIPrincipal& aSubjectPrincipal,
                       mozilla::ErrorResult& aError);
   int32_t SetTimeout(JSContext* aCx, mozilla::dom::Function& aFunction,
                      int32_t aTimeout,
                      const mozilla::dom::Sequence<JS::Value>& aArguments,
                      mozilla::ErrorResult& aError);
   int32_t SetTimeout(JSContext* aCx, const nsAString& aHandler,
                      int32_t aTimeout,
                      const mozilla::dom::Sequence<JS::Value>& /* unused */,
@@ -1044,18 +1044,17 @@ public:
                       mozilla::ErrorResult& aError);
   void ClearInterval(int32_t aHandle);
   void Atob(const nsAString& aAsciiBase64String, nsAString& aBinaryData,
             mozilla::ErrorResult& aError);
   void Btoa(const nsAString& aBinaryData, nsAString& aAsciiBase64String,
             mozilla::ErrorResult& aError);
   mozilla::dom::DOMStorage* GetSessionStorage(mozilla::ErrorResult& aError);
   mozilla::dom::DOMStorage*
-  GetLocalStorage(const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal,
-                  mozilla::ErrorResult& aError);
+  GetLocalStorage(mozilla::ErrorResult& aError);
   mozilla::dom::Selection* GetSelectionOuter();
   mozilla::dom::Selection* GetSelection(mozilla::ErrorResult& aError);
   already_AddRefed<nsISelection> GetSelection() override;
   mozilla::dom::IDBFactory* GetIndexedDB(mozilla::ErrorResult& aError);
   already_AddRefed<nsICSSDeclaration>
     GetComputedStyle(mozilla::dom::Element& aElt, const nsAString& aPseudoElt,
                      mozilla::ErrorResult& aError) override;
   already_AddRefed<mozilla::dom::MediaQueryList> MatchMediaOuter(const nsAString& aQuery);
@@ -1265,32 +1264,32 @@ public:
   nsIMessageBroadcaster* GetMessageManager(mozilla::ErrorResult& aError);
   nsIMessageBroadcaster* GetGroupMessageManager(const nsAString& aGroup,
                                                 mozilla::ErrorResult& aError);
   void BeginWindowMove(mozilla::dom::Event& aMouseDownEvent,
                        mozilla::dom::Element* aPanel,
                        mozilla::ErrorResult& aError);
 
   void GetDialogArgumentsOuter(JSContext* aCx, JS::MutableHandle<JS::Value> aRetval,
-                               const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                               nsIPrincipal& aSubjectPrincipal,
                                mozilla::ErrorResult& aError);
   void GetDialogArguments(JSContext* aCx, JS::MutableHandle<JS::Value> aRetval,
-                          const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                          nsIPrincipal& aSubjectPrincipal,
                           mozilla::ErrorResult& aError);
   void GetReturnValueOuter(JSContext* aCx, JS::MutableHandle<JS::Value> aReturnValue,
-                           const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                           nsIPrincipal& aSubjectPrincipal,
                            mozilla::ErrorResult& aError);
   void GetReturnValue(JSContext* aCx, JS::MutableHandle<JS::Value> aReturnValue,
-                      const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                      nsIPrincipal& aSubjectPrincipal,
                       mozilla::ErrorResult& aError);
   void SetReturnValueOuter(JSContext* aCx, JS::Handle<JS::Value> aReturnValue,
-                           const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                           nsIPrincipal& aSubjectPrincipal,
                            mozilla::ErrorResult& aError);
   void SetReturnValue(JSContext* aCx, JS::Handle<JS::Value> aReturnValue,
-                      const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                      nsIPrincipal& aSubjectPrincipal,
                       mozilla::ErrorResult& aError);
 
   void GetInterface(JSContext* aCx, nsIJSID* aIID,
                     JS::MutableHandle<JS::Value> aRetval,
                     mozilla::ErrorResult& aError);
 
   already_AddRefed<nsWindowRoot> GetWindowRootOuter();
   already_AddRefed<nsWindowRoot> GetWindowRoot(mozilla::ErrorResult& aError);
@@ -1703,34 +1702,34 @@ protected:
   // Returns CSS pixels based on primary screen.  Outer windows only.
   mozilla::CSSIntPoint GetScreenXY(mozilla::ErrorResult& aError);
 
   nsGlobalWindow* InnerForSetTimeoutOrInterval(mozilla::ErrorResult& aError);
 
   void PostMessageMozOuter(JSContext* aCx, JS::Handle<JS::Value> aMessage,
                            const nsAString& aTargetOrigin,
                            JS::Handle<JS::Value> aTransfer,
-                           const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                           nsIPrincipal& aSubjectPrincipal,
                            mozilla::ErrorResult& aError);
   void PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage,
                       const nsAString& aTargetOrigin,
                       JS::Handle<JS::Value> aTransfer,
-                      const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                      nsIPrincipal& aSubjectPrincipal,
                       mozilla::ErrorResult& aError);
 
   already_AddRefed<nsIVariant>
     ShowModalDialogOuter(const nsAString& aUrl, nsIVariant* aArgument,
                          const nsAString& aOptions,
-                         const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                         nsIPrincipal& aSubjectPrincipal,
                          mozilla::ErrorResult& aError);
 
   already_AddRefed<nsIVariant>
     ShowModalDialog(const nsAString& aUrl, nsIVariant* aArgument,
                     const nsAString& aOptions,
-                    const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                    nsIPrincipal& aSubjectPrincipal,
                     mozilla::ErrorResult& aError);
 
   // Ask the user if further dialogs should be blocked, if dialogs are currently
   // being abused. This is used in the cases where we have no modifiable UI to
   // show, in that case we show a separate dialog to ask this question.
   bool ConfirmDialogIfNeeded();
 
   // Helper called after moving/resizing, to update docShell's presContext
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -2683,16 +2683,19 @@ public:
 
   // QuerySelector and QuerySelectorAll already defined on nsINode
   nsINodeList* GetAnonymousNodes(Element& aElement);
   Element* GetAnonymousElementByAttribute(Element& aElement,
                                           const nsAString& aAttrName,
                                           const nsAString& aAttrValue);
   Element* GetBindingParent(nsINode& aNode);
   void LoadBindingDocument(const nsAString& aURI,
+                           nsIPrincipal& aSubjectPrincipal,
+                           mozilla::ErrorResult& rv);
+  void LoadBindingDocument(const nsAString& aURI,
                            const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal,
                            mozilla::ErrorResult& rv);
   mozilla::dom::XPathExpression*
     CreateExpression(const nsAString& aExpression,
                      mozilla::dom::XPathNSResolver* aResolver,
                      mozilla::ErrorResult& rv);
   nsINode* CreateNSResolver(nsINode& aNodeResolver);
   already_AddRefed<mozilla::dom::XPathResult>
--- a/dom/base/nsObjectLoadingContent.cpp
+++ b/dom/base/nsObjectLoadingContent.cpp
@@ -3532,34 +3532,32 @@ nsObjectLoadingContent::ShouldPlay(Fallb
     return true;
   case nsIPluginTag::STATE_CLICKTOPLAY:
     return false;
   }
   MOZ_CRASH("Unexpected enabledState");
 }
 
 nsIDocument*
-nsObjectLoadingContent::GetContentDocument(const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal)
+nsObjectLoadingContent::GetContentDocument(nsIPrincipal& aSubjectPrincipal)
 {
-  MOZ_ASSERT(aSubjectPrincipal.isSome());
-
   nsCOMPtr<nsIContent> thisContent =
     do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
 
   if (!thisContent->IsInComposedDoc()) {
     return nullptr;
   }
 
   nsIDocument *sub_doc = thisContent->OwnerDoc()->GetSubDocumentFor(thisContent);
   if (!sub_doc) {
     return nullptr;
   }
 
   // Return null for cross-origin contentDocument.
-  if (!aSubjectPrincipal.value()->SubsumesConsideringDomain(sub_doc->NodePrincipal())) {
+  if (!aSubjectPrincipal.SubsumesConsideringDomain(sub_doc->NodePrincipal())) {
     return nullptr;
   }
 
   return sub_doc;
 }
 
 void
 nsObjectLoadingContent::LegacyCall(JSContext* aCx,
--- a/dom/base/nsObjectLoadingContent.h
+++ b/dom/base/nsObjectLoadingContent.h
@@ -175,17 +175,17 @@ class nsObjectLoadingContent : public ns
     // id.  If in doubt, return true.
     static bool MayResolve(jsid aId);
 
     // Helper for WebIDL enumeration
     void GetOwnPropertyNames(JSContext* aCx, nsTArray<nsString>& /* unused */,
                              mozilla::ErrorResult& aRv);
 
     // WebIDL API
-    nsIDocument* GetContentDocument(const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal);
+    nsIDocument* GetContentDocument(nsIPrincipal& aSubjectPrincipal);
     void GetActualType(nsAString& aType) const
     {
       CopyUTF8toUTF16(mContentType, aType);
     }
     uint32_t DisplayedType() const
     {
       return mType;
     }
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -6902,23 +6902,23 @@ class CGCallGenerator(CGThing):
     isFallible is a boolean indicating whether the call should be fallible.
 
     resultVar: If the returnType is not void, then the result of the call is
     stored in a C++ variable named by resultVar. The caller is responsible for
     declaring the result variable. If the caller doesn't care about the result
     value, resultVar can be omitted.
     """
     def __init__(self, isFallible, needsSubjectPrincipal, arguments, argsPre,
-                 returnType, extendedAttributes, descriptorProvider,
+                 returnType, extendedAttributes, descriptor,
                  nativeMethodName, static, object="self", argsPost=[],
                  resultVar=None):
         CGThing.__init__(self)
 
         result, resultOutParam, resultRooter, resultArgs, resultConversion = \
-            getRetvalDeclarationForType(returnType, descriptorProvider)
+            getRetvalDeclarationForType(returnType, descriptor)
 
         args = CGList([CGGeneric(arg) for arg in argsPre], ", ")
         for a, name in arguments:
             arg = CGGeneric(name)
 
             # Now constify the things that need it
             def needsConst(a):
                 if a.type.isDictionary():
@@ -7003,26 +7003,42 @@ class CGCallGenerator(CGThing):
         elif result is not None:
             assert resultOutParam is None
             call = CGWrapper(call, pre=resultVar + " = ")
 
         call = CGWrapper(call, post=";\n")
         self.cgRoot.append(call)
 
         if needsSubjectPrincipal:
-            self.cgRoot.prepend(CGGeneric(dedent(
-               """
-               Maybe<nsIPrincipal*> subjectPrincipal;
-               if (NS_IsMainThread()) {
-                 JSCompartment* compartment = js::GetContextCompartment(cx);
-                 MOZ_ASSERT(compartment);
-                 JSPrincipals* principals = JS_GetCompartmentPrincipals(compartment);
-                 subjectPrincipal.emplace(nsJSPrincipals::get(principals));
-               }
-               """)))
+            getPrincipal = dedent(
+                """
+                JSCompartment* compartment = js::GetContextCompartment(cx);
+                MOZ_ASSERT(compartment);
+                JSPrincipals* principals = JS_GetCompartmentPrincipals(compartment);
+                """)
+
+            if descriptor.interface.isExposedInAnyWorker():
+                self.cgRoot.prepend(CGGeneric(fill(
+                    """
+                    Maybe<nsIPrincipal*> subjectPrincipal;
+                    if (NS_IsMainThread()) {
+                      $*{getPrincipal}
+                      subjectPrincipal.emplace(nsJSPrincipals::get(principals));
+                    }
+                    """,
+                    getPrincipal=getPrincipal)))
+            else:
+                self.cgRoot.prepend(CGGeneric(fill(
+                    """
+                    $*{getPrincipal}
+                    // Initializing a nonnull is pretty darn annoying...
+                    NonNull<nsIPrincipal> subjectPrincipal;
+                    subjectPrincipal = static_cast<nsIPrincipal*>(nsJSPrincipals::get(principals));
+                    """,
+                    getPrincipal=getPrincipal)))
 
         if isFallible:
             self.cgRoot.prepend(CGGeneric("binding_detail::FastErrorResult rv;\n"))
             self.cgRoot.append(CGGeneric(dedent(
                 """
                 if (MOZ_UNLIKELY(rv.MaybeSetPendingException(cx))) {
                   return false;
                 }
@@ -13988,17 +14004,17 @@ class CGNativeMember(ClassMethod):
                                  "aRetVal"))
         elif returnType.isAny():
             args.append(Argument("JS::MutableHandle<JS::Value>", "aRetVal"))
         elif returnType.isObject() or returnType.isSpiderMonkeyInterface():
             args.append(Argument("JS::MutableHandle<JSObject*>", "aRetVal"))
 
         # And the nsIPrincipal
         if self.member.getExtendedAttribute('NeedsSubjectPrincipal'):
-            args.append(Argument("const Maybe<nsIPrincipal*>&", "aPrincipal"))
+            args.append(Argument("nsIPrincipal&", "aPrincipal"))
         # And the ErrorResult
         if 'infallible' not in self.extendedAttrs:
             # Use aRv so it won't conflict with local vars named "rv"
             args.append(Argument("ErrorResult&", "aRv"))
         # The legacycaller thisval
         if self.member.isMethod() and self.member.isLegacycaller():
             # If it has an identifier, we can't deal with it yet
             assert self.member.isIdentifierLess()
--- a/dom/bindings/test/TestBindingHeader.h
+++ b/dom/bindings/test/TestBindingHeader.h
@@ -928,16 +928,19 @@ public:
   void SetJsonifierShouldSkipThis3(TestCallbackInterface&);
   void ThrowingMethod(ErrorResult& aRv);
   bool GetThrowingAttr(ErrorResult& aRv) const;
   void SetThrowingAttr(bool arg, ErrorResult& aRv);
   bool GetThrowingGetterAttr(ErrorResult& aRv) const;
   void SetThrowingGetterAttr(bool arg);
   bool ThrowingSetterAttr() const;
   void SetThrowingSetterAttr(bool arg, ErrorResult& aRv);
+  void NeedsSubjectPrincipalMethod(nsIPrincipal&);
+  bool NeedsSubjectPrincipalAttr(nsIPrincipal&);
+  void SetNeedsSubjectPrincipalAttr(bool, nsIPrincipal&);
   int16_t LegacyCall(const JS::Value&, uint32_t, TestInterface&);
   void PassArgsWithDefaults(JSContext*, const Optional<int32_t>&,
                             TestInterface*, const Dict&, double,
                             const Optional<float>&);
 
   void SetDashed_attribute(int8_t);
   int8_t Dashed_attribute();
   void Dashed_method();
--- a/dom/bindings/test/TestCodeGen.webidl
+++ b/dom/bindings/test/TestCodeGen.webidl
@@ -938,16 +938,18 @@ interface TestInterface {
   void passRenamedInterface(TestRenamedInterface arg);
   [PutForwards=writableByte] readonly attribute TestInterface putForwardsAttr;
   [PutForwards=writableByte, LenientThis] readonly attribute TestInterface putForwardsAttr2;
   [PutForwards=writableByte, ChromeOnly] readonly attribute TestInterface putForwardsAttr3;
   [Throws] void throwingMethod();
   [Throws] attribute boolean throwingAttr;
   [GetterThrows] attribute boolean throwingGetterAttr;
   [SetterThrows] attribute boolean throwingSetterAttr;
+  [NeedsSubjectPrincipal] void needsSubjectPrincipalMethod();
+  [NeedsSubjectPrincipal] attribute boolean needsSubjectPrincipalAttr;
   legacycaller short(unsigned long arg1, TestInterface arg2);
   void passArgsWithDefaults(optional long arg1,
                             optional TestInterface? arg2 = null,
                             optional Dict arg3, optional double arg4 = 5.0,
                             optional float arg5);
 
   attribute any jsonifierShouldSkipThis;
   attribute TestParentInterface jsonifierShouldSkipThis2;
--- a/dom/bindings/test/TestJSImplGen.webidl
+++ b/dom/bindings/test/TestJSImplGen.webidl
@@ -789,16 +789,20 @@ interface TestJSImplInterface {
   void passRenamedInterface(TestRenamedInterface arg);
   [PutForwards=writableByte] readonly attribute TestJSImplInterface putForwardsAttr;
   [PutForwards=writableByte, LenientThis] readonly attribute TestJSImplInterface putForwardsAttr2;
   [PutForwards=writableByte, ChromeOnly] readonly attribute TestJSImplInterface putForwardsAttr3;
   [Throws] void throwingMethod();
   [Throws] attribute boolean throwingAttr;
   [GetterThrows] attribute boolean throwingGetterAttr;
   [SetterThrows] attribute boolean throwingSetterAttr;
+  // NeedsSubjectPrincipal not supported on JS-implemented things for
+  // now, because we always pass in the caller principal anyway.
+  //  [NeedsSubjectPrincipal] void needsSubjectPrincipalMethod();
+  //  [NeedsSubjectPrincipal] attribute boolean needsSubjectPrincipalAttr;
   // legacycaller short(unsigned long arg1, TestInterface arg2);
   void passArgsWithDefaults(optional long arg1,
                             optional TestInterface? arg2 = null,
                             optional Dict arg3, optional double arg4 = 5.0,
                             optional float arg5);
   attribute any jsonifierShouldSkipThis;
   attribute TestParentInterface jsonifierShouldSkipThis2;
   attribute TestCallbackInterface jsonifierShouldSkipThis3;
--- a/dom/events/ClipboardEvent.cpp
+++ b/dom/events/ClipboardEvent.cpp
@@ -71,17 +71,17 @@ ClipboardEvent::Constructor(const Global
   if (e->mEventIsInternal) {
     InternalClipboardEvent* event = e->mEvent->AsClipboardEvent();
     if (event) {
       // Always create a clipboardData for the copy event. If this is changed to
       // support other types of events, make sure that read/write privileges are
       // checked properly within DataTransfer.
       clipboardData = new DataTransfer(ToSupports(e), eCopy, false, -1);
       clipboardData->SetData(aParam.mDataType, aParam.mData,
-                             Some(aGlobal.GetSubjectPrincipal()), aRv);
+                             *aGlobal.GetSubjectPrincipal(), aRv);
       NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
     }
   }
 
   e->InitClipboardEvent(aType, aParam.mBubbles, aParam.mCancelable,
                         clipboardData);
   e->SetTrusted(trusted);
   e->SetComposed(aParam.mComposed);
--- a/dom/events/DataTransfer.cpp
+++ b/dom/events/DataTransfer.cpp
@@ -279,21 +279,20 @@ DataTransfer::SetEffectAllowedInt(uint32
 NS_IMETHODIMP
 DataTransfer::GetMozUserCancelled(bool* aUserCancelled)
 {
   *aUserCancelled = MozUserCancelled();
   return NS_OK;
 }
 
 already_AddRefed<FileList>
-DataTransfer::GetFiles(const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+DataTransfer::GetFiles(nsIPrincipal& aSubjectPrincipal,
                        ErrorResult& aRv)
 {
-  MOZ_ASSERT(aSubjectPrincipal.isSome());
-  return mItems->Files(aSubjectPrincipal.value());
+  return mItems->Files(&aSubjectPrincipal);
 }
 
 NS_IMETHODIMP
 DataTransfer::GetFiles(nsIDOMFileList** aFileList)
 {
   if (!aFileList) {
     return NS_ERROR_FAILURE;
   }
@@ -305,93 +304,68 @@ DataTransfer::GetFiles(nsIDOMFileList** 
   // This code is also called from C++ code, which expects it to have a System
   // Principal, and thus the SubjectPrincipal cannot be used.
   RefPtr<FileList> files = mItems->Files(nsContentUtils::GetSystemPrincipal());
 
   files.forget(aFileList);
   return NS_OK;
 }
 
-already_AddRefed<DOMStringList>
-DataTransfer::GetTypes(ErrorResult& aRv) const
+void
+DataTransfer::GetTypes(nsTArray<nsString>& aTypes,
+                       nsIPrincipal& aSubjectPrincipal) const
 {
-  RefPtr<DOMStringList> types = new DOMStringList();
-
+  // When called from bindings, aTypes will be empty, but since we might have
+  // Gecko-internal callers too, clear it to be safe.
+  aTypes.Clear();
+  
   const nsTArray<RefPtr<DataTransferItem>>* items = mItems->MozItemsAt(0);
   if (NS_WARN_IF(!items)) {
-    return types.forget();
+    return;
   }
 
   for (uint32_t i = 0; i < items->Length(); i++) {
     DataTransferItem* item = items->ElementAt(i);
     MOZ_ASSERT(item);
 
-    if (item->ChromeOnly() && !nsContentUtils::LegacyIsCallerChromeOrNativeCode()) {
+    if (item->ChromeOnly() && !nsContentUtils::IsSystemPrincipal(&aSubjectPrincipal)) {
       continue;
     }
 
     nsAutoString type;
     item->GetType(type);
     if (item->Kind() == DataTransferItem::KIND_STRING || type.EqualsASCII(kFileMime)) {
       // If the entry has kind KIND_STRING, we want to add it to the list.
-      if (NS_WARN_IF(!types->Add(type))) {
-        aRv.Throw(NS_ERROR_FAILURE);
-        return nullptr;
-      }
+      aTypes.AppendElement(type);
     }
   }
 
   for (uint32_t i = 0; i < mItems->Length(); ++i) {
-    ErrorResult rv;
     bool found = false;
-    DataTransferItem* item = mItems->IndexedGetter(i, found, rv);
-    if (!found || rv.Failed() || item->Kind() != DataTransferItem::KIND_FILE) {
-      rv.SuppressException();
+    DataTransferItem* item = mItems->IndexedGetter(i, found);
+    MOZ_ASSERT(found);
+    if (item->Kind() != DataTransferItem::KIND_FILE) {
       continue;
     }
-    if (NS_WARN_IF(!types->Add(NS_LITERAL_STRING("Files")))) {
-      aRv.Throw(NS_ERROR_FAILURE);
-      return nullptr;
-    }
+    aTypes.AppendElement(NS_LITERAL_STRING("Files"));
     break;
   }
-
-  return types.forget();
-}
-
-NS_IMETHODIMP
-DataTransfer::GetTypes(nsISupports** aTypes)
-{
-  if (NS_WARN_IF(!aTypes)) {
-    return NS_ERROR_FAILURE;
-  }
-
-  ErrorResult rv;
-  RefPtr<DOMStringList> types = GetTypes(rv);
-  if (NS_WARN_IF(rv.Failed())) {
-    return rv.StealNSResult();
-  }
-
-  types.forget(aTypes);
-  return NS_OK;
 }
 
 void
 DataTransfer::GetData(const nsAString& aFormat, nsAString& aData,
-                      const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                      nsIPrincipal& aSubjectPrincipal,
                       ErrorResult& aRv)
 {
-  MOZ_ASSERT(aSubjectPrincipal.isSome());
-
   // return an empty string if data for the format was not found
   aData.Truncate();
 
   nsCOMPtr<nsIVariant> data;
   nsresult rv =
-    GetDataAtInternal(aFormat, 0, aSubjectPrincipal.value(),
+    GetDataAtInternal(aFormat, 0, &aSubjectPrincipal,
                       getter_AddRefs(data));
   if (NS_FAILED(rv)) {
     if (rv != NS_ERROR_DOM_INDEX_SIZE_ERR) {
       aRv.Throw(rv);
     }
     return;
   }
 
@@ -429,44 +403,32 @@ DataTransfer::GetData(const nsAString& a
       }
     }
     else {
       aData = stringdata;
     }
   }
 }
 
-NS_IMETHODIMP
-DataTransfer::GetData(const nsAString& aFormat, nsAString& aData)
-{
-  ErrorResult rv;
-  GetData(aFormat, aData, Some(nsContentUtils::SubjectPrincipal()), rv);
-  return rv.StealNSResult();
-}
-
 void
 DataTransfer::SetData(const nsAString& aFormat, const nsAString& aData,
-                      const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                      nsIPrincipal& aSubjectPrincipal,
                       ErrorResult& aRv)
 {
-  MOZ_ASSERT(aSubjectPrincipal.isSome());
-
   RefPtr<nsVariantCC> variant = new nsVariantCC();
   variant->SetAsAString(aData);
 
-  aRv = SetDataAtInternal(aFormat, variant, 0, aSubjectPrincipal.value());
+  aRv = SetDataAtInternal(aFormat, variant, 0, &aSubjectPrincipal);
 }
 
 void
 DataTransfer::ClearData(const Optional<nsAString>& aFormat,
-                        const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                        nsIPrincipal& aSubjectPrincipal,
                         ErrorResult& aRv)
 {
-  MOZ_ASSERT(aSubjectPrincipal.isSome());
-
   if (mReadOnly) {
     aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
     return;
   }
 
   if (MozItemCount() == 0) {
     return;
   }
@@ -643,23 +605,21 @@ DataTransfer::GetDataAtInternal(const ns
   data.forget(aData);
   return NS_OK;
 }
 
 void
 DataTransfer::MozGetDataAt(JSContext* aCx, const nsAString& aFormat,
                            uint32_t aIndex,
                            JS::MutableHandle<JS::Value> aRetval,
-                           const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                           nsIPrincipal& aSubjectPrincipal,
                            mozilla::ErrorResult& aRv)
 {
-  MOZ_ASSERT(aSubjectPrincipal.isSome());
-
   nsCOMPtr<nsIVariant> data;
-  aRv = GetDataAtInternal(aFormat, aIndex, aSubjectPrincipal.value(),
+  aRv = GetDataAtInternal(aFormat, aIndex, &aSubjectPrincipal,
                           getter_AddRefs(data));
   if (aRv.Failed()) {
     return;
   }
 
   if (!data) {
     aRetval.setNull();
     return;
@@ -688,16 +648,22 @@ DataTransfer::PrincipalMaySetData(const 
         aType.EqualsASCII(kFilePromiseMime)) {
       NS_WARNING("Disallowing adding x-moz-file or x-moz-file-promize types to DataTransfer");
       return false;
     }
   }
   return true;
 }
 
+void
+DataTransfer::TypesListMayHaveChanged()
+{
+  DataTransferBinding::ClearCachedTypesValue(this);
+}
+
 nsresult
 DataTransfer::SetDataAtInternal(const nsAString& aFormat, nsIVariant* aData,
                                 uint32_t aIndex,
                                 nsIPrincipal* aSubjectPrincipal)
 {
   if (aFormat.IsEmpty()) {
     return NS_OK;
   }
@@ -729,36 +695,32 @@ DataTransfer::SetDataAtInternal(const ns
   }
 
   return SetDataWithPrincipal(aFormat, aData, aIndex, aSubjectPrincipal);
 }
 
 void
 DataTransfer::MozSetDataAt(JSContext* aCx, const nsAString& aFormat,
                            JS::Handle<JS::Value> aData, uint32_t aIndex,
-                           const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                           nsIPrincipal& aSubjectPrincipal,
                            ErrorResult& aRv)
 {
-  MOZ_ASSERT(aSubjectPrincipal.isSome());
-
   nsCOMPtr<nsIVariant> data;
   aRv = nsContentUtils::XPConnect()->JSValToVariant(aCx, aData,
                                                     getter_AddRefs(data));
   if (!aRv.Failed()) {
-    aRv = SetDataAtInternal(aFormat, data, aIndex, aSubjectPrincipal.value());
+    aRv = SetDataAtInternal(aFormat, data, aIndex, &aSubjectPrincipal);
   }
 }
 
 void
 DataTransfer::MozClearDataAt(const nsAString& aFormat, uint32_t aIndex,
-                             const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                             nsIPrincipal& aSubjectPrincipal,
                              ErrorResult& aRv)
 {
-  MOZ_ASSERT(aSubjectPrincipal.isSome());
-
   if (mReadOnly) {
     aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
     return;
   }
 
   if (aIndex >= MozItemCount()) {
     aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
     return;
@@ -783,25 +745,24 @@ DataTransfer::MozClearDataAt(const nsASt
   if (aIndex == 0 && mItems->MozItemCount() > 1 &&
       mItems->MozItemsAt(0)->Length() == 0) {
     mItems->PopIndexZero();
   }
 }
 
 void
 DataTransfer::MozClearDataAtHelper(const nsAString& aFormat, uint32_t aIndex,
-                                   const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                                   nsIPrincipal& aSubjectPrincipal,
                                    ErrorResult& aRv)
 {
   MOZ_ASSERT(!mReadOnly);
   MOZ_ASSERT(aIndex < MozItemCount());
   MOZ_ASSERT(aIndex == 0 ||
              (mEventMessage != eCut && mEventMessage != eCopy &&
               mEventMessage != ePaste));
-  MOZ_ASSERT(aSubjectPrincipal.isSome());
 
   nsAutoString format;
   GetRealFormat(aFormat, format);
 
   mItems->MozRemoveByTypeAt(format, aIndex, aSubjectPrincipal, aRv);
 }
 
 void
@@ -825,21 +786,19 @@ DataTransfer::SetDragImage(nsIDOMElement
   nsCOMPtr<Element> image = do_QueryInterface(aImage);
   if (image) {
     SetDragImage(*image, aX, aY, rv);
   }
   return rv.StealNSResult();
 }
 
 already_AddRefed<Promise>
-DataTransfer::GetFilesAndDirectories(const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+DataTransfer::GetFilesAndDirectories(nsIPrincipal& aSubjectPrincipal,
                                      ErrorResult& aRv)
 {
-  MOZ_ASSERT(aSubjectPrincipal.isSome());
-
   nsCOMPtr<nsINode> parentNode = do_QueryInterface(mParent);
   if (!parentNode) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   nsCOMPtr<nsIGlobalObject> global = parentNode->OwnerDoc()->GetScopeObject();
   MOZ_ASSERT(global);
@@ -848,17 +807,17 @@ DataTransfer::GetFilesAndDirectories(con
     return nullptr;
   }
 
   RefPtr<Promise> p = Promise::Create(global, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
-  RefPtr<FileList> files = mItems->Files(aSubjectPrincipal.value());
+  RefPtr<FileList> files = mItems->Files(&aSubjectPrincipal);
   if (NS_WARN_IF(!files)) {
     return nullptr;
   }
 
   Sequence<RefPtr<File>> filesSeq;
   files->ToSequence(filesSeq, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
@@ -866,17 +825,17 @@ DataTransfer::GetFilesAndDirectories(con
 
   p->MaybeResolve(filesSeq);
 
   return p.forget();
 }
 
 already_AddRefed<Promise>
 DataTransfer::GetFiles(bool aRecursiveFlag,
-                       const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                       nsIPrincipal& aSubjectPrincipal,
                        ErrorResult& aRv)
 {
   // Currently we don't support directories.
   return GetFilesAndDirectories(aSubjectPrincipal, aRv);
 }
 
 void
 DataTransfer::AddElement(Element& aElement, ErrorResult& aRv)
@@ -1470,18 +1429,18 @@ DataTransfer::FillAllExternalData()
   }
 }
 
 void
 DataTransfer::FillInExternalCustomTypes(uint32_t aIndex,
                                         nsIPrincipal* aPrincipal)
 {
   RefPtr<DataTransferItem> item = new DataTransferItem(this,
-                                                       NS_LITERAL_STRING(kCustomTypesMime));
-  item->SetKind(DataTransferItem::KIND_STRING);
+                                                       NS_LITERAL_STRING(kCustomTypesMime),
+                                                       DataTransferItem::KIND_STRING);
   item->SetIndex(aIndex);
 
   nsCOMPtr<nsIVariant> variant = item->DataNoSecurityCheck();
   if (!variant) {
     return;
   }
 
   FillInExternalCustomTypes(variant, aIndex, aPrincipal);
--- a/dom/events/DataTransfer.h
+++ b/dom/events/DataTransfer.h
@@ -131,41 +131,42 @@ public:
     } else {
       aEffectAllowed.AssignASCII(sEffects[mEffectAllowed]);
     }
   }
 
   void SetDragImage(Element& aElement, int32_t aX, int32_t aY,
                     ErrorResult& aRv);
 
-  already_AddRefed<DOMStringList> GetTypes(ErrorResult& rv) const;
+  void GetTypes(nsTArray<nsString>& aTypes,
+                nsIPrincipal& aSubjectPrincipal) const;
 
   void GetData(const nsAString& aFormat, nsAString& aData,
-               const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+               nsIPrincipal& aSubjectPrincipal,
                ErrorResult& aRv);
 
   void SetData(const nsAString& aFormat, const nsAString& aData,
-               const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+               nsIPrincipal& aSubjectPrincipal,
                ErrorResult& aRv);
 
   void ClearData(const mozilla::dom::Optional<nsAString>& aFormat,
-                 const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                 nsIPrincipal& aSubjectPrincipal,
                  mozilla::ErrorResult& aRv);
 
   already_AddRefed<FileList>
-  GetFiles(const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+  GetFiles(nsIPrincipal& aSubjectPrincipal,
            mozilla::ErrorResult& aRv);
 
   already_AddRefed<Promise>
-  GetFilesAndDirectories(const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+  GetFilesAndDirectories(nsIPrincipal& aSubjectPrincipal,
                          mozilla::ErrorResult& aRv);
 
   already_AddRefed<Promise>
   GetFiles(bool aRecursiveFlag,
-           const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+           nsIPrincipal& aSubjectPrincipal,
            ErrorResult& aRv);
 
 
   void AddElement(Element& aElement, mozilla::ErrorResult& aRv);
 
   uint32_t MozItemCount() const;
 
   void GetMozCursor(nsString& aCursor)
@@ -176,27 +177,27 @@ public:
       aCursor.AssignLiteral("auto");
     }
   }
 
   already_AddRefed<DOMStringList> MozTypesAt(uint32_t aIndex,
                                              mozilla::ErrorResult& aRv) const;
 
   void MozClearDataAt(const nsAString& aFormat, uint32_t aIndex,
-                      const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                      nsIPrincipal& aSubjectPrincipal,
                       mozilla::ErrorResult& aRv);
 
   void MozSetDataAt(JSContext* aCx, const nsAString& aFormat,
                     JS::Handle<JS::Value> aData, uint32_t aIndex,
-                    const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                    nsIPrincipal& aSubjectPrincipal,
                     mozilla::ErrorResult& aRv);
 
   void MozGetDataAt(JSContext* aCx, const nsAString& aFormat,
                     uint32_t aIndex, JS::MutableHandle<JS::Value> aRetval,
-                    const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                    nsIPrincipal& aSubjectPrincipal,
                     mozilla::ErrorResult& aRv);
 
   bool MozUserCancelled() const
   {
     return mUserCancelled;
   }
 
   already_AddRefed<nsINode> GetMozSourceNode();
@@ -284,16 +285,21 @@ public:
   // converts some formats used for compatibility in aInFormat into aOutFormat.
   // Text and text/unicode become text/plain, and URL becomes text/uri-list
   void GetRealFormat(const nsAString& aInFormat, nsAString& aOutFormat) const;
 
   static bool PrincipalMaySetData(const nsAString& aFormat,
                                   nsIVariant* aData,
                                   nsIPrincipal* aPrincipal);
 
+  // Notify the DataTransfer that the list returned from GetTypes may have
+  // changed.  This can happen due to items we care about for purposes of
+  // GetTypes being added or removed or changing item kinds.
+  void TypesListMayHaveChanged();
+
 protected:
 
   // caches text and uri-list data formats that exist in the drag service or
   // clipboard for retrieval later.
   nsresult CacheExternalData(const char* aFormat, uint32_t aIndex,
                              nsIPrincipal* aPrincipal, bool aHidden);
 
   // caches the formats that exist in the drag service that were added by an
@@ -316,17 +322,17 @@ protected:
   void FillAllExternalData();
 
   void FillInExternalCustomTypes(uint32_t aIndex, nsIPrincipal* aPrincipal);
 
   void FillInExternalCustomTypes(nsIVariant* aData, uint32_t aIndex,
                                  nsIPrincipal* aPrincipal);
 
   void MozClearDataAtHelper(const nsAString& aFormat, uint32_t aIndex,
-                            const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                            nsIPrincipal& aSubjectPrincipal,
                             mozilla::ErrorResult& aRv);
 
   nsCOMPtr<nsISupports> mParent;
 
   // the drop effect and effect allowed
   uint32_t mDropEffect;
   uint32_t mEffectAllowed;
 
--- a/dom/events/DataTransferItem.cpp
+++ b/dom/events/DataTransferItem.cpp
@@ -69,22 +69,16 @@ DataTransferItem::Clone(DataTransfer* aD
   it->mData = mData;
   it->mPrincipal = mPrincipal;
   it->mChromeOnly = mChromeOnly;
 
   return it.forget();
 }
 
 void
-DataTransferItem::SetType(const nsAString& aType)
-{
-  mType = aType;
-}
-
-void
 DataTransferItem::SetData(nsIVariant* aData)
 {
   // Invalidate our file cache, we will regenerate it with the new data
   mCachedFile = nullptr;
 
   if (!aData) {
     // We are holding a temporary null which will later be filled.
     // These are provided by the system, and have guaranteed properties about
@@ -225,36 +219,33 @@ DataTransferItem::FillInExternalData()
         supportscstr->GetData(str);
         variant->SetAsACString(str);
       }
     }
   }
 
   SetData(variant);
 
-#ifdef DEBUG
   if (oldKind != Kind()) {
     NS_WARNING("Clipboard data provided by the OS does not match predicted kind");
+    mDataTransfer->TypesListMayHaveChanged();
   }
-#endif
 }
 
 already_AddRefed<File>
-DataTransferItem::GetAsFile(const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+DataTransferItem::GetAsFile(nsIPrincipal& aSubjectPrincipal,
                             ErrorResult& aRv)
 {
-  MOZ_ASSERT(aSubjectPrincipal.isSome());
-
   if (mKind != KIND_FILE) {
     return nullptr;
   }
 
   // This is done even if we have an mCachedFile, as it performs the necessary
   // permissions checks to ensure that we are allowed to access this type.
-  nsCOMPtr<nsIVariant> data = Data(aSubjectPrincipal.value(), aRv);
+  nsCOMPtr<nsIVariant> data = Data(&aSubjectPrincipal, aRv);
   if (NS_WARN_IF(!data || aRv.Failed())) {
     return nullptr;
   }
 
   // Generate the dom::File from the stored data, caching it so that the
   // same object is returned in the future.
   if (!mCachedFile) {
     nsCOMPtr<nsISupports> supports;
@@ -278,21 +269,19 @@ DataTransferItem::GetAsFile(const Maybe<
     }
   }
 
   RefPtr<File> file = mCachedFile;
   return file.forget();
 }
 
 already_AddRefed<FileSystemEntry>
-DataTransferItem::GetAsEntry(const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+DataTransferItem::GetAsEntry(nsIPrincipal& aSubjectPrincipal,
                              ErrorResult& aRv)
 {
-  MOZ_ASSERT(aSubjectPrincipal.isSome());
-
   RefPtr<File> file = GetAsFile(aSubjectPrincipal, aRv);
   if (NS_WARN_IF(aRv.Failed()) || !file) {
     return nullptr;
   }
 
   nsCOMPtr<nsIGlobalObject> global;
   // This is annoying, but DataTransfer may have various things as parent.
   nsCOMPtr<EventTarget> target =
@@ -381,29 +370,27 @@ DataTransferItem::CreateFileFromInputStr
   }
 
   return File::CreateMemoryFile(mDataTransfer, data, available, fileName,
                                 mType, PR_Now());
 }
 
 void
 DataTransferItem::GetAsString(FunctionStringCallback* aCallback,
-                              const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                              nsIPrincipal& aSubjectPrincipal,
                               ErrorResult& aRv)
 {
-  MOZ_ASSERT(aSubjectPrincipal.isSome());
-
   if (!aCallback || mKind != KIND_STRING) {
     return;
   }
 
   // Theoretically this should be done inside of the runnable, as it might be an
   // expensive operation on some systems, however we wouldn't get access to the
   // NS_ERROR_DOM_SECURITY_ERROR messages which may be raised by this method.
-  nsCOMPtr<nsIVariant> data = Data(aSubjectPrincipal.value(), aRv);
+  nsCOMPtr<nsIVariant> data = Data(&aSubjectPrincipal, aRv);
   if (NS_WARN_IF(!data || aRv.Failed())) {
     return;
   }
 
   nsAutoString stringData;
   nsresult rv = data->GetAsAString(stringData);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return;
--- a/dom/events/DataTransferItem.h
+++ b/dom/events/DataTransferItem.h
@@ -30,33 +30,33 @@ public:
   // we can have other kinds then just FILE and STRING. These others are simply
   // marked as "other" and can only be produced throug the Moz* APIs.
   enum eKind {
     KIND_FILE,
     KIND_STRING,
     KIND_OTHER,
   };
 
-  DataTransferItem(DataTransfer* aDataTransfer, const nsAString& aType)
+  DataTransferItem(DataTransfer* aDataTransfer, const nsAString& aType,
+                   eKind aKind = KIND_OTHER)
     : mIndex(0)
     , mChromeOnly(false)
-    , mKind(KIND_OTHER)
+    , mKind(aKind)
     , mType(aType)
     , mDataTransfer(aDataTransfer)
   {
     MOZ_ASSERT(mDataTransfer, "Must be associated with a DataTransfer");
   }
 
   already_AddRefed<DataTransferItem> Clone(DataTransfer* aDataTransfer) const;
 
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
-  // NOTE: This accesses the subject principal, and should not be called from C++
   void GetAsString(FunctionStringCallback* aCallback,
-                   const Maybe<nsIPrincipal*>& aPrincipal,
+                   nsIPrincipal& aSubjectPrincipal,
                    ErrorResult& aRv);
 
   void GetKind(nsAString& aKind) const
   {
     switch (mKind) {
     case KIND_FILE:
       aKind = NS_LITERAL_STRING("file");
       return;
@@ -68,32 +68,27 @@ public:
       return;
     }
   }
 
   void GetType(nsAString& aType) const
   {
     aType = mType;
   }
-  void SetType(const nsAString& aType);
 
   eKind Kind() const
   {
     return mKind;
   }
-  void SetKind(eKind aKind)
-  {
-    mKind = aKind;
-  }
 
   already_AddRefed<File>
-  GetAsFile(const Maybe<nsIPrincipal*>& aSubjectPrincipal, ErrorResult& aRv);
+  GetAsFile(nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv);
 
   already_AddRefed<FileSystemEntry>
-  GetAsEntry(const Maybe<nsIPrincipal*>& aSubjectPrincipal, ErrorResult& aRv);
+  GetAsEntry(nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv);
 
   DataTransfer* GetParentObject() const
   {
     return mDataTransfer;
   }
 
   nsIPrincipal* Principal() const
   {
@@ -101,16 +96,19 @@ public:
   }
   void SetPrincipal(nsIPrincipal* aPrincipal)
   {
     mPrincipal = aPrincipal;
   }
 
   already_AddRefed<nsIVariant> DataNoSecurityCheck();
   already_AddRefed<nsIVariant> Data(nsIPrincipal* aPrincipal, ErrorResult& aRv);
+
+  // Note: This can modify the mKind.  Callers of this method must let the
+  // relevant DataTransfer know, because its types list can change as a result.
   void SetData(nsIVariant* aData);
 
   uint32_t Index() const
   {
     return mIndex;
   }
   void SetIndex(uint32_t aIndex)
   {
@@ -133,17 +131,17 @@ private:
   ~DataTransferItem() {}
   already_AddRefed<File> CreateFileFromInputStream(nsIInputStream* aStream);
 
   // The index in the 2d mIndexedItems array
   uint32_t mIndex;
 
   bool mChromeOnly;
   eKind mKind;
-  nsString mType;
+  const nsString mType;
   nsCOMPtr<nsIVariant> mData;
   nsCOMPtr<nsIPrincipal> mPrincipal;
   RefPtr<DataTransfer> mDataTransfer;
 
   // File cache for nsIFile application/x-moz-file entries.
   RefPtr<File> mCachedFile;
 };
 
--- a/dom/events/DataTransferItemList.cpp
+++ b/dom/events/DataTransferItemList.cpp
@@ -77,36 +77,34 @@ DataTransferItemList::Clone(DataTransfer
     list->mItems[i] = list->mIndexedItems[index][subIndex];
   }
 
   return list.forget();
 }
 
 void
 DataTransferItemList::Remove(uint32_t aIndex,
-                             const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                             nsIPrincipal& aSubjectPrincipal,
                              ErrorResult& aRv)
 {
-  MOZ_ASSERT(aSubjectPrincipal.isSome());
-
   if (mDataTransfer->IsReadOnly()) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
   if (aIndex >= Length()) {
     aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
     return;
   }
 
   ClearDataHelper(mItems[aIndex], aIndex, -1, aSubjectPrincipal, aRv);
 }
 
 DataTransferItem*
-DataTransferItemList::IndexedGetter(uint32_t aIndex, bool& aFound, ErrorResult& aRv) const
+DataTransferItemList::IndexedGetter(uint32_t aIndex, bool& aFound) const
 {
   if (aIndex >= mItems.Length()) {
     aFound = false;
     return nullptr;
   }
 
   MOZ_ASSERT(mItems[aIndex]);
   aFound = true;
@@ -121,21 +119,19 @@ DataTransferItemList::MozItemCount() con
   // if it is empty, scripts using the moz* APIs should see it as not existing.
   if (length == 1 && mIndexedItems[0].IsEmpty()) {
     return 0;
   }
   return length;
 }
 
 void
-DataTransferItemList::Clear(const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+DataTransferItemList::Clear(nsIPrincipal& aSubjectPrincipal,
                             ErrorResult& aRv)
 {
-  MOZ_ASSERT(aSubjectPrincipal.isSome());
-
   if (NS_WARN_IF(mDataTransfer->IsReadOnly())) {
     return;
   }
 
   uint32_t count = Length();
   for (uint32_t i = 0; i < count; i++) {
     // We always remove the last item first, to avoid moving items around in
     // memory as much
@@ -144,81 +140,75 @@ DataTransferItemList::Clear(const Maybe<
   }
 
   MOZ_ASSERT(Length() == 0);
 }
 
 DataTransferItem*
 DataTransferItemList::Add(const nsAString& aData,
                           const nsAString& aType,
-                          const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                          nsIPrincipal& aSubjectPrincipal,
                           ErrorResult& aRv)
 {
-  MOZ_ASSERT(aSubjectPrincipal.isSome());
-
   if (NS_WARN_IF(mDataTransfer->IsReadOnly())) {
     return nullptr;
   }
 
   nsCOMPtr<nsIVariant> data(new storage::TextVariant(aData));
 
   nsAutoString format;
   mDataTransfer->GetRealFormat(aType, format);
 
-  if (!DataTransfer::PrincipalMaySetData(format, data,
-                                         aSubjectPrincipal.value())) {
+  if (!DataTransfer::PrincipalMaySetData(format, data, &aSubjectPrincipal)) {
     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
     return nullptr;
   }
 
   // We add the textual data to index 0. We set aInsertOnly to true, as we don't
   // want to update an existing entry if it is already present, as per the spec.
   RefPtr<DataTransferItem> item =
-    SetDataWithPrincipal(format, data, 0, aSubjectPrincipal.value(),
+    SetDataWithPrincipal(format, data, 0, &aSubjectPrincipal,
                          /* aInsertOnly = */ true,
                          /* aHidden = */ false,
                          aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
   MOZ_ASSERT(item->Kind() != DataTransferItem::KIND_FILE);
 
   return item;
 }
 
 DataTransferItem*
 DataTransferItemList::Add(File& aData,
-                          const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                          nsIPrincipal& aSubjectPrincipal,
                           ErrorResult& aRv)
 {
-  MOZ_ASSERT(aSubjectPrincipal.isSome());
-
   if (mDataTransfer->IsReadOnly()) {
     return nullptr;
   }
 
   nsCOMPtr<nsISupports> supports = do_QueryObject(&aData);
   nsCOMPtr<nsIWritableVariant> data = new nsVariant();
   data->SetAsISupports(supports);
 
   nsAutoString type;
   aData.GetType(type);
 
-  if (!DataTransfer::PrincipalMaySetData(type, data,
-                                         aSubjectPrincipal.value())) {
+  if (!DataTransfer::PrincipalMaySetData(type, data, &aSubjectPrincipal)) {
     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
     return nullptr;
   }
 
   // We need to add this as a new item, as multiple files can't exist in the
   // same item in the Moz DataTransfer layout. It will be appended at the end of
   // the internal specced layout.
   uint32_t index = mIndexedItems.Length();
   RefPtr<DataTransferItem> item =
-    SetDataWithPrincipal(type, data, index, aSubjectPrincipal.value(),
+    SetDataWithPrincipal(type, data, index, &aSubjectPrincipal,
                          /* aInsertOnly = */ true,
                          /* aHidden = */ false,
                          aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
   MOZ_ASSERT(item->Kind() == DataTransferItem::KIND_FILE);
 
@@ -267,21 +257,19 @@ DataTransferItemList::Files(nsIPrincipal
 
   files = mFiles;
   return files.forget();
 }
 
 void
 DataTransferItemList::MozRemoveByTypeAt(const nsAString& aType,
                                         uint32_t aIndex,
-                                        const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                                        nsIPrincipal& aSubjectPrincipal,
                                         ErrorResult& aRv)
 {
-  MOZ_ASSERT(aSubjectPrincipal.isSome());
-
   if (NS_WARN_IF(mDataTransfer->IsReadOnly() ||
                  aIndex >= mIndexedItems.Length())) {
     return;
   }
 
   bool removeAll = aType.IsEmpty();
 
   nsTArray<RefPtr<DataTransferItem>>& items = mIndexedItems[aIndex];
@@ -365,16 +353,20 @@ DataTransferItemList::SetDataWithPrincip
                         || !subsumes))) {
           aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
           return nullptr;
         }
         item->SetPrincipal(aPrincipal);
 
         DataTransferItem::eKind oldKind = item->Kind();
         item->SetData(aData);
+        if (oldKind != item->Kind()) {
+          // Types list may have changed, even if aIndex == 0.
+          mDataTransfer->TypesListMayHaveChanged();
+        }
 
         if (aIndex != 0) {
           // If the item changes from being a file to not a file or vice-versa,
           // its presence in the mItems array may need to change.
           if (item->Kind() == DataTransferItem::KIND_FILE &&
               oldKind != DataTransferItem::KIND_FILE) {
             // not file => file
             mItems.AppendElement(item);
@@ -428,19 +420,25 @@ DataTransferItemList::AppendNewItem(uint
   item->SetChromeOnly(aHidden);
 
   mIndexedItems[aIndex].AppendElement(item);
 
   // We only want to add the item to the main mItems list if the index we are
   // adding to is 0, or the item we are adding is a file. If we add an item
   // which is not a file to a non-zero index, invariants could be broken.
   // (namely the invariant that there are not 2 non-file entries in the items
-  // array with the same type)
-  if (!aHidden && (item->Kind() == DataTransferItem::KIND_FILE || aIndex == 0)) {
-    mItems.AppendElement(item);
+  // array with the same type).
+  //
+  // We also want to update our DataTransfer's type list any time we're adding a
+  // KIND_FILE item, or an item at index 0.
+  if (item->Kind() == DataTransferItem::KIND_FILE || aIndex == 0) {
+    if (!aHidden) {
+      mItems.AppendElement(item);
+    }
+    mDataTransfer->TypesListMayHaveChanged();
   }
 
   return item;
 }
 
 const nsTArray<RefPtr<DataTransferItem>>*
 DataTransferItemList::MozItemsAt(uint32_t aIndex) // -- INDEXED
 {
@@ -470,60 +468,60 @@ DataTransferItemList::PopIndexZero()
 
 void
 DataTransferItemList::ClearAllItems()
 {
   // We always need to have index 0, so don't delete that one
   mItems.Clear();
   mIndexedItems.Clear();
   mIndexedItems.SetLength(1);
+  mDataTransfer->TypesListMayHaveChanged();
 
   // Re-generate files (into an empty list)
   RegenerateFiles();
 }
 
 void
 DataTransferItemList::ClearDataHelper(DataTransferItem* aItem,
                                       uint32_t aIndexHint,
                                       uint32_t aMozOffsetHint,
-                                      const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                                      nsIPrincipal& aSubjectPrincipal,
                                       ErrorResult& aRv)
 {
   MOZ_ASSERT(aItem);
   if (NS_WARN_IF(mDataTransfer->IsReadOnly())) {
     return;
   }
 
-  if (aItem->Principal() && aSubjectPrincipal.isSome() &&
-      !aSubjectPrincipal.value()->Subsumes(aItem->Principal())) {
+  if (aItem->Principal() && !aSubjectPrincipal.Subsumes(aItem->Principal())) {
     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
     return;
   }
 
   // Check if the aIndexHint is actually the index, and then remove the item
   // from aItems
-  ErrorResult rv;
   bool found;
-  if (IndexedGetter(aIndexHint, found, rv) == aItem) {
+  if (IndexedGetter(aIndexHint, found) == aItem) {
     mItems.RemoveElementAt(aIndexHint);
   } else {
     mItems.RemoveElement(aItem);
   }
-  rv.SuppressException();
 
   // Check if the aMozIndexHint and aMozOffsetHint are actually the index and
   // offset, and then remove them from mIndexedItems
   MOZ_ASSERT(aItem->Index() < mIndexedItems.Length());
   nsTArray<RefPtr<DataTransferItem>>& items = mIndexedItems[aItem->Index()];
   if (aMozOffsetHint < items.Length() && aItem == items[aMozOffsetHint]) {
     items.RemoveElementAt(aMozOffsetHint);
   } else {
     items.RemoveElement(aItem);
   }
 
+  mDataTransfer->TypesListMayHaveChanged();
+
   // Check if we should remove the index. We never remove index 0.
   if (items.Length() == 0 && aItem->Index() != 0) {
     mIndexedItems.RemoveElementAt(aItem->Index());
 
     // Update the index of every element which has now been shifted
     for (uint32_t i = aItem->Index(); i < mIndexedItems.Length(); i++) {
       nsTArray<RefPtr<DataTransferItem>>& items = mIndexedItems[i];
       for (uint32_t j = 0; j < items.Length(); j++) {
@@ -559,25 +557,23 @@ DataTransferItemList::RegenerateFiles()
 void
 DataTransferItemList::GenerateFiles(FileList* aFiles,
                                     nsIPrincipal* aFilesPrincipal)
 {
   MOZ_ASSERT(aFiles);
   MOZ_ASSERT(aFilesPrincipal);
   uint32_t count = Length();
   for (uint32_t i = 0; i < count; i++) {
-    ErrorResult rv;
     bool found;
-    RefPtr<DataTransferItem> item = IndexedGetter(i, found, rv);
-    if (NS_WARN_IF(!found || rv.Failed())) {
-      continue;
-    }
+    RefPtr<DataTransferItem> item = IndexedGetter(i, found);
+    MOZ_ASSERT(found);
 
     if (item->Kind() == DataTransferItem::KIND_FILE) {
-      RefPtr<File> file = item->GetAsFile(Some(aFilesPrincipal), rv);
+      IgnoredErrorResult rv;
+      RefPtr<File> file = item->GetAsFile(*aFilesPrincipal, rv);
       if (NS_WARN_IF(rv.Failed() || !file)) {
         continue;
       }
       aFiles->Append(file);
     }
   }
 }
 
--- a/dom/events/DataTransferItemList.h
+++ b/dom/events/DataTransferItemList.h
@@ -46,76 +46,86 @@ public:
                                JS::Handle<JSObject*> aGivenProto) override;
 
   uint32_t Length() const
   {
     return mItems.Length();
   };
 
   DataTransferItem* Add(const nsAString& aData, const nsAString& aType,
-                        const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                        nsIPrincipal& aSubjectPrincipal,
                         ErrorResult& rv);
   DataTransferItem* Add(File& aData,
-                        const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                        nsIPrincipal& aSubjectPrincipal,
                         ErrorResult& aRv);
 
   void Remove(uint32_t aIndex,
-              const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+              nsIPrincipal& aSubjectPrincipal,
               ErrorResult& aRv);
 
-  DataTransferItem* IndexedGetter(uint32_t aIndex, bool& aFound,
-                                  ErrorResult& aRv) const;
+  DataTransferItem* IndexedGetter(uint32_t aIndex, bool& aFound) const;
 
   DataTransfer* GetParentObject() const
   {
     return mDataTransfer;
   }
 
-  void Clear(const Maybe<nsIPrincipal*>& aSubjectPrincipal, ErrorResult& aRv);
+  void Clear(nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv);
 
   already_AddRefed<DataTransferItem>
   SetDataWithPrincipal(const nsAString& aType, nsIVariant* aData,
                        uint32_t aIndex, nsIPrincipal* aPrincipal,
                        bool aInsertOnly, bool aHidden, ErrorResult& aRv);
 
   already_AddRefed<FileList> Files(nsIPrincipal* aPrincipal);
 
   // Moz-style helper methods for interacting with the stored data
   void MozRemoveByTypeAt(const nsAString& aType, uint32_t aIndex,
-                         const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                         nsIPrincipal& aSubjectPrincipal,
                          ErrorResult& aRv);
   DataTransferItem* MozItemByTypeAt(const nsAString& aType, uint32_t aIndex);
   const nsTArray<RefPtr<DataTransferItem>>* MozItemsAt(uint32_t aIndex);
   uint32_t MozItemCount() const;
 
   // Causes everything in indexes above 0 to shift down one index.
   void PopIndexZero();
 
   // Delete every item in the DataTransferItemList, without checking for
   // permissions or read-only status (for internal use only).
   void ClearAllItems();
 
 private:
   void ClearDataHelper(DataTransferItem* aItem, uint32_t aIndexHint,
                        uint32_t aMozOffsetHint,
-                       const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                       nsIPrincipal& aSubjectPrincipal,
                        ErrorResult& aRv);
   DataTransferItem* AppendNewItem(uint32_t aIndex, const nsAString& aType,
                                   nsIVariant* aData, nsIPrincipal* aPrincipal,
                                   bool aHidden);
   void RegenerateFiles();
   void GenerateFiles(FileList* aFiles, nsIPrincipal* aFilesPrincipal);
 
   ~DataTransferItemList() {}
 
   RefPtr<DataTransfer> mDataTransfer;
   bool mIsExternal;
   RefPtr<FileList> mFiles;
   // The principal for which mFiles is cached
   nsCOMPtr<nsIPrincipal> mFilesPrincipal;
+  // mItems is the list of items that corresponds to the spec concept of a
+  // DataTransferItemList.  That is, this is the thing the spec's indexed getter
+  // operates on.  The items in here are a subset of the items present in the
+  // arrays that live in mIndexedItems.
   nsTArray<RefPtr<DataTransferItem>> mItems;
+  // mIndexedItems represents all our items.  For any given index, all items at
+  // that index have different types in the GetType() sense.  That means that
+  // representing multiple items with the same type (e.g. multiple files)
+  // requires using multiple indices.
+  //
+  // There is always a (possibly empty) list of items at index 0, so
+  // mIndexedItems.Length() >= 1 at all times.
   nsTArray<nsTArray<RefPtr<DataTransferItem>>> mIndexedItems;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_DataTransferItemList_h
--- a/dom/events/test/test_dragstart.html
+++ b/dom/events/test/test_dragstart.html
@@ -113,17 +113,17 @@ function doDragStartSelection(event)
   is(event.pageX, 14, "dragstart pageX");
   is(event.pageY, 14, "dragstart pageY");
 
   var dt = event.dataTransfer;
   ok(dt instanceof DataTransfer, "dataTransfer is DataTransfer");
   gDataTransfer = dt;
 
   var types = dt.types;
-  is(types instanceof DOMStringList, true, "initial types is a DOMStringList");
+  ok(Array.isArray(types), "initial types is an Array");
   checkTypes(dt, ["text/_moz_htmlcontext", "text/_moz_htmlinfo", "text/html", "text/plain"], 0, "initial selection");
 
   is(dt.getData("text/plain"), "This is a draggable bit of text.", "initial selection text/plain");
   is(dt.getData("text/html"), "<div id=\"draggable\" ondragstart=\"doDragStartSelection(event)\">This is a <em>draggable</em> bit of text.</div>",
      "initial selection text/html");
 
   // text/unicode and Text are available for compatibility. They retrieve the
   // text/plain data
@@ -143,17 +143,17 @@ function doDragStartSelection(event)
   setTimeout(afterDragTests, 0);
 }
 
 function test_DataTransfer(dt)
 {
   is(dt.mozItemCount, 0, "empty itemCount");
 
   var types = dt.types;
-  is(types instanceof DOMStringList, true, "empty types is a DOMStringList");
+  ok(Array.isArray(types), "empty types is an Array");
   checkTypes(dt, [], 0, "empty");
   is(dt.getData("text/plain"), "", "empty data is empty");
 
   // calling setDataAt requires an index that is 0 <= index <= dt.itemCount
   expectError(() => dt.mozSetDataAt("text/plain", "Some Text", 1),
               "IndexSizeError", "setDataAt index too high");
 
   is(dt.mozUserCancelled, false, "userCancelled");
deleted file mode 100644
--- a/dom/fmradio/FMRadio.cpp
+++ /dev/null
@@ -1,500 +0,0 @@
-/* -*- 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 "mozilla/dom/FMRadio.h"
-#include "nsContentUtils.h"
-#include "mozilla/Hal.h"
-#include "mozilla/HalTypes.h"
-#include "mozilla/Preferences.h"
-#include "mozilla/dom/FMRadioBinding.h"
-#include "mozilla/dom/ContentChild.h"
-#include "mozilla/dom/PFMRadioChild.h"
-#include "mozilla/dom/FMRadioService.h"
-#include "mozilla/dom/TypedArray.h"
-#include "AudioChannelService.h"
-#include "DOMRequest.h"
-#include "nsDOMClassInfo.h"
-#include "nsIDocShell.h"
-#include "nsIInterfaceRequestorUtils.h"
-#include "nsIAudioManager.h"
-
-#undef LOG
-#define LOG(args...) FM_LOG("FMRadio", args)
-
-// The pref indicates if the device has an internal antenna.
-// If the pref is true, the antanna will be always available.
-#define DOM_FM_ANTENNA_INTERNAL_PREF "dom.fmradio.antenna.internal"
-
-using mozilla::Preferences;
-
-BEGIN_FMRADIO_NAMESPACE
-
-class FMRadioRequest final : public FMRadioReplyRunnable
-                           , public DOMRequest
-{
-public:
-  NS_DECL_ISUPPORTS_INHERITED
-
-  FMRadioRequest(nsPIDOMWindowInner* aWindow, FMRadio* aFMRadio)
-    : DOMRequest(aWindow)
-    , mType(FMRadioRequestArgs::T__None)
-  {
-    // |FMRadio| inherits from |nsIDOMEventTarget| and |nsISupportsWeakReference|
-    // which both inherits from nsISupports, so |nsISupports| is an ambiguous
-    // base of |FMRadio|, we have to cast |aFMRadio| to one of the base classes.
-    mFMRadio = do_GetWeakReference(static_cast<nsIDOMEventTarget*>(aFMRadio));
-  }
-
-  FMRadioRequest(nsPIDOMWindowInner* aWindow, FMRadio* aFMRadio,
-    FMRadioRequestArgs::Type aType)
-    : DOMRequest(aWindow)
-  {
-    MOZ_ASSERT(aType >= FMRadioRequestArgs::T__None &&
-               aType <= FMRadioRequestArgs::T__Last,
-               "Wrong FMRadioRequestArgs in FMRadioRequest");
-
-    mFMRadio = do_GetWeakReference(static_cast<nsIDOMEventTarget*>(aFMRadio));
-    mType = aType;
-  }
-
-  NS_IMETHOD
-  Run() override
-  {
-    MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
-
-    nsCOMPtr<nsIDOMEventTarget> target = do_QueryReferent(mFMRadio);
-    if (!target) {
-      return NS_OK;
-    }
-
-    FMRadio* fmRadio = static_cast<FMRadio*>(
-      static_cast<nsIDOMEventTarget*>(target));
-
-    if (fmRadio->mIsShutdown) {
-      return NS_OK;
-    }
-
-    switch (mResponseType.type()) {
-      case FMRadioResponseType::TErrorResponse:
-        FireError(mResponseType.get_ErrorResponse().error());
-        break;
-      case FMRadioResponseType::TSuccessResponse:
-        if (mType == FMRadioRequestArgs::TEnableRequestArgs) {
-          fmRadio->EnableAudioChannelAgent();
-        }
-
-        FireSuccess(JS::UndefinedHandleValue);
-        break;
-      default:
-        MOZ_CRASH();
-    }
-
-    return NS_OK;
-  }
-
-protected:
-  ~FMRadioRequest() { }
-
-private:
-  FMRadioRequestArgs::Type mType;
-  nsWeakPtr mFMRadio;
-};
-
-NS_IMPL_ISUPPORTS_INHERITED0(FMRadioRequest, DOMRequest)
-
-FMRadio::FMRadio()
-  : mHeadphoneState(hal::SWITCH_STATE_OFF)
-  , mRdsGroupMask(0)
-  , mAudioChannelAgentEnabled(false)
-  , mHasInternalAntenna(false)
-  , mIsShutdown(false)
-{
-  LOG("FMRadio is initialized.");
-}
-
-FMRadio::~FMRadio()
-{
-}
-
-void
-FMRadio::Init(nsPIDOMWindowInner *aWindow)
-{
-  BindToOwner(aWindow);
-
-  IFMRadioService::Singleton()->AddObserver(this);
-
-  mHasInternalAntenna = Preferences::GetBool(DOM_FM_ANTENNA_INTERNAL_PREF,
-                                             /* default = */ false);
-  if (mHasInternalAntenna) {
-    LOG("We have an internal antenna.");
-  } else {
-    mHeadphoneState = hal::GetCurrentSwitchState(hal::SWITCH_HEADPHONES);
-    hal::RegisterSwitchObserver(hal::SWITCH_HEADPHONES, this);
-  }
-
-  nsCOMPtr<nsIAudioChannelAgent> audioChannelAgent =
-    do_CreateInstance("@mozilla.org/audiochannelagent;1");
-  NS_ENSURE_TRUE_VOID(audioChannelAgent);
-
-  audioChannelAgent->InitWithWeakCallback(
-    GetOwner(),
-    nsIAudioChannelAgent::AUDIO_AGENT_CHANNEL_CONTENT,
-    this);
-
-  // Once all necessary resources are got successfully, we just enabled
-  // mAudioChannelAgent.
-  mAudioChannelAgent = audioChannelAgent;
-}
-
-void
-FMRadio::Shutdown()
-{
-  IFMRadioService::Singleton()->RemoveObserver(this);
-
-  if (!mHasInternalAntenna) {
-    hal::UnregisterSwitchObserver(hal::SWITCH_HEADPHONES, this);
-  }
-
-  mIsShutdown = true;
-}
-
-JSObject*
-FMRadio::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
-{
-  return FMRadioBinding::Wrap(aCx, this, aGivenProto);
-}
-
-void
-FMRadio::Notify(const hal::SwitchEvent& aEvent)
-{
-  MOZ_ASSERT(!mHasInternalAntenna);
-
-  if (mHeadphoneState != aEvent.status()) {
-    mHeadphoneState = aEvent.status();
-
-    DispatchTrustedEvent(NS_LITERAL_STRING("antennaavailablechange"));
-  }
-}
-
-void
-FMRadio::Notify(const FMRadioEventType& aType)
-{
-  switch (aType) {
-    case FrequencyChanged:
-      DispatchTrustedEvent(NS_LITERAL_STRING("frequencychange"));
-      break;
-    case EnabledChanged:
-      if (Enabled()) {
-        DispatchTrustedEvent(NS_LITERAL_STRING("enabled"));
-      } else {
-        if (mAudioChannelAgentEnabled) {
-          mAudioChannelAgent->NotifyStoppedPlaying();
-          mAudioChannelAgentEnabled = false;
-        }
-
-        DispatchTrustedEvent(NS_LITERAL_STRING("disabled"));
-      }
-      break;
-    case RDSEnabledChanged:
-      if (RdsEnabled()) {
-        DispatchTrustedEvent(NS_LITERAL_STRING("rdsenabled"));
-      } else {
-        DispatchTrustedEvent(NS_LITERAL_STRING("rdsdisabled"));
-      }
-      break;
-    case PIChanged:
-      DispatchTrustedEvent(NS_LITERAL_STRING("pichange"));
-      break;
-    case PSChanged:
-      DispatchTrustedEvent(NS_LITERAL_STRING("pschange"));
-      break;
-    case RadiotextChanged:
-      DispatchTrustedEvent(NS_LITERAL_STRING("rtchange"));
-      break;
-    case PTYChanged:
-      DispatchTrustedEvent(NS_LITERAL_STRING("ptychange"));
-      break;
-    case NewRDSGroup:
-      DispatchTrustedEvent(NS_LITERAL_STRING("newrdsgroup"));
-      break;
-    default:
-      MOZ_CRASH();
-  }
-}
-
-/* static */
-bool
-FMRadio::Enabled()
-{
-  return IFMRadioService::Singleton()->IsEnabled();
-}
-
-bool
-FMRadio::RdsEnabled()
-{
-  return IFMRadioService::Singleton()->IsRDSEnabled();
-}
-
-bool
-FMRadio::AntennaAvailable() const
-{
-  return mHasInternalAntenna ? true : (mHeadphoneState != hal::SWITCH_STATE_OFF) &&
-    (mHeadphoneState != hal::SWITCH_STATE_UNKNOWN);
-}
-
-Nullable<double>
-FMRadio::GetFrequency() const
-{
-  return Enabled() ?
-    Nullable<double>(IFMRadioService::Singleton()->GetFrequency()) :
-    Nullable<double>();
-}
-
-double
-FMRadio::FrequencyUpperBound() const
-{
-  return IFMRadioService::Singleton()->GetFrequencyUpperBound();
-}
-
-double
-FMRadio::FrequencyLowerBound() const
-{
-  return IFMRadioService::Singleton()->GetFrequencyLowerBound();
-}
-
-double
-FMRadio::ChannelWidth() const
-{
-  return IFMRadioService::Singleton()->GetChannelWidth();
-}
-
-uint32_t
-FMRadio::RdsGroupMask() const
-{
-  return mRdsGroupMask;
-}
-
-void
-FMRadio::SetRdsGroupMask(uint32_t aRdsGroupMask)
-{
-  mRdsGroupMask = aRdsGroupMask;
-  IFMRadioService::Singleton()->SetRDSGroupMask(aRdsGroupMask);
-}
-
-Nullable<unsigned short>
-FMRadio::GetPi() const
-{
-  return IFMRadioService::Singleton()->GetPi();
-}
-
-Nullable<uint8_t>
-FMRadio::GetPty() const
-{
-  return IFMRadioService::Singleton()->GetPty();
-}
-
-void
-FMRadio::GetPs(DOMString& aPsname) const
-{
-  if (!IFMRadioService::Singleton()->GetPs(aPsname)) {
-    aPsname.SetNull();
-  }
-}
-
-void
-FMRadio::GetRt(DOMString& aRadiotext) const
-{
-  if (!IFMRadioService::Singleton()->GetRt(aRadiotext)) {
-    aRadiotext.SetNull();
-  }
-}
-
-void
-FMRadio::GetRdsgroup(JSContext* cx, JS::MutableHandle<JSObject*> retval)
-{
-  uint64_t group;
-  if (!IFMRadioService::Singleton()->GetRdsgroup(group)) {
-    return;
-  }
-
-  JSObject *rdsgroup = Uint16Array::Create(cx, this, 4);
-  JS::AutoCheckCannotGC nogc;
-  bool isShared = false;
-  uint16_t *data = JS_GetUint16ArrayData(rdsgroup, &isShared, nogc);
-  MOZ_ASSERT(!isShared);  // Because created above.
-  data[3] = group & 0xFFFF;
-  group >>= 16;
-  data[2] = group & 0xFFFF;
-  group >>= 16;
-  data[1] = group & 0xFFFF;
-  group >>= 16;
-  data[0] = group & 0xFFFF;
-
-  JS::ExposeObjectToActiveJS(rdsgroup);
-  retval.set(rdsgroup);
-}
-
-already_AddRefed<DOMRequest>
-FMRadio::Enable(double aFrequency)
-{
-  nsCOMPtr<nsPIDOMWindowInner> win = GetOwner();
-  if (!win) {
-    return nullptr;
-  }
-
-  RefPtr<FMRadioRequest> r =
-    new FMRadioRequest(win, this, FMRadioRequestArgs::TEnableRequestArgs);
-  IFMRadioService::Singleton()->Enable(aFrequency, r);
-
-  return r.forget();
-}
-
-already_AddRefed<DOMRequest>
-FMRadio::Disable()
-{
-  nsCOMPtr<nsPIDOMWindowInner> win = GetOwner();
-  if (!win) {
-    return nullptr;
-  }
-
-  RefPtr<FMRadioRequest> r = new FMRadioRequest(win, this);
-  IFMRadioService::Singleton()->Disable(r);
-
-  return r.forget();
-}
-
-already_AddRefed<DOMRequest>
-FMRadio::SetFrequency(double aFrequency)
-{
-  nsCOMPtr<nsPIDOMWindowInner> win = GetOwner();
-  if (!win) {
-    return nullptr;
-  }
-
-  RefPtr<FMRadioRequest> r = new FMRadioRequest(win, this);
-  IFMRadioService::Singleton()->SetFrequency(aFrequency, r);
-
-  return r.forget();
-}
-
-already_AddRefed<DOMRequest>
-FMRadio::SeekUp()
-{
-  nsCOMPtr<nsPIDOMWindowInner> win = GetOwner();
-  if (!win) {
-    return nullptr;
-  }
-
-  RefPtr<FMRadioRequest> r = new FMRadioRequest(win, this);
-  IFMRadioService::Singleton()->Seek(hal::FM_RADIO_SEEK_DIRECTION_UP, r);
-
-  return r.forget();
-}
-
-already_AddRefed<DOMRequest>
-FMRadio::SeekDown()
-{
-  nsCOMPtr<nsPIDOMWindowInner> win = GetOwner();
-  if (!win) {
-    return nullptr;
-  }
-
-  RefPtr<FMRadioRequest> r = new FMRadioRequest(win, this);
-  IFMRadioService::Singleton()->Seek(hal::FM_RADIO_SEEK_DIRECTION_DOWN, r);
-
-  return r.forget();
-}
-
-already_AddRefed<DOMRequest>
-FMRadio::CancelSeek()
-{
-  nsCOMPtr<nsPIDOMWindowInner> win = GetOwner();
-  if (!win) {
-    return nullptr;
-  }
-
-  RefPtr<FMRadioRequest> r = new FMRadioRequest(win, this);
-  IFMRadioService::Singleton()->CancelSeek(r);
-
-  return r.forget();
-}
-
-already_AddRefed<DOMRequest>
-FMRadio::EnableRDS()
-{
-  nsCOMPtr<nsPIDOMWindowInner> win = GetOwner();
-  if (!win) {
-    return nullptr;
-  }
-
-  RefPtr<FMRadioRequest> r = new FMRadioRequest(win, this);
-  IFMRadioService::Singleton()->EnableRDS(r);
-  return r.forget();
-}
-
-already_AddRefed<DOMRequest>
-FMRadio::DisableRDS()
-{
-  nsCOMPtr<nsPIDOMWindowInner> win = GetOwner();
-  if (!win) {
-    return nullptr;
-  }
-
-  RefPtr<FMRadioRequest> r = new FMRadioRequest(win, this);
-  FMRadioService::Singleton()->DisableRDS(r);
-  return r.forget();
-}
-
-void
-FMRadio::EnableAudioChannelAgent()
-{
-  NS_ENSURE_TRUE_VOID(mAudioChannelAgent);
-
-  AudioPlaybackConfig config;
-  nsresult rv = mAudioChannelAgent->NotifyStartedPlaying(&config,
-                                                         AudioChannelService::AudibleState::eAudible);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return;
-  }
-
-  WindowVolumeChanged(config.mVolume, config.mMuted);
-  WindowSuspendChanged(config.mSuspend);
-
-  mAudioChannelAgentEnabled = true;
-}
-
-NS_IMETHODIMP
-FMRadio::WindowVolumeChanged(float aVolume, bool aMuted)
-{
-  // TODO : Not support to change volume now, so we just close it.
-  IFMRadioService::Singleton()->EnableAudio(!aMuted);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-FMRadio::WindowSuspendChanged(nsSuspendedTypes aSuspend)
-{
-  bool enable = (aSuspend == nsISuspendedTypes::NONE_SUSPENDED);
-  IFMRadioService::Singleton()->EnableAudio(enable);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-FMRadio::WindowAudioCaptureChanged(bool aCapture)
-{
-  return NS_OK;
-}
-
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(FMRadio)
-  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
-  NS_INTERFACE_MAP_ENTRY(nsIAudioChannelAgentCallback)
-NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
-
-NS_IMPL_ADDREF_INHERITED(FMRadio, DOMEventTargetHelper)
-NS_IMPL_RELEASE_INHERITED(FMRadio, DOMEventTargetHelper)
-
-END_FMRADIO_NAMESPACE
-
deleted file mode 100644
--- a/dom/fmradio/FMRadio.h
+++ /dev/null
@@ -1,126 +0,0 @@
-/* -*- 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/. */
-
-#ifndef mozilla_dom_FMRadio_h
-#define mozilla_dom_FMRadio_h
-
-#include "AudioChannelAgent.h"
-#include "FMRadioCommon.h"
-#include "mozilla/DOMEventTargetHelper.h"
-#include "mozilla/HalTypes.h"
-#include "nsCycleCollectionParticipant.h"
-#include "nsWeakReference.h"
-
-BEGIN_FMRADIO_NAMESPACE
-
-class DOMRequest;
-
-class FMRadio final : public DOMEventTargetHelper
-                    , public hal::SwitchObserver
-                    , public FMRadioEventObserver
-                    , public nsSupportsWeakReference
-                    , public nsIAudioChannelAgentCallback
-
-{
-  friend class FMRadioRequest;
-
-public:
-  FMRadio();
-
-  NS_DECL_ISUPPORTS_INHERITED
-  NS_DECL_NSIAUDIOCHANNELAGENTCALLBACK
-
-  NS_REALLY_FORWARD_NSIDOMEVENTTARGET(DOMEventTargetHelper)
-
-  void Init(nsPIDOMWindowInner *aWindow);
-  void Shutdown();
-
-  /* hal::SwitchObserver */
-  virtual void Notify(const hal::SwitchEvent& aEvent) override;
-  /* FMRadioEventObserver */
-  virtual void Notify(const FMRadioEventType& aType) override;
-
-  nsPIDOMWindowInner* GetParentObject() const
-  {
-    return GetOwner();
-  }
-
-  virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
-
-  static bool Enabled();
-
-  bool RdsEnabled();
-
-  bool AntennaAvailable() const;
-
-  Nullable<double> GetFrequency() const;
-
-  double FrequencyUpperBound() const;
-
-  double FrequencyLowerBound() const;
-
-  double ChannelWidth() const;
-
-  uint32_t RdsGroupMask() const;
-
-  void SetRdsGroupMask(uint32_t aRdsGroupMask);
-
-  Nullable<unsigned short> GetPi() const;
-
-  Nullable<uint8_t> GetPty() const;
-
-  void GetPs(DOMString& aPsname) const;
-
-  void GetRt(DOMString& aRadiotext) const;
-
-  void GetRdsgroup(JSContext* cx, JS::MutableHandle<JSObject*> retval);
-
-  already_AddRefed<DOMRequest> Enable(double aFrequency);
-
-  already_AddRefed<DOMRequest> Disable();
-
-  already_AddRefed<DOMRequest> SetFrequency(double aFrequency);
-
-  already_AddRefed<DOMRequest> SeekUp();
-
-  already_AddRefed<DOMRequest> SeekDown();
-
-  already_AddRefed<DOMRequest> CancelSeek();
-
-  already_AddRefed<DOMRequest> EnableRDS();
-
-  already_AddRefed<DOMRequest> DisableRDS();
-
-  IMPL_EVENT_HANDLER(enabled);
-  IMPL_EVENT_HANDLER(disabled);
-  IMPL_EVENT_HANDLER(rdsenabled);
-  IMPL_EVENT_HANDLER(rdsdisabled);
-  IMPL_EVENT_HANDLER(antennaavailablechange);
-  IMPL_EVENT_HANDLER(frequencychange);
-  IMPL_EVENT_HANDLER(pichange);
-  IMPL_EVENT_HANDLER(ptychange);
-  IMPL_EVENT_HANDLER(pschange);
-  IMPL_EVENT_HANDLER(rtchange);
-  IMPL_EVENT_HANDLER(newrdsgroup);
-
-private:
-  ~FMRadio();
-
-  void EnableAudioChannelAgent();
-
-  hal::SwitchState mHeadphoneState;
-  uint32_t mRdsGroupMask;
-  bool mAudioChannelAgentEnabled;
-  bool mHasInternalAntenna;
-  bool mIsShutdown;
-
-  nsCOMPtr<nsIAudioChannelAgent> mAudioChannelAgent;
-};
-
-END_FMRADIO_NAMESPACE
-
-#endif // mozilla_dom_FMRadio_h
-
deleted file mode 100644
--- a/dom/fmradio/FMRadioCommon.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/* -*- 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/. */
-
-#ifndef FMRADIOCOMMON_H_
-#define FMRADIOCOMMON_H_
-
-#include "mozilla/Observer.h"
-
-#undef FM_LOG
-#if defined(ANDROID)
-#include <android/log.h>
-#define FM_LOG(FMRADIO_LOG_INFO, args...) \
-  __android_log_print(ANDROID_LOG_INFO, \
-                      FMRADIO_LOG_INFO, \
-                      ## args)
-#else
-#define FM_LOG(args...)
-#endif
-
-#define BEGIN_FMRADIO_NAMESPACE \
-  namespace mozilla { namespace dom {
-#define END_FMRADIO_NAMESPACE \
-  } /* namespace dom */ } /* namespace mozilla */
-
-BEGIN_FMRADIO_NAMESPACE
-
-enum FMRadioEventType
-{
-  FrequencyChanged,
-  EnabledChanged,
-  RDSEnabledChanged,
-  PIChanged,
-  PSChanged,
-  PTYChanged,
-  RadiotextChanged,
-  NewRDSGroup
-};
-
-typedef mozilla::Observer<FMRadioEventType>     FMRadioEventObserver;
-typedef mozilla::ObserverList<FMRadioEventType> FMRadioEventObserverList;
-
-END_FMRADIO_NAMESPACE
-
-#endif /* FMRADIOCOMMON_H_ */
-
deleted file mode 100644
--- a/dom/fmradio/FMRadioService.cpp
+++ /dev/null
@@ -1,1221 +0,0 @@
-/* -*- 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 "FMRadioService.h"
-#include "mozilla/Hal.h"
-#include "mozilla/ClearOnShutdown.h"
-#include "nsIAudioManager.h"
-#include "AudioManager.h"
-#include "nsDOMClassInfo.h"
-#include "nsContentUtils.h"
-#include "mozilla/LazyIdleThread.h"
-#include "mozilla/Preferences.h"
-#include "mozilla/dom/FMRadioChild.h"
-#include "mozilla/dom/ScriptSettings.h"
-#include "nsIObserverService.h"
-#include "nsISettingsService.h"
-#include "nsJSUtils.h"
-#include "mozilla/dom/BindingUtils.h"
-#include "mozilla/dom/SettingChangeNotificationBinding.h"
-#include "mozilla/DebugOnly.h"
-
-#define TUNE_THREAD_TIMEOUT_MS  5000
-
-#define BAND_87500_108000_kHz 1
-#define BAND_76000_108000_kHz 2
-#define BAND_76000_90000_kHz  3
-
-#define MOZSETTINGS_CHANGED_ID "mozsettings-changed"
-#define SETTING_KEY_AIRPLANEMODE_ENABLED "airplaneMode.enabled"
-
-#define DOM_PARSED_RDS_GROUPS ((0x2 << 30) | (0x3 << 4) | (0x3 << 0))
-
-using mozilla::Preferences;
-
-BEGIN_FMRADIO_NAMESPACE
-
-// static
-IFMRadioService*
-IFMRadioService::Singleton()
-{
-  if (!XRE_IsParentProcess()) {
-    return FMRadioChild::Singleton();
-  } else {
-    return FMRadioService::Singleton();
-  }
-}
-
-StaticRefPtr<FMRadioService> FMRadioService::sFMRadioService;
-
-FMRadioService::FMRadioService()
-  : mPendingFrequencyInKHz(0)
-  , mState(Disabled)
-  , mHasReadAirplaneModeSetting(false)
-  , mAirplaneModeEnabled(false)
-  , mRDSEnabled(false)
-  , mPendingRequest(nullptr)
-  , mObserverList(FMRadioEventObserverList())
-  , mRDSGroupMask(0)
-  , mLastPI(0)
-  , mPI(0)
-  , mPTY(0)
-  , mPISet(false)
-  , mPTYSet(false)
-  , mRDSLock("FMRadioService::mRDSLock")
-  , mPSNameState(0)
-  , mRadiotextAB(false)
-  , mRDSGroupSet(false)
-  , mPSNameSet(false)
-  , mRadiotextSet(false)
-{
-  memset(mPSName, 0, sizeof(mPSName));
-  memset(mRadiotext, 0, sizeof(mRadiotext));
-  memset(mTempPSName, 0, sizeof(mTempPSName));
-  memset(mTempRadiotext, 0, sizeof(mTempRadiotext));
-
-  // Read power state and frequency from Hal.
-  mEnabled = hal::IsFMRadioOn();
-  if (mEnabled) {
-    mPendingFrequencyInKHz = hal::GetFMRadioFrequency();
-    SetState(Enabled);
-  }
-
-  switch (Preferences::GetInt("dom.fmradio.band", BAND_87500_108000_kHz)) {
-    case BAND_76000_90000_kHz:
-      mUpperBoundInKHz = 90000;
-      mLowerBoundInKHz = 76000;
-      break;
-    case BAND_76000_108000_kHz:
-      mUpperBoundInKHz = 108000;
-      mLowerBoundInKHz = 76000;
-      break;
-    case BAND_87500_108000_kHz:
-    default:
-      mUpperBoundInKHz = 108000;
-      mLowerBoundInKHz = 87500;
-      break;
-  }
-
-  mChannelWidthInKHz = Preferences::GetInt("dom.fmradio.channelWidth", 100);
-  switch (mChannelWidthInKHz) {
-    case 50:
-    case 100:
-    case 200:
-      break;
-    default:
-      NS_WARNING("Invalid channel width specified in dom.fmradio.channelwidth");
-      mChannelWidthInKHz = 100;
-      break;
-  }
-
-  mPreemphasis = Preferences::GetInt("dom.fmradio.preemphasis", 50);
-  switch (mPreemphasis) {
-    // values in microseconds
-    case 0:
-    case 50:
-    case 75:
-      break;
-    default:
-      NS_WARNING("Invalid preemphasis specified in dom.fmradio.preemphasis");
-      mPreemphasis = 50;
-      break;
-  }
-
-  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
-
-  if (obs && NS_FAILED(obs->AddObserver(this,
-                                        MOZSETTINGS_CHANGED_ID,
-                                        /* useWeak */ false))) {
-    NS_WARNING("Failed to add settings change observer!");
-  }
-
-  hal::RegisterFMRadioObserver(this);
-  hal::RegisterFMRadioRDSObserver(this);
-}
-
-FMRadioService::~FMRadioService()
-{
-  hal::UnregisterFMRadioRDSObserver(this);
-  hal::UnregisterFMRadioObserver(this);
-}
-
-void
-FMRadioService::EnableFMRadio()
-{
-  hal::FMRadioSettings info;
-  info.upperLimit() = mUpperBoundInKHz;
-  info.lowerLimit() = mLowerBoundInKHz;
-  info.spaceType() = mChannelWidthInKHz;
-  info.preEmphasis() = mPreemphasis;
-
-  hal::EnableFMRadio(info);
-
-  if (!mTuneThread) {
-    // hal::FMRadioSeek and hal::SetFMRadioFrequency run on this thread. These
-    // call ioctls that can stall the main thread, so we run them here.
-    mTuneThread = new LazyIdleThread(
-      TUNE_THREAD_TIMEOUT_MS, NS_LITERAL_CSTRING("FM Tuning"));
-  }
-}
-
-/**
- * Read the airplane-mode setting, if the airplane-mode is not enabled, we
- * enable the FM radio.
- */
-class ReadAirplaneModeSettingTask final : public nsISettingsServiceCallback
-{
-public:
-  NS_DECL_ISUPPORTS
-
-  ReadAirplaneModeSettingTask(RefPtr<FMRadioReplyRunnable> aPendingRequest)
-    : mPendingRequest(aPendingRequest) { }
-
-  NS_IMETHOD
-  Handle(const nsAString& aName, JS::Handle<JS::Value> aResult)
-  {
-    RefPtr<FMRadioService> fmRadioService = FMRadioService::Singleton();
-    MOZ_ASSERT(mPendingRequest == fmRadioService->mPendingRequest);
-    
-    fmRadioService->mHasReadAirplaneModeSetting = true;
-
-    if (!aResult.isBoolean()) {
-      // Failed to read the setting value, set the state back to Disabled.
-      fmRadioService->TransitionState(
-        ErrorResponse(NS_LITERAL_STRING("Unexpected error")), Disabled);
-      return NS_OK;
-    }
-
-    fmRadioService->mAirplaneModeEnabled = aResult.toBoolean();
-    if (!fmRadioService->mAirplaneModeEnabled) {
-      NS_DispatchToMainThread(NS_NewRunnableFunction(
-        [fmRadioService] () -> void {
-          fmRadioService->EnableFMRadio();
-        }
-      ));
-    } else {
-      // Airplane mode is enabled, set the state back to Disabled.
-      fmRadioService->TransitionState(ErrorResponse(
-        NS_LITERAL_STRING("Airplane mode currently enabled")), Disabled);
-    }
-
-    return NS_OK;
-  }
-
-  NS_IMETHOD
-  HandleError(const nsAString& aName)
-  {
-    FMRadioService* fmRadioService = FMRadioService::Singleton();
-    MOZ_ASSERT(mPendingRequest == fmRadioService->mPendingRequest);
-
-    fmRadioService->TransitionState(ErrorResponse(
-      NS_LITERAL_STRING("Unexpected error")), Disabled);
-
-    return NS_OK;
-  }
-
-protected:
-  ~ReadAirplaneModeSettingTask() {}
-
-private:
-  RefPtr<FMRadioReplyRunnable> mPendingRequest;
-};
-
-NS_IMPL_ISUPPORTS(ReadAirplaneModeSettingTask, nsISettingsServiceCallback)
-
-void
-FMRadioService::DisableFMRadio()
-{
-  if (mTuneThread) {
-    mTuneThread->Shutdown();
-    mTuneThread = nullptr;
-  }
-  // Fix Bug 796733. DisableFMRadio should be called before
-  // SetFmRadioAudioEnabled to prevent the annoying beep sound.
-  hal::DisableFMRadio();
-  EnableAudio(false);
-};
-
-void
-FMRadioService::DispatchFMRadioEventToMainThread(enum FMRadioEventType aType)
-{
-  RefPtr<FMRadioService> self = this;
-  NS_DispatchToMainThread(NS_NewRunnableFunction(
-    [self, aType] () -> void {
-      self->NotifyFMRadioEvent(aType);
-    }
-  ));
-}
-
-void
-FMRadioService::TransitionState(const FMRadioResponseType& aResponse,
-                                FMRadioState aState)
-{
-  if (mPendingRequest) {
-    mPendingRequest->SetReply(aResponse);
-    NS_DispatchToMainThread(mPendingRequest);
-  }
-
-  SetState(aState);
-}
-
-void
-FMRadioService::SetState(FMRadioState aState)
-{
-  mState = aState;
-  mPendingRequest = nullptr;
-}
-
-void
-FMRadioService::AddObserver(FMRadioEventObserver* aObserver)
-{
-  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
-  mObserverList.AddObserver(aObserver);
-}
-
-void
-FMRadioService::RemoveObserver(FMRadioEventObserver* aObserver)
-{
-  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
-  mObserverList.RemoveObserver(aObserver);
-
-  if (mObserverList.Length() == 0)
-  {
-    // Turning off the FM radio HW because observer list is empty.
-    if (hal::IsFMRadioOn()) {
-      DoDisable();
-    }
-  }
-}
-
-void
-FMRadioService::EnableAudio(bool aAudioEnabled)
-{
-  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
-
-  nsCOMPtr<nsIAudioManager> audioManager =
-    do_GetService("@mozilla.org/telephony/audiomanager;1");
-  if (!audioManager) {
-    return;
-  }
-
-  bool audioEnabled;
-  audioManager->GetFmRadioAudioEnabled(&audioEnabled);
-  if (audioEnabled != aAudioEnabled) {
-    audioManager->SetFmRadioAudioEnabled(aAudioEnabled);
-  }
-}
-
-/**
- * Round the frequency to match the range of frequency and the channel width. If
- * the given frequency is out of range, return 0. For example:
- *  - lower: 87500KHz, upper: 108000KHz, channel width: 200KHz
- *    87.6MHz is rounded to 87700KHz
- *    87.58MHz is rounded to 87500KHz
- *    87.49MHz is rounded to 87500KHz
- *    109MHz is not rounded, 0 will be returned
- *
- * We take frequency in MHz to prevent precision losing, and return rounded
- * value in KHz for Gonk using.
- */
-int32_t
-FMRadioService::RoundFrequency(double aFrequencyInMHz)
-{
-  double halfChannelWidthInMHz = mChannelWidthInKHz / 1000.0 / 2;
-
-  // Make sure 87.49999MHz would be rounded to the lower bound when
-  // the lower bound is 87500KHz.
-  if (aFrequencyInMHz < mLowerBoundInKHz / 1000.0 - halfChannelWidthInMHz ||
-      aFrequencyInMHz > mUpperBoundInKHz / 1000.0 + halfChannelWidthInMHz) {
-    return 0;
-  }
-
-  int32_t partToBeRounded = round(aFrequencyInMHz * 1000) - mLowerBoundInKHz;
-  int32_t roundedPart = round(partToBeRounded / (double)mChannelWidthInKHz) *
-                        mChannelWidthInKHz;
-
-  return mLowerBoundInKHz + roundedPart;
-}
-
-bool
-FMRadioService::IsEnabled() const
-{
-  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
-  return hal::IsFMRadioOn();
-}
-
-bool
-FMRadioService::IsRDSEnabled() const
-{
-  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
-  return mRDSEnabled;
-}
-
-double
-FMRadioService::GetFrequency() const
-{
-  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
-  if (IsEnabled()) {
-    int32_t frequencyInKHz = hal::GetFMRadioFrequency();
-    return frequencyInKHz / 1000.0;
-  }
-
-  return 0;
-}
-
-double
-FMRadioService::GetFrequencyUpperBound() const
-{
-  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
-  return mUpperBoundInKHz / 1000.0;
-}
-
-double
-FMRadioService::GetFrequencyLowerBound() const
-{
-  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
-  return mLowerBoundInKHz / 1000.0;
-}
-
-double
-FMRadioService::GetChannelWidth() const
-{
-  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
-  return mChannelWidthInKHz / 1000.0;
-}
-
-Nullable<unsigned short>
-FMRadioService::GetPi() const
-{
-  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
-  if (!mPISet) {
-    return Nullable<unsigned short>();
-  }
-  return Nullable<unsigned short>(mPI);
-}
-
-Nullable<uint8_t>
-FMRadioService::GetPty() const
-{
-  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
-  if (!mPTYSet) {
-    return Nullable<uint8_t>();
-  }
-  return Nullable<uint8_t>(mPTY);
-}
-
-bool
-FMRadioService::GetPs(nsString& aPSName)
-{
-  MutexAutoLock lock(mRDSLock);
-  if (mPSNameSet) {
-    aPSName = nsString(mPSName);
-  }
-  return mPSNameSet;
-}
-
-bool
-FMRadioService::GetRt(nsString& aRadiotext)
-{
-  MutexAutoLock lock(mRDSLock);
-  if (mRadiotextSet) {
-    aRadiotext = nsString(mRadiotext);
-  }
-  return mRadiotextSet;
-}
-
-bool
-FMRadioService::GetRdsgroup(uint64_t& aRDSGroup)
-{
-  MutexAutoLock lock(mRDSLock);
-  aRDSGroup = mRDSGroup;
-  return mRDSGroupSet;
-}
-
-void
-FMRadioService::Enable(double aFrequencyInMHz,
-                       FMRadioReplyRunnable* aReplyRunnable)
-{
-  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
-  MOZ_ASSERT(aReplyRunnable);
-
-  switch (mState) {
-    case Seeking:
-    case Enabled:
-      aReplyRunnable->SetReply(
-        ErrorResponse(NS_LITERAL_STRING("FM radio currently enabled")));
-      NS_DispatchToMainThread(aReplyRunnable);
-      return;
-    case Disabling:
-      aReplyRunnable->SetReply(
-        ErrorResponse(NS_LITERAL_STRING("FM radio currently disabling")));
-      NS_DispatchToMainThread(aReplyRunnable);
-      return;
-    case Enabling:
-      aReplyRunnable->SetReply(
-        ErrorResponse(NS_LITERAL_STRING("FM radio currently enabling")));
-      NS_DispatchToMainThread(aReplyRunnable);
-      return;
-    case Disabled:
-      break;
-  }
-
-  int32_t roundedFrequency = RoundFrequency(aFrequencyInMHz);
-
-  if (!roundedFrequency) {
-    aReplyRunnable->SetReply(ErrorResponse(
-      NS_LITERAL_STRING("Frequency is out of range")));
-    NS_DispatchToMainThread(aReplyRunnable);
-    return;
-  }
-
-  if (mHasReadAirplaneModeSetting && mAirplaneModeEnabled) {
-    aReplyRunnable->SetReply(ErrorResponse(
-      NS_LITERAL_STRING("Airplane mode currently enabled")));
-    NS_DispatchToMainThread(aReplyRunnable);
-    return;
-  }
-
-  SetState(Enabling);
-  // Cache the enable request just in case disable() is called
-  // while the FM radio HW is being enabled.
-  mPendingRequest = aReplyRunnable;
-
-  // Cache the frequency value, and set it after the FM radio HW is enabled
-  mPendingFrequencyInKHz = roundedFrequency;
-
-  if (!mHasReadAirplaneModeSetting) {
-    nsCOMPtr<nsISettingsService> settings =
-      do_GetService("@mozilla.org/settingsService;1");
-
-    nsCOMPtr<nsISettingsServiceLock> settingsLock;
-    nsresult rv = settings->CreateLock(nullptr, getter_AddRefs(settingsLock));
-    if (NS_FAILED(rv)) {
-      TransitionState(ErrorResponse(
-        NS_LITERAL_STRING("Can't create settings lock")), Disabled);
-      return;
-    }
-
-    RefPtr<ReadAirplaneModeSettingTask> callback =
-      new ReadAirplaneModeSettingTask(mPendingRequest);
-
-    rv = settingsLock->Get(SETTING_KEY_AIRPLANEMODE_ENABLED, callback);
-    if (NS_FAILED(rv)) {
-      TransitionState(ErrorResponse(
-        NS_LITERAL_STRING("Can't get settings lock")), Disabled);
-    }
-
-    return;
-  }
-
-  RefPtr<FMRadioService> self = this;
-  NS_DispatchToMainThread(NS_NewRunnableFunction(
-    [self] () -> void {
-      self->EnableFMRadio();
-    }
-  ));
-}
-
-void
-FMRadioService::Disable(FMRadioReplyRunnable* aReplyRunnable)
-{
-  // When airplane-mode is enabled, we will call this function from
-  // FMRadioService::Observe without passing a FMRadioReplyRunnable,
-  // so we have to check if |aReplyRunnable| is null before we dispatch it.
-  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
-
-  switch (mState) {
-    case Disabling:
-      if (aReplyRunnable) {
-        aReplyRunnable->SetReply(
-          ErrorResponse(NS_LITERAL_STRING("FM radio currently disabling")));
-        NS_DispatchToMainThread(aReplyRunnable);
-      }
-      return;
-    case Disabled:
-      if (aReplyRunnable) {
-        aReplyRunnable->SetReply(
-          ErrorResponse(NS_LITERAL_STRING("FM radio currently disabled")));
-        NS_DispatchToMainThread(aReplyRunnable);
-      }
-      return;
-    case Enabled:
-    case Enabling:
-    case Seeking:
-      break;
-  }
-
-  RefPtr<FMRadioReplyRunnable> enablingRequest = mPendingRequest;
-
-  // If the FM Radio is currently seeking, no fail-to-seek or similar
-  // event will be fired, execute the seek callback manually.
-  if (mState == Seeking) {
-    TransitionState(ErrorResponse(
-      NS_LITERAL_STRING("Seek action is cancelled")), Disabling);
-  }
-
-  FMRadioState preState = mState;
-  SetState(Disabling);
-  mPendingRequest = aReplyRunnable;
-
-  if (preState == Enabling) {
-    // If the radio is currently enabling, we fire the error callback on the
-    // enable request immediately. When the radio finishes enabling, we'll call
-    // DoDisable and fire the success callback on the disable request.
-    enablingRequest->SetReply(
-      ErrorResponse(NS_LITERAL_STRING("Enable action is cancelled")));
-    NS_DispatchToMainThread(enablingRequest);
-
-    // If we haven't read the airplane mode settings yet we won't enable the
-    // FM radio HW, so fail the disable request immediately.
-    if (!mHasReadAirplaneModeSetting) {
-      SetState(Disabled);
-
-      if (aReplyRunnable) {
-        aReplyRunnable->SetReply(SuccessResponse());
-        NS_DispatchToMainThread(aReplyRunnable);
-      }
-    }
-
-    return;
-  }
-
-  DoDisable();
-}
-
-void
-FMRadioService::DoDisable()
-{
-  // To make such codes work:
-  //    navigator.mozFMRadio.disable();
-  //    navigator.mozFMRadio.ondisabled = function() {
-  //      console.log("We will catch disabled event ");
-  //    };
-  // we need to call hal::DisableFMRadio() asynchronously. Same reason for
-  // EnableFMRadio and hal::SetFMRadioFrequency.
-  RefPtr<FMRadioService> self = this;
-  NS_DispatchToMainThread(NS_NewRunnableFunction(
-    [self] () -> void {
-      self->DisableFMRadio();
-    }
-  ));
-}
-
-void
-FMRadioService::SetFrequency(double aFrequencyInMHz,
-                             FMRadioReplyRunnable* aReplyRunnable)
-{
-  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
-  MOZ_ASSERT(aReplyRunnable);
-
-  switch (mState) {
-    case Disabled:
-      aReplyRunnable->SetReply(
-        ErrorResponse(NS_LITERAL_STRING("FM radio currently disabled")));
-      NS_DispatchToMainThread(aReplyRunnable);
-      return;
-    case Enabling:
-      aReplyRunnable->SetReply(
-        ErrorResponse(NS_LITERAL_STRING("FM radio currently enabling")));
-      NS_DispatchToMainThread(aReplyRunnable);
-      return;
-    case Disabling:
-      aReplyRunnable->SetReply(
-        ErrorResponse(NS_LITERAL_STRING("FM radio currently disabling")));
-      NS_DispatchToMainThread(aReplyRunnable);
-      return;
-    case Seeking:
-      hal::CancelFMRadioSeek();
-      TransitionState(ErrorResponse(
-        NS_LITERAL_STRING("Seek action is cancelled")), Enabled);
-      break;
-    case Enabled:
-      break;
-  }
-
-  int32_t roundedFrequency = RoundFrequency(aFrequencyInMHz);
-
-  if (!roundedFrequency) {
-    aReplyRunnable->SetReply(ErrorResponse(
-      NS_LITERAL_STRING("Frequency is out of range")));
-    NS_DispatchToMainThread(aReplyRunnable);
-    return;
-  }
-
-  mTuneThread->Dispatch(
-    NS_NewRunnableFunction(
-      [roundedFrequency] () -> void {
-        hal::SetFMRadioFrequency(roundedFrequency);
-      }
-    ), nsIThread::DISPATCH_NORMAL);
-
-  aReplyRunnable->SetReply(SuccessResponse());
-  NS_DispatchToMainThread(aReplyRunnable);
-}
-
-void
-FMRadioService::Seek(hal::FMRadioSeekDirection aDirection,
-                     FMRadioReplyRunnable* aReplyRunnable)
-{
-  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
-  MOZ_ASSERT(aReplyRunnable);
-
-  switch (mState) {
-    case Enabling:
-      aReplyRunnable->SetReply(
-        ErrorResponse(NS_LITERAL_STRING("FM radio currently enabling")));
-      NS_DispatchToMainThread(aReplyRunnable);
-      return;
-    case Disabled:
-      aReplyRunnable->SetReply(
-        ErrorResponse(NS_LITERAL_STRING("FM radio currently disabled")));
-      NS_DispatchToMainThread(aReplyRunnable);
-      return;
-    case Seeking:
-      aReplyRunnable->SetReply(
-        ErrorResponse(NS_LITERAL_STRING("FM radio currently seeking")));
-      NS_DispatchToMainThread(aReplyRunnable);
-      return;
-    case Disabling:
-      aReplyRunnable->SetReply(
-        ErrorResponse(NS_LITERAL_STRING("FM radio currently disabling")));
-      NS_DispatchToMainThread(aReplyRunnable);
-      return;
-    case Enabled:
-      break;
-  }
-
-  SetState(Seeking);
-  mPendingRequest = aReplyRunnable;
-
-  mTuneThread->Dispatch(
-    NS_NewRunnableFunction(
-      [aDirection] () -> void {
-        switch (aDirection) {
-          case hal::FM_RADIO_SEEK_DIRECTION_UP:
-          case hal::FM_RADIO_SEEK_DIRECTION_DOWN:
-            hal::FMRadioSeek(aDirection);
-            break;
-          default:
-            MOZ_CRASH();
-        }
-      }
-    ), nsIThread::DISPATCH_NORMAL);
-}
-
-void
-FMRadioService::CancelSeek(FMRadioReplyRunnable* aReplyRunnable)
-{
-  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
-  MOZ_ASSERT(aReplyRunnable);
-
-  // We accept canceling seek request only if it's currently seeking.
-  if (mState != Seeking) {
-    aReplyRunnable->SetReply(
-      ErrorResponse(NS_LITERAL_STRING("FM radio currently not seeking")));
-    NS_DispatchToMainThread(aReplyRunnable);
-    return;
-  }
-
-  // Cancel the seek immediately to prevent it from completing.
-  hal::CancelFMRadioSeek();
-
-  TransitionState(
-    ErrorResponse(NS_LITERAL_STRING("Seek action is cancelled")), Enabled);
-
-  aReplyRunnable->SetReply(SuccessResponse());
-  NS_DispatchToMainThread(aReplyRunnable);
-}
-
-void
-FMRadioService::SetRDSGroupMask(uint32_t aRDSGroupMask)
-{
-  mRDSGroupMask = aRDSGroupMask;
-  if (hal::IsFMRadioOn() && mRDSEnabled) {
-    DebugOnly<bool> enabled = hal::EnableRDS(mRDSGroupMask | DOM_PARSED_RDS_GROUPS);
-    MOZ_ASSERT(enabled);
-  }
-}
-
-void
-FMRadioService::EnableRDS(FMRadioReplyRunnable* aReplyRunnable)
-{
-  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
-  MOZ_ASSERT(aReplyRunnable);
-
-  if (hal::IsFMRadioOn()) {
-    if (!hal::EnableRDS(mRDSGroupMask | DOM_PARSED_RDS_GROUPS)) {
-      aReplyRunnable->SetReply(
-        ErrorResponse(NS_LITERAL_STRING("Could not enable RDS")));
-      NS_DispatchToMainThread(aReplyRunnable);
-      return;
-    }
-  }
-
-  mRDSEnabled = true;
-
-  aReplyRunnable->SetReply(SuccessResponse());
-  NS_DispatchToMainThread(aReplyRunnable);
-
-  DispatchFMRadioEventToMainThread(RDSEnabledChanged);
-}
-
-void
-FMRadioService::DisableRDS(FMRadioReplyRunnable* aReplyRunnable)
-{
-  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
-  MOZ_ASSERT(aReplyRunnable);
-
-  if (hal::IsFMRadioOn()) {
-    hal::DisableRDS();
-  }
-
-  aReplyRunnable->SetReply(SuccessResponse());
-  NS_DispatchToMainThread(aReplyRunnable);
-
-  if (mRDSEnabled) {
-    mRDSEnabled = false;
-
-    DispatchFMRadioEventToMainThread(RDSEnabledChanged);
-  }
-}
-
-NS_IMETHODIMP
-FMRadioService::Observe(nsISupports* aSubject,
-                        const char* aTopic,
-                        const char16_t* aData)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(sFMRadioService);
-
-  if (strcmp(aTopic, MOZSETTINGS_CHANGED_ID) != 0) {
-    return NS_OK;
-  }
-
-  // The string that we're interested in will be a JSON string looks like:
-  //  {"key":"airplaneMode.enabled","value":true}
-  RootedDictionary<dom::SettingChangeNotification> setting(RootingCx());
-  if (!WrappedJSToDictionary(aSubject, setting)) {
-    return NS_OK;
-  }
-  if (!setting.mKey.EqualsASCII(SETTING_KEY_AIRPLANEMODE_ENABLED)) {
-    return NS_OK;
-  }
-  if (!setting.mValue.isBoolean()) {
-    return NS_OK;
-  }
-
-  mAirplaneModeEnabled = setting.mValue.toBoolean();
-  mHasReadAirplaneModeSetting = true;
-
-  // Disable the FM radio HW if Airplane mode is enabled.
-  if (mAirplaneModeEnabled) {
-    Disable(nullptr);
-  }
-
-  return NS_OK;
-}
-
-void
-FMRadioService::NotifyFMRadioEvent(FMRadioEventType aType)
-{
-  mObserverList.Broadcast(aType);
-}
-
-void
-FMRadioService::Notify(const hal::FMRadioOperationInformation& aInfo)
-{
-  switch (aInfo.operation()) {
-    case hal::FM_RADIO_OPERATION_ENABLE:
-      MOZ_ASSERT(hal::IsFMRadioOn());
-      MOZ_ASSERT(mState == Disabling || mState == Enabling);
-
-      // If we're disabling, disable the radio right now.
-      if (mState == Disabling) {
-        DoDisable();
-        return;
-      }
-
-      // Fire success callback on the enable request.
-      TransitionState(SuccessResponse(), Enabled);
-
-      // To make sure the FM app will get the right frequency after the FM
-      // radio is enabled, we have to set the frequency first.
-      hal::SetFMRadioFrequency(mPendingFrequencyInKHz);
-
-      // Bug 949855: enable audio after the FM radio HW is enabled, to make sure
-      // 'hw.fm.isAnalog' could be detected as |true| during first time launch.
-      // This case is for audio output on analog path, i.e. 'ro.moz.fm.noAnalog'
-      // is not |true|.
-      EnableAudio(true);
-
-      // Update the current frequency without sending the`FrequencyChanged`
-      // event, to make sure the FM app will get the right frequency when the
-      // `EnabledChange` event is sent.
-      mPendingFrequencyInKHz = hal::GetFMRadioFrequency();
-      UpdatePowerState();
-
-      // The frequency was changed from '0' to some meaningful number, so we
-      // should send the `FrequencyChanged` event manually.
-      NotifyFMRadioEvent(FrequencyChanged);
-
-      if (mRDSEnabled) {
-        mRDSEnabled = hal::EnableRDS(mRDSGroupMask | DOM_PARSED_RDS_GROUPS);
-        if (!mRDSEnabled) {
-          NotifyFMRadioEvent(RDSEnabledChanged);
-        }
-      }
-      break;
-    case hal::FM_RADIO_OPERATION_DISABLE:
-      MOZ_ASSERT(mState == Disabling);
-
-      mPISet = false;
-      mPTYSet = false;
-      memset(mPSName, 0, sizeof(mPSName));
-      memset(mRadiotext, 0, sizeof(mRadiotext));
-      TransitionState(SuccessResponse(), Disabled);
-      UpdatePowerState();
-      break;
-    case hal::FM_RADIO_OPERATION_SEEK:
-
-      // Seek action might be cancelled by SetFrequency(), we need to check if
-      // the current state is Seeking.
-      if (mState == Seeking) {
-        TransitionState(SuccessResponse(), Enabled);
-      }
-
-      UpdateFrequency();
-      break;
-    case hal::FM_RADIO_OPERATION_TUNE:
-      UpdateFrequency();
-      break;
-    default:
-      MOZ_CRASH();
-  }
-}
-
-/* This is defined by the RDS standard */
-static const uint16_t sRDSToUnicodeMap[256] = {
-  // The lower half differs from ASCII in 0x1F, 0x24, 0x5E, 0x7E
-  // Most control characters are replaced with 0x20 (space)
-  // 0x0-
-  0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020,
-  0x0020, 0x0009, 0x000A, 0x000B, 0x0020, 0x00D0, 0x0020, 0x0020,
-
-  // 0x1-
-  0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020,
-  0x0020, 0x0020, 0x0020, 0x001B, 0x0020, 0x0020, 0x0020, 0x00AD,
-
-  // 0x2-
-  0x0020, 0x0021, 0x0022, 0x0023, 0x00A4, 0x0025, 0x0026, 0x0027,
-  0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
-
-  // 0x3-
-  0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
-  0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
-
-  // 0x4-
-  0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
-  0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
-
-  // 0x5-
-  0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
-  0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x2015, 0x005F,
-
-  // 0x6-
-  0x2551, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
-  0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
-
-  // 0x7-
-  0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
-  0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x00AF, 0x007F,
-
-  // 0x8-
-  0x00E1, 0x00E0, 0x00E9, 0x00E8, 0x00ED, 0x00EC, 0x00F3, 0x00F2,
-  0x00FA, 0x00F9, 0x00D1, 0x00C7, 0x015E, 0x00DF, 0x00A1, 0x0132,
-
-  // 0x9-
-  0x00E2, 0x00E4, 0x00EA, 0x00EB, 0x00EE, 0x00EF, 0x00F4, 0x00F6,
-  0x00FB, 0x00FC, 0x00F1, 0x00E7, 0x015F, 0x011F, 0x0131, 0x0133,
-
-  // 0xA-
-  0x00AA, 0x03B1, 0x00A9, 0x2030, 0x011E, 0x011B, 0x0148, 0x0151,
-  0x03C0, 0x20AC, 0x00A3, 0x0024, 0x2190, 0x2191, 0x2192, 0x2193,
-
-  // 0xB-
-  0x00BA, 0x00B9, 0x00B2, 0x00B3, 0x00B1, 0x0130, 0x0144, 0x0171,
-  0x03BC, 0x00BF, 0x00F7, 0x00B0, 0x00BC, 0x00BD, 0x00BE, 0x00A7,
-
-  // 0xC-
-  0x00C1, 0x00C0, 0x00C9, 0x00C8, 0x00CD, 0x00CC, 0x00D3, 0x00D2,
-  0x00DA, 0x00D9, 0x0158, 0x010C, 0x0160, 0x017D, 0x00D0, 0x013F,
-
-  // 0xD-
-  0x00C2, 0x00C4, 0x00CA, 0x00CB, 0x00CE, 0x00CF, 0x00D4, 0x00D6,
-  0x00DB, 0x00DC, 0x0159, 0x010D, 0x0161, 0x017E, 0x0111, 0x0140,
-
-  // 0xE-
-  0x00C3, 0x00C5, 0x00C6, 0x0152, 0x0177, 0x00DD, 0x00D5, 0x00D8,
-  0x00DE, 0x014A, 0x0154, 0x0106, 0x015A, 0x0179, 0x0166, 0x00F0,
-
-  // 0xF-
-  0x00E3, 0x00E5, 0x00E6, 0x0153, 0x0175, 0x00FD, 0x00F5, 0x00F8,
-  0x00FE, 0x014B, 0x0155, 0x0107, 0x015B, 0x017A, 0x0167, 0x0020,
-};
-
-void
-FMRadioService::Notify(const hal::FMRadioRDSGroup& aRDSGroup)
-{
-  uint16_t blocks[4];
-  blocks[0] = aRDSGroup.blockA();
-  blocks[1] = aRDSGroup.blockB();
-  blocks[2] = aRDSGroup.blockC();
-  blocks[3] = aRDSGroup.blockD();
-
-  /* Bit 11 in block B determines whether this is a type B group. */
-  uint16_t lastPI = blocks[1] & (1 << 11) ? blocks[2] : mLastPI;
-
-  /* Update PI if it's not set or if we get two PI with the new value. */
-  if ((mPI != blocks[0] && lastPI == blocks[0]) || !mPISet) {
-    mPI = blocks[0];
-    if (!mPISet) {
-      mPSNameState = 0;
-      mRadiotextState = 0;
-      memset(mTempPSName, 0, sizeof(mTempPSName));
-      memset(mTempRadiotext, 0, sizeof(mTempRadiotext));
-    }
-    mPISet = true;
-
-    DispatchFMRadioEventToMainThread(PIChanged);
-  }
-  mLastPI = blocks[0];
-
-  /* PTY is also updated using the same logic as PI */
-  uint16_t pty = (blocks[1] >> 5) & 0x1F;
-  if ((mPTY != pty && pty == mLastPTY) || !mPTYSet) {
-    mPTY = pty;
-    mPTYSet = true;
-
-    DispatchFMRadioEventToMainThread(PTYChanged);
-  }
-  mLastPTY = pty;
-
-  uint16_t grouptype = blocks[1] >> 11;
-  switch (grouptype) {
-    case 0: // 0a
-    case 1: // 0b
-    {
-      uint16_t segmentAddr = (blocks[1] & 0x3);
-      // mPSNameState is a bitmask that lets us ensure all segments
-      // are received before updating the PS name.
-      if (!segmentAddr) {
-        mPSNameState = 1;
-      } else {
-        mPSNameState |= 1 << segmentAddr;
-      }
-
-      uint16_t offset = segmentAddr << 1;
-      mTempPSName[offset] = sRDSToUnicodeMap[blocks[3] >> 8];
-      mTempPSName[offset + 1] = sRDSToUnicodeMap[blocks[3] & 0xFF];
-
-      if (mPSNameState != 0xF) {
-        break;
-      }
-
-      mPSNameState = 0;
-      if (memcmp(mTempPSName, mPSName, sizeof(mTempPSName))) {
-        MutexAutoLock lock(mRDSLock);
-        mPSNameSet = true;
-        memcpy(mPSName, mTempPSName, sizeof(mTempPSName));
-
-        DispatchFMRadioEventToMainThread(PSChanged);
-      }
-      break;
-    }
-    case 4: // 2a Radiotext
-    {
-      uint16_t segmentAddr = (blocks[1] & 0xF);
-      bool textAB = blocks[1] & (1 << 5);
-      if (textAB != mRadiotextAB) {
-        mRadiotextState = 0;
-        memset(mTempRadiotext, 0, sizeof(mTempRadiotext));
-        mRadiotextAB = textAB;
-        MutexAutoLock lock(mRDSLock);
-        memset(mRadiotext, 0, sizeof(mRadiotext));
-
-        DispatchFMRadioEventToMainThread(RadiotextChanged);
-      }
-
-      // mRadiotextState is a bitmask that lets us ensure all segments
-      // are received before updating the radiotext.
-      if (!segmentAddr) {
-        mRadiotextState = 1;
-      } else {
-        mRadiotextState |= 1 << segmentAddr;
-      }
-
-      uint8_t segment[4];
-      segment[0] = blocks[2] >> 8;
-      segment[1] = blocks[2] & 0xFF;
-      segment[2] = blocks[3] >> 8;
-      segment[3] = blocks[3] & 0xFF;
-
-      uint16_t offset = segmentAddr << 2;
-      bool done = false;
-      for (int i = 0; i < 4; i++) {
-        if (segment[i] == '\r') {
-          mTempRadiotext[offset++] = 0;
-          done = true;
-        } else {
-          mTempRadiotext[offset++] = sRDSToUnicodeMap[segment[i]];
-        }
-      }
-      if (offset == 64) {
-        done = true;
-      }
-
-      if (!done ||
-          (mRadiotextState + 1) != (1 << ((blocks[1] & 0xF) + 1)) ||
-          !memcmp(mTempRadiotext, mRadiotext, sizeof(mTempRadiotext))) {
-        break;
-      }
-
-      MutexAutoLock lock(mRDSLock);
-      mRadiotextSet = true;
-      memcpy(mRadiotext, mTempRadiotext, sizeof(mTempRadiotext));
-
-      DispatchFMRadioEventToMainThread(RadiotextChanged);
-      break;
-    }
-    case 5: // 2b Radiotext
-    {
-      uint16_t segmentAddr = (blocks[1] & 0xF);
-      bool textAB = blocks[1] & (1 << 5);
-      if (textAB != mRadiotextAB) {
-        mRadiotextState = 0;
-        memset(mTempRadiotext, 0, sizeof(mTempRadiotext));
-        mRadiotextAB = textAB;
-        MutexAutoLock lock(mRDSLock);
-        memset(mRadiotext, 0, sizeof(mRadiotext));
-
-        DispatchFMRadioEventToMainThread(RadiotextChanged);
-      }
-
-      if (!segmentAddr) {
-        mRadiotextState = 1;
-      } else {
-        mRadiotextState |= 1 << segmentAddr;
-      }
-      uint8_t segment[2];
-      segment[0] = blocks[3] >> 8;
-      segment[1] = blocks[3] & 0xFF;
-
-      uint16_t offset = segmentAddr << 1;
-      bool done = false;
-      for (int i = 0; i < 2; i++) {
-        if (segment[i] == '\r') {
-          mTempRadiotext[offset++] = 0;
-          done = true;
-        } else {
-          mTempRadiotext[offset++] = sRDSToUnicodeMap[segment[i]];
-        }
-      }
-      if (offset == 32) {
-        done = true;
-      }
-
-      if (!done ||
-          (mRadiotextState + 1) != (1 << ((blocks[1] & 0xF) + 1)) ||
-          !memcmp(mTempRadiotext, mRadiotext, sizeof(mTempRadiotext))) {
-        break;
-      }
-
-      MutexAutoLock lock(mRDSLock);
-      mRadiotextSet = true;
-      memcpy(mRadiotext, mTempRadiotext, sizeof(mTempRadiotext));
-
-      DispatchFMRadioEventToMainThread(RadiotextChanged);
-      break;
-    }
-    case 31: // 15b Fast Tuning and Switching
-    {
-      uint16_t secondPty = (blocks[3] >> 5) & 0x1F;
-      if (pty == mPTY || pty != secondPty) {
-        break;
-      }
-      mPTY = pty;
-
-      DispatchFMRadioEventToMainThread(PTYChanged);
-      break;
-    }
-  }
-
-  // Only notify users of raw RDS groups that they're interested in.
-  // We always receive DOM_PARSED_RDS_GROUPS when RDS is enabled.
-  if (!(mRDSGroupMask & (1 << grouptype))) {
-    return;
-  }
-
-  uint64_t newgroup = blocks[0];
-  newgroup <<= 16;
-  newgroup |= blocks[1];
-  newgroup <<= 16;
-  newgroup |= blocks[2];
-  newgroup <<= 16;
-  newgroup |= blocks[3];
-
-  MutexAutoLock lock(mRDSLock);
-  mRDSGroup = newgroup;
-  mRDSGroupSet = true;
-
-  DispatchFMRadioEventToMainThread(NewRDSGroup);
-}
-
-void
-FMRadioService::UpdatePowerState()
-{
-  bool enabled = hal::IsFMRadioOn();
-  if (enabled != mEnabled) {
-    mEnabled = enabled;
-    NotifyFMRadioEvent(EnabledChanged);
-  }
-}
-
-void
-FMRadioService::UpdateFrequency()
-{
-  int32_t frequency = hal::GetFMRadioFrequency();
-  if (mPendingFrequencyInKHz != frequency) {
-    mPendingFrequencyInKHz = frequency;
-    NotifyFMRadioEvent(FrequencyChanged);
-    mPISet = false;
-    mPTYSet = false;
-    memset(mPSName, 0, sizeof(mPSName));
-    memset(mRadiotext, 0, sizeof(mRadiotext));
-    mRDSGroupSet = false;
-    mPSNameSet = false;
-    mRadiotextSet = false;
-  }
-}
-
-// static
-FMRadioService*
-FMRadioService::Singleton()
-{
-  MOZ_ASSERT(XRE_IsParentProcess());
-  MOZ_ASSERT(NS_IsMainThread());
-
-  if (!sFMRadioService) {
-    sFMRadioService = new FMRadioService();
-    ClearOnShutdown(&sFMRadioService);
-  }
-
-  return sFMRadioService;
-}
-
-NS_IMPL_ISUPPORTS(FMRadioService, nsIObserver)
-
-END_FMRADIO_NAMESPACE
-
deleted file mode 100644
--- a/dom/fmradio/FMRadioService.h
+++ /dev/null
@@ -1,270 +0,0 @@
-/* -*- 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/. */
-
-#ifndef mozilla_dom_fmradioservice_h__
-#define mozilla_dom_fmradioservice_h__
-
-#include "mozilla/dom/Nullable.h"
-#include "mozilla/dom/PFMRadioRequest.h"
-#include "FMRadioCommon.h"
-#include "mozilla/Hal.h"
-#include "mozilla/Mutex.h"
-#include "mozilla/StaticPtr.h"
-#include "mozilla/Services.h"
-#include "nsThreadUtils.h"
-#include "nsIObserver.h"
-#include "nsXULAppAPI.h"
-
-BEGIN_FMRADIO_NAMESPACE
-
-class FMRadioReplyRunnable : public Runnable
-{
-public:
-  FMRadioReplyRunnable() : mResponseType(SuccessResponse()) {}
-  virtual ~FMRadioReplyRunnable() {}
-
-  void
-  SetReply(const FMRadioResponseType& aResponseType)
-  {
-    mResponseType = aResponseType;
-  }
-
-protected:
-  FMRadioResponseType mResponseType;
-};
-
-/**
- * The FMRadio Service Interface for FMRadio.
- *
- * There are two concrete classes which implement this interface:
- *  - FMRadioService
- *    It's used in the main process, implements all the logics about FM Radio.
- *
- *  - FMRadioChild
- *    It's used in subprocess. It's a kind of proxy which just sends all
- *    the requests to main process through IPC channel.
- *
- * All the requests coming from the content page will be redirected to the
- * concrete class object.
- *
- * Consider navigator.mozFMRadio.enable(). Here is the call sequence:
- *  - OOP
- *    Child:
- *      (1) Call navigator.mozFMRadio.enable().
- *      (2) Return a DOMRequest object, and call FMRadioChild.Enable() with a
- *          FMRadioReplyRunnable object.
- *      (3) Send IPC message to main process.
- *    Parent:
- *      (4) Call FMRadioService::Enable() with a FMRadioReplyRunnable object.
- *      (5) Call hal::EnableFMRadio().
- *      (6) Notify FMRadioService object when FM radio HW is enabled.
- *      (7) Dispatch the FMRadioReplyRunnable object created in (4).
- *      (8) Send IPC message back to child process.
- *    Child:
- *      (9) Dispatch the FMRadioReplyRunnable object created in (2).
- *     (10) Fire success callback of the DOMRequest Object created in (2).
- *                     _ _ _ _ _ _ _ _ _ _ _ _ _ _
- *                    |            OOP            |
- *                    |                           |
- *   Page  FMRadio    |    FMRadioChild       IPC |    FMRadioService   Hal
- *    | (1)  |        |          |             |  |           |          |
- *    |----->|    (2) |          |             |  |           |          |
- *    |      |--------|--------->|      (3)    |  |           |          |
- *    |      |        |          |-----------> |  |   (4)     |          |
- *    |      |        |          |             |--|---------->|  (5)     |
- *    |      |        |          |             |  |           |--------->|
- *    |      |        |          |             |  |           |  (6)     |
- *    |      |        |          |             |  |   (7)     |<---------|
- *    |      |        |          |      (8)    |<-|-----------|          |
- *    |      |    (9) |          |<----------- |  |           |          |
- *    | (10) |<-------|----------|             |  |           |          |
- *    |<-----|        |          |             |  |           |          |
- *                    |                           |
- *                    |_ _ _ _ _ _ _ _ _ _ _ _ _ _|
- *  - non-OOP
- *    In non-OOP model, we don't need to send messages between processes, so
- *    the call sequences are much more simpler, it almost just follows the
- *    sequences presented in OOP model: (1) (2) (5) (6) (9) and (10).
- *
- */
-class IFMRadioService
-{
-protected:
-  virtual ~IFMRadioService() { }
-
-public:
-  virtual bool IsEnabled() const = 0;
-  virtual bool IsRDSEnabled() const = 0;
-  virtual double GetFrequency() const = 0;
-  virtual double GetFrequencyUpperBound() const = 0;
-  virtual double GetFrequencyLowerBound() const = 0;
-  virtual double GetChannelWidth() const = 0;
-  virtual Nullable<unsigned short> GetPi() const = 0;
-  virtual Nullable<uint8_t> GetPty() const = 0;
-  virtual bool GetPs(nsString& aPsname) = 0;
-  virtual bool GetRt(nsString& aRadiotext) = 0;
-  virtual bool GetRdsgroup(uint64_t& aRDSGroup) = 0;
-
-  virtual void Enable(double aFrequency, FMRadioReplyRunnable* aReplyRunnable) = 0;
-  virtual void Disable(FMRadioReplyRunnable* aReplyRunnable) = 0;
-  virtual void SetFrequency(double aFrequency, FMRadioReplyRunnable* aReplyRunnable) = 0;
-  virtual void Seek(mozilla::hal::FMRadioSeekDirection aDirection,
-                    FMRadioReplyRunnable* aReplyRunnable) = 0;
-  virtual void CancelSeek(FMRadioReplyRunnable* aReplyRunnable) = 0;
-  virtual void SetRDSGroupMask(uint32_t aRDSGroupMask) = 0;
-  virtual void EnableRDS(FMRadioReplyRunnable* aReplyRunnable) = 0;
-  virtual void DisableRDS(FMRadioReplyRunnable* aReplyRunnable) = 0;
-
-  /**
-   * Register handler to receive the FM Radio events, including:
-   *   - StateChangedEvent
-   *   - FrequencyChangedEvent
-   *
-   * Called by FMRadio and FMRadioParent.
-   */
-  virtual void AddObserver(FMRadioEventObserver* aObserver) = 0;
-  virtual void RemoveObserver(FMRadioEventObserver* aObserver) = 0;
-
-  // Enable/Disable FMRadio
-  virtual void EnableAudio(bool aAudioEnabled) = 0;
-
-  /**
-   * Static method to return the singleton instance. If it's in the child
-   * process, we will get an object of FMRadioChild.
-   */
-  static IFMRadioService* Singleton();
-};
-
-enum FMRadioState
-{
-  Disabled,
-  Disabling,
-  Enabling,
-  Enabled,
-  Seeking
-};
-
-class FMRadioService final : public IFMRadioService
-                           , public hal::FMRadioObserver
-                           , public hal::FMRadioRDSObserver
-                           , public nsIObserver
-{
-  friend class ReadAirplaneModeSettingTask;
-  friend class EnableRunnable;
-  friend class DisableRunnable;
-  friend class NotifyRunnable;
-
-public:
-  static FMRadioService* Singleton();
-
-  NS_DECL_ISUPPORTS
-
-  virtual bool IsEnabled() const override;
-  virtual bool IsRDSEnabled() const override;
-  virtual double GetFrequency() const override;
-  virtual double GetFrequencyUpperBound() const override;
-  virtual double GetFrequencyLowerBound() const override;
-  virtual double GetChannelWidth() const override;
-  virtual Nullable<unsigned short> GetPi() const override;
-  virtual Nullable<uint8_t> GetPty() const override;
-  virtual bool GetPs(nsString& aPsname) override;
-  virtual bool GetRt(nsString& aRadiotext) override;
-  virtual bool GetRdsgroup(uint64_t& aRDSGroup) override;
-
-  virtual void Enable(double aFrequency,
-                      FMRadioReplyRunnable* aReplyRunnable) override;
-  virtual void Disable(FMRadioReplyRunnable* aReplyRunnable) override;
-  virtual void SetFrequency(double aFrequency,
-                            FMRadioReplyRunnable* aReplyRunnable) override;
-  virtual void Seek(mozilla::hal::FMRadioSeekDirection aDirection,
-                    FMRadioReplyRunnable* aReplyRunnable) override;
-  virtual void CancelSeek(FMRadioReplyRunnable* aReplyRunnable) override;
-  virtual void SetRDSGroupMask(uint32_t aRDSGroupMask) override;
-  virtual void EnableRDS(FMRadioReplyRunnable* aReplyRunnable) override;
-  virtual void DisableRDS(FMRadioReplyRunnable* aReplyRunnable) override;
-
-  virtual void AddObserver(FMRadioEventObserver* aObserver) override;
-  virtual void RemoveObserver(FMRadioEventObserver* aObserver) override;
-
-  virtual void EnableAudio(bool aAudioEnabled) override;
-
-  /* FMRadioObserver */
-  void Notify(const hal::FMRadioOperationInformation& aInfo) override;
-  /* FMRadioRDSObserver */
-  void Notify(const hal::FMRadioRDSGroup& aRDSGroup) override;
-
-  void EnableFMRadio();
-  void DisableFMRadio();
-  void DispatchFMRadioEventToMainThread(enum FMRadioEventType aType);
-
-  NS_DECL_NSIOBSERVER
-
-protected:
-  FMRadioService();
-  virtual ~FMRadioService();
-
-private:
-  int32_t RoundFrequency(double aFrequencyInMHz);
-
-  void NotifyFMRadioEvent(FMRadioEventType aType);
-  void DoDisable();
-  void TransitionState(const FMRadioResponseType& aResponse, FMRadioState aState);
-  void SetState(FMRadioState aState);
-  void UpdatePowerState();
-  void UpdateFrequency();
-
-private:
-  bool mEnabled;
-
-  int32_t mPendingFrequencyInKHz;
-
-  FMRadioState mState;
-
-  bool mHasReadAirplaneModeSetting;
-  bool mAirplaneModeEnabled;
-  bool mRDSEnabled;
-
-  uint32_t mUpperBoundInKHz;
-  uint32_t mLowerBoundInKHz;
-  uint32_t mChannelWidthInKHz;
-  uint32_t mPreemphasis;
-
-  nsCOMPtr<nsIThread> mTuneThread;
-  RefPtr<FMRadioReplyRunnable> mPendingRequest;
-
-  FMRadioEventObserverList mObserverList;
-
-  static StaticRefPtr<FMRadioService> sFMRadioService;
-
-  uint32_t mRDSGroupMask;
-
-  uint16_t mLastPI;
-  uint16_t mLastPTY;
-  Atomic<uint32_t> mPI;
-  Atomic<uint32_t> mPTY;
-  Atomic<bool> mPISet;
-  Atomic<bool> mPTYSet;
-
-  /* Protects mPSName, mRadiotext, and mRDSGroup */
-  Mutex mRDSLock;
-  char16_t mPSName[9];
-  char16_t mRadiotext[65];
-  uint64_t mRDSGroup;
-
-  uint8_t mPSNameState;
-  uint16_t mRadiotextState;
-  uint16_t mTempPSName[8];
-  uint16_t mTempRadiotext[64];
-  bool mRadiotextAB;
-  bool mRDSGroupSet;
-  bool mPSNameSet;
-  bool mRadiotextSet;
-};
-
-END_FMRADIO_NAMESPACE
-
-#endif // mozilla_dom_fmradioservice_h__
-
deleted file mode 100644
--- a/dom/fmradio/ipc/FMRadioChild.cpp
+++ /dev/null
@@ -1,352 +0,0 @@
-/* -*- 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 "FMRadioChild.h"
-#include "mozilla/dom/ContentChild.h"
-#include "mozilla/dom/FMRadioRequestChild.h"
-
-using namespace mozilla::hal;
-
-BEGIN_FMRADIO_NAMESPACE
-
-StaticAutoPtr<FMRadioChild> FMRadioChild::sFMRadioChild;
-
-FMRadioChild::FMRadioChild()
-  : mEnabled(false)
-  , mRDSEnabled(false)
-  , mRDSGroupSet(false)
-  , mPSNameSet(false)
-  , mRadiotextSet(false)
-  , mFrequency(0)
-  , mRDSGroup(0)
-  , mRDSGroupMask(0)
-  , mObserverList(FMRadioEventObserverList())
-{
-  MOZ_COUNT_CTOR(FMRadioChild);
-
-  ContentChild::GetSingleton()->SendPFMRadioConstructor(this);
-
-  StatusInfo statusInfo;
-  SendGetStatusInfo(&statusInfo);
-
-  mEnabled = statusInfo.enabled();
-  mFrequency = statusInfo.frequency();
-  mUpperBound = statusInfo.upperBound();
-  mLowerBound= statusInfo.lowerBound();
-  mChannelWidth = statusInfo.channelWidth();
-}
-
-FMRadioChild::~FMRadioChild()
-{
-  MOZ_COUNT_DTOR(FMRadioChild);
-}
-
-bool
-FMRadioChild::IsEnabled() const
-{
-  return mEnabled;
-}
-
-bool
-FMRadioChild::IsRDSEnabled() const
-{
-  return mRDSEnabled;
-}
-
-double
-FMRadioChild::GetFrequency() const
-{
-  return mFrequency;
-}
-
-
-double
-FMRadioChild::GetFrequencyUpperBound() const
-{
-  return mUpperBound;
-}
-
-double
-FMRadioChild::GetFrequencyLowerBound() const
-{
-  return mLowerBound;
-}
-
-double
-FMRadioChild::GetChannelWidth() const
-{
-  return mChannelWidth;
-}
-
-Nullable<unsigned short>
-FMRadioChild::GetPi() const
-{
-  return mPI;
-}
-
-Nullable<uint8_t>
-FMRadioChild::GetPty() const
-{
-  return mPTY;
-}
-
-bool
-FMRadioChild::GetPs(nsString& aPSName)
-{
-  if (mPSNameSet) {
-    aPSName = mPSName;
-  }
-  return mPSNameSet;
-}
-
-bool
-FMRadioChild::GetRt(nsString& aRadiotext)
-{
-  if (mRadiotextSet) {
-    aRadiotext = mRadiotext;
-  }
-  return mRadiotextSet;
-}
-
-bool
-FMRadioChild::GetRdsgroup(uint64_t& aRDSGroup)
-{
-  aRDSGroup = mRDSGroup;
-  return mRDSGroupSet;
-}
-
-void
-FMRadioChild::Enable(double aFrequency, FMRadioReplyRunnable* aReplyRunnable)
-{
-  SendRequest(aReplyRunnable, EnableRequestArgs(aFrequency));
-}
-
-void
-FMRadioChild::Disable(FMRadioReplyRunnable* aReplyRunnable)
-{
-  SendRequest(aReplyRunnable, DisableRequestArgs());
-}
-
-void
-FMRadioChild::SetFrequency(double aFrequency,
-                           FMRadioReplyRunnable* aReplyRunnable)
-{
-  SendRequest(aReplyRunnable, SetFrequencyRequestArgs(aFrequency));
-}
-
-void
-FMRadioChild::Seek(FMRadioSeekDirection aDirection,
-                   FMRadioReplyRunnable* aReplyRunnable)
-{
-  SendRequest(aReplyRunnable, SeekRequestArgs(aDirection));
-}
-
-void
-FMRadioChild::CancelSeek(FMRadioReplyRunnable* aReplyRunnable)
-{
-  SendRequest(aReplyRunnable, CancelSeekRequestArgs());
-}
-
-void
-FMRadioChild::SetRDSGroupMask(uint32_t aRDSGroupMask)
-{
-  mRDSGroupMask = aRDSGroupMask;
-  SendSetRDSGroupMask(aRDSGroupMask);
-}
-
-void
-FMRadioChild::EnableRDS(FMRadioReplyRunnable* aReplyRunnable)
-{
-  SendRequest(aReplyRunnable, EnableRDSArgs());
-}
-
-void
-FMRadioChild::DisableRDS(FMRadioReplyRunnable* aReplyRunnable)
-{
-  SendRequest(aReplyRunnable, DisableRDSArgs());
-}
-
-inline void
-FMRadioChild::NotifyFMRadioEvent(FMRadioEventType aType)
-{
-  mObserverList.Broadcast(aType);
-}
-
-void
-FMRadioChild::AddObserver(FMRadioEventObserver* aObserver)
-{
-  mObserverList.AddObserver(aObserver);
-}
-
-void
-FMRadioChild::RemoveObserver(FMRadioEventObserver* aObserver)
-{
-  mObserverList.RemoveObserver(aObserver);
-}
-
-void
-FMRadioChild::SendRequest(FMRadioReplyRunnable* aReplyRunnable,
-                          FMRadioRequestArgs aArgs)
-{
-  PFMRadioRequestChild* childRequest = new FMRadioRequestChild(aReplyRunnable);
-  SendPFMRadioRequestConstructor(childRequest, aArgs);
-}
-
-bool
-FMRadioChild::RecvNotifyFrequencyChanged(const double& aFrequency)
-{
-  mFrequency = aFrequency;
-  NotifyFMRadioEvent(FrequencyChanged);
-
-  if (!mPI.IsNull()) {
-    mPI.SetNull();
-    NotifyFMRadioEvent(PIChanged);
-  }
-  if (!mPTY.IsNull()) {
-    mPTY.SetNull();
-    NotifyFMRadioEvent(PTYChanged);
-  }
-  if (mPSNameSet) {
-    mPSNameSet = false;
-    mPSName.Truncate();
-    NotifyFMRadioEvent(PSChanged);
-  }
-  if (mRadiotextSet) {
-    mRadiotextSet = false;
-    mRadiotext.Truncate();
-    NotifyFMRadioEvent(RadiotextChanged);
-  }
-  mRDSGroupSet = false;
-  return true;
-}
-
-bool
-FMRadioChild::RecvNotifyEnabledChanged(const bool& aEnabled,
-                                       const double& aFrequency)
-{
-  mEnabled = aEnabled;
-  mFrequency = aFrequency;
-  if (!mEnabled) {
-    mPI.SetNull();
-    mPTY.SetNull();
-    mPSName.Truncate();
-    mRadiotext.Truncate();
-    mRDSGroupSet = false;
-    mPSNameSet = false;
-    mRadiotextSet = false;
-  }
-  NotifyFMRadioEvent(EnabledChanged);
-  return true;
-}
-
-bool
-FMRadioChild::RecvNotifyRDSEnabledChanged(const bool& aEnabled)
-{
-  mRDSEnabled = aEnabled;
-  NotifyFMRadioEvent(RDSEnabledChanged);
-  return true;
-}
-
-bool
-FMRadioChild::RecvNotifyPIChanged(const bool& aValid,
-                                  const uint16_t& aCode)
-{
-  if (aValid) {
-    mPI.SetValue(aCode);
-  } else {
-    mPI.SetNull();
-  }
-  NotifyFMRadioEvent(PIChanged);
-  return true;
-}
-
-bool
-FMRadioChild::RecvNotifyPTYChanged(const bool& aValid,
-                                   const uint8_t& aPTY)
-{
-  if (aValid) {
-    mPTY.SetValue(aPTY);
-  } else {
-    mPTY.SetNull();
-  }
-  NotifyFMRadioEvent(PTYChanged);
-  return true;
-}
-
-bool
-FMRadioChild::RecvNotifyPSChanged(const nsString& aPSName)
-{
-  mPSNameSet = true;
-  mPSName = aPSName;
-  NotifyFMRadioEvent(PSChanged);
-  return true;
-}
-
-bool
-FMRadioChild::RecvNotifyRadiotextChanged(const nsString& aRadiotext)
-{
-  mRadiotextSet = true;
-  mRadiotext = aRadiotext;
-  NotifyFMRadioEvent(RadiotextChanged);
-  return true;
-}
-
-bool
-FMRadioChild::RecvNotifyNewRDSGroup(const uint64_t& aGroup)
-{
-  uint16_t grouptype = (aGroup >> 43) & 0x1F;
-  if (!(mRDSGroupMask & (1 << grouptype))) {
-    return true;
-  }
-
-  mRDSGroupSet = true;
-  mRDSGroup = aGroup;
-  NotifyFMRadioEvent(NewRDSGroup);
-  return true;
-}
-
-bool
-FMRadioChild::Recv__delete__()
-{
-  return true;
-}
-
-PFMRadioRequestChild*
-FMRadioChild::AllocPFMRadioRequestChild(const FMRadioRequestArgs& aArgs)
-{
-  MOZ_CRASH();
-  return nullptr;
-}
-
-bool
-FMRadioChild::DeallocPFMRadioRequestChild(PFMRadioRequestChild* aActor)
-{
-  delete aActor;
-  return true;
-}
-
-void
-FMRadioChild::EnableAudio(bool aAudioEnabled)
-{
-  SendEnableAudio(aAudioEnabled);
-}
-
-// static
-FMRadioChild*
-FMRadioChild::Singleton()
-{
-  MOZ_ASSERT(!XRE_IsParentProcess());
-  MOZ_ASSERT(NS_IsMainThread());
-
-  if (!sFMRadioChild) {
-    sFMRadioChild = new FMRadioChild();
-  }
-
-  return sFMRadioChild;
-}
-
-END_FMRADIO_NAMESPACE
-
deleted file mode 100644
--- a/dom/fmradio/ipc/FMRadioChild.h
+++ /dev/null
@@ -1,134 +0,0 @@
-/* -*- 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/. */
-
-#ifndef mozilla_dom_fmradiochild_h__
-#define mozilla_dom_fmradiochild_h__
-
-#include "FMRadioCommon.h"
-#include "FMRadioService.h"
-#include "mozilla/dom/PFMRadioChild.h"
-#include "mozilla/StaticPtr.h"
-
-BEGIN_FMRADIO_NAMESPACE
-
-/**
- * FMRadioChild plays two roles:
- *   - Kind of proxy of FMRadioService
- *     Redirect all the requests  coming from web content to FMRadioService
- *     in parent through IPC channel.
- *   - Child Actor of PFMRadio
- *     IPC channel to transfer the requests.
- */
-class FMRadioChild final : public IFMRadioService
-                         , public PFMRadioChild
-{
-public:
-  static FMRadioChild* Singleton();
-  ~FMRadioChild();
-
-  void SendRequest(FMRadioReplyRunnable* aReplyRunnable,
-                   FMRadioRequestArgs aArgs);
-
-  /* IFMRadioService */
-  virtual bool IsEnabled() const override;
-  virtual bool IsRDSEnabled() const override;
-  virtual double GetFrequency() const override;
-  virtual double GetFrequencyUpperBound() const override;
-  virtual double GetFrequencyLowerBound() const override;
-  virtual double GetChannelWidth() const override;
-  virtual Nullable<unsigned short> GetPi() const override;
-  virtual Nullable<uint8_t> GetPty() const override;
-  virtual bool GetPs(nsString& aPSName) override;
-  virtual bool GetRt(nsString& aRadiotext) override;
-  virtual bool GetRdsgroup(uint64_t& aRDSGroup) override;
-
-  virtual void Enable(double aFrequency,
-                      FMRadioReplyRunnable* aReplyRunnable) override;
-  virtual void Disable(FMRadioReplyRunnable* aReplyRunnable) override;
-  virtual void SetFrequency(double frequency,
-                            FMRadioReplyRunnable* aReplyRunnable) override;
-  virtual void Seek(mozilla::hal::FMRadioSeekDirection aDirection,
-                    FMRadioReplyRunnable* aReplyRunnable) override;
-  virtual void CancelSeek(FMRadioReplyRunnable* aReplyRunnable) override;
-  virtual void SetRDSGroupMask(uint32_t aRDSGroupMask) override;
-  virtual void EnableRDS(FMRadioReplyRunnable* aReplyRunnable) override;
-  virtual void DisableRDS(FMRadioReplyRunnable* aReplyRunnable) override;
-
-  virtual void AddObserver(FMRadioEventObserver* aObserver) override;
-  virtual void RemoveObserver(FMRadioEventObserver* aObserver) override;
-
-  virtual void EnableAudio(bool aAudioEnabled) override;
-
-  /* PFMRadioChild */
-  virtual bool
-  Recv__delete__() override;
-
-  virtual bool
-  RecvNotifyFrequencyChanged(const double& aFrequency) override;
-
-  virtual bool
-  RecvNotifyEnabledChanged(const bool& aEnabled,
-                           const double& aFrequency) override;
-
-  virtual bool
-  RecvNotifyRDSEnabledChanged(const bool& aEnabled) override;
-
-  virtual bool
-  RecvNotifyPIChanged(const bool& aValid,
-                      const uint16_t& aCode) override;
-
-  virtual bool
-  RecvNotifyPTYChanged(const bool& aValid,
-                       const uint8_t& aPTY) override;
-
-  virtual bool
-  RecvNotifyPSChanged(const nsString& aPSName) override;
-
-  virtual bool
-  RecvNotifyRadiotextChanged(const nsString& aRadiotext) override;
-
-  virtual bool
-  RecvNotifyNewRDSGroup(const uint64_t& aGroup) override;
-
-  virtual PFMRadioRequestChild*
-  AllocPFMRadioRequestChild(const FMRadioRequestArgs& aArgs) override;
-
-  virtual bool
-  DeallocPFMRadioRequestChild(PFMRadioRequestChild* aActor) override;
-
-private:
-  FMRadioChild();
-
-  void Init();
-
-  inline void NotifyFMRadioEvent(FMRadioEventType aType);
-
-  bool mEnabled;
-  bool mRDSEnabled;
-  bool mRDSGroupSet;
-  bool mPSNameSet;
-  bool mRadiotextSet;
-  double mFrequency;
-  double mUpperBound;
-  double mLowerBound;
-  double mChannelWidth;
-  Nullable<unsigned short> mPI;
-  Nullable<uint8_t> mPTY;
-  nsAutoString mPSName;
-  nsAutoString mRadiotext;
-  uint64_t mRDSGroup;
-  uint32_t mRDSGroupMask;
-
-  FMRadioEventObserverList mObserverList;
-
-private:
-  static StaticAutoPtr<FMRadioChild> sFMRadioChild;
-};
-
-END_FMRADIO_NAMESPACE
-
-#endif // mozilla_dom_fmradiochild_h__
-
deleted file mode 100644
--- a/dom/fmradio/ipc/FMRadioParent.cpp
+++ /dev/null
@@ -1,167 +0,0 @@
-/* -*- 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 "FMRadioParent.h"
-#include "mozilla/Unused.h"
-#include "mozilla/dom/ContentParent.h"
-#include "mozilla/DebugOnly.h"
-#include "FMRadioRequestParent.h"
-#include "FMRadioService.h"
-
-BEGIN_FMRADIO_NAMESPACE
-
-FMRadioParent::FMRadioParent()
-{
-  MOZ_COUNT_CTOR(FMRadioParent);
-
-  IFMRadioService::Singleton()->AddObserver(this);
-}
-
-FMRadioParent::~FMRadioParent()
-{
-  MOZ_COUNT_DTOR(FMRadioParent);
-
-  IFMRadioService::Singleton()->RemoveObserver(this);
-}
-
-void
-FMRadioParent::ActorDestroy(ActorDestroyReason aWhy)
-{
-  // Implement me! Bug 1005146
-}
-
-bool
-FMRadioParent::RecvGetStatusInfo(StatusInfo* aStatusInfo)
-{
-  aStatusInfo->enabled() = IFMRadioService::Singleton()->IsEnabled();
-  aStatusInfo->frequency() = IFMRadioService::Singleton()->GetFrequency();
-  aStatusInfo->upperBound() =
-    IFMRadioService::Singleton()->GetFrequencyUpperBound();
-  aStatusInfo->lowerBound() =
-    IFMRadioService::Singleton()->GetFrequencyLowerBound();
-  aStatusInfo->channelWidth() =
-    IFMRadioService::Singleton()->GetChannelWidth();
-  return true;
-}
-
-PFMRadioRequestParent*
-FMRadioParent::AllocPFMRadioRequestParent(const FMRadioRequestArgs& aArgs)
-{
-  RefPtr<FMRadioRequestParent> requestParent = new FMRadioRequestParent();
-
-  switch (aArgs.type()) {
-    case FMRadioRequestArgs::TEnableRequestArgs:
-      IFMRadioService::Singleton()->Enable(
-        aArgs.get_EnableRequestArgs().frequency(), requestParent);
-      break;
-    case FMRadioRequestArgs::TDisableRequestArgs:
-      IFMRadioService::Singleton()->Disable(requestParent);
-      break;
-    case FMRadioRequestArgs::TSetFrequencyRequestArgs:
-      IFMRadioService::Singleton()->SetFrequency(
-        aArgs.get_SetFrequencyRequestArgs().frequency(), requestParent);
-      break;
-    case FMRadioRequestArgs::TSeekRequestArgs:
-      IFMRadioService::Singleton()->Seek(
-        aArgs.get_SeekRequestArgs().direction(), requestParent);
-      break;
-    case FMRadioRequestArgs::TCancelSeekRequestArgs:
-      IFMRadioService::Singleton()->CancelSeek(requestParent);
-      break;
-    case FMRadioRequestArgs::TEnableRDSArgs:
-      IFMRadioService::Singleton()->EnableRDS(requestParent);
-      break;
-    case FMRadioRequestArgs::TDisableRDSArgs:
-      IFMRadioService::Singleton()->DisableRDS(requestParent);
-      break;
-    default:
-      MOZ_CRASH();
-  }
-
-  // Balanced in DeallocPFMRadioRequestParent
-  return requestParent.forget().take();
-}
-
-bool
-FMRadioParent::DeallocPFMRadioRequestParent(PFMRadioRequestParent* aActor)
-{
-  FMRadioRequestParent* parent = static_cast<FMRadioRequestParent*>(aActor);
-  NS_RELEASE(parent);
-  return true;
-}
-
-void
-FMRadioParent::Notify(const FMRadioEventType& aType)
-{
-  switch (aType) {
-    case FrequencyChanged:
-      Unused << SendNotifyFrequencyChanged(
-        IFMRadioService::Singleton()->GetFrequency());
-      break;
-    case EnabledChanged:
-      Unused << SendNotifyEnabledChanged(
-        IFMRadioService::Singleton()->IsEnabled(),
-        IFMRadioService::Singleton()->GetFrequency());
-      break;
-    case RDSEnabledChanged:
-      Unused << SendNotifyRDSEnabledChanged(
-        IFMRadioService::Singleton()->IsRDSEnabled());
-      break;
-    case PIChanged: {
-      Nullable<unsigned short> pi =
-        IFMRadioService::Singleton()->GetPi();
-      Unused << SendNotifyPIChanged(!pi.IsNull(),
-                                    pi.IsNull() ? 0 : pi.Value());
-      break;
-    }
-    case PTYChanged: {
-      Nullable<uint8_t> pty = IFMRadioService::Singleton()->GetPty();
-      Unused << SendNotifyPTYChanged(!pty.IsNull(),
-                                     pty.IsNull() ? 0 : pty.Value());
-      break;
-    }
-    case PSChanged: {
-      nsAutoString psname;
-      IFMRadioService::Singleton()->GetPs(psname);
-      Unused << SendNotifyPSChanged(psname);
-      break;
-    }
-    case RadiotextChanged: {
-      nsAutoString radiotext;
-      IFMRadioService::Singleton()->GetRt(radiotext);
-      Unused << SendNotifyRadiotextChanged(radiotext);
-      break;
-    }
-    case NewRDSGroup: {
-      uint64_t group;
-      DebugOnly<bool> rdsgroupset =
-        IFMRadioService::Singleton()->GetRdsgroup(group);
-      MOZ_ASSERT(rdsgroupset);
-      Unused << SendNotifyNewRDSGroup(group);
-      break;
-    }
-    default:
-      NS_RUNTIMEABORT("not reached");
-      break;
-  }
-}
-
-bool
-FMRadioParent::RecvEnableAudio(const bool& aAudioEnabled)
-{
-  IFMRadioService::Singleton()->EnableAudio(aAudioEnabled);
-  return true;
-}
-
-bool
-FMRadioParent::RecvSetRDSGroupMask(const uint32_t& aRDSGroupMask)
-{
-  IFMRadioService::Singleton()->SetRDSGroupMask(aRDSGroupMask);
-  return true;
-}
-
-END_FMRADIO_NAMESPACE
-
deleted file mode 100644
--- a/dom/fmradio/ipc/FMRadioParent.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/* -*- 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/. */
-
-#ifndef mozilla_dom_fmradioparent_h__
-#define mozilla_dom_fmradioparent_h__
-
-#include "FMRadioCommon.h"
-#include "mozilla/dom/PFMRadioParent.h"
-#include "mozilla/HalTypes.h"
-
-BEGIN_FMRADIO_NAMESPACE
-
-class PFMRadioRequestParent;
-
-class FMRadioParent final : public PFMRadioParent
-                          , public FMRadioEventObserver
-{
-public:
-  FMRadioParent();
-  ~FMRadioParent();
-
-  virtual void
-  ActorDestroy(ActorDestroyReason aWhy) override;
-
-  virtual bool
-  RecvGetStatusInfo(StatusInfo* aStatusInfo) override;
-
-  virtual PFMRadioRequestParent*
-  AllocPFMRadioRequestParent(const FMRadioRequestArgs& aArgs) override;
-
-  virtual bool
-  DeallocPFMRadioRequestParent(PFMRadioRequestParent* aActor) override;
-
-  /* FMRadioEventObserver */
-  virtual void Notify(const FMRadioEventType& aType) override;
-
-  virtual bool
-  RecvEnableAudio(const bool& aAudioEnabled) override;
-
-  virtual bool
-  RecvSetRDSGroupMask(const uint32_t& aRDSGroupMask) override;
-};
-
-END_FMRADIO_NAMESPACE
-
-#endif // mozilla_dom_fmradioparent_h__
-
deleted file mode 100644
--- a/dom/fmradio/ipc/FMRadioRequestChild.cpp
+++ /dev/null
@@ -1,34 +0,0 @@
-/* -*- 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 "mozilla/dom/PFMRadioRequestChild.h"
-#include "FMRadioRequestChild.h"
-#include "FMRadioService.h"
-
-BEGIN_FMRADIO_NAMESPACE
-
-FMRadioRequestChild::FMRadioRequestChild(FMRadioReplyRunnable* aReplyRunnable)
-  : mReplyRunnable(aReplyRunnable)
-{
-  MOZ_COUNT_CTOR(FMRadioRequestChild);
-}
-
-FMRadioRequestChild::~FMRadioRequestChild()
-{
-  MOZ_COUNT_DTOR(FMRadioRequestChild);
-}
-
-bool
-FMRadioRequestChild::Recv__delete__(const FMRadioResponseType& aType)
-{
-  mReplyRunnable->SetReply(aType);
-  NS_DispatchToMainThread(mReplyRunnable);
-
-  return true;
-}
-
-END_FMRADIO_NAMESPACE
-
deleted file mode 100644
--- a/dom/fmradio/ipc/FMRadioRequestChild.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/* -*- 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/. */
-
-#ifndef mozilla_dom_fmradiorequestchild_h__
-#define mozilla_dom_fmradiorequestchild_h__
-
-#include "FMRadioCommon.h"
-#include "mozilla/dom/PFMRadioRequestChild.h"
-#include "DOMRequest.h"
-
-BEGIN_FMRADIO_NAMESPACE
-
-class FMRadioReplyRunnable;
-
-class FMRadioRequestChild final : public PFMRadioRequestChild
-{
-public:
-  FMRadioRequestChild(FMRadioReplyRunnable* aReplyRunnable);
-  ~FMRadioRequestChild();
-
-  virtual bool
-  Recv__delete__(const FMRadioResponseType& aResponse) override;
-
-private:
-  RefPtr<FMRadioReplyRunnable> mReplyRunnable;
-};
-
-END_FMRADIO_NAMESPACE
-
-#endif // mozilla_dom_fmradiorequestchild_h__
-
deleted file mode 100644
--- a/dom/fmradio/ipc/FMRadioRequestParent.cpp
+++ /dev/null
@@ -1,44 +0,0 @@
-/* -*- 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 "FMRadioRequestParent.h"
-#include "FMRadioService.h"
-#include "mozilla/Unused.h"
-#include "mozilla/dom/PFMRadio.h"
-
-BEGIN_FMRADIO_NAMESPACE
-
-FMRadioRequestParent::FMRadioRequestParent()
-  : mActorDestroyed(false)
-{
-  MOZ_COUNT_CTOR(FMRadioRequestParent);
-}
-
-FMRadioRequestParent::~FMRadioRequestParent()
-{
-  MOZ_COUNT_DTOR(FMRadioRequestParent);
-}
-
-void
-FMRadioRequestParent::ActorDestroy(ActorDestroyReason aWhy)
-{
-  mActorDestroyed = true;
-}
-
-nsresult
-FMRadioRequestParent::Run()
-{
-  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
-
-  if (!mActorDestroyed) {
-    Unused << Send__delete__(this, mResponseType);
-  }
-
-  return NS_OK;
-}
-
-END_FMRADIO_NAMESPACE
-
deleted file mode 100644
--- a/dom/fmradio/ipc/FMRadioRequestParent.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/* -*- 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/. */
-
-#ifndef mozilla_dom_fmradiorequestparent_h__
-#define mozilla_dom_fmradiorequestparent_h__
-
-#include "FMRadioCommon.h"
-#include "mozilla/dom/PFMRadioRequestParent.h"
-#include "FMRadioService.h"
-
-BEGIN_FMRADIO_NAMESPACE
-
-class FMRadioRequestParent final : public PFMRadioRequestParent
-                                 , public FMRadioReplyRunnable
-{
-public:
-  FMRadioRequestParent();
-  ~FMRadioRequestParent();
-
-  virtual void ActorDestroy(ActorDestroyReason aWhy) override;
-
-  NS_IMETHOD Run();
-
-private:
-  bool mActorDestroyed;
-};
-
-END_FMRADIO_NAMESPACE
-
-#endif // mozilla_dom_fmradiorequestparent_h__
-
deleted file mode 100644
--- a/dom/fmradio/ipc/PFMRadio.ipdl
+++ /dev/null
@@ -1,137 +0,0 @@
-/* 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 protocol PContent;
-include protocol PFMRadioRequest;
-
-using mozilla::hal::FMRadioSeekDirection from "mozilla/HalTypes.h";
-
-namespace mozilla {
-namespace dom {
-
-struct EnableRequestArgs
-{
-  double frequency;
-};
-
-struct DisableRequestArgs
-{
-};
-
-struct SetFrequencyRequestArgs
-{
-  double frequency;
-};
-
-struct SeekRequestArgs
-{
-  FMRadioSeekDirection direction;
-};
-
-struct CancelSeekRequestArgs
-{
-};
-
-struct EnableRDSArgs
-{
-};
-
-struct DisableRDSArgs
-{
-};
-
-union FMRadioRequestArgs
-{
-  EnableRequestArgs;
-  DisableRequestArgs;
-  SetFrequencyRequestArgs;
-  SeekRequestArgs;
-  CancelSeekRequestArgs;
-  EnableRDSArgs;
-  DisableRDSArgs;
-};
-
-struct StatusInfo
-{
-  bool enabled;
-  double frequency;
-  double upperBound;
-  double lowerBound;
-  double channelWidth;
-};
-
-sync protocol PFMRadio
-{
-  manager PContent;
-  manages PFMRadioRequest;
-
-child:
-  /**
-   * Sent when the frequency is changed.
-   */
-  async NotifyFrequencyChanged(double frequency);
-  /**
-   * Sent when the power state of FM radio HW is changed.
-   */
-  async NotifyEnabledChanged(bool enabled, double frequency);
-  /**
-   * Sent when RDS is enabled or disabled.
-   */
-  async NotifyRDSEnabledChanged(bool enabled);
-  /**
-   * Sent when we have a new PI code.
-   */
-  async NotifyPIChanged(bool valid, uint16_t code);
-  /**
-   * Sent when we have a new PTY
-   */
-  async NotifyPTYChanged(bool valid, uint8_t pty);
-  /**
-   * Sent when we have a new PS name.
-   */
-  async NotifyPSChanged(nsString psname);
-  /**
-   * Sent when we have new radiotext.
-   */
-  async NotifyRadiotextChanged(nsString radiotext);
-  /**
-   * Sent when a full RDS group is received.
-   */
-  async NotifyNewRDSGroup(uint64_t data);
-
-  async __delete__();
-
-parent:
-  /**
-   * Get the current status infomation of FM radio HW synchronously.
-   * Sent when the singleton object of FMRadioChild is initialized.
-   */
-  sync GetStatusInfo() returns (StatusInfo info);
-
-  /**
-   * Send request to parent process to operate the FM radio HW.
-   *
-   * We don't have separate Enable/SetFrequency/etc. methods instead here,
-   * because we can leverage the IPC messaging mechanism to manage the mapping
-   * of the asynchronous request and the DOMRequest we returned to the caller
-   * on web content, otherwise, we have to do the mapping stuff manually which
-   * is more error prone.
-   */
-  async PFMRadioRequest(FMRadioRequestArgs requestType);
-
-  /**
-   * Enable/Disable audio
-   */
-  async EnableAudio(bool audioEnabled);
-
-  /**
-   * Set RDS group mask
-   */
-  async SetRDSGroupMask(uint32_t groupMask);
-};
-
-} // namespace dom
-} // namespace mozilla
-
deleted file mode 100644
--- a/dom/fmradio/ipc/PFMRadioRequest.ipdl
+++ /dev/null
@@ -1,43 +0,0 @@
-/* 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 protocol PFMRadio;
-
-namespace mozilla {
-namespace dom {
-
-struct ErrorResponse
-{
-  nsString error;
-};
-
-struct SuccessResponse
-{
-};
-
-union FMRadioResponseType
-{
-  ErrorResponse;
-  SuccessResponse;
-};
-
-/**
- * The protocol is used for sending asynchronous operation requests of
- * FM radio HW from child to parent, and the type of the request is defined in
- * FMRadioRequestArgs.
- *
- * When the request completed, the result, i.e. FMRadioResponseType, will be
- * sent back to child from parent in the `__delete__` message.
- */
-async protocol PFMRadioRequest
-{
-  manager PFMRadio;
-
-child:
-  async __delete__(FMRadioResponseType response);
-};
-
-} // namespace dom
-} // namespace mozilla
-
deleted file mode 100644
--- a/dom/fmradio/ipc/moz.build
+++ /dev/null
@@ -1,31 +0,0 @@
-# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# 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/.
-
-EXPORTS.mozilla.dom += [
-    'FMRadioChild.h',
-    'FMRadioParent.h',
-    'FMRadioRequestChild.h',
-    'FMRadioRequestParent.h',
-]
-
-UNIFIED_SOURCES += [
-    'FMRadioChild.cpp',
-    'FMRadioParent.cpp',
-    'FMRadioRequestChild.cpp',
-    'FMRadioRequestParent.cpp',
-]
-
-LOCAL_INCLUDES += [
-    '/dom/base',
-]
-
-include('/ipc/chromium/chromium-config.mozbuild')
-
-FINAL_LIBRARY = 'xul'
-
-LOCAL_INCLUDES += [
-    '/dom/fmradio',
-]
deleted file mode 100644
--- a/dom/fmradio/moz.build
+++ /dev/null
@@ -1,34 +0,0 @@
-# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# 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/.
-
-if CONFIG['MOZ_B2G_FM']:
-    DIRS += [
-        'ipc',
-    ]
-
-    EXPORTS.mozilla.dom += [
-        'FMRadio.h',
-        'FMRadioCommon.h',
-        'FMRadioService.h',
-    ]
-
-    UNIFIED_SOURCES += [
-        'FMRadio.cpp',
-        'FMRadioService.cpp',
-    ]
-
-    FINAL_LIBRARY = 'xul'
-
-IPDL_SOURCES += [
-    'ipc/PFMRadio.ipdl',
-    'ipc/PFMRadioRequest.ipdl',
-]
-
-LOCAL_INCLUDES += [
-    '../base',
-    '../system/gonk',
-]
-include('/ipc/chromium/chromium-config.mozbuild')
deleted file mode 100644
--- a/dom/fmradio/test/marionette/manifest.ini
+++ /dev/null
@@ -1,11 +0,0 @@
-[DEFAULT]
-run-if = buildapp == 'b2g'
-
-[test_enable_disable.js]
-[test_set_frequency.js]
-[test_cancel_seek.js]
-[test_one_seek_at_once.js]
-[test_seek_up_and_down.js]
-[test_bug862672.js]
-[test_bug876597.js]
-
deleted file mode 100644
--- a/dom/fmradio/test/marionette/test_bug862672.js
+++ /dev/null
@@ -1,53 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-MARIONETTE_TIMEOUT = 10000;
-
-SpecialPowers.addPermission("fmradio", true, document);
-
-var FMRadio = window.navigator.mozFMRadio;
-
-function verifyInitialState() {
-  log("Verifying initial state.");
-  ok(FMRadio);
-  is(FMRadio.enabled, false);
-  enableThenDisable();
-}
-
-function enableThenDisable() {
-  log("Enable FM Radio and disable it immediately.");
-  var frequency = FMRadio.frequencyLowerBound + FMRadio.channelWidth;
-  var request = FMRadio.enable(frequency);
-  ok(request);
-
-  var failedToEnable = false;
-  request.onerror = function() {
-    failedToEnable = true;
-  };
-
-  var enableCompleted = false;
-  request.onsuccess = function() {
-    ok(!failedToEnable);
-    enableCompleted = true;
-  };
-
-  var disableReq = FMRadio.disable();
-  ok(disableReq);
-
-  disableReq.onsuccess = function() {
-    // There are two possibilities which depends on the system
-    // process scheduling (bug 911063 comment 0):
-    //   - enable fails
-    //   - enable's onsuccess fires before disable's onsucess
-    ok(failedToEnable || enableCompleted);
-    ok(!FMRadio.enabled);
-    finish();
-  };
-
-  disableReq.onerror = function() {
-    ok(false, "Disable request should not fail.");
-  };
-}
-
-verifyInitialState();
-
deleted file mode 100644
--- a/dom/fmradio/test/marionette/test_bug876597.js
+++ /dev/null
@@ -1,92 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-MARIONETTE_TIMEOUT = 10000;
-
-SpecialPowers.addPermission("fmradio", true, document);
-SpecialPowers.addPermission("settings-read", true, document);
-SpecialPowers.addPermission("settings-write", true, document);
-SpecialPowers.addPermission("settings-api-read", true, document);
-SpecialPowers.addPermission("settings-api-write", true, document);
-
-var FMRadio = window.navigator.mozFMRadio;
-var mozSettings = window.navigator.mozSettings;
-var KEY = "airplaneMode.enabled";
-
-function verifyInitialState() {
-  log("Verifying initial state.");
-  ok(FMRadio);
-  is(FMRadio.enabled, false);
-  ok(mozSettings);
-
-  checkAirplaneModeSettings();
-}
-
-function checkAirplaneModeSettings() {
-  log("Checking airplane mode settings");
-  let req = mozSettings.createLock().get(KEY);
-  req.onsuccess = function(event) {
-    ok(!req.result[KEY], "Airplane mode is disabled.");
-    enableFMRadio();
-  };
-
-  req.onerror = function() {
-    ok(false, "Error occurs when reading settings value.");
-    finish();
-  };
-}
-
-function enableFMRadio() {
-  log("Enable FM radio");
-  let frequency = FMRadio.frequencyLowerBound + FMRadio.channelWidth;
-  let req = FMRadio.enable(frequency);
-
-  req.onsuccess = function() {
-    enableAirplaneMode();
-  };
-
-  req.onerror = function() {
-    ok(false, "Failed to enable FM radio.");
-  };
-}
-
-function enableAirplaneMode() {
-  log("Enable airplane mode");
-  FMRadio.ondisabled = function() {
-    FMRadio.ondisabled = null;
-    enableFMRadioWithAirplaneModeEnabled();
-  };
-
-  let settings = {};
-  settings[KEY] = true;
-  mozSettings.createLock().set(settings);
-}
-
-function enableFMRadioWithAirplaneModeEnabled() {
-  log("Enable FM radio with airplane mode enabled");
-  let frequency = FMRadio.frequencyLowerBound + FMRadio.channelWidth;
-  let req = FMRadio.enable(frequency);
-  req.onerror = cleanUp();
-
-  req.onsuccess = function() {
-    ok(false, "FMRadio could be enabled when airplane mode is enabled.");
-  };
-}
-
-function cleanUp() {
-  let settings = {};
-  settings[KEY] = false;
-  let req = mozSettings.createLock().set(settings);
-
-  req.onsuccess = function() {
-    ok(!FMRadio.enabled);
-    finish();
-  };
-
-  req.onerror = function() {
-    ok(false, "Error occurs when setting value");
-  };
-}
-
-verifyInitialState();
-
deleted file mode 100644
--- a/dom/fmradio/test/marionette/test_cancel_seek.js
+++ /dev/null
@@ -1,69 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-MARIONETTE_TIMEOUT = 10000;
-
-SpecialPowers.addPermission("fmradio", true, document);
-
-var FMRadio = window.navigator.mozFMRadio;
-
-function verifyInitialState() {
-  log("Verifying initial state.");
-  ok(FMRadio);
-  is(FMRadio.enabled, false);
-  setUp();
-}
-
-function setUp() {
-  let frequency = FMRadio.frequencyLowerBound + FMRadio.channelWidth;
-  FMRadio.enable(frequency);
-  FMRadio.onenabled = seek;
-}
-
-function seek() {
-  log("Seek up");
-  var request = FMRadio.seekUp();
-  ok(request);
-
-  var seekUpIsCancelled = false;
-  request.onerror = function() {
-    seekUpIsCancelled = true;
-  };
-
-  var seekUpCompleted = false;
-  request.onsuccess = function() {
-    ok(!seekUpIsCancelled);
-    seekUpCompleted = true;
-  };
-
-  log("Seek up");
-  var cancelSeekReq = FMRadio.cancelSeek();
-  ok(cancelSeekReq);
-
-  // There are two possibilities which depends on the system
-  // process scheduling (bug 911063 comment 0):
-  //   - seekup action is canceled
-  //   - seekup's onsuccess fires before cancelSeek's onerror
-
-  cancelSeekReq.onsuccess = function() {
-    ok(seekUpIsCancelled, "Seekup request failed.");
-    cleanUp();
-  };
-
-  cancelSeekReq.onerror = function() {
-    ok(seekUpCompleted);
-    cleanUp();
-  };
-}
-
-function cleanUp() {
-  FMRadio.disable();
-  FMRadio.ondisabled = function() {
-    FMRadio.ondisabled = null;
-    ok(!FMRadio.enabled);
-    finish();
-  };
-}
-
-verifyInitialState();
-
deleted file mode 100644
--- a/dom/fmradio/test/marionette/test_enable_disable.js
+++ /dev/null
@@ -1,85 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-MARIONETTE_TIMEOUT = 10000;
-
-SpecialPowers.addPermission("fmradio", true, document);
-
-var FMRadio = window.navigator.mozFMRadio;
-
-function verifyInitialState() {
-  log("Verifying initial state.");
-  ok(FMRadio);
-  is(FMRadio.enabled, false);
-
-  log("Verifying attributes when disabled.");
-  is(FMRadio.frequency, null);
-  ok(FMRadio.frequencyLowerBound);
-  ok(FMRadio.frequencyUpperBound);
-  ok(FMRadio.frequencyUpperBound > FMRadio.frequencyLowerBound);
-  ok(FMRadio.channelWidth);
-
-  enableFMRadio();
-}
-
-function enableFMRadio() {
-  log("Verifying behaviors when enabled.");
-  var frequency = FMRadio.frequencyLowerBound + FMRadio.channelWidth;
-  var request = FMRadio.enable(frequency);
-  ok(request, "FMRadio.enable(r" + frequency + ") returns request");
-
-  request.onsuccess = function() {
-    ok(FMRadio.enabled);
-    ok(typeof FMRadio.frequency == "number");
-    ok(FMRadio.frequency > FMRadio.frequencyLowerBound);
-  };
-
-  request.onerror = function() {
-    ok(null, "Failed to enable");
-  };
-
-  var enabled = false;
-  FMRadio.onenabled = function() {
-    FMRadio.onenabled = null;
-    enabled = FMRadio.enabled;
-  };
-
-  FMRadio.onfrequencychange = function() {
-    log("Check if 'onfrequencychange' event is fired after the 'enabled' event");
-    FMRadio.onfrequencychange = null;
-    ok(enabled, "FMRadio is enabled when handling `onfrequencychange`");
-    disableFMRadio();
-  };
-}
-
-function disableFMRadio() {
-  log("Verify behaviors when disabled");
-
-  // There are two possibilities which depends on the system
-  // process scheduling (bug 911063 comment 0):
-  //   - seek fails
-  //   - seek's onsuccess fires before disable's onsucess
-  var seekRequest = FMRadio.seekUp();
-  var seekCompletes = false;
-  var failedToSeek = false;
-  seekRequest.onerror = function() {
-    ok(!seekCompletes);
-    failedToSeek = true;
-  };
-
-  seekRequest.onsuccess = function() {
-    ok(!failedToSeek);
-    seekCompletes = true;
-  };
-
-  FMRadio.disable();
-  FMRadio.ondisabled = function() {
-    FMRadio.ondisabled = null;
-    ok(seekCompletes || failedToSeek);
-    ok(!FMRadio.enabled);
-    finish();
-  };
-}
-
-verifyInitialState();
-
deleted file mode 100644
--- a/dom/fmradio/test/marionette/test_one_seek_at_once.js
+++ /dev/null
@@ -1,73 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-MARIONETTE_TIMEOUT = 10000;
-
-SpecialPowers.addPermission("fmradio", true, document);
-
-var FMRadio = window.navigator.mozFMRadio;
-
-function verifyInitialState() {
-  log("Verifying initial state.");
-  ok(FMRadio);
-  is(FMRadio.enabled, false);
-  setUp();
-}
-
-function setUp() {
-  let frequency = FMRadio.frequencyLowerBound + FMRadio.channelWidth;
-  FMRadio.enable(frequency);
-  FMRadio.onenabled = seek;
-}
-
-function seek() {
-  var request = FMRadio.seekUp();
-  ok(request);
-
-  // There are two possibilities which depends on the system
-  // process scheduling (bug 911063 comment 0):
-  //   - the second seek fails
-  //   - both seeks are executed
-
-  request.onerror = function() {
-    ok(!firstSeekCompletes);
-    cleanUp();
-  };
-
-  var firstSeekCompletes = false;
-  request.onsuccess = function() {
-    firstSeekCompletes = true;
-  };
-
-  var seekAgainReq = FMRadio.seekUp();
-  ok(seekAgainReq);
-
-  seekAgainReq.onerror = function() {
-    log("Cancel the first seek to finish the test");
-    let cancelReq = FMRadio.cancelSeek();
-    ok(cancelReq);
-
-    // It's possible that the first seek completes when the
-    // cancel request is handled.
-    cancelReq.onerror = function() {
-      cleanUp();
-    };
-  };
-
-  seekAgainReq.onsuccess = function() {
-    ok(firstSeekCompletes);
-    cleanUp();
-  };
-}
-
-function cleanUp() {
-  FMRadio.disable();
-  FMRadio.ondisabled = function() {
-    FMRadio.ondisabled = null;
-    ok(!FMRadio.enabled);
-    finish();
-  };
-}
-
-verifyInitialState();
-
deleted file mode 100644
--- a/dom/fmradio/test/marionette/test_seek_up_and_down.js
+++ /dev/null
@@ -1,61 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-MARIONETTE_TIMEOUT = 30000;
-
-SpecialPowers.addPermission("fmradio", true, document);
-
-var FMRadio = window.navigator.mozFMRadio;
-
-function verifyInitialState() {
-  log("Verifying initial state.");
-  ok(FMRadio);
-  is(FMRadio.enabled, false);
-  setUp();
-}
-
-function setUp() {
-  let frequency = FMRadio.frequencyLowerBound + FMRadio.channelWidth;
-  FMRadio.enable(frequency);
-  FMRadio.onenabled = seekUp;
-}
-
-function seekUp() {
-  log("Seek up");
-  var request = FMRadio.seekUp();
-  ok(request);
-
-  request.onsuccess = function() {
-    seekDown();
-  };
-
-  request.onerror = function() {
-    ok(false, "Seekup request should not fail.");
-  };
-}
-
-function seekDown() {
-  log("Seek down");
-  var request = FMRadio.seekDown();
-  ok(request);
-
-  request.onsuccess = function() {
-    cleanUp();
-  };
-
-  request.onerror = function() {
-    ok(false, "Seekdown request should not fail.");
-  };
-}
-
-function cleanUp() {
-  FMRadio.disable();
-  FMRadio.ondisabled = function() {
-    FMRadio.ondisabled = null;
-    ok(!FMRadio.enabled);
-    finish();
-  };
-}
-
-verifyInitialState();
-
deleted file mode 100644
--- a/dom/fmradio/test/marionette/test_set_frequency.js
+++ /dev/null
@@ -1,87 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-MARIONETTE_TIMEOUT = 10000;
-
-SpecialPowers.addPermission("fmradio", true, document);
-
-var FMRadio = window.navigator.mozFMRadio;
-
-function verifyInitialState() {
-  log("Verifying initial state.");
-  ok(FMRadio);
-  is(FMRadio.enabled, false);
-  setUp();
-}
-
-function setUp() {
-  let frequency = FMRadio.frequencyLowerBound + FMRadio.channelWidth;
-  FMRadio.enable(frequency);
-  FMRadio.onenabled = setFrequency;
-}
-
-function setFrequency() {
-  log("Set Frequency");
-  let frequency = FMRadio.frequency + FMRadio.channelWidth;
-  var request = FMRadio.setFrequency(frequency);
-  ok(request);
-
-  request.onsuccess = setOutOfRangeFrequency;
-  request.onerror = function() {
-    ok(false, "setFrequency request should not fail.");
-  };
-}
-
-function setOutOfRangeFrequency() {
-  log("Set Frequency that out of the range");
-  var request = FMRadio.setFrequency(FMRadio.frequencyUpperBound + 1);
-  ok(request);
-
-  request.onsuccess = function() {
-    ok(false, "The request of setting an out-of-range frequency should fail.");
-  };
-  request.onerror = setFrequencyWhenSeeking;
-}
-
-function setFrequencyWhenSeeking() {
-  log("Set frequency when seeking");
-  var request = FMRadio.seekUp();
-  ok(request);
-
-  // There are two possibilities which depends on the system
-  // process scheduling (bug 911063 comment 0):
-  //   - seek fails
-  //   - seek's onsuccess fires before setFrequency's onsucess
-
-  var failedToSeek = false;
-  request.onerror = function() {
-    failedToSeek = true;
-  };
-
-  var seekCompletes = false;
-  request.onsuccess = function() {
-    ok(!failedToSeek);
-    seekCompletes = true;
-  };
-
-  var frequency = FMRadio.frequencyUpperBound - FMRadio.channelWidth;
-  var setFreqRequest = FMRadio.setFrequency(frequency);
-  ok(setFreqRequest);
-
-  setFreqRequest.onsuccess = function() {
-    ok(failedToSeek || seekCompletes);
-    cleanUp();
-  };
-}
-
-function cleanUp() {
-  FMRadio.disable();
-  FMRadio.ondisabled = function() {
-    FMRadio.ondisabled = null;
-    ok(!FMRadio.enabled);
-    finish();
-  };
-}
-
-verifyInitialState();
-
--- a/dom/html/HTMLIFrameElement.h
+++ b/dom/html/HTMLIFrameElement.h
@@ -163,17 +163,17 @@ public:
   {
     SetHTMLAttr(nsGkAtoms::referrerpolicy, aReferrer, aError);
   }
   void GetReferrerPolicy(nsAString& aReferrer)
   {
     GetEnumAttr(nsGkAtoms::referrerpolicy, EmptyCString().get(), aReferrer);
   }
   nsIDocument*
-  GetSVGDocument(const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal)
+  GetSVGDocument(nsIPrincipal& aSubjectPrincipal)
   {
     return GetContentDocument(aSubjectPrincipal);
   }
   bool Mozbrowser() const
   {
     return GetBoolAttr(nsGkAtoms::mozbrowser);
   }
   void SetMozbrowser(bool aAllow, ErrorResult& aError)
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -2836,21 +2836,19 @@ NS_IMETHODIMP
 HTMLInputElement::MozIsTextField(bool aExcludePassword, bool* aResult)
 {
   *aResult = MozIsTextField(aExcludePassword);
   return NS_OK;
 }
 
 void
 HTMLInputElement::SetUserInput(const nsAString& aInput,
-                               const mozilla::Maybe<nsIPrincipal*>& aPrincipal) {
-  MOZ_ASSERT(aPrincipal.isSome());
-
+                               nsIPrincipal& aSubjectPrincipal) {
   if (mType == NS_FORM_INPUT_FILE &&
-      !nsContentUtils::IsSystemPrincipal(aPrincipal.value())) {
+      !nsContentUtils::IsSystemPrincipal(&aSubjectPrincipal)) {
     return;
   }
 
   SetUserInput(aInput);
 }
 
 NS_IMETHODIMP
 HTMLInputElement::SetUserInput(const nsAString& aValue)
--- a/dom/html/HTMLInputElement.h
+++ b/dom/html/HTMLInputElement.h
@@ -816,17 +816,17 @@ public:
     return mNumberControlSpinnerIsSpinning && !mNumberControlSpinnerSpinsUp;
   }
 
   bool MozIsTextField(bool aExcludePassword);
 
   nsIEditor* GetEditor();
 
   void SetUserInput(const nsAString& aInput,
-                    const mozilla::Maybe<nsIPrincipal*>& aPrincipal);
+                    nsIPrincipal& aSubjectPrincipal);
 
   // XPCOM GetPhonetic() is OK
 
   /**
    * If aValue contains a valid floating-point number in the format specified
    * by the HTML 5 spec:
    *
    *   http://www.whatwg.org/specs/web-apps/current-work/multipage/common-microsyntaxes.html#floating-point-numbers
--- a/dom/html/HTMLObjectElement.cpp
+++ b/dom/html/HTMLObjectElement.cpp
@@ -461,23 +461,23 @@ HTMLObjectElement::TabIndexDefault()
 }
 
 NS_IMETHODIMP
 HTMLObjectElement::GetContentDocument(nsIDOMDocument **aContentDocument)
 {
   NS_ENSURE_ARG_POINTER(aContentDocument);
 
   nsCOMPtr<nsIDOMDocument> domDoc =
-    do_QueryInterface(GetContentDocument(Some(nsContentUtils::SubjectPrincipal())));
+    do_QueryInterface(GetContentDocument(*nsContentUtils::SubjectPrincipal()));
   domDoc.forget(aContentDocument);
   return NS_OK;
 }
 
 nsPIDOMWindowOuter*
-HTMLObjectElement::GetContentWindow(const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal)
+HTMLObjectElement::GetContentWindow(nsIPrincipal& aSubjectPrincipal)
 {
   nsIDocument* doc = GetContentDocument(aSubjectPrincipal);
   if (doc) {
     return doc->GetWindow();
   }
 
   return nullptr;
 }
--- a/dom/html/HTMLObjectElement.h
+++ b/dom/html/HTMLObjectElement.h
@@ -153,17 +153,17 @@ public:
   }
   void SetHeight(const nsAString& aValue, ErrorResult& aRv)
   {
     SetHTMLAttr(nsGkAtoms::height, aValue, aRv);
   }
   using nsObjectLoadingContent::GetContentDocument;
 
   nsPIDOMWindowOuter*
-  GetContentWindow(const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal);
+  GetContentWindow(nsIPrincipal& aSubjectPrincipal);
 
   using nsIConstraintValidation::CheckValidity;
   using nsIConstraintValidation::ReportValidity;
   using nsIConstraintValidation::GetValidationMessage;
   void GetAlign(DOMString& aValue)
   {
     GetHTMLAttr(nsGkAtoms::align, aValue);
   }
@@ -234,17 +234,17 @@ public:
     GetHTMLAttr(nsGkAtoms::border, aValue);
   }
   void SetBorder(const nsAString& aValue, ErrorResult& aRv)
   {
     SetHTMLAttr(nsGkAtoms::border, aValue, aRv);
   }
 
   nsIDocument*
-  GetSVGDocument(const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal)
+  GetSVGDocument(nsIPrincipal& aSubjectPrincipal)
   {
     return GetContentDocument(aSubjectPrincipal);
   }
 
 private:
   /**
    * Calls LoadObject with the correct arguments to start the plugin load.
    */
--- a/dom/html/HTMLSharedObjectElement.h
+++ b/dom/html/HTMLSharedObjectElement.h
@@ -183,17 +183,17 @@ public:
   {
     SetHTMLAttr(nsGkAtoms::type, aValue, aRv);
   }
   // width covered by <applet>
   // height covered by <applet>
   // align covered by <applet>
   // name covered by <applet>
   nsIDocument*
-  GetSVGDocument(const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal)
+  GetSVGDocument(nsIPrincipal& aSubjectPrincipal)
   {
     return GetContentDocument(aSubjectPrincipal);
   }
 
   /**
    * Calls LoadObject with the correct arguments to start the plugin load.
    */
   void StartObjectLoad(bool aNotify, bool aForceLoad);
--- a/dom/html/nsGenericHTMLFrameElement.cpp
+++ b/dom/html/nsGenericHTMLFrameElement.cpp
@@ -75,37 +75,36 @@ nsGenericHTMLFrameElement::~nsGenericHTM
   }
 }
 
 nsresult
 nsGenericHTMLFrameElement::GetContentDocument(nsIDOMDocument** aContentDocument)
 {
   NS_PRECONDITION(aContentDocument, "Null out param");
   nsCOMPtr<nsIDOMDocument> document =
-    do_QueryInterface(GetContentDocument(Some(nsContentUtils::SubjectPrincipal())));
+    do_QueryInterface(GetContentDocument(*nsContentUtils::SubjectPrincipal()));
   document.forget(aContentDocument);
   return NS_OK;
 }
 
 nsIDocument*
-nsGenericHTMLFrameElement::GetContentDocument(const Maybe<nsIPrincipal*>& aSubjectPrincipal)
+nsGenericHTMLFrameElement::GetContentDocument(nsIPrincipal& aSubjectPrincipal)
 {
   nsCOMPtr<nsPIDOMWindowOuter> win = GetContentWindow();
   if (!win) {
     return nullptr;
   }
 
   nsIDocument *doc = win->GetDoc();
   if (!doc) {
     return nullptr;
   }
 
   // Return null for cross-origin contentDocument.
-  if (!aSubjectPrincipal.value()
-        ->SubsumesConsideringDomain(doc->NodePrincipal())) {
+  if (!aSubjectPrincipal.SubsumesConsideringDomain(doc->NodePrincipal())) {
     return nullptr;
   }
   return doc;
 }
 
 already_AddRefed<nsPIDOMWindowOuter>
 nsGenericHTMLFrameElement::GetContentWindow()
 {
--- a/dom/html/nsGenericHTMLFrameElement.h
+++ b/dom/html/nsGenericHTMLFrameElement.h
@@ -97,18 +97,17 @@ public:
 
 protected:
   virtual ~nsGenericHTMLFrameElement();
 
   // This doesn't really ensure a frame loader in all cases, only when
   // it makes sense.
   void EnsureFrameLoader();
   nsresult LoadSrc();
-  nsIDocument*
-  GetContentDocument(const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal);
+  nsIDocument* GetContentDocument(nsIPrincipal& aSubjectPrincipal);
   nsresult GetContentDocument(nsIDOMDocument** aContentDocument);
   already_AddRefed<nsPIDOMWindowOuter> GetContentWindow();
 
   RefPtr<nsFrameLoader> mFrameLoader;
 
   /**
    * True when the element is created by the parser using the
    * NS_FROM_PARSER_NETWORK flag.
--- a/dom/html/nsHTMLDocument.cpp
+++ b/dom/html/nsHTMLDocument.cpp
@@ -2838,20 +2838,28 @@ nsHTMLDocument::SetDesignMode(const nsAS
   SetDesignMode(aDesignMode, nsContentUtils::GetCurrentJSContext()
                                ? Some(nsContentUtils::SubjectPrincipal())
                                : Nothing(), rv);
   return rv.StealNSResult();
 }
 
 void
 nsHTMLDocument::SetDesignMode(const nsAString& aDesignMode,
+                              nsIPrincipal& aSubjectPrincipal,
+                              ErrorResult& rv)
+{
+  SetDesignMode(aDesignMode, Some(&aSubjectPrincipal), rv);
+}
+
+void
+nsHTMLDocument::SetDesignMode(const nsAString& aDesignMode,
                               const Maybe<nsIPrincipal*>& aSubjectPrincipal,
                               ErrorResult& rv)
 {
-  if (!nsContentUtils::LegacyIsCallerNativeCode() &&
+  if (aSubjectPrincipal.isSome() &&
       !aSubjectPrincipal.value()->Subsumes(NodePrincipal())) {
     rv.Throw(NS_ERROR_DOM_PROP_ACCESS_DENIED);
     return;
   }
   bool editableMode = HasFlag(NODE_IS_EDITABLE);
   if (aDesignMode.LowerCaseEqualsASCII(editableMode ? "off" : "on")) {
     SetEditableFlag(!editableMode);
 
--- a/dom/html/nsHTMLDocument.h
+++ b/dom/html/nsHTMLDocument.h
@@ -206,21 +206,24 @@ public:
        bool aReplace,
        mozilla::ErrorResult& rv);
   void Close(mozilla::ErrorResult& rv);
   void Write(JSContext* cx, const mozilla::dom::Sequence<nsString>& aText,
              mozilla::ErrorResult& rv);
   void Writeln(JSContext* cx, const mozilla::dom::Sequence<nsString>& aText,
                mozilla::ErrorResult& rv);
   void GetDesignMode(nsAString& aDesignMode,
-                     const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal)
+                     nsIPrincipal& aSubjectPrincipal)
   {
     GetDesignMode(aDesignMode);
   }
   void SetDesignMode(const nsAString& aDesignMode,
+                     nsIPrincipal& aSubjectPrincipal,
+                     mozilla::ErrorResult& rv);
+  void SetDesignMode(const nsAString& aDesignMode,
                      const mozilla::Maybe<nsIPrincipal*>& aSubjectPrincipal,
                      mozilla::ErrorResult& rv);
   bool ExecCommand(const nsAString& aCommandID, bool aDoShowUI,
                    const nsAString& aValue, mozilla::ErrorResult& rv);
   bool QueryCommandEnabled(const nsAString& aCommandID,
                            mozilla::ErrorResult& rv);
   bool QueryCommandIndeterm(const nsAString& aCommandID,
                             mozilla::ErrorResult& rv);
--- a/dom/interfaces/events/nsIDOMDataTransfer.idl
+++ b/dom/interfaces/events/nsIDOMDataTransfer.idl
@@ -62,29 +62,16 @@ interface nsIDOMDataTransfer : nsISuppor
   /**
    * Holds a list of all the local files available on this data transfer.
    * A dataTransfer containing no files will return an empty list, and an
    * invalid index access on the resulting file list will return null. 
    */
   readonly attribute nsIDOMFileList files;
 
   /**
-   * Holds a list of the format types of the data that is stored for the first
-   * item, in the same order the data was added. An empty list will be
-   * returned if no data was added.
-   */
-  readonly attribute nsISupports types;
-
-  /**
-   * Retrieves the data for a given format, or an empty string if data for
-   * that format does not exist or the data transfer contains no data.
-   */
-  DOMString getData(in DOMString format);
-
-  /**
    * Set the image to be used for dragging if a custom one is desired. Most of
    * the time, this would not be set, as a default image is created from the
    * node that was dragged.
    *
    * If the node is an HTML img element, an HTML canvas element or a XUL image
    * element, the image data is used. Otherwise, image should be a visible
    * node and the drag image will be created from this. If image is null, any
    * custom drag image is cleared and the default is used instead.
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -170,17 +170,16 @@
 #endif
 
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/icc/IccChild.h"
 #include "mozilla/dom/mobileconnection/MobileConnectionChild.h"
 #include "mozilla/dom/mobilemessage/SmsChild.h"
 #include "mozilla/dom/devicestorage/DeviceStorageRequestChild.h"
 #include "mozilla/dom/bluetooth/PBluetoothChild.h"
-#include "mozilla/dom/PFMRadioChild.h"
 #include "mozilla/dom/PPresentationChild.h"
 #include "mozilla/dom/PresentationIPCService.h"
 #include "mozilla/ipc/InputStreamUtils.h"
 
 #ifdef MOZ_WEBSPEECH
 #include "mozilla/dom/PSpeechSynthesisChild.h"
 #endif
 
@@ -2073,40 +2072,16 @@ ContentChild::DeallocPBluetoothChild(PBl
 #ifdef MOZ_B2G_BT
   delete aActor;
   return true;
 #else
   MOZ_CRASH("No support for bluetooth on this platform!");
 #endif
 }
 
-PFMRadioChild*
-ContentChild::AllocPFMRadioChild()
-{
-#ifdef MOZ_B2G_FM
-  NS_RUNTIMEABORT("No one should be allocating PFMRadioChild actors");
-  return nullptr;
-#else
-  NS_RUNTIMEABORT("No support for FMRadio on this platform!");
-  return nullptr;
-#endif
-}
-
-bool
-ContentChild::DeallocPFMRadioChild(PFMRadioChild* aActor)
-{
-#ifdef MOZ_B2G_FM
-  delete aActor;
-  return true;
-#else
-  NS_RUNTIMEABORT("No support for FMRadio on this platform!");
-  return false;
-#endif
-}
-
 PSpeechSynthesisChild*
 ContentChild::AllocPSpeechSynthesisChild()
 {
 #ifdef MOZ_WEBSPEECH
   MOZ_CRASH("No one should be allocating PSpeechSynthesisChild actors");
 #else
   return nullptr;
 #endif
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -356,20 +356,16 @@ public:
   virtual PStorageChild* AllocPStorageChild() override;
 
   virtual bool DeallocPStorageChild(PStorageChild* aActor) override;
 
   virtual PBluetoothChild* AllocPBluetoothChild() override;
 
   virtual bool DeallocPBluetoothChild(PBluetoothChild* aActor) override;
 
-  virtual PFMRadioChild* AllocPFMRadioChild() override;
-
-  virtual bool DeallocPFMRadioChild(PFMRadioChild* aActor) override;
-
   virtual PPresentationChild* AllocPPresentationChild() override;
 
   virtual bool DeallocPPresentationChild(PPresentationChild* aActor) override;
 
   virtual PFlyWebPublishedServerChild*
     AllocPFlyWebPublishedServerChild(const nsString& name,
                                      const FlyWebPublishOptions& params) override;
 
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -49,17 +49,16 @@
 #include "mozilla/dom/ExternalHelperAppParent.h"
 #include "mozilla/dom/GetFilesHelper.h"
 #include "mozilla/dom/GeolocationBinding.h"
 #include "mozilla/dom/MediaKeySystemAccess.h"
 #include "mozilla/dom/Notification.h"
 #include "mozilla/dom/PContentBridgeParent.h"
 #include "mozilla/dom/PContentPermissionRequestParent.h"
 #include "mozilla/dom/PCycleCollectWithLogsParent.h"
-#include "mozilla/dom/PFMRadioParent.h"
 #include "mozilla/dom/PMemoryReportRequestParent.h"
 #include "mozilla/dom/ServiceWorkerRegistrar.h"
 #include "mozilla/dom/bluetooth/PBluetoothParent.h"
 #include "mozilla/dom/devicestorage/DeviceStorageRequestParent.h"
 #include "mozilla/dom/icc/IccParent.h"
 #include "mozilla/dom/mobileconnection/MobileConnectionParent.h"
 #include "mozilla/dom/mobilemessage/SmsParent.h"
 #include "mozilla/dom/power/PowerManagerService.h"
@@ -231,20 +230,16 @@ using namespace mozilla::system;
 
 #ifdef MOZ_B2G_BT
 #include "BluetoothParent.h"
 #include "BluetoothService.h"
 #endif
 
 #include "mozilla/RemoteSpellCheckEngineParent.h"
 
-#ifdef MOZ_B2G_FM
-#include "mozilla/dom/FMRadioParent.h"
-#endif
-
 #include "Crypto.h"
 
 #ifdef MOZ_WEBSPEECH
 #include "mozilla/dom/SpeechSynthesisParent.h"
 #endif
 
 #if defined(MOZ_CONTENT_SANDBOX) && defined(XP_LINUX)
 #include "mozilla/SandboxInfo.h"
@@ -3567,42 +3562,16 @@ ContentParent::RecvPBluetoothConstructor
   NS_ENSURE_TRUE(btService, false);
 
   return static_cast<BluetoothParent*>(aActor)->InitWithService(btService);
 #else
   MOZ_CRASH("No support for bluetooth on this platform!");
 #endif
 }
 
-PFMRadioParent*
-ContentParent::AllocPFMRadioParent()
-{
-#ifdef MOZ_B2G_FM
-  if (!AssertAppProcessPermission(this, "fmradio")) {
-  return nullptr;
-  }
-  return new FMRadioParent();
-#else
-  NS_WARNING("No support for FMRadio on this platform!");
-  return nullptr;
-#endif
-}
-
-bool
-ContentParent::DeallocPFMRadioParent(PFMRadioParent* aActor)
-{
-#ifdef MOZ_B2G_FM
-  delete aActor;
-  return true;
-#else
-  NS_WARNING("No support for FMRadio on this platform!");
-  return false;
-#endif
-}
-
 PPresentationParent*
 ContentParent::AllocPPresentationParent()
 {
   RefPtr<PresentationParent> actor = new PresentationParent();
   return actor.forget().take();
 }
 
 bool
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -864,20 +864,16 @@ private:
   virtual bool DeallocPStorageParent(PStorageParent* aActor) override;
 
   virtual PBluetoothParent* AllocPBluetoothParent() override;
 
   virtual bool DeallocPBluetoothParent(PBluetoothParent* aActor) override;
 
   virtual bool RecvPBluetoothConstructor(PBluetoothParent* aActor) override;
 
-  virtual PFMRadioParent* AllocPFMRadioParent() override;
-
-  virtual bool DeallocPFMRadioParent(PFMRadioParent* aActor) override;
-
   virtual PPresentationParent* AllocPPresentationParent() override;
 
   virtual bool DeallocPPresentationParent(PPresentationParent* aActor) override;
 
   virtual bool RecvPPresentationConstructor(PPresentationParent* aActor) override;
 
   virtual PFlyWebPublishedServerParent*
     AllocPFlyWebPublishedServerParent(const nsString& name,
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -13,17 +13,16 @@ include protocol PContentBridge;
 include protocol PContentPermissionRequest;
 include protocol PCycleCollectWithLogs;
 include protocol PCrashReporter;
 include protocol PPSMContentDownloader;
 include protocol PExternalHelperApp;
 include protocol PHandlerService;
 include protocol PDeviceStorageRequest;
 include protocol PFileDescriptorSet;
-include protocol PFMRadio;
 include protocol PHal;
 include protocol PHeapSnapshotTempFileHelper;
 include protocol PIcc;
 include protocol PProcessHangMonitor;
 include protocol PImageBridge;
 include protocol PMedia;
 include protocol PMemoryReportRequest;
 include protocol PMobileConnection;
@@ -233,50 +232,16 @@ struct DeviceStorageLocationInfo {
   nsString music;
   nsString pictures;
   nsString videos;
   nsString sdcard;
   nsString apps;
   nsString crashes;
 };
 
-struct FMRadioRequestEnableParams
-{
-  double frequency;
-};
-
-struct FMRadioRequestDisableParams
-{
-
-};
-
-struct FMRadioRequestSetFrequencyParams
-{
-  double frequency;
-};
-
-struct FMRadioRequestSeekParams
-{
-  bool upward;
-};
-
-struct FMRadioRequestCancelSeekParams
-{
-
-};
-
-union FMRadioRequestParams
-{
-  FMRadioRequestEnableParams;
-  FMRadioRequestDisableParams;
-  FMRadioRequestSetFrequencyParams;
-  FMRadioRequestSeekParams;
-  FMRadioRequestCancelSeekParams;
-};
-
 union PrefValue {
   nsCString;
   int32_t;
   bool;
 };
 
 union MaybePrefValue {
   PrefValue;
@@ -390,17 +355,16 @@ nested(upto inside_cpow) sync protocol P
     manages PBrowser;
     manages PContentPermissionRequest;
     manages PCrashReporter;
     manages PCycleCollectWithLogs;
     manages PDeviceStorageRequest;
     manages PPSMContentDownloader;
     manages PExternalHelperApp;
     manages PFileDescriptorSet;
-    manages PFMRadio;
     manages PHal;
     manages PHandlerService;
     manages PHeapSnapshotTempFileHelper;
     manages PIcc;
     manages PMedia;
     manages PMemoryReportRequest;
     manages PMobileConnection;
     manages PNecko;
@@ -869,18 +833,16 @@ parent:
     async PTelephony();
 
     async PVoicemail();
 
     async PMedia();
 
     async PBluetooth();
 
-    async PFMRadio();
-
     async PWebrtcGlobal();
 
     async PPresentation();
 
     async PFlyWebPublishedServer(nsString name, FlyWebPublishOptions params);
 
     // Services remoting
 
--- a/dom/ipc/ProcessHangMonitor.cpp
+++ b/dom/ipc/ProcessHangMonitor.cpp
@@ -302,17 +302,18 @@ HangMonitorChild::InterruptCallback()
     forcePaintEpoch = mForcePaintEpoch;
 
     mForcePaint = false;
   }
 
   if (forcePaint) {
     RefPtr<TabChild> tabChild = TabChild::FindTabChild(forcePaintTab);
     if (tabChild) {
-      JS::AutoAssertOnGC aaogc(mContext);
+      JS::AutoAssertOnGC nogc(mContext);
+      JS::AutoAssertOnBarrier nobarrier(mContext);
       tabChild->ForcePaint(forcePaintEpoch);
     }
   }
 }
 
 void
 HangMonitorChild::Shutdown()
 {
@@ -384,16 +385,17 @@ HangMonitorChild::RecvForcePaint(const T
   {
     MonitorAutoLock lock(mMonitor);
     mForcePaint = true;
     mForcePaintTab = aTabId;
     mForcePaintEpoch = aLayerObserverEpoch;
   }
 
   JS_RequestInterruptCallback(mContext);
+  JS::RequestGCInterruptCallback(mContext);
 
   return true;
 }
 
 void
 HangMonitorChild::Open(Transport* aTransport, ProcessId aPid,
                        MessageLoop* aIOLoop)
 {
@@ -1182,16 +1184,17 @@ mozilla::CreateHangMonitorParent(Content
 PProcessHangMonitorChild*
 mozilla::CreateHangMonitorChild(mozilla::ipc::Transport* aTransport,
                                 base::ProcessId aOtherPid)
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
 
   JSContext* cx = danger::GetJSContext();
   JS_AddInterruptCallback(cx, InterruptCallback);
+  JS::AddGCInterruptCallback(cx, InterruptCallback);
 
   ProcessHangMonitor* monitor = ProcessHangMonitor::GetOrCreate();
   HangMonitorChild* child = new HangMonitorChild(monitor);
 
   monitor->MonitorLoop()->PostTask(NewNonOwningRunnableMethod
                                    <mozilla::ipc::Transport*,
                                     base::ProcessId,
                                     MessageLoop*>(child,
--- a/dom/ipc/moz.build
+++ b/dom/ipc/moz.build
@@ -125,17 +125,16 @@ LOCAL_INCLUDES += [
     '/chrome',
     '/docshell/base',
     '/dom/base',
     '/dom/bluetooth/common',
     '/dom/bluetooth/ipc',
     '/dom/devicestorage',
     '/dom/events',
     '/dom/filesystem',
-    '/dom/fmradio/ipc',
     '/dom/geolocation',
     '/dom/media/webspeech/synth/ipc',
     '/dom/mobilemessage/ipc',
     '/dom/security',
     '/dom/storage',
     '/dom/workers',
     '/embedding/components/printingui/ipc',
     '/extensions/cookie',
new file mode 100644
--- /dev/null
+++ b/dom/media/test/crashtests/1223670.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+<script>
+
+function boom()
+{
+
+    var ac = new window.AudioContext("publicnotification");
+
+    setTimeout(function() {
+        document.documentElement.removeAttribute("class");
+        var htmlAudio = new Audio();
+        var stream = htmlAudio.mozCaptureStreamUntilEnded();
+        ac.createMediaStreamSource(stream);
+    }, 0);
+}
+
+</script>
+</head>
+<body onload="boom();">
+</body>
+</html>
--- a/dom/media/test/crashtests/crashtests.list
+++ b/dom/media/test/crashtests/crashtests.list
@@ -76,16 +76,17 @@ load 1041466.html
 load 1045650.html
 load 1080986.html
 load 1122218.html
 load 1127188.html
 load 1157994.html
 skip-if(!B2G) load 1158427.html
 load 1185176.html
 load 1185192.html
+load 1223670.html
 load 1228484.html
 load 1304948.html
 load analyser-channels-1.html
 load audiocontext-double-suspend.html
 load buffer-source-duration-1.html
 load buffer-source-ended-1.html
 load buffer-source-resampling-start-1.html
 load doppler-1.html
--- a/dom/moz.build
+++ b/dom/moz.build
@@ -52,17 +52,16 @@ DIRS += [
     'phonenumberutils',
     'devicestorage',
     'encoding',
     'events',
     'fetch',
     'filehandle',
     'filesystem',
     'flyweb',
-    'fmradio',
     'gamepad',
     'geolocation',
     'grid',
     'html',
     'icc',
     'inputport',
     'json',
     'jsurl',
--- a/dom/permission/moz.build
+++ b/dom/permission/moz.build
@@ -29,19 +29,16 @@ EXTRA_JS_MODULES += [
 
 XPCSHELL_TESTS_MANIFESTS += ['tests/unit/xpcshell.ini']
 
 MOCHITEST_MANIFESTS += ['tests/mochitest.ini']
 
 if CONFIG['MOZ_B2G_RIL']:
     MOCHITEST_MANIFESTS += ['tests/mochitest-ril.ini']
 
-if CONFIG['MOZ_B2G_FM']:
-    MOCHITEST_MANIFESTS += ['tests/mochitest-fm.ini']
-
 if CONFIG['MOZ_B2G_BT']:
     MOCHITEST_MANIFESTS += ['tests/mochitest-bt.ini']
 
 if CONFIG['MOZ_WEBSMS_BACKEND']:
     MOCHITEST_MANIFESTS += ['tests/mochitest-websms.ini']
 
 if CONFIG['MOZ_TIME_MANAGER']:
     MOCHITEST_MANIFESTS += ['tests/mochitest-time.ini']
deleted file mode 100644
--- a/dom/permission/tests/mochitest-fm.ini
+++ /dev/null
@@ -1,1 +0,0 @@
-[test_fmradio.html]
deleted file mode 100644
--- a/dom/permission/tests/test_fmradio.html
+++ /dev/null
@@ -1,31 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=815105
--->
-<head>
-  <meta charset="utf-8">
-  <title>Test for Bug 815105 </title>
-  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-</head>
-<body>
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=815105">Mozilla Bug 815105 </a>
-<p id="display"></p>
-<div id="content" style="display: none"></div>
-<pre id="test">
-<script type="application/javascript;version=1.8" src="file_framework.js"></script>
-<script type="application/javascript;version=1.8">
-var gData = [
-  {
-    perm: ["fmradio"],
-    needParentPerm: true,
-    obj: "mozFMRadio",
-    webidl: "FMRadio",
-  },
-]
-</script>
-</pre>
-</body>
-</html>
-
--- a/dom/security/nsContentSecurityManager.cpp
+++ b/dom/security/nsContentSecurityManager.cpp
@@ -164,17 +164,17 @@ DoCORSChecks(nsIChannel* aChannel, nsILo
 }
 
 static nsresult
 DoContentSecurityChecks(nsIChannel* aChannel, nsILoadInfo* aLoadInfo)
 {
   nsCOMPtr<nsIURI> uri;
   nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
   NS_ENSURE_SUCCESS(rv, rv);
-  
+
   nsContentPolicyType contentPolicyType =
     aLoadInfo->GetExternalContentPolicyType();
   nsContentPolicyType internalContentPolicyType =
     aLoadInfo->InternalContentPolicyType();
   nsCString mimeTypeGuess;
   nsCOMPtr<nsINode> requestingContext = nullptr;
 
 #ifdef DEBUG
@@ -666,10 +666,29 @@ nsContentSecurityManager::IsOriginPotent
   }
 
   if (host.Equals("127.0.0.1") ||
       host.Equals("localhost") ||
       host.Equals("::1")) {
     *aIsTrustWorthy = true;
     return NS_OK;
   }
+
+  // If a host is not considered secure according to the default algorithm, then
+  // check to see if it has been whitelisted by the user.  We only apply this
+  // whitelist for network resources, i.e., those with scheme "http" or "ws".
+  // The pref should contain a comma-separated list of hostnames.
+  if (scheme.EqualsLiteral("http") || scheme.EqualsLiteral("ws")) {
+    nsAdoptingCString whitelist = Preferences::GetCString("dom.securecontext.whitelist");
+    if (whitelist) {
+      nsCCharSeparatedTokenizer tokenizer(whitelist, ',');
+      while (tokenizer.hasMoreTokens()) {
+        const nsCSubstring& allowedHost = tokenizer.nextToken();
+        if (host.Equals(allowedHost)) {
+          *aIsTrustWorthy = true;
+          return NS_OK;
+        }
+      }
+    }
+  }
+
   return NS_OK;
 }
--- a/dom/security/test/unit/test_isOriginPotentiallyTrustworthy.js
+++ b/dom/security/test/unit/test_isOriginPotentiallyTrustworthy.js
@@ -14,26 +14,34 @@ Cu.import("resource://gre/modules/XPCOMU
 XPCOMUtils.defineLazyServiceGetter(this, "gScriptSecurityManager",
                                    "@mozilla.org/scriptsecuritymanager;1",
                                    "nsIScriptSecurityManager");
 
 XPCOMUtils.defineLazyServiceGetter(this, "gContentSecurityManager",
                                    "@mozilla.org/contentsecuritymanager;1",
                                    "nsIContentSecurityManager");
 
+var prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
+prefs.setCharPref("dom.securecontext.whitelist", "example.net,example.org");
+
 add_task(function* test_isOriginPotentiallyTrustworthy() {
   for (let [uriSpec, expectedResult] of [
     ["http://example.com/", false],
     ["https://example.com/", true],
     ["http://localhost/", true],
     ["http://127.0.0.1/", true],
     ["file:///", true],
     ["resource:///", true],
+    ["app://", true],
     ["moz-extension://", true],
+    ["wss://example.com/", true],
     ["about:config", false],
     ["urn:generic", false],
+    ["http://example.net/", true],
+    ["ws://example.org/", true],
+    ["chrome://example.net/content/messenger.xul", false],
   ]) {
     let uri = NetUtil.newURI(uriSpec);
     let principal = gScriptSecurityManager.getCodebasePrincipal(uri);
     Assert.equal(gContentSecurityManager.isOriginPotentiallyTrustworthy(principal),
                  expectedResult);
   }
 });
--- a/dom/storage/DOMStorage.cpp
+++ b/dom/storage/DOMStorage.cpp
@@ -62,69 +62,61 @@ DOMStorage::~DOMStorage()
 
 /* virtual */ JSObject*
 DOMStorage::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return StorageBinding::Wrap(aCx, this, aGivenProto);
 }
 
 uint32_t
-DOMStorage::GetLength(const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+DOMStorage::GetLength(nsIPrincipal& aSubjectPrincipal,
                       ErrorResult& aRv)
 {
-  MOZ_ASSERT(aSubjectPrincipal.isSome());
-
-  if (!CanUseStorage(nullptr, aSubjectPrincipal, this)) {
+  if (!CanUseStorage(aSubjectPrincipal)) {
     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
     return 0;
   }
 
   uint32_t length;
   aRv = mCache->GetLength(this, &length);
   return length;
 }
 
 void
 DOMStorage::Key(uint32_t aIndex, nsAString& aResult,
-                const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                nsIPrincipal& aSubjectPrincipal,
                 ErrorResult& aRv)
 {
-  MOZ_ASSERT(aSubjectPrincipal.isSome());
-
-  if (!CanUseStorage(nullptr, aSubjectPrincipal, this)) {
+  if (!CanUseStorage(aSubjectPrincipal)) {
     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
     return;
   }
 
   aRv = mCache->GetKey(this, aIndex, aResult);
 }
 
 void
 DOMStorage::GetItem(const nsAString& aKey, nsAString& aResult,
-                    const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                    nsIPrincipal& aSubjectPrincipal,
                     ErrorResult& aRv)
 {
-  MOZ_ASSERT(aSubjectPrincipal.isSome());
-
-  if (!CanUseStorage(nullptr, aSubjectPrincipal, this)) {
+  if (!CanUseStorage(aSubjectPrincipal)) {
     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
     return;
   }
 
   aRv = mCache->GetItem(this, aKey, aResult);
 }
 
 void
 DOMStorage::SetItem(const nsAString& aKey, const nsAString& aData,
-                    const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                    nsIPrincipal& aSubjectPrincipal,
                     ErrorResult& aRv)
 {
-  MOZ_ASSERT(aSubjectPrincipal.isSome());
-
-  if (!CanUseStorage(nullptr, aSubjectPrincipal, this)) {
+  if (!CanUseStorage(aSubjectPrincipal)) {
     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
     return;
   }
 
   nsString data;
   bool ok = data.Assign(aData, fallible);
   if (!ok) {
     aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
@@ -139,44 +131,40 @@ DOMStorage::SetItem(const nsAString& aKe
 
   if (!aRv.ErrorCodeIs(NS_SUCCESS_DOM_NO_OPERATION)) {
     BroadcastChangeNotification(aKey, old, aData);
   }
 }
 
 void
 DOMStorage::RemoveItem(const nsAString& aKey,
-                       const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                       nsIPrincipal& aSubjectPrincipal,
                        ErrorResult& aRv)
 {
-  MOZ_ASSERT(aSubjectPrincipal.isSome());
-
-  if (!CanUseStorage(nullptr, aSubjectPrincipal, this)) {
+  if (!CanUseStorage(aSubjectPrincipal)) {
     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
     return;
   }
 
   nsAutoString old;
   aRv = mCache->RemoveItem(this, aKey, old);
   if (aRv.Failed()) {
     return;
   }
 
   if (!aRv.ErrorCodeIs(NS_SUCCESS_DOM_NO_OPERATION)) {
     BroadcastChangeNotification(aKey, old, NullString());
   }
 }
 
 void
-DOMStorage::Clear(const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+DOMStorage::Clear(nsIPrincipal& aSubjectPrincipal,
                   ErrorResult& aRv)
 {
-  MOZ_ASSERT(aSubjectPrincipal.isSome());
-
-  if (!CanUseStorage(nullptr, aSubjectPrincipal, this)) {
+  if (!CanUseStorage(aSubjectPrincipal)) {
     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
     return;
   }
 
   aRv = mCache->Clear(this);
   if (NS_WARN_IF(aRv.Failed())) {
     return;
   }
@@ -240,47 +228,34 @@ DOMStorage::BroadcastChangeNotification(
                                   ? u"localStorage"
                                   : u"sessionStorage");
   NS_DispatchToMainThread(r);
 }
 
 static const char kPermissionType[] = "cookie";
 static const char kStorageEnabled[] = "dom.storage.enabled";
 
-// static, public
 bool
-DOMStorage::CanUseStorage(nsPIDOMWindowInner* aWindow,
-                          const Maybe<nsIPrincipal*>& aSubjectPrincipal,
-                          DOMStorage* aStorage)
+DOMStorage::CanUseStorage(nsIPrincipal& aSubjectPrincipal)
 {
   // This method is responsible for correct setting of mIsSessionOnly.
 
   if (!mozilla::Preferences::GetBool(kStorageEnabled)) {
     return false;
   }
 
-  nsContentUtils::StorageAccess access = nsContentUtils::StorageAccess::eDeny;
-  if (aWindow) {
-    access = nsContentUtils::StorageAllowedForWindow(aWindow);
-  } else if (aStorage) {
-    access = nsContentUtils::StorageAllowedForPrincipal(aStorage->mPrincipal);
-  }
+  nsContentUtils::StorageAccess access =
+    nsContentUtils::StorageAllowedForPrincipal(mPrincipal);
 
   if (access == nsContentUtils::StorageAccess::eDeny) {
     return false;
   }
 
-  if (aStorage) {
-    aStorage->mIsSessionOnly = access <= nsContentUtils::StorageAccess::eSessionScoped;
-
-    MOZ_ASSERT(aSubjectPrincipal.isSome());
-    return aStorage->CanAccess(aSubjectPrincipal.value());
-  }
-
-  return true;
+  mIsSessionOnly = access <= nsContentUtils::StorageAccess::eSessionScoped;
+  return CanAccess(&aSubjectPrincipal);
 }
 
 DOMStorage::StorageType
 DOMStorage::GetType() const
 {
   return mManager->Type();
 }
 
@@ -315,18 +290,17 @@ bool
 DOMStorage::CanAccess(nsIPrincipal* aPrincipal)
 {
   return !aPrincipal || aPrincipal->Subsumes(mPrincipal);
 }
 
 void
 DOMStorage::GetSupportedNames(nsTArray<nsString>& aKeys)
 {
-  if (!CanUseStorage(nullptr, Some(nsContentUtils::SubjectPrincipal()),
-                     this)) {
+  if (!CanUseStorage(*nsContentUtils::SubjectPrincipal())) {
     // return just an empty array
     aKeys.Clear();
     return;
   }
 
   mCache->GetKeys(this, aKeys);
 }
 
--- a/dom/storage/DOMStorage.h
+++ b/dom/storage/DOMStorage.h
@@ -65,84 +65,83 @@ public:
   // WebIDL
   JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   nsPIDOMWindowInner* GetParentObject() const
   {
     return mWindow;
   }
 
-  uint32_t GetLength(const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+  uint32_t GetLength(nsIPrincipal& aSubjectPrincipal,
                      ErrorResult& aRv);
 
   void Key(uint32_t aIndex, nsAString& aResult,
-           const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+           nsIPrincipal& aSubjectPrincipal,
            ErrorResult& aRv);
 
   void GetItem(const nsAString& aKey, nsAString& aResult,
-               const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+               nsIPrincipal& aSubjectPrincipal,
                ErrorResult& aRv);
 
   void GetSupportedNames(nsTArray<nsString>& aKeys);
 
   void NamedGetter(const nsAString& aKey, bool& aFound, nsAString& aResult,
-                   const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                   nsIPrincipal& aSubjectPrincipal,
                    ErrorResult& aRv)
   {
     GetItem(aKey, aResult, aSubjectPrincipal, aRv);
     aFound = !aResult.IsVoid();
   }
 
   void SetItem(const nsAString& aKey, const nsAString& aValue,
-               const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+               nsIPrincipal& aSubjectPrincipal,
                ErrorResult& aRv);
 
   void NamedSetter(const nsAString& aKey, const nsAString& aValue,
-                   const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                   nsIPrincipal& aSubjectPrincipal,
                    ErrorResult& aRv)
   {
     SetItem(aKey, aValue, aSubjectPrincipal, aRv);
   }
 
   void RemoveItem(const nsAString& aKey,
-                  const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                  nsIPrincipal& aSubjectPrincipal,
                   ErrorResult& aRv);
 
   void NamedDeleter(const nsAString& aKey, bool& aFound,
-                    const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+                    nsIPrincipal& aSubjectPrincipal,
                     ErrorResult& aRv)
   {
     RemoveItem(aKey, aSubjectPrincipal, aRv);
 
     aFound = !aRv.ErrorCodeIs(NS_SUCCESS_DOM_NO_OPERATION);
   }
 
-  void Clear(const Maybe<nsIPrincipal*>& aSubjectPrincipal,
+  void Clear(nsIPrincipal& aSubjectPrincipal,
              ErrorResult& aRv);
 
-  // The method checks whether the caller can use a storage.
-  // CanUseStorage is called before any DOM initiated operation
-  // on a storage is about to happen and ensures that the storage's
-  // session-only flag is properly set according the current settings.
-  // It is an optimization since the privileges check and session only
-  // state determination are complex and share the code (comes hand in
-  // hand together).
-  static bool CanUseStorage(nsPIDOMWindowInner* aWindow,
-                            const Maybe<nsIPrincipal*>& aSubjectPrincipal,
-                            DOMStorage* aStorage = nullptr);
-
   bool IsPrivate() const;
   bool IsSessionOnly() const { return mIsSessionOnly; }
 
   bool IsForkOf(const DOMStorage* aOther) const
   {
     MOZ_ASSERT(aOther);
     return mCache == aOther->mCache;
   }
 
+protected:
+  // The method checks whether the caller can use a storage.
+  // CanUseStorage is called before any DOM initiated operation
+  // on a storage is about to happen and ensures that the storage's
+  // session-only flag is properly set according the current settings.
+  // It is an optimization since the privileges check and session only
+  // state determination are complex and share the code (comes hand in
+  // hand together).
+  bool CanUseStorage(nsIPrincipal& aSubjectPrincipal);
+
 private:
   ~DOMStorage();
 
   friend class DOMStorageManager;
   friend class DOMStorageCache;
 
   nsCOMPtr<nsPIDOMWindowInner> mWindow;
   RefPtr<DOMStorageManager> mManager;
rename from dom/u2f/tests/test_frame_appid_facet.html
rename to dom/u2f/tests/frame_appid_facet.html
--- a/dom/u2f/tests/test_frame_appid_facet.html
+++ b/dom/u2f/tests/frame_appid_facet.html
@@ -8,51 +8,56 @@
 <script class="testbody" type="text/javascript">
 "use strict";
 
 local_is(window.location.origin, "https://example.com", "Is loaded correctly");
 
 var version = "U2F_V2";
 var challenge = new Uint8Array(16);
 
+local_expectThisManyTests(5);
+
 u2f.register(null, [{
   version: version,
   challenge: bytesToBase64UrlSafe(challenge),
 }], [], function(res){
   local_is(res.errorCode, 0, "Null AppID should work.");
+  local_completeTest();
 });
 
 u2f.register("", [{
   version: version,
   challenge: bytesToBase64UrlSafe(challenge),
 }], [], function(res){
   local_is(res.errorCode, 0, "Empty AppID should work.");
+  local_completeTest();
 });
 
 // Test: Correct TLD, but incorrect scheme
 u2f.register("http://example.com/appId", [{
   version: version,
   challenge: bytesToBase64UrlSafe(challenge),
 }], [], function(res){
   local_isnot(res.errorCode, 0, "HTTP scheme is disallowed");
+  local_completeTest();
 });
 
 // Test: Correct TLD, and also HTTPS
 u2f.register("https://example.com/appId", [{
   version: version,
   challenge: bytesToBase64UrlSafe(challenge),
 }], [], function(res){
   local_is(res.errorCode, 0, "HTTPS origin for example.com should work");
+  local_completeTest();
 });
 
 // Test: Dynamic origin
 u2f.register(window.location.origin + "/otherAppId", [{
   version: version,
   challenge: bytesToBase64UrlSafe(challenge),
 }], [], function(res){
   local_is(res.errorCode, 0, "Direct window origin should work");
+  local_completeTest();
 });
 
-local_finished();
-
 </script>
 </body>
 </html>
rename from dom/u2f/tests/test_frame_appid_facet_insecure.html
rename to dom/u2f/tests/frame_appid_facet_insecure.html
--- a/dom/u2f/tests/test_frame_appid_facet_insecure.html
+++ b/dom/u2f/tests/frame_appid_facet_insecure.html
@@ -8,48 +8,53 @@
 <script class="testbody" type="text/javascript">
 "use strict";
 
 local_is(window.location.origin, "http://mochi.test:8888", "Is loaded correctly");
 
 var version = "U2F_V2";
 var challenge = new Uint8Array(16);
 
+local_expectThisManyTests(5);
+
 u2f.register(null, [{
   version: version,
   challenge: bytesToBase64UrlSafe(challenge),
 }], [], function(res){
   local_isnot(res.errorCode, 0, "Insecure origin disallowed for null AppID");
+  local_completeTest();
 });
 
 u2f.register("", [{
   version: version,
   challenge: bytesToBase64UrlSafe(challenge),
 }], [], function(res){
   local_isnot(res.errorCode, 0, "Insecure origin disallowed for empty AppID");
+  local_completeTest();
 });
 
 u2f.register("http://example.com/appId", [{
   version: version,
   challenge: bytesToBase64UrlSafe(challenge),
 }], [], function(res){
   local_isnot(res.errorCode, 0, "Insecure origin disallowed for HTTP AppID");
+  local_completeTest();
 });
 
 u2f.register("https://example.com/appId", [{
   version: version,
   challenge: bytesToBase64UrlSafe(challenge),
 }], [], function(res){
   local_isnot(res.errorCode, 0, "Insecure origin disallowed for HTTPS AppID from HTTP origin");
+  local_completeTest();
 });
 
 u2f.register(window.location.origin + "/otherAppId", [{
   version: version,
   challenge: bytesToBase64UrlSafe(challenge),
 }], [], function(res){
   local_isnot(res.errorCode, 0, "Insecure origin disallowed for HTTP origin");
+  local_completeTest();
 });
 
-local_finished();
-
 </script>
 </body>
 </html>
rename from dom/u2f/tests/test_frame_appid_facet_subdomain.html
rename to dom/u2f/tests/frame_appid_facet_subdomain.html
--- a/dom/u2f/tests/test_frame_appid_facet_subdomain.html
+++ b/dom/u2f/tests/frame_appid_facet_subdomain.html
@@ -8,53 +8,49 @@
 <script class="testbody" type="text/javascript">
 "use strict";
 
 var version = "U2F_V2";
 var challenge = new Uint8Array(16);
 
 local_is(window.location.origin, "https://test1.example.com", "Is loaded correctly");
 
+local_expectThisManyTests(4);
+
 // same domain check
 u2f.register("https://test1.example.com/appId", [{
   version: version,
   challenge: bytesToBase64UrlSafe(challenge),
 }], [], function(res){
   local_is(res.errorCode, 0, "AppID should work from a different path of this domain");
-  step2();
+  local_completeTest();
+});
+
+// same domain check, but wrong scheme
+u2f.register("http://test1.example.com/appId", [{
+  version: version,
+  challenge: bytesToBase64UrlSafe(challenge),
+}], [], function(res){
+  local_isnot(res.errorCode, 0, "AppID should not work when using a different scheme");
+  local_completeTest();
 });
 
-function step2() {
-  // same domain check, but wrong scheme
-  u2f.register("http://test1.example.com/appId", [{
-    version: version,
-    challenge: bytesToBase64UrlSafe(challenge),
-  }], [], function(res){
-    local_isnot(res.errorCode, 0, "AppID should not work when using a different scheme");
-    step3();
-  });
-}
+// eTLD+1 subdomain check
+u2f.register("https://example.com/appId", [{
+  version: version,
+  challenge: bytesToBase64UrlSafe(challenge),
+}], [], function(res){
+  local_isnot(res.errorCode, 0, "AppID should not work from another subdomain in this registered domain");
+  local_completeTest();
+});
 
-function step3() {
-  // eTLD+1 subdomain check
-  u2f.register("https://example.com/appId", [{
-    version: version,
-    challenge: bytesToBase64UrlSafe(challenge),
-  }], [], function(res){
-    local_isnot(res.errorCode, 0, "AppID should not work from another subdomain in this registered domain");
-    step4();
-  });
-}
-
-function step4() {
-  // other domain check
-  u2f.register("https://mochi.test:8888/appId", [{
-    version: version,
-    challenge: bytesToBase64UrlSafe(challenge),
-  }], [], function(res){
-    local_isnot(res.errorCode, 0, "AppID should not work from other domains");
-    local_finished();
-  });
-}
+// other domain check
+u2f.register("https://mochi.test:8888/appId", [{
+  version: version,
+  challenge: bytesToBase64UrlSafe(challenge),
+}], [], function(res){
+  local_isnot(res.errorCode, 0, "AppID should not work from other domains");
+  local_completeTest();
+});
 
 </script>
 </body>
 </html>
--- a/dom/u2f/tests/frame_no_token.html
+++ b/dom/u2f/tests/frame_no_token.html
@@ -11,17 +11,20 @@
 var challenge = new Uint8Array(16);
 window.crypto.getRandomValues(challenge);
 
 var regRequest = {
   version: "U2F_V2",
   challenge: bytesToBase64UrlSafe(challenge),
 };
 
-u2f.register(window.location.origin, [regRequest], [], function (regResponse) {
-  parent.postMessage(regResponse.errorCode, '*');
+local_expectThisManyTests(1);
+
+u2f.register(window.location.origin, [regRequest], [], function (res) {
+  local_isnot(res.errorCode, 0, "The registration should be rejected.");
+  local_completeTest();
 });
 
 </script>
 
 </body>
 </html>
 
rename from dom/u2f/tests/test_frame_register.html
rename to dom/u2f/tests/frame_register.html
--- a/dom/u2f/tests/test_frame_register.html
+++ b/dom/u2f/tests/frame_register.html
@@ -8,67 +8,74 @@
 <script class="testbody" type="text/javascript">
 "use strict";
 
 var version = "U2F_V2";
 var challenge = new Uint8Array(16);
 
 local_is(window.location.origin, "https://example.com", "Is loaded correctly");
 
-// eTLD+1 check
+local_expectThisManyTests(7);
+
+// basic check
 u2f.register("https://example.com/appId", [{
   version: version,
   challenge: bytesToBase64UrlSafe(challenge),
 }], [], function(res){
-  local_is(res.errorCode, 0, "AppID should work from a subdomain");
+  local_is(res.errorCode, 0, "AppID should work from the domain");
+  local_completeTest();
 });
 
 u2f.register("https://example.net/appId", [{
   version: version,
   challenge: bytesToBase64UrlSafe(challenge),
 }], [], function(res){
   local_is(res.errorCode, 2, "AppID should not work from other domains");
+  local_completeTest();
 });
 
 u2f.register("", [], [], function(res){
   local_is(res.errorCode, 2, "Empty register requests");
+  local_completeTest();
 });
 
 local_doesThrow(function(){
   u2f.register("", null, [], null);
 }, "Non-array register requests");
 
 local_doesThrow(function(){
   u2f.register("", [], null, null);
 }, "Non-array sign requests");
 
 local_doesThrow(function(){
   u2f.register("", null, null, null);
 }, "Non-array for both arguments");
 
 u2f.register("", [{}], [], function(res){
   local_is(res.errorCode, 2, "Empty request");
+  local_completeTest();
 });
 
 u2f.register("https://example.net/appId", [{
     version: version,
   }], [], function(res){
-  local_is(res.errorCode, 2, "Missing challenge");
+    local_is(res.errorCode, 2, "Missing challenge");
+    local_completeTest();
 });
 
 u2f.register("https://example.net/appId", [{
     challenge: bytesToBase64UrlSafe(challenge),
   }], [], function(res){
-  local_is(res.errorCode, 2, "Missing version");
+   local_is(res.errorCode, 2, "Missing version");
+   local_completeTest();
 });
 
 u2f.register("https://example.net/appId", [{
     version: "a_version_00",
     challenge: bytesToBase64UrlSafe(challenge),
   }], [], function(res){
-  local_is(res.errorCode, 2, "Invalid version");
+    local_is(res.errorCode, 2, "Invalid version");
+    local_completeTest();
 });
 
-local_finished();
-
 </script>
 </body>
 </html>
rename from dom/u2f/tests/test_frame_register_sign.html
rename to dom/u2f/tests/frame_register_sign.html
--- a/dom/u2f/tests/mochitest.ini
+++ b/dom/u2f/tests/mochitest.ini
@@ -1,17 +1,21 @@
 [DEFAULT]
 support-files =
+  frame_appid_facet.html
+  frame_appid_facet_insecure.html
+  frame_appid_facet_subdomain.html
   frame_no_token.html
-  u2futil.js
-  test_frame_appid_facet.html
-  test_frame_register.html
-  test_frame_register_sign.html
-  test_frame_appid_facet_insecure.html
-  test_frame_appid_facet_subdomain.html
+  frame_register.html
+  frame_register_sign.html
+  pkijs/asn1.js
   pkijs/common.js
-  pkijs/asn1.js
   pkijs/x509_schema.js
   pkijs/x509_simpl.js
+  u2futil.js
 
 [test_util_methods.html]
 [test_no_token.html]
-[test_frame.html]
+[test_register.html]
+[test_register_sign.html]
+[test_appid_facet.html]
+[test_appid_facet_insecure.html]
+[test_appid_facet_subdomain.html]
new file mode 100644
--- /dev/null
+++ b/dom/u2f/tests/test_appid_facet.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<head>
+  <title>Test for AppID / FacetID behavior for FIDO Universal Second Factor</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script src="u2futil.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1231681">Mozilla Bug 1231681</a>
+
+<div id="framediv">
+  <iframe id="testing_frame"></iframe>
+</div>
+
+<pre id="log"></pre>
+
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+SpecialPowers.pushPrefEnv({"set": [["security.webauth.u2f", true],
+                                   ["security.webauth.u2f_enable_softtoken", true],
+                                   ["security.webauth.u2f_enable_usbtoken", false]]},
+function() {
+  // listen for messages from the test harness
+  window.addEventListener("message", handleEventMessage, false);
+  document.getElementById('testing_frame').src = "https://example.com/tests/dom/u2f/tests/frame_appid_facet.html";
+});
+
+</script>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/u2f/tests/test_appid_facet_insecure.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<head>
+  <title>Test for AppID / FacetID behavior for FIDO Universal Second Factor</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script src="u2futil.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1231681">Mozilla Bug 1231681</a>
+
+<div id="framediv">
+  <iframe id="testing_frame"></iframe>
+</div>
+
+<pre id="log"></pre>
+
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+SpecialPowers.pushPrefEnv({"set": [["security.webauth.u2f", true],
+                                   ["security.webauth.u2f_enable_softtoken", true],
+                                   ["security.webauth.u2f_enable_usbtoken", false]]},
+function() {
+  // listen for messages from the test harness
+  window.addEventListener("message", handleEventMessage, false);
+  document.getElementById('testing_frame').src = "http://mochi.test:8888/tests/dom/u2f/tests/frame_appid_facet_insecure.html";
+});
+
+</script>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/u2f/tests/test_appid_facet_subdomain.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<head>
+  <title>Test for AppID / FacetID behavior for FIDO Universal Second Factor</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script src="u2futil.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1231681">Mozilla Bug 1231681</a>
+
+<div id="framediv">
+  <iframe id="testing_frame"></iframe>
+</div>
+
+<pre id="log"></pre>
+
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+SpecialPowers.pushPrefEnv({"set": [["security.webauth.u2f", true],
+                                   ["security.webauth.u2f_enable_softtoken", true],
+                                   ["security.webauth.u2f_enable_usbtoken", false]]},
+function() {
+  // listen for messages from the test harness
+  window.addEventListener("message", handleEventMessage, false);
+  document.getElementById('testing_frame').src = "https://test1.example.com/tests/dom/u2f/tests/frame_appid_facet_subdomain.html";
+});
+
+</script>
+
+</body>
+</html>
deleted file mode 100644
--- a/dom/u2f/tests/test_frame.html
+++ /dev/null
@@ -1,64 +0,0 @@
-<!DOCTYPE html>
-<meta charset=utf-8>
-<head>
-  <title>Test for AppID / FacetID behavior for FIDO Universal Second Factor</title>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="u2futil.js"></script>
-
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
-</head>
-<body>
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1231681">Mozilla Bug 1231681</a>
-
-<div id="framediv">
-  <iframe id="testing_frame"></iframe>
-</div>
-
-<pre id="log"></pre>
-
-<script class="testbody" type="text/javascript">
-
-SpecialPowers.pushPrefEnv({"set": [["security.webauth.u2f", true],
-                                   ["security.webauth.u2f_enable_softtoken", true]]},
-function() {
-  var testList = [
-    "https://example.com/tests/dom/u2f/tests/test_frame_register.html",
-    "https://example.com/tests/dom/u2f/tests/test_frame_register_sign.html",
-    "http://mochi.test:8888/tests/dom/u2f/tests/test_frame_appid_facet_insecure.html",
-    "https://example.com/tests/dom/u2f/tests/test_frame_appid_facet.html",
-    "https://test1.example.com/tests/dom/u2f/tests/test_frame_appid_facet_subdomain.html"
-  ];
-
-  function log(msg) {
-    document.getElementById("log").textContent += "\n" + msg;
-  }
-
-  function nextTest() {
-    if (testList.length < 1) {
-      SimpleTest.finish();
-      return;
-    }
-
-    document.getElementById('testing_frame').src = testList.shift();
-  }
-
-  // listen for a messages from the mixed content test harness
-  function receiveMessage(event) {
-    if ("test" in event.data) {
-      var summary = event.data.test + ": " + event.data.msg;
-      log(event.data.status + ": " + summary);
-      ok(event.data.status, summary);
-    } else if ("done" in event.data) {
-      nextTest();
-    }
-  }
-
-  window.addEventListener("message", receiveMessage, false);
-  nextTest();
-});
-
-SimpleTest.waitForExplicitFinish();
-</script>
-
-  </body>
-</html>
--- a/dom/u2f/tests/test_no_token.html
+++ b/dom/u2f/tests/test_no_token.html
@@ -16,21 +16,17 @@
 <script class="testbody" type="text/javascript">
 
 SimpleTest.waitForExplicitFinish();
 
 SpecialPowers.pushPrefEnv({"set": [["security.webauth.u2f", true],
                                    ["security.webauth.u2f_enable_softtoken", false],
                                    ["security.webauth.u2f_enable_usbtoken", false]]},
 function() {
-  onmessage = function(event) {
-    //event.data is the response.errorCode
-    isnot(event.data, 0, "The registration should be rejected.");
-    SimpleTest.finish();
-  }
-
+  // listen for messages from the test harness
+  window.addEventListener("message", handleEventMessage, false);
   document.getElementById('testing_frame').src = 'frame_no_token.html';
 });
 
 </script>
 
 </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/dom/u2f/tests/test_register.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<head>
+  <title>Test for Register behavior for FIDO Universal Second Factor</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script src="u2futil.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1231681">Mozilla Bug 1231681</a>
+
+<div id="framediv">
+  <iframe id="testing_frame"></iframe>
+</div>
+
+<pre id="log"></pre>
+
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+SpecialPowers.pushPrefEnv({"set": [["security.webauth.u2f", true],
+                                   ["security.webauth.u2f_enable_softtoken", true],
+                                   ["security.webauth.u2f_enable_usbtoken", false]]},
+function() {
+  // listen for messages from the test harness
+  window.addEventListener("message", handleEventMessage, false);
+  document.getElementById('testing_frame').src = "https://example.com/tests/dom/u2f/tests/frame_register.html";
+});
+
+</script>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/u2f/tests/test_register_sign.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<head>
+  <title>Register and Sign Test for FIDO Universal Second Factor</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script src="u2futil.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1231681">Mozilla Bug 1231681</a>
+
+<div id="framediv">
+  <iframe id="testing_frame"></iframe>
+</div>
+
+<pre id="log"></pre>
+
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+SpecialPowers.pushPrefEnv({"set": [["security.webauth.u2f", true],
+                                   ["security.webauth.u2f_enable_softtoken", true],
+                                   ["security.webauth.u2f_enable_usbtoken", false]]},
+function() {
+  // listen for messages from the test harness
+  window.addEventListener("message", handleEventMessage, false);
+  document.getElementById('testing_frame').src = "https://example.com/tests/dom/u2f/tests/frame_appid_facet.html";
+});
+
+</script>
+
+</body>
+</html>
--- a/dom/u2f/tests/u2futil.js
+++ b/dom/u2f/tests/u2futil.js
@@ -1,8 +1,32 @@
+// Used by local_addTest() / local_completeTest()
+var _countCompletions = 0;
+var _expectedCompletions = 0;
+
+function handleEventMessage(event) {
+  if ("test" in event.data) {
+    let summary = event.data.test + ": " + event.data.msg;
+    log(event.data.status + ": " + summary);
+    ok(event.data.status, summary);
+  } else if ("done" in event.data) {
+    SimpleTest.finish();
+  } else {
+    ok(false, "Unexpected message in the test harness: " + event.data)
+  }
+}
+
+function log(msg) {
+  console.log(msg)
+  let logBox = document.getElementById("log");
+  if (logBox) {
+    logBox.textContent += "\n" + msg;
+  }
+}
+
 function local_is(value, expected, message) {
   if (value === expected) {
     local_ok(true, message);
   } else {
     local_ok(false, message + " unexpectedly: " + value + " !== " + expected);
   }
 }
 
@@ -15,41 +39,59 @@ function local_isnot(value, expected, me
 }
 
 function local_ok(expression, message) {
   let body = {"test": this.location.pathname, "status":expression, "msg": message}
   parent.postMessage(body, "http://mochi.test:8888");
 }
 
 function local_doesThrow(fn, name) {
-  var gotException = false;
+  let gotException = false;
   try {
     fn();
   } catch (ex) { gotException = true; }
   local_ok(gotException, name);
 };
 
+function local_expectThisManyTests(count) {
+  if (_expectedCompletions > 0) {
+    local_ok(false, "Error: local_expectThisManyTests should only be called once.");
+  }
+  _expectedCompletions = count;
+}
+
+function local_completeTest() {
+  _countCompletions += 1
+  if (_countCompletions == _expectedCompletions) {
+    log("All tests completed.")
+    local_finished();
+  }
+  if (_countCompletions > _expectedCompletions) {
+    local_ok(false, "Error: local_completeTest called more than local_addTest.");
+  }
+}
+
 function local_finished() {
   parent.postMessage({"done":true}, "http://mochi.test:8888");
 }
 
 function string2buffer(str) {
   return (new Uint8Array(str.length)).map((x, i) => str.charCodeAt(i));
 }
 
 function buffer2string(buf) {
-  var str = "";
+  let str = "";
   buf.map(x => str += String.fromCharCode(x));
   return str;
 }
 
 function bytesToBase64(u8a){
-  var CHUNK_SZ = 0x8000;
-  var c = [];
-  for (var i = 0; i < u8a.length; i += CHUNK_SZ) {
+  let CHUNK_SZ = 0x8000;
+  let c = [];
+  for (let i = 0; i < u8a.length; i += CHUNK_SZ) {
     c.push(String.fromCharCode.apply(null, u8a.subarray(i, i + CHUNK_SZ)));
   }
   return window.btoa(c.join(""));
 }
 
 function base64ToBytes(b64encoded) {
   return new Uint8Array(window.atob(b64encoded).split("").map(function(c) {
     return c.charCodeAt(0);
@@ -108,55 +150,55 @@ function deriveAppAndChallengeParam(appI
     return {
       appParam: new Uint8Array(digests[0]),
       challengeParam: new Uint8Array(digests[1]),
     };
   });
 }
 
 function assembleSignedData(appParam, presenceAndCounter, challengeParam) {
-  var signedData = new Uint8Array(32 + 1 + 4 + 32);
+  let signedData = new Uint8Array(32 + 1 + 4 + 32);
   appParam.map((x, i) => signedData[0 + i] = x);
   presenceAndCounter.map((x, i) => signedData[32 + i] = x);
   challengeParam.map((x, i) => signedData[37 + i] = x);
   return signedData;
 }
 
 function assembleRegistrationSignedData(appParam, challengeParam, keyHandle, pubKey) {
-  var signedData = new Uint8Array(1 + 32 + 32 + keyHandle.length + 65);
+  let signedData = new Uint8Array(1 + 32 + 32 + keyHandle.length + 65);
   signedData[0] = 0x00;
   appParam.map((x, i) => signedData[1 + i] = x);
   challengeParam.map((x, i) => signedData[33 + i] = x);
   keyHandle.map((x, i) => signedData[65 + i] = x);
   pubKey.map((x, i) => signedData[65 + keyHandle.length + i] = x);
   return signedData;
 }
 
 function sanitizeSigArray(arr) {
   // ECDSA signature fields into WebCrypto must be exactly 32 bytes long, so
   // this method strips leading padding bytes, if added, and also appends
   // padding zeros, if needed.
   if (arr.length > 32) {
     arr = arr.slice(arr.length - 32)
   }
-  var ret = new Uint8Array(32);
+  let ret = new Uint8Array(32);
   ret.set(arr, ret.length - arr.length);
   return ret;
 }
 
 function verifySignature(key, data, derSig) {
-  var sigAsn1 = org.pkijs.fromBER(derSig.buffer);
-  var sigR = new Uint8Array(sigAsn1.result.value_block.value[0].value_block.value_hex);
-  var sigS = new Uint8Array(sigAsn1.result.value_block.value[1].value_block.value_hex);
+  let sigAsn1 = org.pkijs.fromBER(derSig.buffer);
+  let sigR = new Uint8Array(sigAsn1.result.value_block.value[0].value_block.value_hex);
+  let sigS = new Uint8Array(sigAsn1.result.value_block.value[1].value_block.value_hex);
 
   // The resulting R and S values from the ASN.1 Sequence must be fit into 32
   // bytes. Sometimes they have leading zeros, sometimes they're too short, it
   // all depends on what lib generated the signature.
-  var R = sanitizeSigArray(sigR);
-  var S = sanitizeSigArray(sigS);
+  let R = sanitizeSigArray(sigR);
+  let S = sanitizeSigArray(sigS);
 
-  var sigData = new Uint8Array(R.length + S.length);
+  let sigData = new Uint8Array(R.length + S.length);
   sigData.set(R);
   sigData.set(S, R.length);
 
-  var alg = {name: "ECDSA", hash: "SHA-256"};
+  let alg = {name: "ECDSA", hash: "SHA-256"};
   return crypto.subtle.verify(alg, key, sigData, data);
 }
--- a/dom/webidl/DataTransfer.webidl
+++ b/dom/webidl/DataTransfer.webidl
@@ -12,18 +12,18 @@ interface DataTransfer {
            attribute DOMString dropEffect;
            attribute DOMString effectAllowed;
 
   readonly attribute DataTransferItemList items;
 
   [Throws]
   void setDragImage(Element image, long x, long y);
 
-  [Throws]
-  readonly attribute DOMStringList types;
+  [Pure, Cached, Frozen, NeedsSubjectPrincipal]
+  readonly attribute sequence<DOMString> types;
   [Throws, NeedsSubjectPrincipal]
   DOMString getData(DOMString format);
   [Throws, NeedsSubjectPrincipal]
   void setData(DOMString format, DOMString data);
   [Throws, NeedsSubjectPrincipal]
   void clearData(optional DOMString format);
   [Throws, NeedsSubjectPrincipal]
   readonly attribute FileList? files;
--- a/dom/webidl/DataTransferItemList.webidl
+++ b/dom/webidl/DataTransferItemList.webidl
@@ -4,17 +4,16 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/.
  *
  * The origin of this IDL file is:
  * https://html.spec.whatwg.org/multipage/interaction.html#the-datatransferitemlist-interface
  */
 
 interface DataTransferItemList {
   readonly attribute unsigned long length;
-  [Throws]
   getter DataTransferItem (unsigned long index);
   [Throws, NeedsSubjectPrincipal]
   DataTransferItem? add(DOMString data, DOMString type);
   [Throws, NeedsSubjectPrincipal]
   DataTransferItem? add(File data);
   [Throws, NeedsSubjectPrincipal]
   void remove(unsigned long index);
   [Throws, NeedsSubjectPrincipal]
deleted file mode 100644
--- a/dom/webidl/FMRadio.webidl
+++ /dev/null
@@ -1,176 +0,0 @@
-/* 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/. */
-
-interface FMRadio : EventTarget {
-  /* Indicates if the FM radio is enabled. */
-  readonly attribute boolean enabled;
-
-  /* Indicates if RDS reception is enabled */
-  readonly attribute boolean rdsEnabled;
-
-  /* Indicates if the antenna is plugged and available. */
-  readonly attribute boolean antennaAvailable;
-
-  /**
-   * Current frequency in MHz. The value will be null if the FM radio is
-   * disabled.
-   */
-  readonly attribute double? frequency;
-
-  /* The upper bound of frequency in MHz. */
-  readonly attribute double frequencyUpperBound;
-
-  /* The lower bound of frequency in MHz. */
-  readonly attribute double frequencyLowerBound;
-
-  /**
-   * The difference in frequency between two "adjacent" channels, in MHz. That
-   * is, any two radio channels' frequencies differ by at least channelWidth
-   * MHz. Usually, the value is one of:
-   *  - 0.05 MHz
-   *  - 0.1  MHz
-   *  - 0.2  MHz
-   */
-  readonly attribute double channelWidth;
-
-  /**
-   * This is a mask consisting of bits corresponding to
-   * (1 << groupcode) that can be specified to receive
-   * raw RDS groups of specific group types. Note that
-   * groupcode corresponds to the upper 5 bits in block B.
-   */
-  attribute unsigned long rdsGroupMask;
-
-  /**
-   * The Program Identification (PI) code.
-   * Available if RDS is enabled on both the station and on this device.
-   * The value is null otherwise.
-   */
-  readonly attribute unsigned short? pi;
-
-  /**
-   * The Program Type (PTY) code.
-   * Available if RDS is enabled on both the station and on this device.
-   * The value is null otherwise.
-   */
-  readonly attribute octet? pty;
-
-  /**
-   * The Program Service (PS) name.
-   * Available if RDS is enabled on the station and on this device
-   */
-  readonly attribute DOMString? ps;
-
-  /**
-   * The radiotext, as provided by group 2A/2B.
-   * Available if RDS is enabled on the station and on this device
-   */
-  readonly attribute DOMString? rt;
-
-  /**
-   * The last RDS group received.
-   * Available if RDS is enabled on the station and on this device
-   */
-  readonly attribute Uint16Array? rdsgroup;
-
-  /* Fired when the FM radio is enabled. */
-  attribute EventHandler onenabled;
-
-  /* Fired when the FM radio is disabled. */
-  attribute EventHandler ondisabled;
-
-  /* Fired when the RDS is enabled. */
-  attribute EventHandler onrdsenabled;
-
-  /* Fired when the RDS is disabled. */
-  attribute EventHandler onrdsdisabled;
-
-  /**
-   * Fired when the antenna becomes available or unavailable, i.e., fired when
-   * the antennaAvailable attribute changes.
-   */
-  attribute EventHandler onantennaavailablechange;
-
-  /* Fired when the FM radio's frequency is changed. */
-  attribute EventHandler onfrequencychange;
-
-  /* Fired when the PI code changes */
-  attribute EventHandler onpichange;
-
-  /* Fired when the PTY changes */
-  attribute EventHandler onptychange;
-
-  /* Fired when the PS name changes */
-  attribute EventHandler onpschange;
-
-  /* Fired when the radiotext changes */
-  attribute EventHandler onrtchange;
-
-  /* Fired when we get a new RDS group */
-  attribute EventHandler onnewrdsgroup;
-
-  /**
-   * Power the FM radio off. The disabled event will be fired if this request
-   * completes successfully.
-   */
-  DOMRequest disable();
-
-  /**
-   * Power the FM radio on, and tune the radio to the given frequency in MHz.
-   * This will fail if the given frequency is out of range. The enabled event
-   * and frequencychange event will be fired if this request completes
-   * successfully.
-   */
-  DOMRequest enable(double frequency);
-
-  /**
-   * Tune the FM radio to the given frequency. This will fail if the given
-   * frequency is out of range.
-   *
-   * Note that the FM radio may not tuned to the exact frequency given. To get
-   * the frequency the radio is actually tuned to, wait for the request to fire
-   * sucess (or wait for the frequencychange event to fire), and then read the
-   * frequency attribute.
-   */
-  DOMRequest setFrequency(double frequency);
-
-  /**
-   * Tell the FM radio to seek up to the next channel. If the frequency is
-   * successfully changed, the frequencychange event will be triggered.
-   *
-   * Only one seek is allowed at once: If the radio is seeking when the seekUp
-   * is called, error will be fired.
-   */
-  DOMRequest seekUp();
-
-  /**
-   * Tell the FM radio to seek down to the next channel. If the frequency is
-   * successfully changed, the frequencychange event will be triggered.
-   *
-   * Only one seek is allowed at once: If the radio is seeking when the
-   * seekDown is called, error will be fired.
-   */
-  DOMRequest seekDown();
-
-  /**
-   * Cancel the seek action. If the radio is not currently seeking up or down,
-   * error will be fired.
-   */
-  DOMRequest cancelSeek();
-
-  /**
-   * Enable RDS reception.
-   *
-   * If the radio is off, RDS will be enabled when the radio is turned on.
-   */
-  DOMRequest enableRDS();
-
-  /**
-   * Disable RDS reception.
-   *
-   * If the radio is off, RDS will not be enabled when the radio is turned on.
-   */
-  DOMRequest disableRDS();
-};
-
--- a/dom/webidl/Navigator.webidl
+++ b/dom/webidl/Navigator.webidl
@@ -322,23 +322,16 @@ partial interface Navigator {
 
 #ifdef MOZ_B2G_BT
 partial interface Navigator {
   [Throws, ChromeOnly, UnsafeInPrerendering]
   readonly attribute BluetoothManager mozBluetooth;
 };
 #endif // MOZ_B2G_BT
 
-#ifdef MOZ_B2G_FM
-partial interface Navigator {
-  [Throws, ChromeOnly, UnsafeInPrerendering]
-  readonly attribute FMRadio mozFMRadio;
-};
-#endif // MOZ_B2G_FM
-
 #ifdef MOZ_TIME_MANAGER
 // nsIDOMMozNavigatorTime
 partial interface Navigator {
   [Throws, ChromeOnly, UnsafeInPrerendering]
   readonly attribute MozTimeManager mozTime;
 };
 #endif // MOZ_TIME_MANAGER
 
--- a/dom/webidl/Window.webidl
+++ b/dom/webidl/Window.webidl
@@ -104,17 +104,17 @@ interface WindowSessionStorage {
   //[Throws] readonly attribute Storage sessionStorage;
   [Throws] readonly attribute Storage? sessionStorage;
 };
 Window implements WindowSessionStorage;
 
 // http://www.whatwg.org/specs/web-apps/current-work/
 [NoInterfaceObject]
 interface WindowLocalStorage {
-  [Throws, NeedsSubjectPrincipal] readonly attribute Storage? localStorage;
+  [Throws] readonly attribute Storage? localStorage;
 };
 Window implements WindowLocalStorage;
 
 // http://www.whatwg.org/specs/web-apps/current-work/
 partial interface Window {
   void captureEvents();
   void releaseEvents();
 };
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -763,21 +763,16 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk
         'MozWifiManager.webidl',
         'MozWifiP2pManager.webidl',
     ]
 else:
     WEBIDL_FILES += [
         'InstallTrigger.webidl',
     ]
 
-if CONFIG['MOZ_B2G_FM']:
-    WEBIDL_FILES += [
-        'FMRadio.webidl',
-    ]
-
 GENERATED_EVENTS_WEBIDL_FILES = [
     'AddonEvent.webidl',
     'AnimationPlaybackEvent.webidl',
     'AutocompleteErrorEvent.webidl',
     'BlobEvent.webidl',
     'CallEvent.webidl',
     'CallGroupErrorEvent.webidl',
     'CameraClosedEvent.webidl',
--- a/editor/libeditor/EditorEventListener.cpp
+++ b/editor/libeditor/EditorEventListener.cpp
@@ -929,29 +929,26 @@ EditorEventListener::CanDrop(nsIDOMDragE
     return false;
   }
 
   nsCOMPtr<nsIDOMDataTransfer> domDataTransfer;
   aEvent->GetDataTransfer(getter_AddRefs(domDataTransfer));
   nsCOMPtr<DataTransfer> dataTransfer = do_QueryInterface(domDataTransfer);
   NS_ENSURE_TRUE(dataTransfer, false);
 
-  ErrorResult err;
-  RefPtr<DOMStringList> types = dataTransfer->GetTypes(err);
-  if (NS_WARN_IF(err.Failed())) {
-    return false;
-  }
+  nsTArray<nsString> types;
+  dataTransfer->GetTypes(types, *nsContentUtils::GetSystemPrincipal());
 
   // Plaintext editors only support dropping text. Otherwise, HTML and files
   // can be dropped as well.
-  if (!types->Contains(NS_LITERAL_STRING(kTextMime)) &&
-      !types->Contains(NS_LITERAL_STRING(kMozTextInternal)) &&
+  if (!types.Contains(NS_LITERAL_STRING(kTextMime)) &&
+      !types.Contains(NS_LITERAL_STRING(kMozTextInternal)) &&
       (mEditorBase->IsPlaintextEditor() ||
-       (!types->Contains(NS_LITERAL_STRING(kHTMLMime)) &&
-        !types->Contains(NS_LITERAL_STRING(kFileMime))))) {
+       (!types.Contains(NS_LITERAL_STRING(kHTMLMime)) &&
+        !types.Contains(NS_LITERAL_STRING(kFileMime))))) {
     return false;
   }
 
   // If there is no source node, this is probably an external drag and the
   // drop is allowed. The later checks rely on checking if the drag target
   // is the same as the drag source.
   nsCOMPtr<nsIDOMNode> sourceNode;
   dataTransfer->GetMozSourceNode(getter_AddRefs(sourceNode));
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -2711,24 +2711,43 @@ CalculateDisplayPortSize(const CSSSize& 
   if (IsHighMemSystem() && !xIsStationarySpeed) {
     xMultiplier += gfxPrefs::APZXSkateHighMemAdjust();
   }
 
   if (IsHighMemSystem() && !yIsStationarySpeed) {
     yMultiplier += gfxPrefs::APZYSkateHighMemAdjust();
   }
 
-  // Ensure that it is at least as large as the visible area inflated by the
-  // danger zone. If this is not the case then the "AboutToCheckerboard"
-  // function in TiledContentClient.cpp will return true even in the stable
-  // state.
-  float xSize = std::max(aCompositionSize.width * xMultiplier,
-                         aCompositionSize.width + (2 * gfxPrefs::APZDangerZoneX()));
-  float ySize = std::max(aCompositionSize.height * yMultiplier,
-                         aCompositionSize.height + (2 * gfxPrefs::APZDangerZoneY()));
+  return aCompositionSize * CSSSize(xMultiplier, yMultiplier);
+}
+
+/**
+ * Ensures that the displayport is at least as large as the visible area
+ * inflated by the danger zone. If this is not the case then the
+ * "AboutToCheckerboard" function in TiledContentClient.cpp will return true
+ * even in the stable state.
+ */
+static CSSSize
+ExpandDisplayPortToDangerZone(const CSSSize& aDisplayPortSize,
+                              const FrameMetrics& aFrameMetrics)
+{
+  CSSSize dangerZone(0.0f, 0.0f);
+  if (aFrameMetrics.LayersPixelsPerCSSPixel().xScale != 0 &&
+      aFrameMetrics.LayersPixelsPerCSSPixel().yScale != 0) {
+    dangerZone = LayerSize(
+      gfxPrefs::APZDangerZoneX(),
+      gfxPrefs::APZDangerZoneY()) / aFrameMetrics.LayersPixelsPerCSSPixel();
+  }
+  const CSSSize compositionSize = aFrameMetrics.CalculateBoundedCompositedSizeInCssPixels();
+
+  const float xSize = std::max(aDisplayPortSize.width,
+                               compositionSize.width + (2 * dangerZone.width));
+
+  const float ySize = std::max(aDisplayPortSize.height,
+                               compositionSize.height + (2 * dangerZone.height));
 
   return CSSSize(xSize, ySize);
 }
 
 /**
  * Attempts to redistribute any area in the displayport that would get clipped
  * by the scrollable rect, or be inaccessible due to disabled scrolling, to the
  * other axis, while maintaining total displayport area.
@@ -2764,16 +2783,18 @@ const ScreenMargin AsyncPanZoomControlle
   if (aFrameMetrics.GetZoom() != CSSToParentLayerScale2D(0, 0)) {
     velocity = aVelocity / aFrameMetrics.GetZoom();  // avoid division by zero
   }
   CSSRect scrollableRect = aFrameMetrics.GetExpandedScrollableRect();
 
   // Calculate the displayport size based on how fast we're moving along each axis.
   CSSSize displayPortSize = CalculateDisplayPortSize(compositionSize, velocity);
 
+  displayPortSize = ExpandDisplayPortToDangerZone(displayPortSize, aFrameMetrics);
+
   if (gfxPrefs::APZEnlargeDisplayPortWhenClipped()) {
     RedistributeDisplayPortExcess(displayPortSize, scrollableRect);
   }
 
   // We calculate a "displayport" here which is relative to the scroll offset.
   // Note that the scroll offset we have here in the APZ code may not be the
   // same as the base rect that gets used on the layout side when the displayport
   // margins are actually applied, so it is important to only consider the
--- a/js/public/GCAPI.h
+++ b/js/public/GCAPI.h
@@ -479,39 +479,40 @@ IsGenerationalGCEnabled(JSRuntime* rt);
  * Returns the GC's "number". This does not correspond directly to the number
  * of GCs that have been run, but is guaranteed to be monotonically increasing
  * with GC activity.
  */
 extern JS_PUBLIC_API(size_t)
 GetGCNumber();
 
 /**
- * Assert if a GC occurs while this class is live. This class does not disable
- * the static rooting hazard analysis.
+ * Pass a subclass of this "abstract" class to callees to require that they
+ * never GC. Subclasses can use assertions or the hazard analysis to ensure no
+ * GC happens.
  */
-class JS_PUBLIC_API(AutoAssertOnGC)
+class JS_PUBLIC_API(AutoRequireNoGC)
 {
-#ifdef DEBUG
+  protected:
+    AutoRequireNoGC() {}
+    ~AutoRequireNoGC() {}
+};
+
+/**
+ * Release assert if a GC occurs while this class is live. This class does
+ * not disable the static rooting hazard analysis.
+ */
+class JS_PUBLIC_API(AutoAssertOnGC) : public AutoRequireNoGC
+{
     js::gc::GCRuntime* gc;
     size_t gcNumber;
 
   public:
     AutoAssertOnGC();
     explicit AutoAssertOnGC(JSContext* cx);
     ~AutoAssertOnGC();
-
-    static void VerifyIsSafeToGC(JSRuntime* rt);
-#else
-  public:
-    AutoAssertOnGC() {}
-    explicit AutoAssertOnGC(JSContext* cx) {}
-    ~AutoAssertOnGC() {}
-
-    static void VerifyIsSafeToGC(JSRuntime* rt) {}
-#endif
 };
 
 /**
  * Assert if an allocation of a GC thing occurs while this class is live. This
  * class does not disable the static rooting hazard analysis.
  */
 class JS_PUBLIC_API(AutoAssertNoAlloc)
 {
@@ -527,16 +528,30 @@ class JS_PUBLIC_API(AutoAssertNoAlloc)
   public:
     AutoAssertNoAlloc() {}
     explicit AutoAssertNoAlloc(JSContext* cx) {}
     void disallowAlloc(JSRuntime* rt) {}
 #endif
 };
 
 /**
+ * Assert if a GC barrier is invoked while this class is live. This class does
+ * not disable the static rooting hazard analysis.
+ */
+class JS_PUBLIC_API(AutoAssertOnBarrier)
+{
+    JSContext* context;
+    bool prev;
+
+  public:
+    explicit AutoAssertOnBarrier(JSContext* cx);
+    ~AutoAssertOnBarrier();
+};
+
+/**
  * Disable the static rooting hazard analysis in the live region and assert if
  * any allocation that could potentially trigger a GC occurs while this guard
  * object is live. This is most useful to help the exact rooting hazard analysis
  * in complex regions, since it cannot understand dataflow.
  *
  * Note: GC behavior is unpredictable even when deterministic and is generally
  *       non-deterministic in practice. The fact that this guard has not
  *       asserted is not a guarantee that a GC cannot happen in the guarded
@@ -570,23 +585,34 @@ class JS_PUBLIC_API(AutoAssertGCCallback
  * Place AutoCheckCannotGC in scopes that you believe can never GC. These
  * annotations will be verified both dynamically via AutoAssertOnGC, and
  * statically with the rooting hazard analysis (implemented by making the
  * analysis consider AutoCheckCannotGC to be a GC pointer, and therefore
  * complain if it is live across a GC call.) It is useful when dealing with
  * internal pointers to GC things where the GC thing itself may not be present
  * for the static analysis: e.g. acquiring inline chars from a JSString* on the
  * heap.
+ *
+ * We only do the assertion checking in DEBUG builds.
  */
+#ifdef DEBUG
 class JS_PUBLIC_API(AutoCheckCannotGC) : public AutoAssertOnGC
 {
   public:
     AutoCheckCannotGC() : AutoAssertOnGC() {}
     explicit AutoCheckCannotGC(JSContext* cx) : AutoAssertOnGC(cx) {}
 } JS_HAZ_GC_INVALIDATED;
+#else
+class JS_PUBLIC_API(AutoCheckCannotGC) : public AutoRequireNoGC
+{
+  public:
+    AutoCheckCannotGC() {}
+    explicit AutoCheckCannotGC(JSContext* cx) {}
+} JS_HAZ_GC_INVALIDATED;
+#endif
 
 /**
  * Unsets the gray bit for anything reachable from |thing|. |kind| should not be
  * JS::TraceKind::Shape. |thing| should be non-null. The return value indicates
  * if anything was unmarked.
  */
 extern JS_FRIEND_API(bool)
 UnmarkGrayGCThingRecursively(GCCellPtr thing);
@@ -604,26 +630,28 @@ ExposeGCThingToActiveJS(JS::GCCellPtr th
     /*
      * GC things residing in the nursery cannot be gray: they have no mark bits.
      * All live objects in the nursery are moved to tenured at the beginning of
      * each GC slice, so the gray marker never sees nursery things.
      */
     if (IsInsideNursery(thing.asCell()))
         return;
     JS::shadow::Runtime* rt = detail::GetGCThingRuntime(thing.unsafeAsUIntPtr());
+    MOZ_DIAGNOSTIC_ASSERT(rt->allowGCBarriers());
     if (IsIncrementalBarrierNeededOnTenuredGCThing(rt, thing))
         JS::IncrementalReferenceBarrier(thing);
     else if (JS::GCThingIsMarkedGray(thing))
         JS::UnmarkGrayGCThingRecursively(thing);
 }
 
 static MOZ_ALWAYS_INLINE void
 MarkGCThingAsLive(JSRuntime* aRt, JS::GCCellPtr thing)
 {
     JS::shadow::Runtime* rt = JS::shadow::Runtime::asShadowRuntime(aRt);
+    MOZ_DIAGNOSTIC_ASSERT(rt->allowGCBarriers());
     /*
      * Any object in the nursery will not be freed during any GC running at that time.
      */
     if (IsInsideNursery(thing.asCell()))
         return;
     if (IsIncrementalBarrierNeededOnTenuredGCThing(rt, thing))
         JS::IncrementalReferenceBarrier(thing);
 }
@@ -671,11 +699,23 @@ extern JS_FRIEND_API(void)
 PokeGC(JSContext* cx);
 
 /*
  * Internal to Firefox.
  */
 extern JS_FRIEND_API(void)
 NotifyDidPaint(JSContext* cx);
 
+// GC Interrupt callbacks are run during GC. You should not run JS code or use
+// the JS engine at all while the callback is running. Otherwise they resemble
+// normal JS interrupt callbacks.
+typedef bool
+(* GCInterruptCallback)(JSContext* cx);
+
+extern JS_FRIEND_API(bool)
+AddGCInterruptCallback(JSContext* cx, GCInterruptCallback callback);
+
+extern JS_FRIEND_API(void)
+RequestGCInterruptCallback(JSContext* cx);
+
 } /* namespace JS */
 
 #endif /* js_GCAPI_h */
--- a/js/public/SliceBudget.h
+++ b/js/public/SliceBudget.h
@@ -2,16 +2,18 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * 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/. */
 
 #ifndef js_SliceBudget_h
 #define js_SliceBudget_h
 
+#include "mozilla/Atomics.h"
+
 #include <stdint.h>
 
 namespace js {
 
 struct JS_PUBLIC_API(TimeBudget)
 {
     int64_t budget;
 
@@ -31,57 +33,82 @@ struct JS_PUBLIC_API(WorkBudget)
  * to run for unlimited time, and others are bounded. To reduce the number of
  * gettimeofday calls, we only check the time every 1000 operations.
  */
 class JS_PUBLIC_API(SliceBudget)
 {
     static const int64_t unlimitedDeadline = INT64_MAX;
     static const intptr_t unlimitedStartCounter = INTPTR_MAX;
 
-    bool checkOverBudget();
+    bool checkOverBudget(JSContext* maybeCx);
 
     SliceBudget();
 
   public:
     // Memory of the originally requested budget. If isUnlimited, neither of
     // these are in use. If deadline==0, then workBudget is valid. Otherwise
     // timeBudget is valid.
     TimeBudget timeBudget;
     WorkBudget workBudget;
 
     int64_t deadline; /* in microseconds */
-    intptr_t counter;
+    mozilla::Atomic<intptr_t, mozilla::Relaxed> counter;
 
     static const intptr_t CounterReset = 1000;
 
     static const int64_t UnlimitedTimeBudget = -1;
     static const int64_t UnlimitedWorkBudget = -1;
 
     /* Use to create an unlimited budget. */
     static SliceBudget unlimited() { return SliceBudget(); }
 
     /* Instantiate as SliceBudget(TimeBudget(n)). */
     explicit SliceBudget(TimeBudget time);
 
     /* Instantiate as SliceBudget(WorkBudget(n)). */
     explicit SliceBudget(WorkBudget work);
 
+    // Need an explicit copy constructor because Atomic fails to provide one.
+    SliceBudget(const SliceBudget& other)
+        : timeBudget(other.timeBudget),
+          workBudget(other.workBudget),
+          deadline(other.deadline),
+          counter(other.counter)
+    {}
+
+    // Need an explicit operator= because Atomic fails to provide one.
+    SliceBudget& operator=(const SliceBudget& other) {
+        timeBudget = other.timeBudget;
+        workBudget = other.workBudget;
+        deadline = other.deadline;
+        counter = intptr_t(other.counter);
+        return *this;
+    }
+
     void makeUnlimited() {
         deadline = unlimitedDeadline;
         counter = unlimitedStartCounter;
     }
 
+    // Request that checkOverBudget be called the next time isOverBudget is
+    // called.
+    void requestFullCheck() {
+        counter = 0;
+    }
+
     void step(intptr_t amt = 1) {
         counter -= amt;
     }
 
-    bool isOverBudget() {
+    // Only need to pass maybeCx if the GC interrupt callback should be checked
+    // (and possibly invoked).
+    bool isOverBudget(JSContext* maybeCx = nullptr) {
         if (counter > 0)
             return false;
-        return checkOverBudget();
+        return checkOverBudget(maybeCx);
     }
 
     bool isWorkBudget() const { return deadline == 0; }
     bool isTimeBudget() const { return deadline > 0 && !isUnlimited(); }
     bool isUnlimited() const { return deadline == unlimitedDeadline; }
 
     int describe(char* buffer, size_t maxlen) const;
 };
--- a/js/src/builtin/SIMD.cpp
+++ b/js/src/builtin/SIMD.cpp
@@ -185,17 +185,17 @@ template bool js::ToSimdConstant<Int16x8
 template bool js::ToSimdConstant<Int32x4>(JSContext* cx, HandleValue v, jit::SimdConstant* out);
 template bool js::ToSimdConstant<Float32x4>(JSContext* cx, HandleValue v, jit::SimdConstant* out);
 template bool js::ToSimdConstant<Bool8x16>(JSContext* cx, HandleValue v, jit::SimdConstant* out);
 template bool js::ToSimdConstant<Bool16x8>(JSContext* cx, HandleValue v, jit::SimdConstant* out);
 template bool js::ToSimdConstant<Bool32x4>(JSContext* cx, HandleValue v, jit::SimdConstant* out);
 
 template<typename Elem>
 static Elem
-TypedObjectMemory(HandleValue v, const JS::AutoAssertOnGC& nogc)
+TypedObjectMemory(HandleValue v, const JS::AutoRequireNoGC& nogc)
 {
     TypedObject& obj = v.toObject().as<TypedObject>();
     return reinterpret_cast<Elem>(obj.typedMem(nogc));
 }
 
 static const ClassOps SimdTypeDescrClassOps = {
     nullptr, /* addProperty */
     nullptr, /* delProperty */
--- a/js/src/builtin/TypedObject.h
+++ b/js/src/builtin/TypedObject.h
@@ -544,24 +544,24 @@ class TypedObject : public ShapedObject
     }
 
     TypeDescr& typeDescr() const {
         return group()->typeDescr();
     }
 
     int32_t offset() const;
     int32_t length() const;
-    uint8_t* typedMem(const JS::AutoAssertOnGC&) const { return typedMem(); }
+    uint8_t* typedMem(const JS::AutoRequireNoGC&) const { return typedMem(); }
     bool isAttached() const;
 
     int32_t size() const {
         return typeDescr().size();
     }
 
-    uint8_t* typedMem(size_t offset, const JS::AutoAssertOnGC& nogc) const {
+    uint8_t* typedMem(size_t offset, const JS::AutoRequireNoGC& nogc) const {
         // It seems a bit surprising that one might request an offset
         // == size(), but it can happen when taking the "address of" a
         // 0-sized value. (In other words, we maintain the invariant
         // that `offset + size <= size()` -- this is always checked in
         // the caller's side.)
         MOZ_ASSERT(offset <= (size_t) size());
         return typedMem(nogc) + offset;
     }
@@ -699,17 +699,17 @@ class InlineTypedObject : public TypedOb
 
     static gc::AllocKind allocKindForTypeDescriptor(TypeDescr* descr) {
         size_t nbytes = descr->size();
         MOZ_ASSERT(nbytes <= MaximumSize);
 
         return gc::GetGCObjectKindForBytes(nbytes + sizeof(TypedObject));
     }
 
-    uint8_t* inlineTypedMem(const JS::AutoAssertOnGC&) const {
+    uint8_t* inlineTypedMem(const JS::AutoRequireNoGC&) const {
         return inlineTypedMem();
     }
 
     uint8_t* inlineTypedMemForGC() const {
         return inlineTypedMem();
     }
 
     static void obj_trace(JSTracer* trace, JSObject* object);
--- a/js/src/gc/Allocator.cpp
+++ b/js/src/gc/Allocator.cpp
@@ -198,17 +198,17 @@ GCRuntime::checkAllocatorState(JSContext
                   kind == AllocKind::JITCODE ||
                   kind == AllocKind::SCOPE);
     MOZ_ASSERT(!rt->isHeapBusy());
     MOZ_ASSERT(isAllocAllowed());
 #endif
 
     // Crash if we perform a GC action when it is not safe.
     if (allowGC && !rt->mainThread.suppressGC)
-        JS::AutoAssertOnGC::VerifyIsSafeToGC(rt);
+        rt->gc.verifyIsSafeToGC();
 
     // For testing out of memory conditions
     if (js::oom::ShouldFailWithOOM()) {
         // If we are doing a fallible allocation, percolate up the OOM
         // instead of reporting it.
         if (allowGC)
             ReportOutOfMemory(cx);
         return false;
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -19,16 +19,17 @@
 #include "gc/StoreBuffer.h"
 #include "gc/Tracer.h"
 #include "js/GCAnnotations.h"
 
 namespace js {
 
 class AutoLockGC;
 class AutoLockHelperThreadState;
+class SliceBudget;
 class VerifyPreTracer;
 
 namespace gc {
 
 typedef Vector<JS::Zone*, 4, SystemAllocPolicy> ZoneVector;
 using BlackGrayEdgeVector = Vector<TenuredCell*, 0, SystemAllocPolicy>;
 
 class AutoMaybeStartBackgroundAllocation;
@@ -716,30 +717,35 @@ class GCRuntime
 
     bool isNurseryAllocAllowed() { return noNurseryAllocationCheck == 0; }
     void disallowNurseryAlloc() { ++noNurseryAllocationCheck; }
     void allowNurseryAlloc() {
         MOZ_ASSERT(!isNurseryAllocAllowed());
         --noNurseryAllocationCheck;
     }
 
+    bool isStrictProxyCheckingEnabled() { return disableStrictProxyCheckingCount == 0; }
+    void disableStrictProxyChecking() { ++disableStrictProxyCheckingCount; }
+    void enableStrictProxyChecking() {
+        MOZ_ASSERT(disableStrictProxyCheckingCount > 0);
+        --disableStrictProxyCheckingCount;
+    }
+#endif // DEBUG
+
     bool isInsideUnsafeRegion() { return inUnsafeRegion != 0; }
     void enterUnsafeRegion() { ++inUnsafeRegion; }
     void leaveUnsafeRegion() {
         MOZ_ASSERT(inUnsafeRegion > 0);
         --inUnsafeRegion;
     }
 
-    bool isStrictProxyCheckingEnabled() { return disableStrictProxyCheckingCount == 0; }
-    void disableStrictProxyChecking() { ++disableStrictProxyCheckingCount; }
-    void enableStrictProxyChecking() {
-        MOZ_ASSERT(disableStrictProxyCheckingCount > 0);
-        --disableStrictProxyCheckingCount;
+    void verifyIsSafeToGC() {
+        MOZ_DIAGNOSTIC_ASSERT(!isInsideUnsafeRegion(),
+                              "[AutoAssertOnGC] possible GC in GC-unsafe region");
     }
-#endif // DEBUG
 
     void setAlwaysPreserveCode() { alwaysPreserveCode = true; }
 
     bool isIncrementalGCAllowed() const { return incrementalAllowed; }
     void disallowIncrementalGC() { incrementalAllowed = false; }
 
     bool isIncrementalGCEnabled() const { return mode == JSGC_MODE_INCREMENTAL && incrementalAllowed; }
     bool isIncrementalGCInProgress() const { return state() != State::NotActive; }
@@ -853,16 +859,29 @@ class GCRuntime
     void startVerifyPreBarriers();
     void endVerifyPreBarriers();
     void finishVerifier();
     bool isVerifyPreBarriersEnabled() const { return !!verifyPreData; }
 #else
     bool isVerifyPreBarriersEnabled() const { return false; }
 #endif
 
+    // GC interrupt callbacks.
+    bool addInterruptCallback(JS::GCInterruptCallback callback);
+    void requestInterruptCallback();
+
+    bool checkInterruptCallback(JSContext* cx) {
+        if (interruptCallbackRequested) {
+            invokeInterruptCallback(cx);
+            return true;
+        }
+        return false;
+    }
+    void invokeInterruptCallback(JSContext* cx);
+
     // Free certain LifoAlloc blocks when it is safe to do so.
     void freeUnusedLifoBlocksAfterSweeping(LifoAlloc* lifo);
     void freeAllLifoBlocksAfterSweeping(LifoAlloc* lifo);
     void freeAllLifoBlocksAfterMinorGC(LifoAlloc* lifo);
 
     // Queue a thunk to run after the next minor GC.
     void callAfterMinorGC(void (*thunk)(void* data), void* data) {
         nursery.queueSweepAction(thunk, data);
@@ -1065,16 +1084,23 @@ class GCRuntime
     mozilla::Atomic<uint64_t, mozilla::ReleaseAcquire> nextCellUniqueId_;
 
     /*
      * Number of the committed arenas in all GC chunks including empty chunks.
      */
     mozilla::Atomic<uint32_t, mozilla::ReleaseAcquire> numArenasFreeCommitted;
     VerifyPreTracer* verifyPreData;
 
+    // GC interrupt callbacks.
+    using GCInterruptCallbackVector = js::Vector<JS::GCInterruptCallback, 2, js::SystemAllocPolicy>;
+    GCInterruptCallbackVector interruptCallbacks;
+
+    mozilla::Atomic<bool, mozilla::Relaxed> interruptCallbackRequested;
+    SliceBudget* currentBudget;
+
   private:
     bool chunkAllocationSinceLastGC;
     int64_t lastGCTime;
 
     JSGCMode mode;
 
     mozilla::Atomic<size_t, mozilla::ReleaseAcquire> numActiveZoneIters;
 
@@ -1339,25 +1365,25 @@ class GCRuntime
      * collector.
      */
     CallbackVector<JSTraceDataOp> blackRootTracers;
     Callback<JSTraceDataOp> grayRootTracer;
 
     /* Always preserve JIT code during GCs, for testing. */
     bool alwaysPreserveCode;
 
-#ifdef DEBUG
     /*
      * Some regions of code are hard for the static rooting hazard analysis to
      * understand. In those cases, we trade the static analysis for a dynamic
      * analysis. When this is non-zero, we should assert if we trigger, or
      * might trigger, a GC.
      */
     int inUnsafeRegion;
 
+#ifdef DEBUG
     size_t noGCOrAllocatio