merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Thu, 27 Jul 2017 10:57:25 +0200
changeset 420064 e5693cea1ec944ca077c7a46c5f127c828a90f1b
parent 419973 c1ed71da57073d0f5fdbd279c9ad6037f9ae2d06 (current diff)
parent 420063 c8262422f24494702bc965a241ca6bdaadead161 (diff)
child 420065 9090379eaa361e44a2664ef6b73029aeeb7009ba
child 420092 5e9f7561c2eba9eaa1f88833a01db0f242751f7d
child 420130 444f087b3ffb3cfcddcebc8e9593e99f1e065e1f
push id7566
push usermtabara@mozilla.com
push dateWed, 02 Aug 2017 08:25:16 +0000
treeherdermozilla-beta@86913f512c3c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone56.0a1
first release with
nightly linux32
e5693cea1ec9 / 56.0a1 / 20170727100240 / files
nightly linux64
e5693cea1ec9 / 56.0a1 / 20170727100240 / files
nightly win32
e5693cea1ec9 / 56.0a1 / 20170727100347 / files
nightly win64
e5693cea1ec9 / 56.0a1 / 20170727100347 / files
nightly mac
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central a=merge
browser/base/content/browser.js
devtools/client/preferences/devtools.js
docshell/base/nsDocShell.cpp
dom/html/TextTrackManager.cpp
dom/indexedDB/ActorsParent.cpp
dom/media/MediaFormatReader.cpp
dom/media/MediaStreamGraph.cpp
dom/workers/WorkerPrivate.cpp
gfx/layers/apz/src/AsyncPanZoomController.cpp
gfx/layers/apz/src/AsyncPanZoomController.h
js/src/jit/IonBuilder.cpp
js/src/jsgc.cpp
js/src/vm/Debugger.cpp
js/src/wasm/AsmJS.cpp
layout/reftests/bugs/reftest.list
layout/style/FontFaceSet.cpp
modules/libpref/init/all.js
netwerk/base/EventTokenBucket.cpp
netwerk/cache2/CacheStorageService.cpp
netwerk/protocol/http/nsHttpConnectionMgr.cpp
old-configure.in
testing/profiles/prefs_general.js
toolkit/components/url-classifier/content/listmanager.js
toolkit/components/url-classifier/content/moz/lang.js
toolkit/components/url-classifier/content/request-backoff.js
uriloader/exthandler/nsExternalHelperAppService.cpp
--- a/.clang-format-ignore
+++ b/.clang-format-ignore
@@ -19,17 +19,17 @@
 ^gfx/cairo/.*
 ^gfx/graphite2/.*
 ^gfx/harfbuzz/.*
 ^gfx/ots/.*
 ^gfx/qcms/.*
 ^gfx/skia/.*
 ^gfx/vr/openvr/.*
 ^gfx/webrender.*
-^gfx/webrender_traits.*
+^gfx/webrender_api.*
 ^gfx/ycbcr/.*
 ^intl/hyphenation/hyphen/.*
 ^intl/icu/.*
 ^ipc/chromium/.*
 ^js/src/ctypes/libffi/.*
 ^js/src/dtoa.c.*
 ^js/src/jit/arm64/vixl/.*
 ^media/ffvpx/.*
--- a/accessible/base/SelectionManager.cpp
+++ b/accessible/base/SelectionManager.cpp
@@ -43,60 +43,59 @@ SelectionManager::SelectionManager() :
   mCaretOffset(-1), mAccWithCaret(nullptr)
 {
 
 }
 
 void
 SelectionManager::ClearControlSelectionListener()
 {
-  if (!mCurrCtrlFrame)
-    return;
-
-  const nsFrameSelection* frameSel = mCurrCtrlFrame->GetConstFrameSelection();
-  NS_ASSERTION(frameSel, "No frame selection for the element!");
-
-  mCurrCtrlFrame = nullptr;
-  if (!frameSel)
-    return;
 
   // Remove 'this' registered as selection listener for the normal selection.
-  Selection* normalSel = frameSel->GetSelection(SelectionType::eNormal);
-  normalSel->RemoveSelectionListener(this);
+  nsCOMPtr<nsISelection> normalSel = do_QueryReferent(mCurrCtrlNormalSel);
+  if (normalSel) {
+    normalSel->AsSelection()->RemoveSelectionListener(this);
+    mCurrCtrlNormalSel = nullptr;
+  }
 
   // Remove 'this' registered as selection listener for the spellcheck
   // selection.
-  Selection* spellSel = frameSel->GetSelection(SelectionType::eSpellCheck);
-  spellSel->RemoveSelectionListener(this);
+  nsCOMPtr<nsISelection> spellSel = do_QueryReferent(mCurrCtrlSpellSel);
+  if (spellSel) {
+    spellSel->AsSelection()->RemoveSelectionListener(this);
+    mCurrCtrlSpellSel = nullptr;
+  }
 }
 
 void
 SelectionManager::SetControlSelectionListener(dom::Element* aFocusedElm)
 {
   // When focus moves such that the caret is part of a new frame selection
   // this removes the old selection listener and attaches a new one for
   // the current focus.
   ClearControlSelectionListener();
 
-  mCurrCtrlFrame = aFocusedElm->GetPrimaryFrame();
-  if (!mCurrCtrlFrame)
+  nsIFrame* controlFrame = aFocusedElm->GetPrimaryFrame();
+  if (!controlFrame)
     return;
 
-  const nsFrameSelection* frameSel = mCurrCtrlFrame->GetConstFrameSelection();
+  const nsFrameSelection* frameSel = controlFrame->GetConstFrameSelection();
   NS_ASSERTION(frameSel, "No frame selection for focused element!");
   if (!frameSel)
     return;
 
   // Register 'this' as selection listener for the normal selection.
-  Selection* normalSel = frameSel->GetSelection(SelectionType::eNormal);
-  normalSel->AddSelectionListener(this);
+  nsCOMPtr<nsISelection> normalSel = frameSel->GetSelection(SelectionType::eNormal);
+  normalSel->AsSelection()->AddSelectionListener(this);
+  mCurrCtrlNormalSel = do_GetWeakReference(normalSel);
 
   // Register 'this' as selection listener for the spell check selection.
-  Selection* spellSel = frameSel->GetSelection(SelectionType::eSpellCheck);
-  spellSel->AddSelectionListener(this);
+  nsCOMPtr<nsISelection> spellSel = frameSel->GetSelection(SelectionType::eSpellCheck);
+  spellSel->AsSelection()->AddSelectionListener(this);
+  mCurrCtrlSpellSel = do_GetWeakReference(spellSel);
 }
 
 void
 SelectionManager::AddDocSelectionListener(nsIPresShell* aPresShell)
 {
   const nsFrameSelection* frameSel = aPresShell->ConstFrameSelection();
 
   // Register 'this' as selection listener for the normal selection.
--- a/accessible/base/SelectionManager.h
+++ b/accessible/base/SelectionManager.h
@@ -116,17 +116,18 @@ protected:
 
   /**
    * Process DOM selection change. Fire selection and caret move events.
    */
   void ProcessSelectionChanged(SelData* aSelData);
 
 private:
   // Currently focused control.
-  WeakFrame mCurrCtrlFrame;
   int32_t mCaretOffset;
   HyperTextAccessible* mAccWithCaret;
+  nsWeakPtr mCurrCtrlNormalSel;
+  nsWeakPtr mCurrCtrlSpellSel;
 };
 
 } // namespace a11y
 } // namespace mozilla
 
 #endif
--- a/accessible/base/nsAccessibilityService.cpp
+++ b/accessible/base/nsAccessibilityService.cpp
@@ -53,16 +53,17 @@
 #include "Logging.h"
 #endif
 
 #ifdef MOZ_CRASHREPORTER
 #include "nsExceptionHandler.h"
 #endif
 
 #include "nsImageFrame.h"
+#include "nsINamed.h"
 #include "nsIObserverService.h"
 #include "nsLayoutUtils.h"
 #include "nsPluginFrame.h"
 #include "SVGGeometryFrame.h"
 #include "nsTreeBodyFrame.h"
 #include "nsTreeColumns.h"
 #include "nsTreeUtils.h"
 #include "nsXBLPrototypeBinding.h"
@@ -407,16 +408,17 @@ nsAccessibilityService::GetRootDocumentA
   return nullptr;
 }
 
 #ifdef XP_WIN
 static StaticAutoPtr<nsTArray<nsCOMPtr<nsIContent> > > sPendingPlugins;
 static StaticAutoPtr<nsTArray<nsCOMPtr<nsITimer> > > sPluginTimers;
 
 class PluginTimerCallBack final : public nsITimerCallback
+                                , public nsINamed
 {
   ~PluginTimerCallBack() {}
 
 public:
   explicit PluginTimerCallBack(nsIContent* aContent) : mContent(aContent) {}
 
   NS_DECL_ISUPPORTS
 
@@ -439,21 +441,27 @@ public:
 
     // We couldn't get a doc accessible so presumably the document went away.
     // In this case don't leak our ref to the content or timer.
     sPendingPlugins->RemoveElement(mContent);
     sPluginTimers->RemoveElement(aTimer);
     return NS_OK;
   }
 
+  NS_IMETHOD GetName(nsACString& aName) final
+  {
+    aName.AssignLiteral("PluginTimerCallBack");
+    return NS_OK;
+  }
+
 private:
   nsCOMPtr<nsIContent> mContent;
 };
 
-NS_IMPL_ISUPPORTS(PluginTimerCallBack, nsITimerCallback)
+NS_IMPL_ISUPPORTS(PluginTimerCallBack, nsITimerCallback, nsINamed)
 #endif
 
 already_AddRefed<Accessible>
 nsAccessibilityService::CreatePluginAccessible(nsPluginFrame* aFrame,
                                                nsIContent* aContent,
                                                Accessible* aContext)
 {
   // nsPluginFrame means a plugin, so we need to use the accessibility support
--- a/accessible/tests/mochitest/focus/a11y.ini
+++ b/accessible/tests/mochitest/focus/a11y.ini
@@ -1,9 +1,9 @@
 [DEFAULT]
 support-files =
   !/accessible/tests/mochitest/*.js
 
 [test_focusedChild.html]
-skip-if = (os == 'win' && (os_version == '6.2' || os_version == '6.3')) # bug 845134
+skip-if = (os == 'win' && (os_version == '6.2' || os_version == '6.3' || os_version == '10.0.15063')) # bug 845134
 [test_takeFocus.html]
 skip-if = buildapp == 'mulet'
 [test_takeFocus.xul]
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1509,16 +1509,18 @@ pref("browser.translation.ui.show", fals
 // Allows to define the translation engine. Bing is default, Yandex may optionally switched on.
 pref("browser.translation.engine", "bing");
 
 // Telemetry settings.
 // Determines if Telemetry pings can be archived locally.
 pref("toolkit.telemetry.archive.enabled", true);
 // Enables sending the shutdown ping when Firefox shuts down.
 pref("toolkit.telemetry.shutdownPingSender.enabled", true);
+// Enables sending the shutdown ping using the pingsender from the first session.
+pref("toolkit.telemetry.shutdownPingSender.enabledFirstSession", false);
 // Enables sending the 'new-profile' ping on new profiles.
 pref("toolkit.telemetry.newProfilePing.enabled", true);
 
 // Telemetry experiments settings.
 pref("experiments.enabled", true);
 pref("experiments.manifest.fetchIntervalSeconds", 86400);
 pref("experiments.manifest.uri", "https://telemetry-experiment.cdn.mozilla.net/manifest/v1/firefox/%VERSION%/%CHANNEL%");
 // Whether experiments are supported by the current application profile.
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -31,17 +31,17 @@ XPCOMUtils.defineLazyPreferenceGetter(th
           Deprecated:false, E10SUtils:false, ExtensionsUI: false, FormValidationHandler:false,
           GMPInstallManager:false, LightweightThemeManager:false, Log:false,
           LoginManagerParent:false, NewTabUtils:false, PageThumbs:false,
           PluralForm:false, Preferences:false, PrivateBrowsingUtils:false,
           ProcessHangMonitor:false, PromiseUtils:false, ReaderMode:false,
           ReaderParent:false, RecentWindow:false, SafeBrowsing: false,
           SessionStore:false,
           ShortcutUtils:false, SimpleServiceDiscovery:false, SitePermissions:false,
-          Social:false, TabCrashHandler:false, Task:false, TelemetryStopwatch:false,
+          Social:false, TabCrashHandler:false, TelemetryStopwatch:false,
           Translation:false, UITour:false, Utils:false, UpdateUtils:false,
           Weave:false,
           WebNavigationFrames: false, fxAccounts:false, gDevTools:false,
           gDevToolsBrowser:false, webrtcUI:false, ZoomUI:false,
           Marionette:false,
  */
 
 /**
@@ -80,17 +80,16 @@ XPCOMUtils.defineLazyPreferenceGetter(th
   ["RecentWindow", "resource:///modules/RecentWindow.jsm"],
   ["SafeBrowsing", "resource://gre/modules/SafeBrowsing.jsm"],
   ["SessionStore", "resource:///modules/sessionstore/SessionStore.jsm"],
   ["ShortcutUtils", "resource://gre/modules/ShortcutUtils.jsm"],
   ["SimpleServiceDiscovery", "resource://gre/modules/SimpleServiceDiscovery.jsm"],
   ["SitePermissions", "resource:///modules/SitePermissions.jsm"],
   ["Social", "resource:///modules/Social.jsm"],
   ["TabCrashHandler", "resource:///modules/ContentCrashHandlers.jsm"],
-  ["Task", "resource://gre/modules/Task.jsm"],
   ["TelemetryStopwatch", "resource://gre/modules/TelemetryStopwatch.jsm"],
   ["Translation", "resource:///modules/translation/Translation.jsm"],
   ["UITour", "resource:///modules/UITour.jsm"],
   ["UpdateUtils", "resource://gre/modules/UpdateUtils.jsm"],
   ["Utils", "resource://gre/modules/sessionstore/Utils.jsm"],
   ["Weave", "resource://services-sync/main.js"],
   ["WebNavigationFrames", "resource://gre/modules/WebNavigationFrames.jsm"],
   ["fxAccounts", "resource://gre/modules/FxAccounts.jsm"],
--- a/browser/base/content/test/tabcrashed/browser_shown.js
+++ b/browser/base/content/test/tabcrashed/browser_shown.js
@@ -1,14 +1,17 @@
 "use strict";
 
 const PAGE = "data:text/html,<html><body>A%20regular,%20everyday,%20normal%20page.";
 const COMMENTS = "Here's my test comment!";
 const EMAIL = "foo@privacy.com";
 
+// Avoid timeouts, as in bug 1325530
+requestLongerTimeout(2);
+
 add_task(async function setup() {
   await setupLocalCrashReportServer();
 });
 
 /**
  * This function returns a Promise that resolves once the following
  * actions have taken place:
  *
--- a/browser/components/downloads/DownloadsCommon.jsm
+++ b/browser/components/downloads/DownloadsCommon.jsm
@@ -42,28 +42,28 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
                                   "resource://gre/modules/PluralForm.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "AppConstants",
                                   "resource://gre/modules/AppConstants.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "AppMenuNotifications",
                                   "resource://gre/modules/AppMenuNotifications.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "CustomizableUI",
                                   "resource:///modules/CustomizableUI.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "DownloadHistory",
+                                  "resource://gre/modules/DownloadHistory.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Downloads",
                                   "resource://gre/modules/Downloads.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "DownloadUIHelper",
                                   "resource://gre/modules/DownloadUIHelper.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "DownloadUtils",
                                   "resource://gre/modules/DownloadUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
                                   "resource://gre/modules/FileUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "OS",
-                                  "resource://gre/modules/osfile.jsm")
-XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
-                                  "resource://gre/modules/PlacesUtils.jsm");
+                                  "resource://gre/modules/osfile.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
                                   "resource://gre/modules/PrivateBrowsingUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "RecentWindow",
                                   "resource:///modules/RecentWindow.jsm");
 
 XPCOMUtils.defineLazyGetter(this, "DownloadsLogger", () => {
   let { ConsoleAPI } = Cu.import("resource://gre/modules/Console.jsm", {});
   let consoleOptions = {
@@ -737,42 +737,18 @@ DownloadsDataCtor.prototype = {
     if (oldState != newState) {
       if (download.succeeded ||
           (download.canceled && !download.hasPartialData) ||
           download.error) {
         // Store the end time that may be displayed by the views.
         download.endTime = Date.now();
 
         // This state transition code should actually be located in a Downloads
-        // API module (bug 941009).  Moreover, the fact that state is stored as
-        // annotations should be ideally hidden behind methods of
-        // nsIDownloadHistory (bug 830415).
-        if (!this._isPrivate) {
-          try {
-            let downloadMetaData = {
-              state: DownloadsCommon.stateOfDownload(download),
-              endTime: download.endTime,
-            };
-            if (download.succeeded) {
-              downloadMetaData.fileSize = download.target.size;
-            }
-            if (download.error && download.error.reputationCheckVerdict) {
-              downloadMetaData.reputationCheckVerdict =
-                download.error.reputationCheckVerdict;
-            }
-
-            PlacesUtils.annotations.setPageAnnotation(
-                          NetUtil.newURI(download.source.url),
-                          "downloads/metaData",
-                          JSON.stringify(downloadMetaData), 0,
-                          PlacesUtils.annotations.EXPIRE_WITH_HISTORY);
-          } catch (ex) {
-            Cu.reportError(ex);
-          }
-        }
+        // API module (bug 941009).
+        DownloadHistory.updateMetaData(download);
       }
 
       if (download.succeeded ||
           (download.error && download.error.becauseBlocked)) {
         this._notifyDownloadEvent("finish");
       }
     }
 
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -34,17 +34,17 @@ XPCOMUtils.defineLazyModuleGetter(this, 
           FileUtils:false, FormValidationHandler:false, Integration:false,
           LightweightThemeManager:false, LoginHelper:false, LoginManagerParent:false,
           NetUtil:false, NewTabUtils:false, OS:false,
           PageThumbs:false, PdfJs:false, PermissionUI:false, PlacesBackups:false,
           PlacesUtils:false, PluralForm:false, PrivateBrowsingUtils:false,
           ProcessHangMonitor:false, ReaderParent:false, RecentWindow:false,
           RemotePrompt:false, SessionStore:false,
           ShellService:false, SimpleServiceDiscovery:false, TabCrashHandler:false,
-          Task:false, UITour:false, UIState:false, UpdateListener:false, WebChannel:false,
+          UITour:false, UIState:false, UpdateListener:false, WebChannel:false,
           WindowsRegistry:false, webrtcUI:false */
 
 
 
 /**
  * IF YOU ADD OR REMOVE FROM THIS LIST, PLEASE UPDATE THE LIST ABOVE AS WELL.
  * XXX Bug 1325373 is for making eslint detect these automatically.
  */
@@ -89,17 +89,16 @@ let initializedModules = {};
   ["ProcessHangMonitor", "resource:///modules/ProcessHangMonitor.jsm"],
   ["ReaderParent", "resource:///modules/ReaderParent.jsm"],
   ["RecentWindow", "resource:///modules/RecentWindow.jsm"],
   ["RemotePrompt", "resource:///modules/RemotePrompt.jsm"],
   ["SessionStore", "resource:///modules/sessionstore/SessionStore.jsm"],
   ["ShellService", "resource:///modules/ShellService.jsm"],
   ["SimpleServiceDiscovery", "resource://gre/modules/SimpleServiceDiscovery.jsm"],
   ["TabCrashHandler", "resource:///modules/ContentCrashHandlers.jsm"],
-  ["Task", "resource://gre/modules/Task.jsm"],
   ["UIState", "resource://services-sync/UIState.jsm"],
   ["UITour", "resource:///modules/UITour.jsm"],
   ["UpdateListener", "resource://gre/modules/UpdateListener.jsm", "init"],
   ["WebChannel", "resource://gre/modules/WebChannel.jsm"],
   ["WindowsRegistry", "resource://gre/modules/WindowsRegistry.jsm"],
   ["webrtcUI", "resource:///modules/webrtcUI.jsm", "init"],
 ].forEach(([name, resource, init]) => {
   if (init) {
--- a/browser/extensions/webcompat-reporter/content/WebCompatReporter.jsm
+++ b/browser/extensions/webcompat-reporter/content/WebCompatReporter.jsm
@@ -6,16 +6,17 @@ this.EXPORTED_SYMBOLS = ["WebCompatRepor
 
 let { classes: Cc, interfaces: Ci, utils: Cu } = Components;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 const TABLISTENER_JSM = "chrome://webcompat-reporter/content/TabListener.jsm";
 const WIDGET_ID = "webcompat-reporter-button";
+const PREF_STYLO_ENABLED = "layout.css.servo.enabled";
 
 XPCOMUtils.defineLazyModuleGetter(this, "CustomizableUI",
   "resource:///modules/CustomizableUI.jsm");
 
 XPCOMUtils.defineLazyGetter(this, "wcStrings", function() {
   return Services.strings.createBundle(
     "chrome://webcompat-reporter/locale/webcompat.properties");
 });
@@ -115,19 +116,28 @@ let WebCompatReporter = {
   //    so it can show a preview to the user and include it in FormData
   // Note: openWebCompatTab arguments are passed in as an array because they
   // are the result of a promise resolution.
   openWebCompatTab([gBrowser, tabData]) {
     const SCREENSHOT_MESSAGE = "WebCompat:SendScreenshot";
     const FRAMESCRIPT = "chrome://webcompat-reporter/content/wc-frame.js";
     let win = Services.wm.getMostRecentWindow("navigator:browser");
     const WEBCOMPAT_ORIGIN = new win.URL(WebCompatReporter.endpoint).origin;
+    let styloEnabled = Services.prefs.getBoolPref(PREF_STYLO_ENABLED, false);
+
+    let params = new URLSearchParams();
+    params.append("url", `${tabData.url}`);
+    params.append("src", "desktop-reporter");
+    if (styloEnabled) {
+        params.append("details", "layout.css.servo.enabled: true");
+        params.append("label", "type-stylo");
+    }
 
     let tab = gBrowser.loadOneTab(
-      `${WebCompatReporter.endpoint}?url=${encodeURIComponent(tabData.url)}&src=desktop-reporter`,
+      `${WebCompatReporter.endpoint}?${params}`,
       {inBackground: false, triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal()});
 
     // If we successfully got a screenshot blob, add a listener to know when
     // the new tab is loaded before sending it over.
     if (tabData && tabData.blob) {
       let browser = gBrowser.getBrowserForTab(tab);
       let loadedListener = {
         QueryInterface: XPCOMUtils.generateQI(["nsIWebProgressListener",
--- a/build/moz.configure/old.configure
+++ b/build/moz.configure/old.configure
@@ -245,17 +245,16 @@ def old_configure_options(*options):
     '--with-android-distribution-directory',
     '--with-android-max-sdk',
     '--with-android-min-sdk',
     '--with-android-sdk',
     '--with-app-basename',
     '--with-app-name',
     '--with-arch',
     '--with-branding',
-    '--with-crashreporter-enable-percent',
     '--with-cross-lib',
     '--with-debug-label',
     '--with-default-mozilla-five-home',
     '--with-distribution-id',
     '--with-doc-include-dirs',
     '--with-doc-input-dirs',
     '--with-doc-output-dir',
     '--with-float-abi',
--- a/devtools/client/inspector/grids/test/browser.ini
+++ b/devtools/client/inspector/grids/test/browser.ini
@@ -28,8 +28,9 @@ support-files =
 [browser_grids_grid-list-toggle-single-grid.js]
 [browser_grids_grid-outline-cannot-show-outline.js]
 [browser_grids_grid-outline-highlight-area.js]
 [browser_grids_grid-outline-highlight-cell.js]
 [browser_grids_grid-outline-selected-grid.js]
 [browser_grids_grid-outline-updates-on-grid-change.js]
 [browser_grids_highlighter-setting-rules-grid-toggle.js]
 [browser_grids_number-of-css-grids-telemetry.js]
+[browser_grids_restored-after-reload.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/grids/test/browser_grids_restored-after-reload.js
@@ -0,0 +1,87 @@
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that the grid highlighter is re-displayed after reloading a page and the grid
+// item is highlighted.
+
+const TEST_URI = `
+  <style type='text/css'>
+    #grid {
+      display: grid;
+    }
+  </style>
+  <div id="grid">
+    <div id="cell1">cell1</div>
+    <div id="cell2">cell2</div>
+  </div>
+`;
+
+const OTHER_URI = `
+  <style type='text/css'>
+    #grid {
+      display: grid;
+    }
+  </style>
+  <div id="grid">
+    <div id="cell1">cell1</div>
+    <div id="cell2">cell2</div>
+    <div id="cell3">cell3</div>
+  </div>
+`;
+
+add_task(function* () {
+  yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+  let { gridInspector, inspector } = yield openLayoutView();
+  let { document: doc } = gridInspector;
+  let { highlighters, store } = inspector;
+
+  yield selectNode("#grid", inspector);
+  let gridList = doc.getElementById("grid-list");
+  let checkbox = gridList.children[0].querySelector("input");
+
+  info("Toggling ON the CSS grid highlighter from the layout panel.");
+  let onHighlighterShown = highlighters.once("grid-highlighter-shown");
+  let onCheckboxChange = waitUntilState(store, state =>
+    state.grids.length == 1 &&
+    state.grids[0].highlighted);
+  checkbox.click();
+  yield onHighlighterShown;
+  yield onCheckboxChange;
+
+  info("Checking the CSS grid highlighter is created.");
+  ok(highlighters.highlighters[HIGHLIGHTER_TYPE],
+    "CSS grid highlighter is created in the highlighters overlay.");
+  ok(highlighters.gridHighlighterShown, "CSS grid highlighter is shown.");
+
+  info("Reload the page, expect the highlighter to be displayed once again and " +
+    "grid is checked");
+  let onStateRestored = highlighters.once("grid-state-restored");
+  let onGridListRestored = waitUntilState(store, state =>
+    state.grids.length == 1 &&
+    state.grids[0].highlighted);
+  yield refreshTab(gBrowser.selectedTab);
+  let { restored } = yield onStateRestored;
+  yield onGridListRestored;
+
+  info("Check that the grid highlighter can be displayed after reloading the page");
+  ok(restored, "The highlighter state was restored");
+  ok(highlighters.gridHighlighterShown, "CSS grid highlighter is shown.");
+
+  info("Navigate to another URL, and check that the highlighter is hidden and " +
+    "grid is unchecked");
+  let otherUri = "data:text/html;charset=utf-8," + encodeURIComponent(OTHER_URI);
+  onStateRestored = highlighters.once("grid-state-restored");
+  onGridListRestored = waitUntilState(store, state =>
+    state.grids.length == 1 &&
+    !state.grids[0].highlighted);
+  yield navigateTo(inspector, otherUri);
+  ({ restored } = yield onStateRestored);
+  yield onGridListRestored;
+
+  info("Check that the grid highlighter is hidden after navigating to a different page");
+  ok(!restored, "The highlighter state was not restored");
+  ok(!highlighters.gridHighlighterShown, "CSS grid highlighter is hidden.");
+});
--- a/devtools/client/inspector/inspector.js
+++ b/devtools/client/inspector/inspector.js
@@ -103,31 +103,33 @@ function Inspector(toolbox) {
 
   // Store the URL of the target page prior to navigation in order to ensure
   // telemetry counts in the Grid Inspector are not double counted on reload.
   this.previousURL = this.target.url;
 
   this.nodeMenuTriggerInfo = null;
 
   this._handleRejectionIfNotDestroyed = this._handleRejectionIfNotDestroyed.bind(this);
+  this._onContextMenu = this._onContextMenu.bind(this);
   this._onBeforeNavigate = this._onBeforeNavigate.bind(this);
-  this.onNewRoot = this.onNewRoot.bind(this);
-  this._onContextMenu = this._onContextMenu.bind(this);
-  this.onTextBoxContextMenu = this.onTextBoxContextMenu.bind(this);
+  this._onMarkupFrameLoad = this._onMarkupFrameLoad.bind(this);
   this._updateSearchResultsLabel = this._updateSearchResultsLabel.bind(this);
+
+  this.onDetached = this.onDetached.bind(this);
+  this.onMarkupLoaded = this.onMarkupLoaded.bind(this);
   this.onNewSelection = this.onNewSelection.bind(this);
-  this.onDetached = this.onDetached.bind(this);
+  this.onNewRoot = this.onNewRoot.bind(this);
   this.onPaneToggleButtonClicked = this.onPaneToggleButtonClicked.bind(this);
-  this._onMarkupFrameLoad = this._onMarkupFrameLoad.bind(this);
   this.onPanelWindowResize = this.onPanelWindowResize.bind(this);
-  this.onSidebarShown = this.onSidebarShown.bind(this);
+  this.onShowBoxModelHighlighterForNode =
+    this.onShowBoxModelHighlighterForNode.bind(this);
   this.onSidebarHidden = this.onSidebarHidden.bind(this);
   this.onSidebarSelect = this.onSidebarSelect.bind(this);
-  this.onShowBoxModelHighlighterForNode =
-    this.onShowBoxModelHighlighterForNode.bind(this);
+  this.onSidebarShown = this.onSidebarShown.bind(this);
+  this.onTextBoxContextMenu = this.onTextBoxContextMenu.bind(this);
 
   this._target.on("will-navigate", this._onBeforeNavigate);
   this._detectingActorFeatures = this._detectActorFeatures();
 }
 
 Inspector.prototype = {
   /**
    * open is effectively an asynchronous constructor
@@ -774,32 +776,47 @@ Inspector.prototype = {
       // been queued up.
       if (this._pendingSelection != onNodeSelected) {
         return;
       }
       this._pendingSelection = null;
       this.selection.setNodeFront(defaultNode, "navigateaway");
 
       this._initMarkup();
-      this.once("markuploaded", () => {
-        if (!this.markup) {
-          return;
-        }
-        this.markup.expandNode(this.selection.nodeFront);
-        this.emit("new-root");
-      });
+      this.once("markuploaded", this.onMarkupLoaded);
 
       // Setup the toolbar again, since its content may depend on the current document.
       this.setupToolbar();
     };
     this._pendingSelection = onNodeSelected;
     this._getDefaultNodeForSelection()
         .then(onNodeSelected, this._handleRejectionIfNotDestroyed);
   },
 
+  /**
+   * Handler for "markuploaded" event fired on a new root mutation and after the markup
+   * view is initialized. Expands the current selected node and restores the saved
+   * highlighter state.
+   */
+  onMarkupLoaded: Task.async(function* () {
+    if (!this.markup) {
+      return;
+    }
+
+    this.markup.expandNode(this.selection.nodeFront);
+
+    // Restore the highlighter states prior to emitting "new-root".
+    yield Promise.all([
+      this.highlighters.restoreGridState(),
+      this.highlighters.restoreShapeState()
+    ]);
+
+    this.emit("new-root");
+  }),
+
   _selectionCssSelector: null,
 
   /**
    * Set the currently selected node unique css selector.
    * Will store the current target url along with it to allow pre-selection at
    * reload
    */
   set selectionCssSelector(cssSelector = null) {
--- a/devtools/client/inspector/shared/highlighters-overlay.js
+++ b/devtools/client/inspector/shared/highlighters-overlay.js
@@ -47,25 +47,23 @@ function HighlightersOverlay(inspector) 
     shapes: {}
   };
 
   this.onClick = this.onClick.bind(this);
   this.onMarkupMutation = this.onMarkupMutation.bind(this);
   this.onMouseMove = this.onMouseMove.bind(this);
   this.onMouseOut = this.onMouseOut.bind(this);
   this.onWillNavigate = this.onWillNavigate.bind(this);
-  this.onNavigate = this.onNavigate.bind(this);
   this.showGridHighlighter = this.showGridHighlighter.bind(this);
   this.showShapesHighlighter = this.showShapesHighlighter.bind(this);
   this._handleRejection = this._handleRejection.bind(this);
   this._onHighlighterEvent = this._onHighlighterEvent.bind(this);
 
   // Add inspector events, not specific to a given view.
   this.inspector.on("markupmutation", this.onMarkupMutation);
-  this.inspector.target.on("navigate", this.onNavigate);
   this.inspector.target.on("will-navigate", this.onWillNavigate);
 
   EventEmitter.decorate(this);
 }
 
 HighlightersOverlay.prototype = {
   get isRuleView() {
     return this.inspector.sidebar.getCurrentTabID() == "ruleview";
@@ -382,37 +380,59 @@ HighlightersOverlay.prototype = {
     } else if (data.type === "shape-hover-off") {
       this.state.shapes.hoverPoint = null;
       this.emit("hover-shape-point", null);
     }
     this.emit("highlighter-event-handled");
   },
 
   /**
-   * Restore the saved highlighter states.
+   * Restores the saved grid highlighter state.
+   */
+  restoreGridState: Task.async(function* () {
+    try {
+      yield this.restoreState("grid", this.state.grid, this.showGridHighlighter);
+    } catch (e) {
+      this._handleRejection(e);
+    }
+  }),
+
+  /**
+   * Restores the saved shape highlighter state.
+   */
+  restoreShapeState: Task.async(function* () {
+    try {
+      yield this.restoreState("shapes", this.state.shapes, this.showShapesHighlighter);
+    } catch (e) {
+      this._handleRejection(e);
+    }
+  }),
+
+  /**
+   * Helper function called by restoreGridState and restoreShapeState.
+   * Restores the saved highlighter state for the given highlighter and their state.
+   *
    * @param {String} name
    *        The name of the highlighter to be restored
    * @param {Object} state
    *        The state of the highlighter to be restored
    * @param {Function} showFunction
    *        The function that shows the highlighter
    * @return {Promise} that resolves when the highlighter state was restored, and the
    *         expected highlighters are displayed.
    */
   restoreState: Task.async(function* (name, state, showFunction) {
     let { selector, options, url } = state;
+
     if (!selector || url !== this.inspector.target.url) {
       // Bail out if no selector was saved, or if we are on a different page.
       this.emit(`${name}-state-restored`, { restored: false });
       return;
     }
 
-    // Wait for the new root to be ready in the inspector.
-    yield this.onInspectorNewRoot;
-
     let walker = this.inspector.walker;
     let rootNode = yield walker.getRootNode();
     let nodeFront = yield walker.querySelector(rootNode, selector);
 
     if (nodeFront) {
       if (options.hoverPoint) {
         options.hoverPoint = null;
       }
@@ -709,39 +729,24 @@ HighlightersOverlay.prototype = {
         }
       } catch (e) {
         console.error(e);
       }
     }
   }),
 
   /**
-   * Restore saved highlighter state after navigate.
-   */
-  onNavigate: Task.async(function* () {
-    try {
-      yield this.restoreState("grid", this.state.grid, this.showGridHighlighter);
-      yield this.restoreState("shapes", this.state.shapes, this.showShapesHighlighter);
-    } catch (e) {
-      this._handleRejection(e);
-    }
-  }),
-
-  /**
    * Clear saved highlighter shown properties on will-navigate.
    */
   onWillNavigate: function () {
     this.geometryEditorHighlighterShown = null;
     this.gridHighlighterShown = null;
     this.hoveredHighlighterShown = null;
     this.selectorHighlighterShown = null;
     this.shapesHighlighterShown = null;
-
-    // The inspector panel should emit the new-root event when it is ready after navigate.
-    this.onInspectorNewRoot = this.inspector.once("new-root");
   },
 
   /**
    * Destroy this overlay instance, removing it from the view and destroying
    * all initialized highlighters.
    */
   destroy: function () {
     for (let type in this.highlighters) {
@@ -751,17 +756,16 @@ HighlightersOverlay.prototype = {
         }
         this.highlighters[type].finalize();
         this.highlighters[type] = null;
       }
     }
 
     // Remove inspector events.
     this.inspector.off("markupmutation", this.onMarkupMutation);
-    this.inspector.target.off("navigate", this.onNavigate);
     this.inspector.target.off("will-navigate", this.onWillNavigate);
 
     this._lastHovered = null;
 
     this.inspector = null;
     this.highlighters = null;
     this.highlighterUtils = null;
     this.supportsHighlighters = null;
--- a/devtools/client/preferences/devtools.js
+++ b/devtools/client/preferences/devtools.js
@@ -76,16 +76,17 @@ pref("devtools.promote.layoutview", 1);
 pref("devtools.promote.layoutview.showPromoteBar", true);
 
 // Grid highlighter preferences
 pref("devtools.gridinspector.gridOutlineMaxColumns", 50);
 pref("devtools.gridinspector.gridOutlineMaxRows", 50);
 pref("devtools.gridinspector.showGridAreas", false);
 pref("devtools.gridinspector.showGridLineNumbers", false);
 pref("devtools.gridinspector.showInfiniteLines", false);
+pref("devtools.gridinspector.showNegativeLineNumbers", false);
 
 // Whether or not the box model panel is opened in the computed view
 pref("devtools.computed.boxmodel.opened", true);
 // Whether or not the box model panel is opened in the layout view
 pref("devtools.layout.boxmodel.opened", true);
 // Whether or not the grid inspector panel is opened in the layout view
 pref("devtools.layout.grid.opened", true);
 
--- a/devtools/server/actors/highlighters/css-grid.js
+++ b/devtools/server/actors/highlighters/css-grid.js
@@ -29,16 +29,17 @@ const {
 } = require("devtools/shared/layout/dom-matrix-2d");
 const { stringifyGridFragments } = require("devtools/server/actors/utils/css-grid-utils");
 const { LocalizationHelper } = require("devtools/shared/l10n");
 
 const LAYOUT_STRINGS_URI = "devtools/client/locales/layout.properties";
 const LAYOUT_L10N = new LocalizationHelper(LAYOUT_STRINGS_URI);
 
 const CSS_GRID_ENABLED_PREF = "layout.css.grid.enabled";
+const NEGATIVE_LINE_NUMBERS_PREF = "devtools.gridinspector.showNegativeLineNumbers";
 
 const DEFAULT_GRID_COLOR = "#4B0082";
 
 const COLUMNS = "cols";
 const ROWS = "rows";
 
 const GRID_FONT_SIZE = 10;
 const GRID_FONT_FAMILY = "sans-serif";
@@ -347,21 +348,19 @@ class CssGridHighlighter extends AutoRef
   constructor(highlighterEnv) {
     super(highlighterEnv);
 
     this.ID_CLASS_PREFIX = "css-grid-";
 
     this.markup = new CanvasFrameAnonymousContentHelper(this.highlighterEnv,
       this._buildMarkup.bind(this));
 
-    this.onNavigate = this.onNavigate.bind(this);
     this.onPageHide = this.onPageHide.bind(this);
     this.onWillNavigate = this.onWillNavigate.bind(this);
 
-    this.highlighterEnv.on("navigate", this.onNavigate);
     this.highlighterEnv.on("will-navigate", this.onWillNavigate);
 
     let { pageListenerTarget } = highlighterEnv;
     pageListenerTarget.addEventListener("pagehide", this.onPageHide);
 
     // Initialize the <canvas> position to the top left corner of the page
     this._canvasPosition = {
       x: 0,
@@ -585,17 +584,16 @@ class CssGridHighlighter extends AutoRef
       prefix: this.ID_CLASS_PREFIX
     });
 
     return container;
   }
 
   destroy() {
     let { highlighterEnv } = this;
-    highlighterEnv.off("navigate", this.onNavigate);
     highlighterEnv.off("will-navigate", this.onWillNavigate);
 
     let { pageListenerTarget } = highlighterEnv;
     if (pageListenerTarget) {
       pageListenerTarget.removeEventListener("pagehide", this.onPageHide);
     }
 
     this.markup.destroy();
@@ -672,33 +670,32 @@ class CssGridHighlighter extends AutoRef
     let pattern = ctx.createPattern(canvas, "repeat");
 
     gridPatternMap.set(dimension, pattern);
     gCachedGridPattern.set(devicePixelRatio, gridPatternMap);
 
     return pattern;
   }
 
-  /**
-   * Called when the page navigates. Used to clear the cached gap patterns and avoid
-   * using DeadWrapper objects as gap patterns the next time.
-   */
-  onNavigate() {
-    this._clearCache();
-  }
-
   onPageHide({ target }) {
     // If a page hide event is triggered for current window's highlighter, hide the
     // highlighter.
     if (target.defaultView === this.win) {
       this.hide();
     }
   }
 
+  /**
+   * Called when the page will-navigate. Used to hide the grid highlighter and clear
+   * the cached gap patterns and avoid using DeadWrapper obejcts as gap patterns the
+   * next time.
+   */
   onWillNavigate({ isTopLevel }) {
+    this._clearCache();
+
     if (isTopLevel) {
       this.hide();
     }
   }
 
   _show() {
     if (Services.prefs.getBoolPref(CSS_GRID_ENABLED_PREF) && !this.isGrid()) {
       this.hide();
@@ -1140,16 +1137,45 @@ class CssGridHighlighter extends AutoRef
     }
 
     // Line numbers are rendered in a 2nd step to avoid overlapping with existing lines.
     if (this.options.showGridLineNumbers) {
       this.renderLineNumbers(fragment.cols, COLUMNS, "left", "top",
                        this.getFirstRowLinePos(fragment));
       this.renderLineNumbers(fragment.rows, ROWS, "top", "left",
                        this.getFirstColLinePos(fragment));
+
+      if (Services.prefs.getBoolPref(NEGATIVE_LINE_NUMBERS_PREF)) {
+        this.renderNegativeLineNumbers(fragment.cols, COLUMNS, "left", "top",
+                          this.getLastRowLinePos(fragment));
+        this.renderNegativeLineNumbers(fragment.rows, ROWS, "top", "left",
+                          this.getLastColLinePos(fragment));
+      }
+    }
+  }
+
+  /**
+   * Render the negative grid lines given the grid dimension information of the
+   * column or row lines.
+   *
+   * See @param for renderLines.
+   */
+  renderNegativeLineNumbers(gridDimension, dimensionType, mainSide, crossSide,
+            startPos) {
+    let lineStartPos = startPos;
+
+    const { lines } = gridDimension;
+
+    for (let i = 0, line = lines[i]; i < lines.length; line = lines[++i]) {
+      let linePos = line.start;
+
+      const negativeLineNumber = i - lines.length;
+
+      this.renderGridLineNumber(negativeLineNumber, linePos, lineStartPos, line.breadth,
+        dimensionType);
     }
   }
 
   /**
    * Renders the grid area overlay on the css grid highlighter canvas.
    */
   renderGridAreaOverlay() {
     let padding = 1;
@@ -1326,32 +1352,50 @@ class CssGridHighlighter extends AutoRef
    * column or row lines.
    *
    * see @param for renderLines.
    */
   renderLineNumbers(gridDimension, dimensionType, mainSide, crossSide,
               startPos) {
     let lineStartPos = startPos;
 
+    // Keep track of the number of collapsed lines per line position
+    let stackedLines = [];
+
     for (let i = 0; i < gridDimension.lines.length; i++) {
       let line = gridDimension.lines[i];
       let linePos = line.start;
 
       // If you place something using negative numbers, you can trigger some implicit grid
       // creation above and to the left of the explicit grid (assuming a horizontal-tb
       // writing mode).
       // The first explicit grid line gets the number of 1; any implicit grid lines
       // before 1 get negative numbers, but do not get any positivity numbers.
       // Since here we're rendering only the positive line numbers, we have to skip any
       // implicit grid lines before the first tha is explicit.
       // For such lines the API returns always 0 as line's number.
       if (line.number === 0) {
         continue;
       }
 
+      // Check for overlapping lines. We render a second box beneath the last overlapping
+      // line number to indicate there are lines beneath it.
+      const gridLine = gridDimension.tracks[line.number - 1];
+      if (gridLine) {
+        const { breadth }  = gridLine;
+        if (breadth === 0) {
+          stackedLines.push(gridDimension.lines[i].number);
+          if (stackedLines.length > 0) {
+            this.renderGridLineNumber(line.number, linePos, lineStartPos, line.breadth,
+              dimensionType, 1);
+          }
+          continue;
+        }
+      }
+
       this.renderGridLineNumber(line.number, linePos, lineStartPos, line.breadth,
         dimensionType);
     }
   }
 
   /**
    * Render the grid line on the css grid highlighter canvas.
    *
@@ -1420,18 +1464,21 @@ class CssGridHighlighter extends AutoRef
    *         The line position along the x-axis for a column grid line and
    *         y-axis for a row grid line.
    * @param  {Number} startPos
    *         The start position of the cross side of the grid line.
    * @param  {Number} breadth
    *         The grid line breadth value.
    * @param  {String} dimensionType
    *         The grid dimension type which is either the constant COLUMNS or ROWS.
+   * @param  {Number||undefined} stackedLineIndex
+   *         The line index position of the stacked line.
    */
-  renderGridLineNumber(lineNumber, linePos, startPos, breadth, dimensionType) {
+  renderGridLineNumber(lineNumber, linePos, startPos, breadth, dimensionType,
+    stackedLineIndex) {
     let displayPixelRatio = getDisplayPixelRatio(this.win);
     let { devicePixelRatio } = this.win;
     let offset = (displayPixelRatio / 2) % 1;
 
     linePos = Math.round(linePos);
     startPos = Math.round(startPos);
     breadth = Math.round(breadth);
 
@@ -1458,45 +1505,67 @@ class CssGridHighlighter extends AutoRef
 
     let boxWidth = textWidth + 2 * padding;
     let boxHeight = textHeight + 2 * padding;
 
     // Calculate the x & y coordinates for the line number container, so that it is
     // centered on the line, and in the middle of the gap if there is any.
     let x, y;
 
+    let startOffset = (boxHeight + 2) / devicePixelRatio;
+
+    if (Services.prefs.getBoolPref(NEGATIVE_LINE_NUMBERS_PREF)) {
+      // If the line number is negative, offset it from the grid container edge,
+      // (downwards if its a column, rightwards if its a row).
+      if (lineNumber < 0) {
+        startPos += startOffset;
+      } else {
+        startPos -= startOffset;
+      }
+    }
+
     if (dimensionType === COLUMNS) {
       x = linePos + breadth / 2;
       y = startPos;
     } else {
       x = startPos;
       y = linePos + breadth / 2;
     }
 
     [x, y] = apply(this.currentMatrix, [x, y]);
 
     x -= boxWidth / 2;
     y -= boxHeight / 2;
 
+    if (stackedLineIndex) {
+      // Offset the stacked line number by half of the box's width/height
+      const xOffset = boxWidth / 4;
+      const yOffset = boxHeight / 4;
+
+      x += xOffset;
+      y += yOffset;
+    }
+
     if (!this.hasNodeTransformations) {
       x = Math.max(x, padding);
       y = Math.max(y, padding);
     }
 
     // Draw a rounded rectangle with a border width of 2 pixels, a border color matching
     // the grid color and a white background (the line number will be written in black).
     this.ctx.lineWidth = 2 * displayPixelRatio;
     this.ctx.strokeStyle = this.color;
     this.ctx.fillStyle = "white";
     let radius = 2 * displayPixelRatio;
     drawRoundedRect(this.ctx, x, y, boxWidth, boxHeight, radius);
 
     // Write the line number inside of the rectangle.
     this.ctx.fillStyle = "black";
-    this.ctx.fillText(lineNumber, x + padding, y + textHeight + padding);
+    const numberText = stackedLineIndex ? "" : lineNumber;
+    this.ctx.fillText(numberText, x + padding, y + textHeight + padding);
 
     this.ctx.restore();
   }
 
   /**
    * Render the grid gap area on the css grid highlighter canvas.
    *
    * @param  {Number} linePos
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -1766,38 +1766,36 @@ nsDocShell::FirePageHideNotificationInte
 
     // Now make sure our editor, if any, is detached before we go
     // any farther.
     DetachEditorFromWindow();
   }
 }
 
 nsresult
-nsDocShell::DispatchToTabGroup(const char* aName,
-                               TaskCategory aCategory,
+nsDocShell::DispatchToTabGroup(TaskCategory aCategory,
                                already_AddRefed<nsIRunnable>&& aRunnable)
 {
   // Hold the ref so we won't forget to release it.
   nsCOMPtr<nsIRunnable> runnable(aRunnable);
   nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
   if (!win) {
     // Window should only be unavailable after destroyed.
     MOZ_ASSERT(mIsBeingDestroyed);
     return NS_ERROR_FAILURE;
   }
 
   RefPtr<mozilla::dom::TabGroup> tabGroup = win->TabGroup();
-  return tabGroup->Dispatch(aName, aCategory, runnable.forget());
+  return tabGroup->Dispatch(aCategory, runnable.forget());
 }
 
 NS_IMETHODIMP
 nsDocShell::DispatchLocationChangeEvent()
 {
   return DispatchToTabGroup(
-    "nsDocShell::FireDummyOnLocationChange",
     TaskCategory::Other,
     NewRunnableMethod("nsDocShell::FireDummyOnLocationChange",
                       this,
                       &nsDocShell::FireDummyOnLocationChange));
 }
 
 bool
 nsDocShell::MaybeInitTiming()
@@ -6788,28 +6786,22 @@ nsDocShell::RefreshURI(nsIURI* aURI, int
   nsresult rv = aURI->Equals(mCurrentURI, &sameURI);
   if (NS_FAILED(rv)) {
     sameURI = false;
   }
   if (!RefreshAttempted(this, aURI, aDelay, sameURI)) {
     return NS_OK;
   }
 
-  nsRefreshTimer* refreshTimer = new nsRefreshTimer();
+  nsCOMPtr<nsITimerCallback> refreshTimer =
+    new nsRefreshTimer(this, aURI, aDelay, aRepeat, aMetaRefresh);
+
   uint32_t busyFlags = 0;
   GetBusyFlags(&busyFlags);
 
-  nsCOMPtr<nsISupports> dataRef = refreshTimer;  // Get the ref count to 1
-
-  refreshTimer->mDocShell = this;
-  refreshTimer->mURI = aURI;
-  refreshTimer->mDelay = aDelay;
-  refreshTimer->mRepeat = aRepeat;
-  refreshTimer->mMetaRefresh = aMetaRefresh;
-
   if (!mRefreshURIList) {
     mRefreshURIList = nsArray::Create();
   }
 
   if (busyFlags & BUSY_FLAGS_BUSY || (!mIsActive && mDisableMetaRefreshWhenInactive)) {
     // We don't  want to create the timer right now. Instead queue up the request
     // and trigger the timer in EndPageLoad() or whenever we become active.
     mRefreshURIList->AppendElement(refreshTimer, /*weak =*/ false);
@@ -8587,18 +8579,17 @@ nsDocShell::RestorePresentation(nsISHEnt
   // implementations.
 
   // Revoke any pending restore (just in case)
   NS_ASSERTION(!mRestorePresentationEvent.IsPending(),
                "should only have one RestorePresentationEvent");
   mRestorePresentationEvent.Revoke();
 
   RefPtr<RestorePresentationEvent> evt = new RestorePresentationEvent(this);
-  nsresult rv = DispatchToTabGroup("nsDocShell::RestorePresentationEvent",
-                                   TaskCategory::Other,
+  nsresult rv = DispatchToTabGroup(TaskCategory::Other,
                                    RefPtr<RestorePresentationEvent>(evt).forget());
   if (NS_SUCCEEDED(rv)) {
     mRestorePresentationEvent = evt.get();
     // The rest of the restore processing will happen on our event
     // callback.
     *aRestoring = true;
   }
 
@@ -10259,18 +10250,17 @@ nsDocShell::InternalLoad(nsIURI* aURI,
       // Do this asynchronously
       nsCOMPtr<nsIRunnable> ev =
         new InternalLoadEvent(this, aURI, aOriginalURI, aResultPrincipalURI,
                               aLoadReplace, aReferrer, aReferrerPolicy,
                               aTriggeringPrincipal, principalToInherit,
                               aFlags, aTypeHint, aPostData, aHeadersData,
                               aLoadType, aSHEntry, aFirstParty, aSrcdoc,
                               aSourceDocShell, aBaseURI, false);
-      return DispatchToTabGroup("nsDocShell::InternalLoadEvent",
-                                TaskCategory::Other, ev.forget());
+      return DispatchToTabGroup(TaskCategory::Other, ev.forget());
     }
 
     // Just ignore this load attempt
     return NS_OK;
   }
 
   // If a source docshell has been passed, check to see if we are sandboxed
   // from it as the result of an iframe or CSP sandbox.
@@ -13629,47 +13619,57 @@ NS_IMETHODIMP
 nsDocShell::SetLayoutHistoryState(nsILayoutHistoryState* aLayoutHistoryState)
 {
   if (mOSHE) {
     mOSHE->SetLayoutHistoryState(aLayoutHistoryState);
   }
   return NS_OK;
 }
 
-nsRefreshTimer::nsRefreshTimer()
-  : mDelay(0), mRepeat(false), mMetaRefresh(false)
+nsRefreshTimer::nsRefreshTimer(nsDocShell* aDocShell, nsIURI* aURI,
+                               int32_t aDelay, bool aRepeat, bool aMetaRefresh)
+  : mDocShell(aDocShell), mURI(aURI), mDelay(aDelay), mRepeat(aRepeat),
+    mMetaRefresh(aMetaRefresh)
 {
 }
 
 nsRefreshTimer::~nsRefreshTimer()
 {
 }
 
 NS_IMPL_ADDREF(nsRefreshTimer)
 NS_IMPL_RELEASE(nsRefreshTimer)
 
 NS_INTERFACE_MAP_BEGIN(nsRefreshTimer)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsITimerCallback)
   NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
+  NS_INTERFACE_MAP_ENTRY(nsINamed)
 NS_INTERFACE_MAP_END_THREADSAFE
 
 NS_IMETHODIMP
 nsRefreshTimer::Notify(nsITimer* aTimer)
 {
   NS_ASSERTION(mDocShell, "DocShell is somehow null");
 
   if (mDocShell && aTimer) {
     // Get the delay count to determine load type
     uint32_t delay = 0;
     aTimer->GetDelay(&delay);
     mDocShell->ForceRefreshURIFromTimer(mURI, delay, mMetaRefresh, aTimer);
   }
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsRefreshTimer::GetName(nsACString& aName)
+{
+  aName.AssignLiteral("nsRefreshTimer");
+  return NS_OK;
+}
+
 nsDocShell::InterfaceRequestorProxy::InterfaceRequestorProxy(
     nsIInterfaceRequestor* aRequestor)
 {
   if (aRequestor) {
     mWeakPtr = do_GetWeakReference(aRequestor);
   }
 }
 
@@ -14161,18 +14161,17 @@ nsDocShell::OnLinkClick(nsIContent* aCon
   if (NS_FAILED(rv)) {
     target = aTargetSpec;
   }
 
   nsCOMPtr<nsIRunnable> ev =
     new OnLinkClickEvent(this, aContent, aURI, target.get(), aFileName,
                          aPostDataStream, aHeadersDataStream, noOpenerImplied,
                          aIsTrusted, aTriggeringPrincipal);
-  return DispatchToTabGroup("nsDocShell::OnLinkClickEvent",
-                            TaskCategory::UI, ev.forget());
+  return DispatchToTabGroup(TaskCategory::UI, ev.forget());
 }
 
 NS_IMETHODIMP
 nsDocShell::OnLinkClickSync(nsIContent* aContent,
                             nsIURI* aURI,
                             const char16_t* aTargetSpec,
                             const nsAString& aFileName,
                             nsIInputStream* aPostDataStream,
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -39,16 +39,17 @@
 #include "timeline/TimelineMarker.h"
 
 // Threshold value in ms for META refresh based redirects
 #define REFRESH_REDIRECT_TIMER 15000
 
 // Interfaces Needed
 #include "nsIDocCharset.h"
 #include "nsIInterfaceRequestor.h"
+#include "nsINamed.h"
 #include "nsIRefreshURI.h"
 #include "nsIWebNavigation.h"
 #include "nsIWebPageDescriptor.h"
 #include "nsIWebProgressListener.h"
 #include "nsIDocShellLoadInfo.h"
 #include "nsIAuthPromptProvider.h"
 #include "nsILoadContext.h"
 #include "nsIWebShellServices.h"
@@ -101,22 +102,25 @@ class FramingChecker;
 /* internally used ViewMode types */
 enum ViewMode
 {
   viewNormal = 0x0,
   viewSource = 0x1
 };
 
 class nsRefreshTimer : public nsITimerCallback
+                     , public nsINamed
 {
 public:
-  nsRefreshTimer();
+  nsRefreshTimer(nsDocShell* aDocShell, nsIURI* aURI, int32_t aDelay,
+                 bool aRepeat, bool aMetaRefresh);
 
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSITIMERCALLBACK
+  NS_DECL_NSINAMED
 
   int32_t GetDelay() { return mDelay ;}
 
   RefPtr<nsDocShell> mDocShell;
   nsCOMPtr<nsIURI> mURI;
   int32_t mDelay;
   bool mRepeat;
   bool mMetaRefresh;
@@ -1085,18 +1089,17 @@ private:
   // Internal implementation of nsIDocShell::FirePageHideNotification.
   // If aSkipCheckingDynEntries is true, it will not try to remove dynamic
   // subframe entries. This is to avoid redundant RemoveDynEntries calls in all
   // children docshells.
   void FirePageHideNotificationInternal(bool aIsUnload,
                                         bool aSkipCheckingDynEntries);
 
   // Dispatch a runnable to the TabGroup associated to this docshell.
-  nsresult DispatchToTabGroup(const char* aName,
-                              mozilla::TaskCategory aCategory,
+  nsresult DispatchToTabGroup(mozilla::TaskCategory aCategory,
                               already_AddRefed<nsIRunnable>&& aRunnable);
 
 #ifdef DEBUG
   // We're counting the number of |nsDocShells| to help find leaks
   static unsigned long gNumberOfDocShells;
 #endif /* DEBUG */
 
 public:
--- a/docshell/shistory/nsSHEntryShared.cpp
+++ b/docshell/shistory/nsSHEntryShared.cpp
@@ -210,18 +210,17 @@ nsSHEntryShared::RemoveFromBFCacheAsync(
 
   // Release the reference to the contentviewer asynchronously so that the
   // document doesn't get nuked mid-mutation.
 
   if (!mDocument) {
     return NS_ERROR_UNEXPECTED;
   }
   nsCOMPtr<nsIRunnable> evt = new DestroyViewerEvent(mContentViewer, mDocument);
-  nsresult rv = mDocument->Dispatch("nsSHEntryShared::DestroyViewerEvent",
-                                    mozilla::TaskCategory::Other, evt.forget());
+  nsresult rv = mDocument->Dispatch(mozilla::TaskCategory::Other, evt.forget());
   if (NS_FAILED(rv)) {
     NS_WARNING("failed to dispatch DestroyViewerEvent");
   } else {
     // Drop presentation. Only do this if we succeeded in posting the event
     // since otherwise the document could be torn down mid-mutation, causing
     // crashes.
     DropPresentationState();
   }
--- a/dom/base/DOMIntersectionObserver.cpp
+++ b/dom/base/DOMIntersectionObserver.cpp
@@ -428,25 +428,25 @@ DOMIntersectionObserver::Update(nsIDocum
 
     double intersectionRatio;
     if (targetArea > 0.0) {
       intersectionRatio = intersectionArea / targetArea;
     } else {
       intersectionRatio = intersectionRect.isSome() ? 1.0 : 0.0;
     }
 
-    size_t threshold = -1;
+    int32_t threshold = -1;
     if (intersectionRatio > 0.0) {
       if (intersectionRatio >= 1.0) {
         intersectionRatio = 1.0;
-        threshold = mThresholds.Length();
+        threshold = (int32_t)mThresholds.Length();
       } else {
         for (size_t k = 0; k < mThresholds.Length(); ++k) {
           if (mThresholds[k] <= intersectionRatio) {
-            threshold = k + 1;
+            threshold = (int32_t)k + 1;
           } else {
             break;
           }
         }
       }
     } else if (intersectionRect.isSome()) {
       threshold = 0;
     }
--- a/dom/base/DispatcherTrait.cpp
+++ b/dom/base/DispatcherTrait.cpp
@@ -9,21 +9,20 @@
 #include "mozilla/AbstractThread.h"
 #include "mozilla/SchedulerGroup.h"
 #include "nsINamed.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 nsresult
-DispatcherTrait::Dispatch(const char* aName,
-                          TaskCategory aCategory,
+DispatcherTrait::Dispatch(TaskCategory aCategory,
                           already_AddRefed<nsIRunnable>&& aRunnable)
 {
-  return SchedulerGroup::UnlabeledDispatch(aName, aCategory, Move(aRunnable));
+  return SchedulerGroup::UnlabeledDispatch(aCategory, Move(aRunnable));
 }
 
 nsISerialEventTarget*
 DispatcherTrait::EventTargetFor(TaskCategory aCategory) const
 {
   return GetMainThreadSerialEventTarget();
 }
 
--- a/dom/base/DispatcherTrait.h
+++ b/dom/base/DispatcherTrait.h
@@ -21,18 +21,17 @@ class TabGroup;
 // This trait should be attached to classes like nsIGlobalObject and nsIDocument
 // that have a DocGroup or TabGroup attached to them. The methods here should
 // delegate to the DocGroup or TabGroup. We can't use the Dispatcher class
 // directly because it inherits from nsISupports.
 class DispatcherTrait {
 public:
   // This method may or may not be safe off of the main thread. For nsIDocument
   // it is safe. For nsIGlobalWindow it is not safe.
-  virtual nsresult Dispatch(const char* aName,
-                            TaskCategory aCategory,
+  virtual nsresult Dispatch(TaskCategory aCategory,
                             already_AddRefed<nsIRunnable>&& aRunnable);
 
   // This method may or may not be safe off of the main thread. For nsIDocument
   // it is safe. For nsIGlobalWindow it is not safe. The nsISerialEventTarget can
   // always be used off the main thread.
   virtual nsISerialEventTarget* EventTargetFor(TaskCategory aCategory) const;
 
   // Must be called on the main thread. The AbstractThread can always be used
--- a/dom/base/DocGroup.cpp
+++ b/dom/base/DocGroup.cpp
@@ -51,21 +51,20 @@ DocGroup::~DocGroup()
     nsIEventTarget* target = EventTargetFor(TaskCategory::Other);
     NS_ProxyRelease("DocGroup::mReactionsStack", target, mReactionsStack.forget());
   }
 
   mTabGroup->mDocGroups.RemoveEntry(mKey);
 }
 
 nsresult
-DocGroup::Dispatch(const char* aName,
-                   TaskCategory aCategory,
+DocGroup::Dispatch(TaskCategory aCategory,
                    already_AddRefed<nsIRunnable>&& aRunnable)
 {
-  return mTabGroup->Dispatch(aName, aCategory, Move(aRunnable));
+  return mTabGroup->Dispatch(aCategory, Move(aRunnable));
 }
 
 nsISerialEventTarget*
 DocGroup::EventTargetFor(TaskCategory aCategory) const
 {
   return mTabGroup->EventTargetFor(aCategory);
 }
 
--- a/dom/base/DocGroup.h
+++ b/dom/base/DocGroup.h
@@ -75,18 +75,17 @@ public:
     return mDocuments.begin();
   }
   Iterator end()
   {
     MOZ_ASSERT(NS_IsMainThread());
     return mDocuments.end();
   }
 
-  nsresult Dispatch(const char* aName,
-                    TaskCategory aCategory,
+  nsresult Dispatch(TaskCategory aCategory,
                     already_AddRefed<nsIRunnable>&& aRunnable);
 
   nsISerialEventTarget* EventTargetFor(TaskCategory aCategory) const;
 
   AbstractThread*
   AbstractMainThreadFor(TaskCategory aCategory);
 
   // Ensure that it's valid to access the DocGroup at this time.
--- a/dom/base/Selection.cpp
+++ b/dom/base/Selection.cpp
@@ -47,16 +47,17 @@
 
 #include "nsPresContext.h"
 #include "nsIPresShell.h"
 #include "nsCaret.h"
 
 #include "nsITimer.h"
 #include "nsIDOMDocument.h"
 #include "nsIDocument.h"
+#include "nsINamed.h"
 
 #include "nsISelectionController.h"//for the enums
 #include "nsAutoCopyListener.h"
 #include "SelectionChangeListener.h"
 #include "nsCopySupport.h"
 #include "nsIClipboard.h"
 #include "nsIFrameInlines.h"
 #include "nsRefreshDriver.h"
@@ -191,16 +192,17 @@ struct CachedOffsetForFrame {
 
   nsPoint      mCachedFrameOffset;      // cached frame offset
   nsIFrame*    mLastCaretFrame;         // store the frame the caret was last drawn in.
   int32_t      mLastContentOffset;      // store last content offset
   bool mCanCacheFrameOffset;    // cached frame offset is valid?
 };
 
 class nsAutoScrollTimer final : public nsITimerCallback
+                              , public nsINamed
 {
 public:
 
   NS_DECL_ISUPPORTS
 
   nsAutoScrollTimer()
   : mFrameSelection(0), mSelection(0), mPresContext(0), mPoint(0,0), mDelay(30)
   {
@@ -275,16 +277,22 @@ public:
       }
 
       NS_ASSERTION(frame->PresContext() == mPresContext, "document mismatch?");
       mSelection->DoAutoScroll(frame, pt);
     }
     return NS_OK;
   }
 
+  NS_IMETHOD GetName(nsACString& aName) override
+  {
+    aName.AssignLiteral("nsAutoScrollTimer");
+    return NS_OK;
+  }
+
 protected:
   virtual ~nsAutoScrollTimer()
   {
     if (mTimer) {
       mTimer->Cancel();
     }
   }
 
@@ -294,17 +302,17 @@ private:
   nsPresContext *mPresContext;
   // relative to mPresContext's root frame
   nsPoint mPoint;
   nsCOMPtr<nsITimer> mTimer;
   nsCOMPtr<nsIContent> mContent;
   uint32_t mDelay;
 };
 
-NS_IMPL_ISUPPORTS(nsAutoScrollTimer, nsITimerCallback)
+NS_IMPL_ISUPPORTS(nsAutoScrollTimer, nsITimerCallback, nsINamed)
 
 nsresult NS_NewDomSelection(nsISelection **aDomSelection)
 {
   Selection* rlist = new Selection;
   *aDomSelection = (nsISelection *)rlist;
   NS_ADDREF(rlist);
   return NS_OK;
 }
--- a/dom/base/TimeoutExecutor.cpp
+++ b/dom/base/TimeoutExecutor.cpp
@@ -239,16 +239,10 @@ TimeoutExecutor::Notify(nsITimer* aTimer
 
 NS_IMETHODIMP
 TimeoutExecutor::GetName(nsACString& aNameOut)
 {
   aNameOut.AssignLiteral("TimeoutExecutor Runnable");
   return NS_OK;
 }
 
-NS_IMETHODIMP
-TimeoutExecutor::SetName(const char* aName)
-{
-  return NS_ERROR_NOT_IMPLEMENTED;
-}
-
 } // namespace dom
 } // namespace mozilla
--- a/dom/base/TimeoutManager.cpp
+++ b/dom/base/TimeoutManager.cpp
@@ -5,16 +5,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "TimeoutManager.h"
 #include "nsGlobalWindow.h"
 #include "mozilla/Logging.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/ThrottledEventQueue.h"
 #include "mozilla/TimeStamp.h"
+#include "nsIDocShell.h"
+#include "nsINamed.h"
 #include "nsITimeoutHandler.h"
 #include "mozilla/dom/TabGroup.h"
 #include "OrderedTimeoutIterator.h"
 #include "TimeoutExecutor.h"
 #include "TimeoutBudgetManager.h"
 #include "mozilla/net/WebSocketEventService.h"
 #include "mozilla/MediaManager.h"
 
@@ -102,72 +104,29 @@ TimeoutManager::IsBackground() const
 }
 
 bool
 TimeoutManager::IsActive() const
 {
   // A window is considered active if:
   // * It is a chrome window
   // * It is playing audio
-  // * If it is using user media
-  // * If it is using WebRTC
-  // * If it has open WebSockets
-  // * If it has active IndexedDB databases
   //
   // Note that a window can be considered active if it is either in the
   // foreground or in the background.
 
   if (mWindow.IsChromeWindow()) {
     return true;
   }
 
   // Check if we're playing audio
   if (mWindow.AsInner()->IsPlayingAudio()) {
     return true;
   }
 
-  // Check if there are any active IndexedDB databases
-  if (mWindow.AsInner()->HasActiveIndexedDBDatabases()) {
-    return true;
-  }
-
-  // Check if we have active GetUserMedia
-  if (MediaManager::Exists() &&
-      MediaManager::Get()->IsWindowStillActive(mWindow.WindowID())) {
-    return true;
-  }
-
-  bool active = false;
-#if 0
-  // Check if we have active PeerConnections This doesn't actually
-  // work, since we sometimes call IsActive from Resume, which in turn
-  // is sometimes called from nsGlobalWindow::LeaveModalState. The
-  // problem here is that LeaveModalState can be called with pending
-  // exeptions on the js context, and the following call to
-  // HasActivePeerConnection is a JS call, which will assert on that
-  // exception. Also, calling JS is expensive so we should try to fix
-  // this in some other way.
-  nsCOMPtr<IPeerConnectionManager> pcManager =
-    do_GetService(IPEERCONNECTION_MANAGER_CONTRACTID);
-
-  if (pcManager && NS_SUCCEEDED(pcManager->HasActivePeerConnection(
-                     mWindow.WindowID(), &active)) &&
-      active) {
-    return true;
-  }
-#endif // MOZ_WEBRTC
-
-  // Check if we have web sockets
-  RefPtr<WebSocketEventService> eventService = WebSocketEventService::Get();
-  if (eventService &&
-      NS_SUCCEEDED(eventService->HasListenerFor(mWindow.WindowID(), &active)) &&
-      active) {
-    return true;
-  }
-
   return false;
 }
 
 
 uint32_t
 TimeoutManager::CreateFiringId()
 {
   uint32_t id = mNextFiringId;
@@ -241,17 +200,17 @@ TimeoutManager::MinSchedulingDelay() con
   // mExecutionBudget is -15ms
   // factor is 0.01, which is 1 ms/100ms
   // delay is 1000ms
   // then we will compute the minimum delay:
   // max(1000, - (- 15) * 1/0.01) = max(1000, 1500) = 1500
   TimeDuration unthrottled =
     isBackground ? TimeDuration::FromMilliseconds(gMinBackgroundTimeoutValue)
                  : TimeDuration();
-  if (mBudgetThrottleTimeouts && mExecutionBudget < TimeDuration()) {
+  if (BudgetThrottlingEnabled() && mExecutionBudget < TimeDuration()) {
     // Only throttle if execution budget is less than 0
     double factor = 1.0 / GetRegenerationFactor(mWindow.IsBackgroundInternal());
     return TimeDuration::Min(
       TimeDuration::FromMilliseconds(gBudgetThrottlingMaxDelay),
       TimeDuration::Max(unthrottled, -mExecutionBudget.MultDouble(factor)));
   }
   //
   return unthrottled;
@@ -368,17 +327,17 @@ TimeoutManager::UpdateBudget(const TimeS
 
   // The budget is adjusted by increasing it with the time since the
   // last budget update factored with the regeneration rate. If a
   // runnable has executed, subtract that duration from the
   // budget. The budget updated without consideration of wether the
   // window is active or not. If throttling is enabled and the window
   // is active and then becomes inactive, an overdrawn budget will
   // still be counted against the minimum delay.
-  if (mBudgetThrottleTimeouts) {
+  if (BudgetThrottlingEnabled()) {
     bool isBackground = mWindow.IsBackgroundInternal();
     double factor = GetRegenerationFactor(isBackground);
     TimeDuration regenerated = (aNow - mLastBudgetUpdate).MultDouble(factor);
     // Clamp the budget to the maximum allowed budget.
     mExecutionBudget = TimeDuration::Min(
       GetMaxBudget(isBackground), mExecutionBudget - aDuration + regenerated);
   }
   mLastBudgetUpdate = aNow;
@@ -1204,48 +1163,115 @@ TimeoutManager::IsTimeoutTracking(uint32
   return mTrackingTimeouts.ForEachAbortable([&](Timeout* aTimeout) {
       return aTimeout->mTimeoutId == aTimeoutId;
     });
 }
 
 namespace {
 
 class ThrottleTimeoutsCallback final : public nsITimerCallback
+                                     , public nsINamed
 {
 public:
   explicit ThrottleTimeoutsCallback(nsGlobalWindow* aWindow)
     : mWindow(aWindow)
   {
     MOZ_DIAGNOSTIC_ASSERT(aWindow->IsInnerWindow());
   }
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSITIMERCALLBACK
 
+  NS_IMETHOD GetName(nsACString& aName) override
+  {
+    aName.AssignLiteral("ThrottleTimeoutsCallback");
+    return NS_OK;
+  }
+
 private:
   ~ThrottleTimeoutsCallback() {}
 
 private:
   // The strong reference here keeps the Window and hence the TimeoutManager
   // object itself alive.
   RefPtr<nsGlobalWindow> mWindow;
 };
 
-NS_IMPL_ISUPPORTS(ThrottleTimeoutsCallback, nsITimerCallback)
+NS_IMPL_ISUPPORTS(ThrottleTimeoutsCallback, nsITimerCallback, nsINamed)
 
 NS_IMETHODIMP
 ThrottleTimeoutsCallback::Notify(nsITimer* aTimer)
 {
   mWindow->AsInner()->TimeoutManager().StartThrottlingTimeouts();
   mWindow = nullptr;
   return NS_OK;
 }
 
 }
 
+bool
+TimeoutManager::BudgetThrottlingEnabled() const
+{
+  // A window can be throttled using budget if
+  // * It isn't active
+  // * If it isn't using user media
+  // * If it isn't using WebRTC
+  // * If it hasn't got open WebSockets
+  // * If it hasn't got active IndexedDB databases
+  //
+  // Note that we allow both foreground and background to be
+  // considered for budget throttling. What determines if they are if
+  // budget throttling is enabled is the regeneration factor.
+
+  if (!mBudgetThrottleTimeouts || IsActive()) {
+    return false;
+  }
+
+  // Check if there are any active IndexedDB databases
+  if (mWindow.AsInner()->HasActiveIndexedDBDatabases()) {
+    return false;
+  }
+
+  // Check if we have active GetUserMedia
+  if (MediaManager::Exists() &&
+      MediaManager::Get()->IsWindowStillActive(mWindow.WindowID())) {
+    return false;
+  }
+
+  bool active = false;
+#if 0
+  // Check if we have active PeerConnections This doesn't actually
+  // work, since we sometimes call IsActive from Resume, which in turn
+  // is sometimes called from nsGlobalWindow::LeaveModalState. The
+  // problem here is that LeaveModalState can be called with pending
+  // exeptions on the js context, and the following call to
+  // HasActivePeerConnection is a JS call, which will assert on that
+  // exception. Also, calling JS is expensive so we should try to fix
+  // this in some other way.
+  nsCOMPtr<IPeerConnectionManager> pcManager =
+    do_GetService(IPEERCONNECTION_MANAGER_CONTRACTID);
+
+  if (pcManager && NS_SUCCEEDED(pcManager->HasActivePeerConnection(
+                     mWindow.WindowID(), &active)) &&
+      active) {
+    return false;
+  }
+#endif // MOZ_WEBRTC
+
+  // Check if we have web sockets
+  RefPtr<WebSocketEventService> eventService = WebSocketEventService::Get();
+  if (eventService &&
+      NS_SUCCEEDED(eventService->HasListenerFor(mWindow.WindowID(), &active)) &&
+      active) {
+    return false;
+  }
+
+  return true;
+}
+
 void
 TimeoutManager::StartThrottlingTimeouts()
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_DIAGNOSTIC_ASSERT(mThrottleTimeoutsTimer);
 
   MOZ_LOG(gLog, LogLevel::Debug,
           ("TimeoutManager %p started to throttle tracking timeouts\n", this));
--- a/dom/base/TimeoutManager.h
+++ b/dom/base/TimeoutManager.h
@@ -143,16 +143,18 @@ private:
                          const TimeStamp& aNow = TimeStamp::Now());
 
   void RecordExecution(Timeout* aRunningTimeout,
                        Timeout* aTimeout);
 
   void UpdateBudget(const TimeStamp& aNow,
                     const TimeDuration& aDuration = TimeDuration());
 
+  bool BudgetThrottlingEnabled() const;
+
 private:
   struct Timeouts {
     explicit Timeouts(const TimeoutManager& aManager)
       : mManager(aManager)
     {
     }
 
     // Insert aTimeout into the list, before all timeouts that would
--- a/dom/base/nsContentList.cpp
+++ b/dom/base/nsContentList.cpp
@@ -244,17 +244,17 @@ NS_GetContentList(nsINode* aRootNode,
   }
 
   sRecentlyUsedContentLists[recentlyUsedCacheIndex] = list;
   return list.forget();
 }
 
 #ifdef DEBUG
 const nsCacheableFuncStringContentList::ContentListType
-  nsCacheableFuncStringNodeList::sType = nsCacheableFuncStringContentList::eNodeList;
+  nsCachableElementsByNameNodeList::sType = nsCacheableFuncStringContentList::eNodeList;
 const nsCacheableFuncStringContentList::ContentListType
   nsCacheableFuncStringHTMLCollection::sType = nsCacheableFuncStringContentList::eHTMLCollection;
 #endif
 
 // Hashtable for storing nsCacheableFuncStringContentList
 static PLDHashTable* gFuncStringContentListHashTable;
 
 struct FuncStringContentListHashEntry : public PLDHashEntryHdr
@@ -333,43 +333,31 @@ GetFuncStringContentList(nsINode* aRootN
     }
   }
 
   // Don't cache these lists globally
 
   return list.forget();
 }
 
+// Explicit instantiations to avoid link errors
+template
 already_AddRefed<nsContentList>
-NS_GetFuncStringNodeList(nsINode* aRootNode,
-                         nsContentListMatchFunc aFunc,
-                         nsContentListDestroyFunc aDestroyFunc,
-                         nsFuncStringContentListDataAllocator aDataAllocator,
-                         const nsAString& aString)
-{
-  return GetFuncStringContentList<nsCacheableFuncStringNodeList>(aRootNode,
-                                                                 aFunc,
-                                                                 aDestroyFunc,
-                                                                 aDataAllocator,
-                                                                 aString);
-}
-
+GetFuncStringContentList<nsCachableElementsByNameNodeList>(nsINode* aRootNode,
+                                                           nsContentListMatchFunc aFunc,
+                                                           nsContentListDestroyFunc aDestroyFunc,
+                                                           nsFuncStringContentListDataAllocator aDataAllocator,
+                                                           const nsAString& aString);
+template
 already_AddRefed<nsContentList>
-NS_GetFuncStringHTMLCollection(nsINode* aRootNode,
-                               nsContentListMatchFunc aFunc,
-                               nsContentListDestroyFunc aDestroyFunc,
-                               nsFuncStringContentListDataAllocator aDataAllocator,
-                               const nsAString& aString)
-{
-  return GetFuncStringContentList<nsCacheableFuncStringHTMLCollection>(aRootNode,
-                                                                       aFunc,
-                                                                       aDestroyFunc,
-                                                                       aDataAllocator,
-                                                                       aString);
-}
+GetFuncStringContentList<nsCacheableFuncStringHTMLCollection>(nsINode* aRootNode,
+                                                              nsContentListMatchFunc aFunc,
+                                                              nsContentListDestroyFunc aDestroyFunc,
+                                                              nsFuncStringContentListDataAllocator aDataAllocator,
+                                                              const nsAString& aString);
 
 //-----------------------------------------------------
 // nsContentList implementation
 
 nsContentList::nsContentList(nsINode* aRootNode,
                              int32_t aMatchNameSpaceId,
                              nsIAtom* aHTMLMatchAtom,
                              nsIAtom* aXMLMatchAtom,
@@ -1071,24 +1059,43 @@ nsContentList::AssertInSync()
     }
   }
 
   NS_ASSERTION(cnt == mElements.Length(), "Too few elements");
 }
 #endif
 
 //-----------------------------------------------------
-// nsCacheableFuncStringNodeList
+// nsCachableElementsByNameNodeList
 
 JSObject*
-nsCacheableFuncStringNodeList::WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto)
+nsCachableElementsByNameNodeList::WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto)
 {
   return NodeListBinding::Wrap(cx, this, aGivenProto);
 }
 
+void
+nsCachableElementsByNameNodeList::AttributeChanged(nsIDocument* aDocument,
+                                                   Element* aElement,
+                                                   int32_t aNameSpaceID,
+                                                   nsIAtom* aAttribute,
+                                                   int32_t aModType,
+                                                   const nsAttrValue* aOldValue)
+{
+  // No need to rebuild the list if the changed attribute is not the name
+  // attribute.
+  if (aAttribute != nsGkAtoms::name) {
+    return;
+  }
+
+  nsCacheableFuncStringContentList::AttributeChanged(aDocument, aElement,
+                                                     aNameSpaceID, aAttribute,
+                                                     aModType, aOldValue);
+}
+
 //-----------------------------------------------------
 // nsCacheableFuncStringHTMLCollection
 
 JSObject*
 nsCacheableFuncStringHTMLCollection::WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto)
 {
   return HTMLCollectionBinding::Wrap(cx, this, aGivenProto);
 }
--- a/dom/base/nsContentList.h
+++ b/dom/base/nsContentList.h
@@ -541,33 +541,35 @@ protected:
   virtual void RemoveFromCaches() override {
     RemoveFromFuncStringHashtable();
   }
   void RemoveFromFuncStringHashtable();
 
   nsString mString;
 };
 
-class nsCacheableFuncStringNodeList
+class nsCachableElementsByNameNodeList
   : public nsCacheableFuncStringContentList
 {
 public:
-  nsCacheableFuncStringNodeList(nsINode* aRootNode,
-                                nsContentListMatchFunc aFunc,
-                                nsContentListDestroyFunc aDestroyFunc,
-                                nsFuncStringContentListDataAllocator aDataAllocator,
-                                const nsAString& aString)
+  nsCachableElementsByNameNodeList(nsINode* aRootNode,
+                                   nsContentListMatchFunc aFunc,
+                                   nsContentListDestroyFunc aDestroyFunc,
+                                   nsFuncStringContentListDataAllocator aDataAllocator,
+                                   const nsAString& aString)
     : nsCacheableFuncStringContentList(aRootNode, aFunc, aDestroyFunc,
                                        aDataAllocator, aString)
   {
 #ifdef DEBUG
     mType = eNodeList;
 #endif
   }
 
+  NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
+
   virtual JSObject* WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto) override;
 
 #ifdef DEBUG
   static const ContentListType sType;
 #endif
 };
 
 class nsCacheableFuncStringHTMLCollection
--- a/dom/base/nsContentListDeclarations.h
+++ b/dom/base/nsContentListDeclarations.h
@@ -52,22 +52,17 @@ typedef void* (*nsFuncStringContentListD
 // elements in HTML documents and aTagname against everything else.
 // For any other value of aMatchNameSpaceId, the list will match
 // aTagname against all elements.
 already_AddRefed<nsContentList>
 NS_GetContentList(nsINode* aRootNode,
                   int32_t aMatchNameSpaceId,
                   const nsAString& aTagname);
 
+template<class ListType>
 already_AddRefed<nsContentList>
-NS_GetFuncStringNodeList(nsINode* aRootNode,
+GetFuncStringContentList(nsINode* aRootNode,
                          nsContentListMatchFunc aFunc,
                          nsContentListDestroyFunc aDestroyFunc,
                          nsFuncStringContentListDataAllocator aDataAllocator,
                          const nsAString& aString);
-already_AddRefed<nsContentList>
-NS_GetFuncStringHTMLCollection(nsINode* aRootNode,
-                               nsContentListMatchFunc aFunc,
-                               nsContentListDestroyFunc aDestroyFunc,
-                               nsFuncStringContentListDataAllocator aDataAllocator,
-                               const nsAString& aString);
 
 #endif // nsContentListDeclarations_h
--- a/dom/base/nsContentSink.cpp
+++ b/dom/base/nsContentSink.cpp
@@ -1654,14 +1654,8 @@ nsContentSink::NotifyDocElementCreated(n
 }
 
 NS_IMETHODIMP
 nsContentSink::GetName(nsACString& aName)
 {
   aName.AssignASCII("nsContentSink_timer");
   return NS_OK;
 }
-
-NS_IMETHODIMP
-nsContentSink::SetName(const char* aName)
-{
-  return NS_ERROR_NOT_IMPLEMENTED;
-}
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -50,16 +50,17 @@
 
 class imgICache;
 class imgIContainer;
 class imgINotificationObserver;
 class imgIRequest;
 class imgLoader;
 class imgRequestProxy;
 class nsAutoScriptBlockerSuppressNodeRemoved;
+class nsCacheableFuncStringHTMLCollection;
 class nsHtml5StringParser;
 class nsIChannel;
 class nsIConsoleService;
 class nsIContent;
 class nsIContentPolicy;
 class nsIContentSecurityPolicy;
 class nsIDocShellTreeItem;
 class nsIDocumentLoaderFactory;
@@ -2078,20 +2079,21 @@ public:
    * Utility method for getElementsByClassName.  aRootNode is the node (either
    * document or element), which getElementsByClassName was called on.
    */
   static already_AddRefed<nsContentList>
   GetElementsByClassName(nsINode* aRootNode, const nsAString& aClasses)
   {
     NS_PRECONDITION(aRootNode, "Must have root node");
 
-    return NS_GetFuncStringHTMLCollection(aRootNode, MatchClassNames,
-                                          DestroyClassNameArray,
-                                          AllocClassMatchingInfo,
-                                          aClasses);
+    return GetFuncStringContentList<nsCacheableFuncStringHTMLCollection>(aRootNode,
+                                                                         MatchClassNames,
+                                                                         DestroyClassNameArray,
+                                                                         AllocClassMatchingInfo,
+                                                                         aClasses);
   }
 
   /**
    * Returns a presshell for this document, if there is one. This will be
    * aDoc's direct presshell if there is one, otherwise we'll look at all
    * ancestor documents to try to find a presshell, so for example this can
    * still find a presshell for documents in display:none frames that have
    * no presentation. So you have to be careful how you use this presshell ---
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -1577,17 +1577,17 @@ nsWindowSH::GlobalResolve(nsGlobalWindow
   // this is called during Window setup when the Document isn't necessarily
   // hooked up yet.
   if ((id == XPCJSRuntime::Get()->GetStringID(XPCJSContext::IDX_CONTROLLERS) ||
        id == XPCJSRuntime::Get()->GetStringID(XPCJSContext::IDX_CONTROLLERS_CLASS)) &&
       !xpc::IsXrayWrapper(obj) &&
       !nsContentUtils::IsSystemPrincipal(nsContentUtils::ObjectPrincipal(obj)))
   {
     if (aWin->GetDoc()) {
-      aWin->GetDoc()->WarnOnceAbout(nsIDocument::eWindow_Controllers);
+      aWin->GetDoc()->WarnOnceAbout(nsIDocument::eWindow_Cc_ontrollers);
     }
     const JSClass* clazz;
     if (id == XPCJSRuntime::Get()->GetStringID(XPCJSContext::IDX_CONTROLLERS)) {
       clazz = &XULControllersShimClass;
     } else {
       clazz = &ControllersShimClass;
     }
     MOZ_ASSERT(JS_IsGlobalObject(obj));
--- a/dom/base/nsDeprecatedOperationList.h
+++ b/dom/base/nsDeprecatedOperationList.h
@@ -32,17 +32,17 @@ DEPRECATED_OPERATION(GetSetUserData)
 DEPRECATED_OPERATION(MozGetAsFile)
 DEPRECATED_OPERATION(UseOfCaptureEvents)
 DEPRECATED_OPERATION(UseOfReleaseEvents)
 DEPRECATED_OPERATION(UseOfDOM3LoadMethod)
 DEPRECATED_OPERATION(ChromeUseOfDOM3LoadMethod)
 DEPRECATED_OPERATION(ShowModalDialog)
 DEPRECATED_OPERATION(Window_Content)
 DEPRECATED_OPERATION(SyncXMLHttpRequest)
-DEPRECATED_OPERATION(Window_Controllers)
+DEPRECATED_OPERATION(Window_Cc_ontrollers)
 DEPRECATED_OPERATION(ImportXULIntoContent)
 DEPRECATED_OPERATION(PannerNodeDoppler)
 DEPRECATED_OPERATION(NavigatorGetUserMedia)
 DEPRECATED_OPERATION(WebrtcDeprecatedPrefix)
 DEPRECATED_OPERATION(RTCPeerConnectionGetStreams)
 DEPRECATED_OPERATION(AppCache)
 DEPRECATED_OPERATION(PrefixedImageSmoothingEnabled)
 DEPRECATED_OPERATION(PrefixedFullscreenAPI)
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -3045,25 +3045,24 @@ nsIDocument::GetDocGroup() const
     // XXX: Check that the TabGroup is correct as well!
   }
 #endif
 
   return mDocGroup;
 }
 
 nsresult
-nsIDocument::Dispatch(const char* aName,
-                      TaskCategory aCategory,
+nsIDocument::Dispatch(TaskCategory aCategory,
                       already_AddRefed<nsIRunnable>&& aRunnable)
 {
   // Note that this method may be called off the main thread.
   if (mDocGroup) {
-    return mDocGroup->Dispatch(aName, aCategory, Move(aRunnable));
-  }
-  return DispatcherTrait::Dispatch(aName, aCategory, Move(aRunnable));
+    return mDocGroup->Dispatch(aCategory, Move(aRunnable));
+  }
+  return DispatcherTrait::Dispatch(aCategory, Move(aRunnable));
 }
 
 nsISerialEventTarget*
 nsIDocument::EventTargetFor(TaskCategory aCategory) const
 {
   if (mDocGroup) {
     return mDocGroup->EventTargetFor(aCategory);
   }
@@ -4454,18 +4453,17 @@ nsDocument::SetStyleSheetApplicableState
   if (!mSSApplicableStateNotificationPending) {
     MOZ_RELEASE_ASSERT(NS_IsMainThread());
     nsCOMPtr<nsIRunnable> notification =
       NewRunnableMethod("nsDocument::NotifyStyleSheetApplicableStateChanged",
                         this,
                         &nsDocument::NotifyStyleSheetApplicableStateChanged);
     mSSApplicableStateNotificationPending =
       NS_SUCCEEDED(
-        Dispatch("nsDocument::NotifyStyleSheetApplicableStateChanged",
-                 TaskCategory::Other, notification.forget()));
+        Dispatch(TaskCategory::Other, notification.forget()));
   }
 }
 
 void
 nsDocument::NotifyStyleSheetApplicableStateChanged()
 {
   mSSApplicableStateNotificationPending = false;
   nsCOMPtr<nsIObserverService> observerService =
@@ -5402,17 +5400,17 @@ nsDocument::UnblockDOMContentLoaded()
 
   MOZ_ASSERT(mReadyState == READYSTATE_INTERACTIVE);
   if (!mSynchronousDOMContentLoaded) {
     MOZ_RELEASE_ASSERT(NS_IsMainThread());
     nsCOMPtr<nsIRunnable> ev =
       NewRunnableMethod("nsDocument::DispatchContentLoadedEvents",
                         this,
                         &nsDocument::DispatchContentLoadedEvents);
-    Dispatch("nsDocument::DispatchContentLoadedEvents", TaskCategory::Other, ev.forget());
+    Dispatch(TaskCategory::Other, ev.forget());
   } else {
     DispatchContentLoadedEvents();
   }
 }
 
 void
 nsDocument::ContentStateChanged(nsIContent* aContent, EventStates aStateMask)
 {
@@ -7042,18 +7040,17 @@ nsDocument::NotifyPossibleTitleChange(bo
   if (mPendingTitleChangeEvent.IsPending())
     return;
 
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
   RefPtr<nsRunnableMethod<nsDocument, void, false>> event =
     NewNonOwningRunnableMethod("nsDocument::DoNotifyPossibleTitleChange",
                                this,
                                &nsDocument::DoNotifyPossibleTitleChange);
-  nsresult rv = Dispatch("nsDocument::DoNotifyPossibleTitleChange",
-                         TaskCategory::Other, do_AddRef(event));
+  nsresult rv = Dispatch(TaskCategory::Other, do_AddRef(event));
   if (NS_SUCCEEDED(rv)) {
     mPendingTitleChangeEvent = Move(event);
   }
 }
 
 void
 nsDocument::DoNotifyPossibleTitleChange()
 {
@@ -8889,17 +8886,17 @@ private:
 };
 
 void
 nsDocument::PostUnblockOnloadEvent()
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
   nsCOMPtr<nsIRunnable> evt = new nsUnblockOnloadEvent(this);
   nsresult rv =
-    Dispatch("nsUnblockOnloadEvent", TaskCategory::Other, evt.forget());
+    Dispatch(TaskCategory::Other, evt.forget());
   if (NS_SUCCEEDED(rv)) {
     // Stabilize block count so we don't post more events while this one is up
     ++mOnloadBlockCount;
   } else {
     NS_WARNING("failed to dispatch nsUnblockOnloadEvent");
   }
 }
 
@@ -9800,17 +9797,17 @@ void
 nsDocument::UnsuppressEventHandlingAndFireEvents(bool aFireEvents)
 {
   nsTArray<nsCOMPtr<nsIDocument>> documents;
   GetAndUnsuppressSubDocuments(this, &documents);
 
   if (aFireEvents) {
     MOZ_RELEASE_ASSERT(NS_IsMainThread());
     nsCOMPtr<nsIRunnable> ded = new nsDelayedEventDispatcher(documents);
-    Dispatch("nsDelayedEventDispatcher", TaskCategory::Other, ded.forget());
+    Dispatch(TaskCategory::Other, ded.forget());
   } else {
     FireOrClearDelayedEvents(documents, false);
   }
 }
 
 nsISupports*
 nsDocument::GetCurrentContentSink()
 {
@@ -10953,17 +10950,17 @@ private:
 };
 
 /* static */ void
 nsIDocument::AsyncExitFullscreen(nsIDocument* aDoc)
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
   nsCOMPtr<nsIRunnable> exit = new nsCallExitFullscreen(aDoc);
   if (aDoc) {
-    aDoc->Dispatch("nsCallExitFullscreen", TaskCategory::Other, exit.forget());
+    aDoc->Dispatch(TaskCategory::Other, exit.forget());
   } else {
     NS_DispatchToCurrentThread(exit.forget());
   }
 }
 
 static bool
 CountFullscreenSubDocuments(nsIDocument* aDoc, void* aData)
 {
@@ -11234,17 +11231,17 @@ nsDocument::AsyncRequestFullScreen(Uniqu
     MOZ_ASSERT_UNREACHABLE(
       "Must pass non-null element to nsDocument::AsyncRequestFullScreen");
     return;
   }
 
   // Request full-screen asynchronously.
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
   nsCOMPtr<nsIRunnable> event = new nsCallRequestFullScreen(Move(aRequest));
-  Dispatch("nsCallRequestFullScreen", TaskCategory::Other, event.forget());
+  Dispatch(TaskCategory::Other, event.forget());
 }
 
 void
 nsIDocument::DispatchFullscreenError(const char* aMessage)
 {
   RefPtr<AsyncEventDispatcher> asyncDispatcher =
     new AsyncEventDispatcher(this,
                              NS_LITERAL_STRING("fullscreenerror"),
@@ -12111,17 +12108,17 @@ nsDocument::RequestPointerLock(Element* 
     DispatchPointerLockError(this, msg);
     return;
   }
 
   bool userInputOrSystemCaller = EventStateManager::IsHandlingUserInput() ||
                                  aCallerType == CallerType::System;
   nsCOMPtr<nsIRunnable> request =
     new PointerLockRequest(aElement, userInputOrSystemCaller);
-  Dispatch("PointerLockRequest", TaskCategory::Other, request.forget());
+  Dispatch(TaskCategory::Other, request.forget());
 }
 
 bool
 nsDocument::SetPointerLock(Element* aElement, int aCursorStyle)
 {
   MOZ_ASSERT(!aElement || aElement->OwnerDoc() == this,
              "We should be either unlocking pointer (aElement is nullptr), "
              "or locking pointer to an element in this document");
@@ -12296,17 +12293,17 @@ nsDocument::GetVisibilityState() const
 
 /* virtual */ void
 nsDocument::PostVisibilityUpdateEvent()
 {
   nsCOMPtr<nsIRunnable> event =
     NewRunnableMethod("nsDocument::UpdateVisibilityState",
                       this,
                       &nsDocument::UpdateVisibilityState);
-  Dispatch("nsDocument::UpdateVisibilityState", TaskCategory::Other, event.forget());
+  Dispatch(TaskCategory::Other, event.forget());
 }
 
 void
 nsDocument::MaybeActiveMediaComponents()
 {
   if (!mWindow) {
     return;
   }
@@ -12843,18 +12840,17 @@ nsDocument::ScheduleIntersectionObserver
   if (mIntersectionObservers.IsEmpty()) {
     return;
   }
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
   nsCOMPtr<nsIRunnable> notification =
     NewRunnableMethod("nsDocument::NotifyIntersectionObservers",
                       this,
                       &nsDocument::NotifyIntersectionObservers);
-  Dispatch("nsDocument::IntersectionObserverNotification", TaskCategory::Other,
-           notification.forget());
+  Dispatch(TaskCategory::Other, notification.forget());
 }
 
 void
 nsDocument::NotifyIntersectionObservers()
 {
   nsTArray<RefPtr<DOMIntersectionObserver>> observers(mIntersectionObservers.Count());
   for (auto iter = mIntersectionObservers.Iter(); !iter.Done(); iter.Next()) {
     DOMIntersectionObserver* observer = iter.Get()->GetKey();
@@ -13135,18 +13131,17 @@ nsIDocument::RebuildUserFontSet()
   // which starts font loads, whose completion causes another style
   // change reflow).
   if (!mPostedFlushUserFontSet) {
     MOZ_RELEASE_ASSERT(NS_IsMainThread());
     nsCOMPtr<nsIRunnable> ev =
       NewRunnableMethod("nsIDocument::HandleRebuildUserFontSet",
                         this,
                         &nsIDocument::HandleRebuildUserFontSet);
-    if (NS_SUCCEEDED(Dispatch("nsIDocument::HandleRebuildUserFontSet",
-                              TaskCategory::Other, ev.forget()))) {
+    if (NS_SUCCEEDED(Dispatch(TaskCategory::Other, ev.forget()))) {
       mPostedFlushUserFontSet = true;
     }
   }
 }
 
 FontFaceSet*
 nsIDocument::Fonts()
 {
--- a/dom/base/nsGkAtomList.h
+++ b/dom/base/nsGkAtomList.h
@@ -1029,16 +1029,17 @@ GK_ATOM(ontransitionrun, "ontransitionru
 GK_ATOM(ontransitionstart, "ontransitionstart")
 GK_ATOM(onunderflow, "onunderflow")
 GK_ATOM(onunload, "onunload")
 GK_ATOM(onupdatefound, "onupdatefound")
 GK_ATOM(onupdateready, "onupdateready")
 GK_ATOM(onupgradeneeded, "onupgradeneeded")
 GK_ATOM(onussdreceived, "onussdreceived")
 GK_ATOM(onversionchange, "onversionchange")
+GK_ATOM(onvisibilitychange, "onvisibilitychange")
 GK_ATOM(onvoicechange, "onvoicechange")
 GK_ATOM(onvoiceschanged, "onvoiceschanged")
 GK_ATOM(onvrdisplayactivate, "onvrdisplayactivate")
 GK_ATOM(onvrdisplayconnect, "onvrdisplayconnect")
 GK_ATOM(onvrdisplaydeactivate, "onvrdisplaydeactivate")
 GK_ATOM(onvrdisplaydisconnect, "onvrdisplaydisconnect")
 GK_ATOM(onvrdisplaypresentchange, "onvrdisplaypresentchange")
 GK_ATOM(onwebkitAnimationEnd, "onwebkitAnimationEnd")
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -19,16 +19,17 @@
 #include "nsIDOMStorageManager.h"
 #include "mozilla/dom/LocalStorage.h"
 #include "mozilla/dom/Storage.h"
 #include "mozilla/dom/IdleRequest.h"
 #include "mozilla/dom/Performance.h"
 #include "mozilla/dom/StorageEvent.h"
 #include "mozilla/dom/StorageEventBinding.h"
 #include "mozilla/dom/StorageNotifierService.h"
+#include "mozilla/dom/StorageUtils.h"
 #include "mozilla/dom/Timeout.h"
 #include "mozilla/dom/TimeoutHandler.h"
 #include "mozilla/dom/TimeoutManager.h"
 #include "mozilla/IntegerPrintfMacros.h"
 #if defined(MOZ_WIDGET_ANDROID)
 #include "mozilla/dom/WindowOrientationObserver.h"
 #endif
 #include "nsDOMOfflineResourceList.h"
@@ -493,17 +494,29 @@ public:
                              bool aPrivateBrowsing) override
   {
     if (mWindow) {
       mWindow->ObserveStorageNotification(aEvent, aStorageType,
                                           aPrivateBrowsing);
     }
   }
 
-  virtual nsIEventTarget*
+  nsIPrincipal*
+  GetPrincipal() const override
+  {
+    return mWindow ? mWindow->GetPrincipal() : nullptr;
+  }
+
+  bool
+  IsPrivateBrowsing() const override
+  {
+    return mWindow ? mWindow->IsPrivateBrowsing() : false;
+  }
+
+  nsIEventTarget*
   GetEventTarget() const override
   {
     return mWindow ? mWindow->EventTargetFor(TaskCategory::Other) : nullptr;
   }
 
 private:
   ~nsGlobalWindowObserver() = default;
 
@@ -676,22 +689,16 @@ NS_INTERFACE_MAP_END
 NS_IMETHODIMP
 IdleRequestExecutor::GetName(nsACString& aName)
 {
     aName.AssignASCII("IdleRequestExecutor");
     return NS_OK;
 }
 
 NS_IMETHODIMP
-IdleRequestExecutor::SetName(const char* aName)
-{
-  return NS_ERROR_NOT_IMPLEMENTED;
-}
-
-NS_IMETHODIMP
 IdleRequestExecutor::Run()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   mDispatched = false;
   if (mWindow) {
     return mWindow->ExecuteIdleRequest(mDeadline);
   }
@@ -9237,17 +9244,17 @@ nsGlobalWindow::PostMessageMozOuter(JSCo
   JS::Rooted<JS::Value> message(aCx, aMessage);
   JS::Rooted<JS::Value> transfer(aCx, aTransfer);
 
   event->Write(aCx, message, transfer, JS::CloneDataPolicy(), aError);
   if (NS_WARN_IF(aError.Failed())) {
     return;
   }
 
-  aError = Dispatch("PostMessageEvent", TaskCategory::Other, event.forget());
+  aError = Dispatch(TaskCategory::Other, event.forget());
 }
 
 void
 nsGlobalWindow::PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage,
                                const nsAString& aTargetOrigin,
                                JS::Handle<JS::Value> aTransfer,
                                nsIPrincipal& aSubjectPrincipal,
                                ErrorResult& aError)
@@ -9289,17 +9296,17 @@ class nsCloseEvent : public Runnable {
   {}
 
 public:
 
   static nsresult
   PostCloseEvent(nsGlobalWindow* aWindow, bool aIndirect) {
     nsCOMPtr<nsIRunnable> ev = new nsCloseEvent(aWindow, aIndirect);
     nsresult rv =
-      aWindow->Dispatch("nsCloseEvent", TaskCategory::Other, ev.forget());
+      aWindow->Dispatch(TaskCategory::Other, ev.forget());
     if (NS_SUCCEEDED(rv))
       aWindow->MaybeForgiveSpamCount();
     return rv;
   }
 
   NS_IMETHOD Run() override {
     if (mWindow) {
       if (mIndirect) {
@@ -9820,18 +9827,17 @@ private:
   nsCString mTopic;
   nsWeakPtr mWindow;
 };
 
 void
 nsGlobalWindow::NotifyWindowIDDestroyed(const char* aTopic)
 {
   nsCOMPtr<nsIRunnable> runnable = new WindowDestroyedEvent(this, mWindowID, aTopic);
-  nsresult rv =
-    Dispatch("WindowDestroyedEvent", TaskCategory::Other, runnable.forget());
+  nsresult rv = Dispatch(TaskCategory::Other, runnable.forget());
   if (NS_SUCCEEDED(rv)) {
     mNotifiedIDDestroyed = true;
   }
 }
 
 // static
 void
 nsGlobalWindow::NotifyDOMWindowFrozen(nsGlobalWindow* aWindow) {
@@ -11089,17 +11095,17 @@ nsGlobalWindow::DispatchAsyncHashchange(
   rv = aNewURI->GetSpec(newSpec);
   NS_ENSURE_SUCCESS(rv, rv);
 
   NS_ConvertUTF8toUTF16 oldWideSpec(oldSpec);
   NS_ConvertUTF8toUTF16 newWideSpec(newSpec);
 
   nsCOMPtr<nsIRunnable> callback =
     new HashchangeCallback(oldWideSpec, newWideSpec, this);
-  return Dispatch("HashchangeCallback", TaskCategory::Other, callback.forget());
+  return Dispatch(TaskCategory::Other, callback.forget());
 }
 
 nsresult
 nsGlobalWindow::FireHashchange(const nsAString &aOldURL,
                                const nsAString &aNewURL)
 {
   MOZ_ASSERT(IsInnerWindow());
 
@@ -11689,18 +11695,17 @@ nsGlobalWindow::NotifyIdleObserver(IdleO
 {
   MOZ_ASSERT(aIdleObserverHolder);
   aIdleObserverHolder->mPrevNotificationIdle = aCallOnidle;
 
   nsCOMPtr<nsIRunnable> caller =
     new NotifyIdleObserverRunnable(aIdleObserverHolder->mIdleObserver,
                                    aIdleObserverHolder->mTimeInS,
                                    aCallOnidle, this);
-  if (NS_FAILED(Dispatch("NotifyIdleObserverRunnable", TaskCategory::Other,
-                         caller.forget()))) {
+  if (NS_FAILED(Dispatch(TaskCategory::Other, caller.forget()))) {
     NS_WARNING("Failed to dispatch thread for idle observer notification.");
   }
 }
 
 bool
 nsGlobalWindow::ContainsIdleObserver(nsIIdleObserver* aIdleObserver, uint32_t aTimeInS)
 {
   MOZ_ASSERT(aIdleObserver, "Idle observer not instantiated.");
@@ -12367,26 +12372,17 @@ nsGlobalWindow::Observe(nsISupports* aSu
 
 void
 nsGlobalWindow::ObserveStorageNotification(StorageEvent* aEvent,
                                            const char16_t* aStorageType,
                                            bool aPrivateBrowsing)
 {
   MOZ_ASSERT(aEvent);
 
-  // Enforce that the source storage area's private browsing state matches
-  // this window's state.  These flag checks and their maintenance independent
-  // from the principal's OriginAttributes matter because chrome docshells
-  // that are part of private browsing windows can be private browsing without
-  // having their OriginAttributes set (because they have the system
-  // principal).
-  bool isPrivateBrowsing = IsPrivateBrowsing();
-  if (isPrivateBrowsing != aPrivateBrowsing) {
-    return;
-  }
+  MOZ_DIAGNOSTIC_ASSERT(IsPrivateBrowsing() == aPrivateBrowsing);
 
   // LocalStorage can only exist on an inner window, and we don't want to
   // generate events on frozen or otherwise-navigated-away from windows.
   // (Actually, this code used to try and buffer events for frozen windows,
   // but it never worked, so we've removed it.  See bug 1285898.)
   if (!IsInnerWindow() || !AsInner()->IsCurrentInnerWindow() || IsFrozen()) {
     return;
   }
@@ -12428,28 +12424,19 @@ nsGlobalWindow::ObserveStorageNotificati
     fireMozStorageChanged = mSessionStorage == changingStorage;
     if (fireMozStorageChanged) {
       eventType.AssignLiteral("MozSessionStorageChanged");
     }
   }
 
   else {
     MOZ_ASSERT(!NS_strcmp(aStorageType, u"localStorage"));
-    nsIPrincipal* storagePrincipal = aEvent->GetPrincipal();
-    if (!storagePrincipal) {
-      return;
-    }
-
-    bool equals = false;
-    nsresult rv = storagePrincipal->Equals(principal, &equals);
-    NS_ENSURE_SUCCESS_VOID(rv);
-
-    if (!equals) {
-      return;
-    }
+
+    MOZ_DIAGNOSTIC_ASSERT(StorageUtils::PrincipalsEqual(aEvent->GetPrincipal(),
+                                                        principal));
 
     fireMozStorageChanged = mLocalStorage == aEvent->GetStorageArea();
 
     if (fireMozStorageChanged) {
       eventType.AssignLiteral("MozLocalStorageChanged");
     }
   }
 
@@ -12891,18 +12878,17 @@ public:
     MOZ_ASSERT(mWin);
     MOZ_ASSERT(mWin->IsOuterWindow());
   }
   ~AutoUnblockScriptClosing()
   {
     void (nsGlobalWindow::*run)() = &nsGlobalWindow::UnblockScriptedClosing;
     nsCOMPtr<nsIRunnable> caller = NewRunnableMethod(
       "AutoUnblockScriptClosing::~AutoUnblockScriptClosing", mWin, run);
-    mWin->Dispatch("nsGlobalWindow::UnblockScriptedClosing",
-                   TaskCategory::Other, caller.forget());
+    mWin->Dispatch(TaskCategory::Other, caller.forget());
   }
 };
 
 nsresult
 nsGlobalWindow::OpenInternal(const nsAString& aUrl, const nsAString& aName,
                              const nsAString& aOptions, bool aDialog,
                              bool aContentModal, bool aCalledNoScript,
                              bool aDoJSFixups, bool aNavigate,
@@ -15395,25 +15381,24 @@ nsPIDOMWindow<T>::GetDocGroup() const
   nsIDocument* doc = GetExtantDoc();
   if (doc) {
     return doc->GetDocGroup();
   }
   return nullptr;
 }
 
 nsresult
-nsGlobalWindow::Dispatch(const char* aName,
-                         TaskCategory aCategory,
+nsGlobalWindow::Dispatch(TaskCategory aCategory,
                          already_AddRefed<nsIRunnable>&& aRunnable)
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
   if (GetDocGroup()) {
-    return GetDocGroup()->Dispatch(aName, aCategory, Move(aRunnable));
-  }
-  return DispatcherTrait::Dispatch(aName, aCategory, Move(aRunnable));
+    return GetDocGroup()->Dispatch(aCategory, Move(aRunnable));
+  }
+  return DispatcherTrait::Dispatch(aCategory, Move(aRunnable));
 }
 
 nsISerialEventTarget*
 nsGlobalWindow::EventTargetFor(TaskCategory aCategory) const
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
   if (GetDocGroup()) {
     return GetDocGroup()->EventTargetFor(aCategory);
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -1824,18 +1824,17 @@ private:
   mozilla::dom::TabGroup* TabGroupOuter();
 
   bool IsBackgroundInternal() const;
 
   void SetIsBackgroundInternal(bool aIsBackground);
 
 public:
   // Dispatch a runnable related to the global.
-  virtual nsresult Dispatch(const char* aName,
-                            mozilla::TaskCategory aCategory,
+  virtual nsresult Dispatch(mozilla::TaskCategory aCategory,
                             already_AddRefed<nsIRunnable>&& aRunnable) override;
 
   virtual nsISerialEventTarget*
   EventTargetFor(mozilla::TaskCategory aCategory) const override;
 
   virtual mozilla::AbstractThread*
   AbstractMainThreadFor(mozilla::TaskCategory aCategory) override;
 
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -2932,18 +2932,17 @@ public:
   virtual void RemoveIntersectionObserver(
     mozilla::dom::DOMIntersectionObserver* aObserver) = 0;
 
   virtual void UpdateIntersectionObservations() = 0;
   virtual void ScheduleIntersectionObserverNotification() = 0;
   virtual void NotifyIntersectionObservers() = 0;
 
   // Dispatch a runnable related to the document.
-  virtual nsresult Dispatch(const char* aName,
-                            mozilla::TaskCategory aCategory,
+  virtual nsresult Dispatch(mozilla::TaskCategory aCategory,
                             already_AddRefed<nsIRunnable>&& aRunnable) override;
 
   virtual nsISerialEventTarget*
   EventTargetFor(mozilla::TaskCategory aCategory) const override;
 
   virtual mozilla::AbstractThread*
   AbstractMainThreadFor(mozilla::TaskCategory aCategory) override;
 
--- a/dom/bindings/TypedArray.h
+++ b/dom/bindings/TypedArray.h
@@ -193,17 +193,17 @@ public:
   }
 
 private:
   TypedArray_base(const TypedArray_base&) = delete;
 };
 
 template<typename T,
          JSObject* UnwrapArray(JSObject*),
-         T* GetData(JSObject*, bool* isShared, const JS::AutoCheckCannotGC&),
+         T* GetData(JSObject*, bool* isShared, const JS::AutoRequireNoGC&),
          void GetLengthAndDataAndSharedness(JSObject*, uint32_t*, bool*, T**),
          JSObject* CreateNew(JSContext*, uint32_t)>
 struct TypedArray
   : public TypedArray_base<T, UnwrapArray, GetLengthAndDataAndSharedness>
 {
 private:
   typedef TypedArray_base<T, UnwrapArray, GetLengthAndDataAndSharedness> Base;
 
--- a/dom/canvas/WebGLContextLossHandler.cpp
+++ b/dom/canvas/WebGLContextLossHandler.cpp
@@ -1,46 +1,54 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* 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 "WebGLContextLossHandler.h"
 
 #include "mozilla/DebugOnly.h"
+#include "nsINamed.h"
 #include "nsISupportsImpl.h"
 #include "nsITimer.h"
 #include "nsThreadUtils.h"
 #include "WebGLContext.h"
 
 namespace mozilla {
 
 class WatchdogTimerEvent final : public nsITimerCallback
+                               , public nsINamed
 {
     const WeakPtr<WebGLContextLossHandler> mHandler;
 
 public:
     NS_DECL_ISUPPORTS
 
     explicit WatchdogTimerEvent(WebGLContextLossHandler* handler)
         : mHandler(handler)
     { }
 
+    NS_IMETHOD GetName(nsACString& aName) override
+    {
+      aName.AssignLiteral("WatchdogTimerEvent");
+      return NS_OK;
+    }
+
 private:
     virtual ~WatchdogTimerEvent() { }
 
     NS_IMETHOD Notify(nsITimer*) override {
         if (mHandler) {
             mHandler->TimerCallback();
         }
         return NS_OK;
     }
 };
 
-NS_IMPL_ISUPPORTS(WatchdogTimerEvent, nsITimerCallback, nsISupports)
+NS_IMPL_ISUPPORTS(WatchdogTimerEvent, nsITimerCallback, nsINamed)
 
 ////////////////////////////////////////
 
 WebGLContextLossHandler::WebGLContextLossHandler(WebGLContext* webgl)
     : mWebGL(webgl)
     , mTimer(do_CreateInstance(NS_TIMER_CONTRACTID))
     , mTimerPending(false)
     , mShouldRunTimerAgain(false)
--- a/dom/canvas/test/imagebitmap_extensions_prepareSources.js
+++ b/dom/canvas/test/imagebitmap_extensions_prepareSources.js
@@ -36,17 +36,16 @@ function prepareSources() {
     ok(gCtx, "[Prepare Sources] gCtx is ok.");
 
     // Prepare gGroundTruthImageData.
     gGroundTruthImageData = gCtx.getImageData(0, 0, gCanvas.width, gCanvas.height);
     ok(gGroundTruthImageData, "[Prepare Sources] gGroundTruthImageData is ok.");
 
     // Prepare image.
     gImage = document.createElement("img");
-    gImage.crossOrigin = "anonymous";
     gImage.src = gCanvas.toDataURL();
     var resolverImage;
     var promiseImage = new Promise(function(resolve, reject) {
       resolverImage = resolve;
     });
     gImage.onload = function() {
       resolverImage(true);
     }
@@ -87,9 +86,9 @@ function prepareSources() {
       promiseImage,
       promisePNGBlob,
       promiseJPEGBlob,
       promiseImageBitmap
     ]))
   }
 
   return promise;
-}
+}
\ No newline at end of file
--- a/dom/canvas/test/test_2d.fill.pattern.imageSmoothingEnabled.html
+++ b/dom/canvas/test/test_2d.fill.pattern.imageSmoothingEnabled.html
@@ -35,17 +35,16 @@ function isNotPixel(ctx, x,y, r,g,b,a, p
 
 SimpleTest.waitForExplicitFinish();
 addLoadEvent(function () {
 
 var canvas = document.getElementById('c');
 var ctx = canvas.getContext('2d');
 
 var img = document.getElementById("img");
-img.crossOrigin = "anonymous";
 img.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==";
 
 img.onload = function () {
   ctx.imageSmoothingEnabled = false;
   ctx.save();
   ctx.fillStyle = "rgb(127, 127, 127);";
   ctx.fillRect(0, 0, canvas.width, canvas.height);
   ctx.scale(16, 16);
--- a/dom/canvas/test/test_canvas.html
+++ b/dom/canvas/test/test_canvas.html
@@ -20882,17 +20882,16 @@ img.onload = wrapFunction(function ()
     ctx667.drawImage(img, 0, 25);
     // (The alpha values do not really survive float->int conversion, so just
     // do approximate comparisons)
     isPixel(ctx667, 12,40, 1,3,254,255, 0);
     isPixel(ctx667, 37,40, 8,252,248,191, 2);
     isPixel(ctx667, 62,40, 6,10,250,127, 4);
     isPixel(ctx667, 87,40, 12,16,244,63, 8);
 });
-img.crossOrigin = "anonymous";
 img.src = canvas667.toDataURL();
 
 
 }
 </script>
 
 <!-- [[[ test_toDataURL.default.html ]]] -->
 
@@ -21009,17 +21008,16 @@ deferTest();
 img.onload = wrapFunction(function ()
 {
     ctx672.drawImage(img, 0, 0);
     isPixel(ctx672, 12,20, 255,255,0,255, 0);
     isPixel(ctx672, 50,20, 0,255,255,255, 0);
     isPixel(ctx672, 87,20, 0,0,255,255, 0);
     isPixel(ctx672, 50,45, 255,255,255,255, 0);
 });
-img.crossOrigin = "anonymous";
 img.src = data;
 
 
 }
 </script>
 
 <!-- [[[ test_toDataURL.unrecognised.html ]]] -->
 
--- a/dom/canvas/test/test_imagebitmap_cropping.html
+++ b/dom/canvas/test/test_imagebitmap_cropping.html
@@ -157,17 +157,16 @@ function prepareSources() {
     gCanvas.height = gVideo.videoHeight;
     gCtx.drawImage(gVideo, 0, 0);
     ok(gCanvas, "[Prepare Sources] gCanvas is ok.");
     ok(gCtx, "[Prepare Sources] gCtx is ok.");
 
     // Prepare image.
     gImage = document.createElement("img");
     gImage.src = gCanvas.toDataURL();
-    gImage.crossOrigin = "anonymous";
     var resolverImage;
     var promiseImage = new Promise(function(resolve, reject) {
       resolverImage = resolve;
     });
     gImage.onload = function() {
       resolverImage(true);
     }
 
--- a/dom/canvas/test/test_toDataURL_alpha.html
+++ b/dom/canvas/test/test_toDataURL_alpha.html
@@ -165,17 +165,16 @@ function do_canvas(row, col, type, optio
         b = colors[2]*colors[3];
 
     img.onload = function ()
     {
         ctx.drawImage(img, 0, 0);
         isPixel(ctx, 50,25, r,g,b,255, 8);
         finishedTests[row + '_' + col] = true;
     };
-    img.crossOrigin = "anonymous";
     img.src = data;
 }
 
 function checkFinished()
 {
     for (var t in finishedTests) {
         if (!finishedTests[t]) {
             setTimeout(checkFinished, 500);
--- a/dom/events/AsyncEventDispatcher.cpp
+++ b/dom/events/AsyncEventDispatcher.cpp
@@ -79,24 +79,24 @@ AsyncEventDispatcher::Cancel()
 }
 
 nsresult
 AsyncEventDispatcher::PostDOMEvent()
 {
   RefPtr<AsyncEventDispatcher> ensureDeletionWhenFailing = this;
   if (NS_IsMainThread()) {
     if (nsCOMPtr<nsIGlobalObject> global = mTarget->GetOwnerGlobal()) {
-      return global->Dispatch("AsyncEventDispatcher", TaskCategory::Other, ensureDeletionWhenFailing.forget());
+      return global->Dispatch(TaskCategory::Other, ensureDeletionWhenFailing.forget());
     }
 
     // Sometimes GetOwnerGlobal returns null because it uses
     // GetScriptHandlingObject rather than GetScopeObject.
     if (nsCOMPtr<nsINode> node = do_QueryInterface(mTarget)) {
       nsCOMPtr<nsIDocument> doc = node->OwnerDoc();
-      return doc->Dispatch("AsyncEventDispatcher", TaskCategory::Other, ensureDeletionWhenFailing.forget());
+      return doc->Dispatch(TaskCategory::Other, ensureDeletionWhenFailing.forget());
     }
   }
   return NS_DispatchToCurrentThread(this);
 }
 
 void
 AsyncEventDispatcher::RunDOMEventWhenSafe()
 {
--- a/dom/events/DataTransferItem.cpp
+++ b/dom/events/DataTransferItem.cpp
@@ -473,17 +473,17 @@ DataTransferItem::GetAsString(FunctionSt
   if (parent && !global) {
     if (nsCOMPtr<dom::EventTarget> target = do_QueryInterface(parent)) {
       global = target->GetOwnerGlobal();
     } else if (nsCOMPtr<nsIDOMEvent> event = do_QueryInterface(parent)) {
       global = event->InternalDOMEvent()->GetParentObject();
     }
   }
   if (global) {
-    rv = global->Dispatch("GASRunnable", TaskCategory::Other, runnable.forget());
+    rv = global->Dispatch(TaskCategory::Other, runnable.forget());
   } else {
     rv = NS_DispatchToMainThread(runnable);
   }
   if (NS_FAILED(rv)) {
     NS_WARNING("Dispatch to main thread Failed in "
                "DataTransferItem::GetAsString!");
   }
 }
--- a/dom/events/EventListenerService.cpp
+++ b/dom/events/EventListenerService.cpp
@@ -366,20 +366,19 @@ EventListenerService::NotifyAboutMainThr
   }
 
   if (!mPendingListenerChanges) {
     mPendingListenerChanges = nsArrayBase::Create();
     nsCOMPtr<nsIRunnable> runnable =
       NewRunnableMethod("EventListenerService::NotifyPendingChanges",
                         this, &EventListenerService::NotifyPendingChanges);
     if (nsCOMPtr<nsIGlobalObject> global = aTarget->GetOwnerGlobal()) {
-      global->Dispatch(nullptr, TaskCategory::Other, runnable.forget());
+      global->Dispatch(TaskCategory::Other, runnable.forget());
     } else if (nsCOMPtr<nsINode> node = do_QueryInterface(aTarget)) {
-      node->OwnerDoc()->Dispatch(nullptr, TaskCategory::Other,
-                                 runnable.forget());
+      node->OwnerDoc()->Dispatch(TaskCategory::Other, runnable.forget());
     } else {
       NS_DispatchToCurrentThread(runnable);
     }
   }
 
   RefPtr<EventListenerChange> changes =
     mPendingListenerChangesSet.LookupForAdd(aTarget).OrInsert(
       [this, aTarget] () {
--- a/dom/events/EventNameList.h
+++ b/dom/events/EventNameList.h
@@ -677,16 +677,21 @@ TOUCH_EVENT(touchcancel,
 DOCUMENT_ONLY_EVENT(readystatechange,
                     eReadyStateChange,
                     EventNameType_HTMLXUL,
                     eBasicEventClass)
 DOCUMENT_ONLY_EVENT(selectionchange,
                     eSelectionChange,
                     EventNameType_HTMLXUL,
                     eBasicEventClass)
+DOCUMENT_ONLY_EVENT(visibilitychange,
+                    eVisibilityChange,
+                    EventNameType_HTMLXUL,
+                    eBasicEventClass)
+
 
 NON_IDL_EVENT(MozMouseHittest,
               eMouseHitTest,
               EventNameType_None,
               eMouseEventClass)
 
 NON_IDL_EVENT(DOMAttrModified,
               eLegacyAttrModified,
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -232,22 +232,16 @@ UITimerCallback::Notify(nsITimer* aTimer
 
 NS_IMETHODIMP
 UITimerCallback::GetName(nsACString& aName)
 {
   aName.AssignASCII("UITimerCallback_timer");
   return NS_OK;
 }
 
-NS_IMETHODIMP
-UITimerCallback::SetName(const char* aName)
-{
-  return NS_ERROR_NOT_IMPLEMENTED;
-}
-
 /******************************************************************/
 /* mozilla::OverOutElementsWrapper                                */
 /******************************************************************/
 
 OverOutElementsWrapper::OverOutElementsWrapper()
   : mLastOverFrame(nullptr)
 {
 }
--- a/dom/events/IMEContentObserver.cpp
+++ b/dom/events/IMEContentObserver.cpp
@@ -1843,17 +1843,17 @@ IMEContentObserver::IMENotificationSende
       }
     }
   }
 
   nsIScriptGlobalObject* globalObject =
     aDocShell ? aDocShell->GetScriptGlobalObject() : nullptr;
   if (globalObject) {
     RefPtr<IMENotificationSender> queuedSender = this;
-    globalObject->Dispatch(nullptr, TaskCategory::Other,
+    globalObject->Dispatch(TaskCategory::Other,
                            queuedSender.forget());
   } else {
     NS_DispatchToCurrentThread(this);
   }
 }
 
 NS_IMETHODIMP
 IMEContentObserver::IMENotificationSender::Run()
--- a/dom/file/FileReader.cpp
+++ b/dom/file/FileReader.cpp
@@ -66,16 +66,17 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INH
                                                DOMEventTargetHelper)
   NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mResultArrayBuffer)
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(FileReader)
   NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
   NS_INTERFACE_MAP_ENTRY(nsIInputStreamCallback)
   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
+  NS_INTERFACE_MAP_ENTRY(nsINamed)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
 NS_IMPL_ADDREF_INHERITED(FileReader, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(FileReader, DOMEventTargetHelper)
 
 class MOZ_RAII FileReaderDecreaseBusyCounter
 {
   RefPtr<FileReader> mFileReader;
@@ -679,16 +680,24 @@ FileReader::OnInputStreamReady(nsIAsyncI
     NS_ENSURE_SUCCESS(rv, rv);
 
     StartProgressEventTimer();
   }
 
   return NS_OK;
 }
 
+// nsINamed
+NS_IMETHODIMP
+FileReader::GetName(nsACString& aName)
+{
+  aName.AssignLiteral("FileReader");
+  return NS_OK;
+}
+
 nsresult
 FileReader::OnLoadEnd(nsresult aStatus)
 {
   // Cancel the progress event timer
   ClearProgressEventTimer();
 
   // FileReader must be in DONE stage after an operation
   mReadyState = DONE;
--- a/dom/file/FileReader.h
+++ b/dom/file/FileReader.h
@@ -7,16 +7,17 @@
 #ifndef mozilla_dom_FileReader_h
 #define mozilla_dom_FileReader_h
 
 #include "mozilla/Attributes.h"
 #include "mozilla/DOMEventTargetHelper.h"
 
 #include "nsIAsyncInputStream.h"
 #include "nsIInterfaceRequestor.h"
+#include "nsINamed.h"
 #include "nsCOMPtr.h"
 #include "nsString.h"
 #include "nsWeakReference.h"
 #include "WorkerHolder.h"
 
 #define NS_PROGRESS_EVENT_INTERVAL 50
 
 class nsITimer;
@@ -36,29 +37,31 @@ extern const uint64_t kUnknownSize;
 
 class FileReaderDecreaseBusyCounter;
 
 class FileReader final : public DOMEventTargetHelper,
                          public nsIInterfaceRequestor,
                          public nsSupportsWeakReference,
                          public nsIInputStreamCallback,
                          public nsITimerCallback,
+                         public nsINamed,
                          public workers::WorkerHolder
 {
   friend class FileReaderDecreaseBusyCounter;
 
 public:
   FileReader(nsIGlobalObject* aGlobal,
              workers::WorkerPrivate* aWorkerPrivate);
 
   NS_DECL_ISUPPORTS_INHERITED
 
   NS_DECL_NSITIMERCALLBACK
   NS_DECL_NSIINPUTSTREAMCALLBACK
   NS_DECL_NSIINTERFACEREQUESTOR
+  NS_DECL_NSINAMED
 
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(FileReader,
                                                          DOMEventTargetHelper)
 
   virtual JSObject* WrapObject(JSContext* aCx,
                                JS::Handle<JSObject*> aGivenProto) override;
 
   // WebIDL
--- a/dom/file/ipc/IPCBlobInputStreamThread.cpp
+++ b/dom/file/ipc/IPCBlobInputStreamThread.cpp
@@ -121,19 +121,17 @@ IPCBlobInputStreamThread::GetOrCreate()
   return gIPCBlobThread;
 }
 
 void
 IPCBlobInputStreamThread::Initialize()
 {
   if (!NS_IsMainThread()) {
     RefPtr<Runnable> runnable = new ThreadInitializeRunnable();
-    SystemGroup::Dispatch("IPCBlobInputStreamThread::Initialize",
-                          TaskCategory::Other,
-                          runnable.forget());
+    SystemGroup::Dispatch(TaskCategory::Other, runnable.forget());
     return;
   }
 
   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
   if (NS_WARN_IF(!obs)) {
     return;
   }
 
--- a/dom/file/nsHostObjectProtocolHandler.cpp
+++ b/dom/file/nsHostObjectProtocolHandler.cpp
@@ -464,23 +464,16 @@ public:
 
   NS_IMETHOD
   GetName(nsACString& aName) override
   {
     aName.AssignLiteral("ReleasingTimerHolder");
     return NS_OK;
   }
 
-  NS_IMETHOD
-  SetName(const char* aName) override
-  {
-    MOZ_CRASH("The name shall never be set!");
-    return NS_OK;
-  }
-
 private:
   explicit ReleasingTimerHolder(nsTArray<nsWeakPtr>&& aArray)
     : mURIs(aArray)
   {}
 
   ~ReleasingTimerHolder()
   {}
 
--- a/dom/flyweb/FlyWebService.cpp
+++ b/dom/flyweb/FlyWebService.cpp
@@ -20,16 +20,17 @@
 #include "mozilla/dom/FlyWebDiscoveryManagerBinding.h"
 #include "prnetdb.h"
 #include "DNS.h"
 #include "nsContentPermissionHelper.h"
 #include "nsSocketTransportService2.h"
 #include "nsSocketTransport2.h"
 #include "nsHashPropertyBag.h"
 #include "nsNetUtil.h"
+#include "nsINamed.h"
 #include "nsISimpleEnumerator.h"
 #include "nsIProperty.h"
 #include "nsICertOverrideService.h"
 
 namespace mozilla {
 namespace dom {
 
 struct FlyWebPublishOptions;
@@ -154,16 +155,17 @@ NS_IMPL_ISUPPORTS(FlyWebPublishServerPer
                   nsIContentPermissionRequest,
                   nsIRunnable)
 
 class FlyWebMDNSService final
   : public nsIDNSServiceDiscoveryListener
   , public nsIDNSServiceResolveListener
   , public nsIDNSRegistrationListener
   , public nsITimerCallback
+  , public nsINamed
 {
   friend class FlyWebService;
 
 private:
   enum DiscoveryState {
     DISCOVERY_IDLE,
     DISCOVERY_STARTING,
     DISCOVERY_RUNNING,
@@ -175,16 +177,22 @@ public:
   NS_DECL_NSIDNSSERVICEDISCOVERYLISTENER
   NS_DECL_NSIDNSSERVICERESOLVELISTENER
   NS_DECL_NSIDNSREGISTRATIONLISTENER
   NS_DECL_NSITIMERCALLBACK
 
   explicit FlyWebMDNSService(FlyWebService* aService,
                              const nsACString& aServiceType);
 
+  NS_IMETHOD GetName(nsACString& aName) override
+  {
+    aName.AssignLiteral("FlyWebMDNSService");
+    return NS_OK;
+  }
+
 private:
   virtual ~FlyWebMDNSService() = default;
 
   nsresult Init();
   nsresult StartDiscovery();
   nsresult StopDiscovery();
 
   void ListDiscoveredServices(nsTArray<FlyWebDiscoveredService>& aServices);
@@ -291,17 +299,18 @@ LogDNSInfo(nsIDNSServiceInfo* aServiceIn
     LOG_I("%s: attributes end", aFunc);
   }
 }
 
 NS_IMPL_ISUPPORTS(FlyWebMDNSService,
                   nsIDNSServiceDiscoveryListener,
                   nsIDNSServiceResolveListener,
                   nsIDNSRegistrationListener,
-                  nsITimerCallback)
+                  nsITimerCallback,
+                  nsINamed)
 
 FlyWebMDNSService::FlyWebMDNSService(
         FlyWebService* aService,
         const nsACString& aServiceType)
   : mService(aService)
   , mServiceType(aServiceType)
   , mDiscoveryActive(false)
   , mNumConsecutiveStartDiscoveryFailures(0)
--- a/dom/geolocation/MLSFallback.cpp
+++ b/dom/geolocation/MLSFallback.cpp
@@ -4,17 +4,17 @@
  * 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 "MLSFallback.h"
 #include "nsGeoPosition.h"
 #include "nsIGeolocationProvider.h"
 #include "nsServiceManagerUtils.h"
 
-NS_IMPL_ISUPPORTS(MLSFallback, nsITimerCallback)
+NS_IMPL_ISUPPORTS(MLSFallback, nsITimerCallback, nsINamed)
 
 MLSFallback::MLSFallback(uint32_t delay)
 : mDelayMs(delay)
 {
 }
 
 MLSFallback::~MLSFallback()
 {
@@ -54,16 +54,23 @@ MLSFallback::Shutdown()
 }
 
 NS_IMETHODIMP
 MLSFallback::Notify(nsITimer* aTimer)
 {
   return CreateMLSFallbackProvider();
 }
 
+NS_IMETHODIMP
+MLSFallback::GetName(nsACString& aName)
+{
+  aName.AssignLiteral("MLSFallback");
+  return NS_OK;
+}
+
 nsresult
 MLSFallback::CreateMLSFallbackProvider()
 {
   if (mMLSFallbackProvider || !mUpdateWatcher) {
     return NS_OK;
   }
 
   nsresult rv;
--- a/dom/geolocation/MLSFallback.h
+++ b/dom/geolocation/MLSFallback.h
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsCOMPtr.h"
 #include "nsITimer.h"
+#include "nsINamed.h"
 
 class nsIGeolocationUpdate;
 class nsIGeolocationProvider;
 
 /*
  This class wraps the NetworkGeolocationProvider in a delayed startup.
  It is for providing a fallback to MLS when:
  1) using another provider as the primary provider, and
@@ -23,20 +24,22 @@ class nsIGeolocationProvider;
 
  MLS has an average response of 3s, so with the 2s default delay, a response can
  be expected in 5s.
 
  Telemetry is recommended to monitor that the primary provider is responding
  first when expected to do so.
 */
 class MLSFallback : public nsITimerCallback
+                  , public nsINamed
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSITIMERCALLBACK
+  NS_DECL_NSINAMED
 
   explicit MLSFallback(uint32_t delayMs = 2000);
   nsresult Startup(nsIGeolocationUpdate* aWatcher);
   nsresult Shutdown();
 
 private:
   nsresult CreateMLSFallbackProvider();
   virtual ~MLSFallback();
--- a/dom/geolocation/nsGeolocation.cpp
+++ b/dom/geolocation/nsGeolocation.cpp
@@ -16,16 +16,17 @@
 #include "mozilla/Unused.h"
 #include "mozilla/WeakPtr.h"
 #include "nsComponentManagerUtils.h"
 #include "nsContentPermissionHelper.h"
 #include "nsContentUtils.h"
 #include "nsDOMClassInfoID.h"
 #include "nsGlobalWindow.h"
 #include "nsIDocument.h"
+#include "nsINamed.h"
 #include "nsIObserverService.h"
 #include "nsIScriptError.h"
 #include "nsPIDOMWindow.h"
 #include "nsServiceManagerUtils.h"
 #include "nsThreadUtils.h"
 #include "nsXULAppAPI.h"
 
 class nsIPrincipal;
@@ -91,25 +92,32 @@ class nsGeolocationRequest final
   nsIPrincipal* GetPrincipal();
 
   bool IsWatch() { return mIsWatchPositionRequest; }
   int32_t WatchId() { return mWatchId; }
  private:
   virtual ~nsGeolocationRequest();
 
   class TimerCallbackHolder final : public nsITimerCallback
+                                  , public nsINamed
   {
   public:
     NS_DECL_ISUPPORTS
     NS_DECL_NSITIMERCALLBACK
 
     explicit TimerCallbackHolder(nsGeolocationRequest* aRequest)
       : mRequest(aRequest)
     {}
 
+    NS_IMETHOD GetName(nsACString& aName) override
+    {
+      aName.AssignLiteral("nsGeolocationRequest::TimerCallbackHolder");
+      return NS_OK;
+    }
+
   private:
     ~TimerCallbackHolder() = default;
     WeakPtr<nsGeolocationRequest> mRequest;
   };
 
   void Notify();
 
   bool mIsWatchPositionRequest;
@@ -632,17 +640,19 @@ nsGeolocationRequest::Shutdown()
   }
 }
 
 
 ////////////////////////////////////////////////////
 // nsGeolocationRequest::TimerCallbackHolder
 ////////////////////////////////////////////////////
 
-NS_IMPL_ISUPPORTS(nsGeolocationRequest::TimerCallbackHolder, nsISupports, nsITimerCallback)
+NS_IMPL_ISUPPORTS(nsGeolocationRequest::TimerCallbackHolder,
+                  nsITimerCallback,
+                  nsINamed)
 
 NS_IMETHODIMP
 nsGeolocationRequest::TimerCallbackHolder::Notify(nsITimer*)
 {
   if (mRequest && mRequest->mLocator) {
     RefPtr<nsGeolocationRequest> request(mRequest);
     request->Notify();
   }
--- a/dom/html/HTMLCanvasElement.cpp
+++ b/dom/html/HTMLCanvasElement.cpp
@@ -525,19 +525,17 @@ HTMLCanvasElement::DispatchPrintCallback
     NS_ENSURE_SUCCESS(rv, rv);
   }
   mPrintState = new HTMLCanvasPrintState(this, mCurrentContext, aCallback);
 
   RefPtr<nsRunnableMethod<HTMLCanvasElement>> renderEvent =
     NewRunnableMethod("dom::HTMLCanvasElement::CallPrintCallback",
                       this,
                       &HTMLCanvasElement::CallPrintCallback);
-  return OwnerDoc()->Dispatch("HTMLCanvasElement::CallPrintCallback",
-                              TaskCategory::Other,
-                              renderEvent.forget());
+  return OwnerDoc()->Dispatch(TaskCategory::Other, renderEvent.forget());
 }
 
 void
 HTMLCanvasElement::CallPrintCallback()
 {
   ErrorResult rv;
   GetMozPrintCallback()->Call(*mPrintState, rv);
 }
@@ -847,17 +845,16 @@ HTMLCanvasElement::ToBlob(JSContext* aCx
   MOZ_ASSERT(global);
 
   nsIntSize elemSize = GetWidthHeight();
   if (elemSize.width == 0 || elemSize.height == 0) {
     // According to spec, blob should return null if either its horizontal
     // dimension or its vertical dimension is zero. See link below.
     // https://html.spec.whatwg.org/multipage/scripting.html#dom-canvas-toblob
     OwnerDoc()->Dispatch(
-      "FireNullBlobEvent",
       TaskCategory::Other,
       NewRunnableMethod<Blob*, const char*>(
         "dom::HTMLCanvasElement::ToBlob",
         &aCallback,
         static_cast<void (BlobCallback::*)(Blob*, const char*)>(
           &BlobCallback::Call),
         nullptr,
         nullptr));
--- a/dom/html/HTMLTrackElement.cpp
+++ b/dom/html/HTMLTrackElement.cpp
@@ -440,18 +440,17 @@ HTMLTrackElement::DispatchTrackRunnable(
   if (!doc) {
     return;
   }
   nsCOMPtr<nsIRunnable> runnable = NewRunnableMethod<const nsString>(
     "dom::HTMLTrackElement::DispatchTrustedEvent",
     this,
     &HTMLTrackElement::DispatchTrustedEvent,
     aEventName);
-  doc->Dispatch("HTMLTrackElement::DispatchTrackRunnable",
-                TaskCategory::Other, runnable.forget());
+  doc->Dispatch(TaskCategory::Other, runnable.forget());
 }
 
 void
 HTMLTrackElement::DispatchTrustedEvent(const nsAString& aName)
 {
   nsIDocument* doc = OwnerDoc();
   if (!doc) {
     return;
--- a/dom/html/TextTrackManager.cpp
+++ b/dom/html/TextTrackManager.cpp
@@ -625,17 +625,16 @@ void
 TextTrackManager::DispatchUpdateCueDisplay()
 {
   if (!mUpdateCueDisplayDispatched && !mShutdown &&
       (mMediaElement->GetHasUserInteraction() || mMediaElement->IsCurrentlyPlaying())) {
     WEBVTT_LOG("DispatchUpdateCueDisplay");
     nsPIDOMWindowInner* win = mMediaElement->OwnerDoc()->GetInnerWindow();
     if (win) {
       nsGlobalWindow::Cast(win)->Dispatch(
-        "TextTrackManager::UpdateCueDisplay",
         TaskCategory::Other,
         NewRunnableMethod("dom::TextTrackManager::UpdateCueDisplay",
                           this,
                           &TextTrackManager::UpdateCueDisplay));
       mUpdateCueDisplayDispatched = true;
     }
   }
 }
@@ -648,17 +647,16 @@ TextTrackManager::DispatchTimeMarchesOn(
   // through its usual monotonic increase during normal playback; current
   // executing call upon completion will check queue for further 'work'.
   if (!mTimeMarchesOnDispatched && !mShutdown &&
       (mMediaElement->GetHasUserInteraction() || mMediaElement->IsCurrentlyPlaying())) {
     WEBVTT_LOG("DispatchTimeMarchesOn");
     nsPIDOMWindowInner* win = mMediaElement->OwnerDoc()->GetInnerWindow();
     if (win) {
       nsGlobalWindow::Cast(win)->Dispatch(
-        "TextTrackManager::TimeMarchesOn",
         TaskCategory::Other,
         NewRunnableMethod("dom::TextTrackManager::TimeMarchesOn",
                           this,
                           &TextTrackManager::TimeMarchesOn));
       mTimeMarchesOnDispatched = true;
     }
   }
 }
--- a/dom/html/nsGenericHTMLElement.cpp
+++ b/dom/html/nsGenericHTMLElement.cpp
@@ -482,17 +482,17 @@ nsGenericHTMLElement::BindToTree(nsIDocu
 {
   nsresult rv = nsGenericHTMLElementBase::BindToTree(aDocument, aParent,
                                                      aBindingParent,
                                                      aCompileEventHandlers);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (aDocument) {
     RegAccessKey();
-    if (HasName()) {
+    if (CanHaveName(NodeInfo()->NameAtom()) && HasName()) {
       aDocument->
         AddToNameTable(this, GetParsedAttr(nsGkAtoms::name)->GetAtomValue());
     }
 
     if (HasFlag(NODE_IS_EDITABLE) && GetContentEditableValue() == eTrue) {
       nsCOMPtr<nsIHTMLDocument> htmlDocument = do_QueryInterface(aDocument);
       if (htmlDocument) {
         htmlDocument->ChangeContentEditableCount(this, +1);
@@ -771,25 +771,26 @@ nsGenericHTMLElement::AfterSetAttr(int32
       }
       ChangeEditableState(editableCountDelta);
     } else if (aName == nsGkAtoms::accesskey) {
       if (aValue && !aValue->Equals(EmptyString(), eIgnoreCase)) {
         SetFlags(NODE_HAS_ACCESSKEY);
         RegAccessKey();
       }
     } else if (aName == nsGkAtoms::name) {
-      if (aValue && !aValue->Equals(EmptyString(), eIgnoreCase) &&
-          CanHaveName(NodeInfo()->NameAtom())) {
+      if (aValue && !aValue->Equals(EmptyString(), eIgnoreCase)) {
         // This may not be quite right because we can have subclass code run
         // before here. But in practice subclasses don't care about this flag,
         // and in particular selector matching does not care.  Otherwise we'd
         // want to handle it like we handle id attributes (in PreIdMaybeChange
         // and PostIdMaybeChange).
         SetHasName();
-        AddToNameTable(aValue->GetAtomValue());
+        if (CanHaveName(NodeInfo()->NameAtom())) {
+          AddToNameTable(aValue->GetAtomValue());
+        }
       }
     }
   }
 
   return nsGenericHTMLElementBase::AfterSetAttr(aNamespaceID, aName,
                                                 aValue, aOldValue, aNotify);
 }
 
--- a/dom/html/nsGenericHTMLElement.h
+++ b/dom/html/nsGenericHTMLElement.h
@@ -728,17 +728,17 @@ protected:
   void AddToNameTable(nsIAtom* aName) {
     NS_ASSERTION(HasName(), "Node doesn't have name?");
     nsIDocument* doc = GetUncomposedDoc();
     if (doc && !IsInAnonymousSubtree()) {
       doc->AddToNameTable(this, aName);
     }
   }
   void RemoveFromNameTable() {
-    if (HasName()) {
+    if (CanHaveName(NodeInfo()->NameAtom()) && HasName()) {
       nsIDocument* doc = GetUncomposedDoc();
       if (doc) {
         doc->RemoveFromNameTable(this, GetParsedAttr(nsGkAtoms::name)->
                                          GetAtomValue());
       }
     }
   }
 
--- a/dom/html/nsHTMLDocument.cpp
+++ b/dom/html/nsHTMLDocument.cpp
@@ -2033,16 +2033,21 @@ nsHTMLDocument::Writeln(JSContext* cx, c
   WriteCommon(cx, aText, true, rv);
 }
 
 bool
 nsHTMLDocument::MatchNameAttribute(Element* aElement, int32_t aNamespaceID,
                                    nsIAtom* aAtom, void* aData)
 {
   NS_PRECONDITION(aElement, "Must have element to work with!");
+
+  if (!aElement->HasName()) {
+    return false;
+  }
+
   nsString* elementName = static_cast<nsString*>(aData);
   return
     aElement->GetNameSpaceID() == kNameSpaceID_XHTML &&
     aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name,
                           *elementName, eCaseMatters);
 }
 
 /* static */
--- a/dom/html/nsHTMLDocument.h
+++ b/dom/html/nsHTMLDocument.h
@@ -188,18 +188,21 @@ public:
   nsIHTMLCollection* Links();
   nsIHTMLCollection* Forms()
   {
     return nsHTMLDocument::GetForms();
   }
   nsIHTMLCollection* Scripts();
   already_AddRefed<nsContentList> GetElementsByName(const nsAString & aName)
   {
-    return NS_GetFuncStringNodeList(this, MatchNameAttribute, nullptr,
-                                    UseExistingNameString, aName);
+    return GetFuncStringContentList<nsCachableElementsByNameNodeList>(this,
+                                                                      MatchNameAttribute,
+                                                                      nullptr,
+                                                                      UseExistingNameString,
+                                                                      aName);
   }
   already_AddRefed<nsIDocument> Open(JSContext* cx,
                                      const nsAString& aType,
                                      const nsAString& aReplace,
                                      mozilla::ErrorResult& rv);
   already_AddRefed<nsPIDOMWindowOuter>
   Open(JSContext* cx,
        const nsAString& aURL,
--- a/dom/html/test/mochitest.ini
+++ b/dom/html/test/mochitest.ini
@@ -606,8 +606,9 @@ skip-if = os == "android" # up/down arro
 [test_bug1295719_event_sequence_for_number_keys.html]
 [test_bug1310865.html]
 [test_bug1315146.html]
 [test_fakepath.html]
 [test_script_module.html]
 support-files =
   file_script_module.html
   file_script_nomodule.html
+[test_getElementsByName_after_mutation.html]
new file mode 100644
--- /dev/null
+++ b/dom/html/test/test_getElementsByName_after_mutation.html
@@ -0,0 +1,51 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1376695
+-->
+<head>
+  <title>Test for Bug 1376695</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=1376695">Mozilla Bug 1376695</a>
+<p id="display"></p>
+<div id="originalFoo" name="foo">
+<pre id="test">
+<script type="application/javascript">
+
+/** Test to ensure that the list returned by getElementsByName is updated after
+  * mutations.
+  **/
+
+var fooList = document.getElementsByName("foo");
+var originalDiv = document.getElementById("originalFoo");
+
+is(fooList.length, 1, "Should find one element with name 'foo' initially");
+is(fooList[0], originalDiv, "Element should be the original div");
+
+var newTree = document.createElement("p");
+var child1 = document.createElement("div");
+var child2 = document.createElement("div");
+child2.setAttribute("name", "foo");
+
+newTree.appendChild(child1);
+newTree.appendChild(child2);
+document.body.appendChild(newTree);
+
+is(fooList.length, 2,
+   "Should find two elements with name 'foo' after appending the new tree");
+is(fooList[1], child2, "Element should be the new appended div with name 'foo'");
+
+document.body.removeChild(newTree);
+
+is(fooList.length, 1,
+   "Should find one element with name 'foo' after removing the new tree");
+is(fooList[0], originalDiv,
+   "Element should be the original div after removing the new tree");
+
+</script>
+</pre>
+</body>
+</html>
--- a/dom/indexedDB/ActorsParent.cpp
+++ b/dom/indexedDB/ActorsParent.cpp
@@ -9878,25 +9878,23 @@ CheckWasmModule(FileHelper* aFileHelper,
                                                             Move(buildId),
                                                             nullptr,
                                                             0,
                                                             0);
   if (NS_WARN_IF(!module)) {
     return NS_ERROR_FAILURE;
   }
 
-  size_t compiledSize;
-  module->serializedSize(nullptr, &compiledSize);
-
+  size_t compiledSize = module->compiledSerializedSize();
   UniquePtr<uint8_t[]> compiled(new (fallible) uint8_t[compiledSize]);
   if (NS_WARN_IF(!compiled)) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
-  module->serialize(nullptr, 0, compiled.get(), compiledSize);
+  module->compiledSerialize(compiled.get(), compiledSize);
 
   nsCOMPtr<nsIInputStream> inputStream;
   rv = NS_NewByteInputStream(getter_AddRefs(inputStream),
                              reinterpret_cast<const char*>(compiled.get()),
                              compiledSize);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
--- a/dom/indexedDB/IDBObjectStore.cpp
+++ b/dom/indexedDB/IDBObjectStore.cpp
@@ -344,35 +344,30 @@ StructuredCloneWriteCallback(JSContext* 
       return true;
     }
   }
 
   if (JS::IsWasmModuleObject(aObj)) {
     RefPtr<JS::WasmModule> module = JS::GetWasmModule(aObj);
     MOZ_ASSERT(module);
 
-    size_t bytecodeSize;
-    size_t compiledSize;
-    module->serializedSize(&bytecodeSize, &compiledSize);
-
+    size_t bytecodeSize = module->bytecodeSerializedSize();
     UniquePtr<uint8_t[]> bytecode(new uint8_t[bytecodeSize]);
     MOZ_ASSERT(bytecode);
-
-    UniquePtr<uint8_t[]> compiled(new uint8_t[compiledSize]);
-    MOZ_ASSERT(compiled);
-
-    module->serialize(bytecode.get(),
-                      bytecodeSize,
-                      compiled.get(),
-                      compiledSize);
+    module->bytecodeSerialize(bytecode.get(), bytecodeSize);
 
     RefPtr<BlobImpl> blobImpl =
       new MemoryBlobImpl(bytecode.release(), bytecodeSize, EmptyString());
     RefPtr<Blob> bytecodeBlob = Blob::Create(nullptr, blobImpl);
 
+    size_t compiledSize = module->compiledSerializedSize();
+    UniquePtr<uint8_t[]> compiled(new uint8_t[compiledSize]);
+    MOZ_ASSERT(compiled);
+    module->compiledSerialize(compiled.get(), compiledSize);
+
     blobImpl =
       new MemoryBlobImpl(compiled.release(), compiledSize, EmptyString());
     RefPtr<Blob> compiledBlob = Blob::Create(nullptr, blobImpl);
 
     if (cloneWriteInfo->mFiles.Length() + 1 > size_t(UINT32_MAX)) {
       MOZ_ASSERT(false, "Fix the structured clone data to use a bigger type!");
       return false;
     }
--- a/dom/indexedDB/IndexedDatabaseManager.cpp
+++ b/dom/indexedDB/IndexedDatabaseManager.cpp
@@ -1102,17 +1102,18 @@ IndexedDatabaseManager::GetLocale()
   MOZ_ASSERT(idbManager, "IDBManager is not ready!");
 
   return idbManager->mLocale;
 }
 #endif
 
 NS_IMPL_ADDREF(IndexedDatabaseManager)
 NS_IMPL_RELEASE_WITH_DESTROY(IndexedDatabaseManager, Destroy())
-NS_IMPL_QUERY_INTERFACE(IndexedDatabaseManager, nsIObserver, nsITimerCallback)
+NS_IMPL_QUERY_INTERFACE(IndexedDatabaseManager, nsIObserver, nsITimerCallback,
+                        nsINamed)
 
 NS_IMETHODIMP
 IndexedDatabaseManager::Observe(nsISupports* aSubject, const char* aTopic,
                                 const char16_t* aData)
 {
   NS_ASSERTION(IsMainProcess(), "Wrong process!");
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
@@ -1158,16 +1159,23 @@ IndexedDatabaseManager::Notify(nsITimer*
     runnable->Dispatch();
   }
 
   mPendingDeleteInfos.Clear();
 
   return NS_OK;
 }
 
+NS_IMETHODIMP
+IndexedDatabaseManager::GetName(nsACString& aName)
+{
+  aName.AssignLiteral("IndexedDatabaseManager");
+  return NS_OK;
+}
+
 already_AddRefed<FileManager>
 FileManagerInfo::GetFileManager(PersistenceType aPersistenceType,
                                 const nsAString& aName) const
 {
   AssertIsOnIOThread();
 
   const nsTArray<RefPtr<FileManager> >& managers =
     GetImmutableArray(aPersistenceType);
--- a/dom/indexedDB/IndexedDatabaseManager.h
+++ b/dom/indexedDB/IndexedDatabaseManager.h
@@ -11,16 +11,17 @@
 
 #include "js/TypeDecls.h"
 #include "mozilla/Atomics.h"
 #include "mozilla/dom/quota/PersistenceType.h"
 #include "mozilla/Mutex.h"
 #include "nsClassHashtable.h"
 #include "nsCOMPtr.h"
 #include "nsHashKeys.h"
+#include "nsINamed.h"
 #include "nsITimer.h"
 
 class nsIEventTarget;
 
 namespace mozilla {
 
 class EventChainPostVisitor;
 
@@ -40,16 +41,17 @@ class BackgroundUtilsChild;
 class FileManager;
 class FileManagerInfo;
 
 } // namespace indexedDB
 
 class IndexedDatabaseManager final
   : public nsIObserver
   , public nsITimerCallback
+  , public nsINamed
 {
   typedef mozilla::dom::quota::PersistenceType PersistenceType;
   typedef mozilla::dom::quota::QuotaManager QuotaManager;
   typedef mozilla::dom::indexedDB::FileManager FileManager;
   typedef mozilla::dom::indexedDB::FileManagerInfo FileManagerInfo;
 
 public:
   enum LoggingMode
@@ -59,16 +61,17 @@ public:
     Logging_Detailed,
     Logging_ConciseProfilerMarks,
     Logging_DetailedProfilerMarks
   };
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSIOBSERVER
   NS_DECL_NSITIMERCALLBACK
+  NS_DECL_NSINAMED
 
   // Returns a non-owning reference.
   static IndexedDatabaseManager*
   GetOrCreate();
 
   // Returns a non-owning reference.
   static IndexedDatabaseManager*
   Get();
--- a/dom/indexedDB/Key.cpp
+++ b/dom/indexedDB/Key.cpp
@@ -3,21 +3,23 @@
 /* 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 "Key.h"
 
 #include <algorithm>
+#include <stdint.h> // for UINT32_MAX, uintptr_t
 #include "IndexedDatabaseManager.h"
 #include "js/Date.h"
 #include "js/Value.h"
 #include "jsfriendapi.h"
 #include "mozilla/Casting.h"
+#include "mozilla/CheckedInt.h"
 #include "mozilla/EndianUtils.h"
 #include "mozilla/FloatingPoint.h"
 #include "mozIStorageStatement.h"
 #include "mozIStorageValueArray.h"
 #include "nsAlgorithm.h"
 #include "nsJSUtils.h"
 #include "ReportInternalError.h"
 #include "xpcpublic.h"
@@ -191,17 +193,20 @@ Key::ToLocaleBasedKey(Key& aTarget, cons
       }
     } else {
       // Decode string and reencode
       uint8_t typeOffset = *it - eString;
       MOZ_ASSERT((typeOffset % eArray == 0) && (typeOffset / eArray <= 2));
 
       nsDependentString str;
       DecodeString(it, end, str);
-      aTarget.EncodeLocaleString(str, typeOffset, aLocale);
+      nsresult rv = aTarget.EncodeLocaleString(str, typeOffset, aLocale);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
     }
   }
   aTarget.TrimBuffer();
   return NS_OK;
 }
 #endif
 
 nsresult
@@ -216,18 +221,17 @@ Key::EncodeJSValInternal(JSContext* aCx,
   }
 
   if (aVal.isString()) {
     nsAutoJSString str;
     if (!str.init(aCx, aVal)) {
       IDB_REPORT_INTERNAL_ERR();
       return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
     }
-    EncodeString(str, aTypeOffset);
-    return NS_OK;
+    return EncodeString(str, aTypeOffset);
   }
 
   if (aVal.isNumber()) {
     double d = aVal.toNumber();
     if (mozilla::IsNaN(d)) {
       return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
     }
     EncodeNumber(d, eFloat + aTypeOffset);
@@ -294,23 +298,21 @@ Key::EncodeJSValInternal(JSContext* aCx,
         IDB_REPORT_INTERNAL_ERR();
         return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
       }
       EncodeNumber(t, eDate + aTypeOffset);
       return NS_OK;
     }
 
     if (JS_IsArrayBufferObject(obj)) {
-      EncodeBinary(obj, /* aIsViewObject */ false, aTypeOffset);
-      return NS_OK;
+      return EncodeBinary(obj, /* aIsViewObject */ false, aTypeOffset);
     }
 
     if (JS_IsArrayBufferViewObject(obj)) {
-      EncodeBinary(obj, /* aIsViewObject */ true, aTypeOffset);
-      return NS_OK;
+      return EncodeBinary(obj, /* aIsViewObject */ true, aTypeOffset);
     }
   }
 
   return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
 }
 
 // static
 nsresult
@@ -410,54 +412,74 @@ Key::DecodeJSValInternal(const unsigned 
 nsresult
 Key::EncodeJSVal(JSContext* aCx,
                  JS::Handle<JS::Value> aVal,
                  uint8_t aTypeOffset)
 {
   return EncodeJSValInternal(aCx, aVal, aTypeOffset, 0);
 }
 
-void
+nsresult
 Key::EncodeString(const nsAString& aString, uint8_t aTypeOffset)
 {
   const char16_t* start = aString.BeginReading();
   const char16_t* end = aString.EndReading();
-  EncodeString(start, end, aTypeOffset);
+  return EncodeString(start, end, aTypeOffset);
+}
+
+template <typename T>
+nsresult
+Key::EncodeString(const T* aStart, const T* aEnd, uint8_t aTypeOffset)
+{
+  return EncodeAsString(aStart, aEnd, eString + aTypeOffset);
 }
 
 template <typename T>
-void
-Key::EncodeString(const T* aStart, const T* aEnd, uint8_t aTypeOffset)
-{
-  EncodeAsString(aStart, aEnd, eString + aTypeOffset);
-}
-
-template <typename T>
-void
+nsresult
 Key::EncodeAsString(const T* aStart, const T* aEnd, uint8_t aType)
 {
   // First measure how long the encoded string will be.
+  if (NS_WARN_IF(aStart > aEnd || UINT32_MAX - 2 < uintptr_t(aEnd - aStart))) {
+    IDB_REPORT_INTERNAL_ERR();
+    return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+  }
 
   // The +2 is for initial 3 and trailing 0. We'll compensate for multi-byte
   // chars below.
-  uint32_t size = (aEnd - aStart) + 2;
+  uint32_t checkedSize = aEnd - aStart;
+  CheckedUint32 size = checkedSize;
+  size += 2;
+
+  MOZ_ASSERT(size.isValid());
 
   const T* start = aStart;
   const T* end = aEnd;
   for (const T* iter = start; iter < end; ++iter) {
     if (*iter > ONE_BYTE_LIMIT) {
       size += char16_t(*iter) > TWO_BYTE_LIMIT ? 2 : 1;
+      if (!size.isValid()) {
+        IDB_REPORT_INTERNAL_ERR();
+        return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+      }
     }
   }
 
   // Allocate memory for the new size
   uint32_t oldLen = mBuffer.Length();
+  size += oldLen;
+
+  if (!size.isValid()) {
+    IDB_REPORT_INTERNAL_ERR();
+    return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+  }
+
   char* buffer;
-  if (!mBuffer.GetMutableData(&buffer, oldLen + size)) {
-    return;
+  if (!mBuffer.GetMutableData(&buffer, size.value())) {
+    IDB_REPORT_INTERNAL_ERR();
+    return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
   }
   buffer += oldLen;
 
   // Write type marker
   *(buffer++) = aType;
 
   // Encode string
   for (const T* iter = start; iter < end; ++iter) {
@@ -476,16 +498,18 @@ Key::EncodeAsString(const T* aStart, con
       *(buffer++) = (char)c;
     }
   }
 
   // Write end marker
   *(buffer++) = eTerminator;
 
   NS_ASSERTION(buffer == mBuffer.EndReading(), "Wrote wrong number of bytes");
+
+  return NS_OK;
 }
 
 #ifdef ENABLE_INTL_API
 nsresult
 Key::EncodeLocaleString(const nsDependentString& aString, uint8_t aTypeOffset,
                         const nsCString& aLocale)
 {
   const int length = aString.Length();
@@ -512,20 +536,19 @@ Key::EncodeLocaleString(const nsDependen
                                     sortKeyLength);
   }
 
   ucol_close(collator);
   if (NS_WARN_IF(sortKeyLength == 0)) {
     return NS_ERROR_FAILURE;
   }
 
-  EncodeString(keyBuffer.Elements(),
-               keyBuffer.Elements()+sortKeyLength,
-               aTypeOffset);
-  return NS_OK;
+  return EncodeString(keyBuffer.Elements(),
+                      keyBuffer.Elements()+sortKeyLength,
+                      aTypeOffset);
 }
 #endif
 
 // static
 nsresult
 Key::DecodeJSVal(const unsigned char*& aPos,
                  const unsigned char* aEnd,
                  JSContext* aCx,
@@ -635,30 +658,30 @@ Key::DecodeNumber(const unsigned char*& 
   // Note: The subtraction from 0 below is necessary to fix
   // MSVC build warning C4146 (negating an unsigned value).
   const uint64_t signbit = FloatingPoint<double>::kSignBit;
   uint64_t bits = number & signbit ? (number & ~signbit) : (0 - number);
 
   return BitwiseCast<double>(bits);
 }
 
-void
+nsresult
 Key::EncodeBinary(JSObject* aObject, bool aIsViewObject, uint8_t aTypeOffset)
 {
   uint8_t* bufferData;
   uint32_t bufferLength;
   bool unused;
 
   if (aIsViewObject) {
     js::GetArrayBufferViewLengthAndData(aObject, &bufferLength, &unused, &bufferData);
   } else {
     js::GetArrayBufferLengthAndData(aObject, &bufferLength, &unused, &bufferData);
   }
 
-  EncodeAsString(bufferData, bufferData + bufferLength, eBinary + aTypeOffset);
+  return EncodeAsString(bufferData, bufferData + bufferLength, eBinary + aTypeOffset);
 }
 
 // static
 JSObject*
 Key::DecodeBinary(const unsigned char*& aPos,
                   const unsigned char* aEnd,
                   JSContext* aCx)
 {
--- a/dom/indexedDB/Key.h
+++ b/dom/indexedDB/Key.h
@@ -282,37 +282,37 @@ private:
 
     mBuffer.Truncate(end + 1 - mBuffer.BeginReading());
   }
 
   // Encoding functions. These append the encoded value to the end of mBuffer
   nsresult
   EncodeJSVal(JSContext* aCx, JS::Handle<JS::Value> aVal, uint8_t aTypeOffset);
 
-  void
+  nsresult
   EncodeString(const nsAString& aString, uint8_t aTypeOffset);
 
   template <typename T>
-  void
+  nsresult
   EncodeString(const T* aStart, const T* aEnd, uint8_t aTypeOffset);
 
   template <typename T>
-  void
+  nsresult
   EncodeAsString(const T* aStart, const T* aEnd, uint8_t aType);
 
 #ifdef ENABLE_INTL_API
   nsresult
   EncodeLocaleString(const nsDependentString& aString, uint8_t aTypeOffset,
                      const nsCString& aLocale);
 #endif
 
   void
   EncodeNumber(double aFloat, uint8_t aType);
 
-  void
+  nsresult
   EncodeBinary(JSObject* aObject, bool aIsViewObject, uint8_t aTypeOffset);
 
   // Decoding functions. aPos points into mBuffer and is adjusted to point
   // past the consumed value.
   static nsresult
   DecodeJSVal(const unsigned char*& aPos,
               const unsigned char* aEnd,
               JSContext* aCx,
--- a/dom/indexedDB/ScriptErrorHelper.cpp
+++ b/dom/indexedDB/ScriptErrorHelper.cpp
@@ -209,19 +209,17 @@ ScriptErrorHelper::Dump(const nsAString&
       new ScriptErrorRunnable(aMessage,
                               aFilename,
                               aLineNumber,
                               aColumnNumber,
                               aSeverityFlag,
                               aIsChrome,
                               aInnerWindowID);
     MOZ_ALWAYS_SUCCEEDS(
-      SystemGroup::Dispatch("indexedDB::ScriptErrorHelper::Dump",
-                            TaskCategory::Other,
-                            runnable.forget()));
+      SystemGroup::Dispatch(TaskCategory::Other, runnable.forget()));
   }
 }
 
 /*static*/ void
 ScriptErrorHelper::DumpLocalizedMessage(const nsACString& aMessageName,
                                         const nsAString& aFilename,
                                         uint32_t aLineNumber,
                                         uint32_t aColumnNumber,
@@ -242,17 +240,15 @@ ScriptErrorHelper::DumpLocalizedMessage(
       new ScriptErrorRunnable(aMessageName,
                               aFilename,
                               aLineNumber,
                               aColumnNumber,
                               aSeverityFlag,
                               aIsChrome,
                               aInnerWindowID);
     MOZ_ALWAYS_SUCCEEDS(
-      SystemGroup::Dispatch("indexedDB::ScriptErrorHelper::DumpLocalizedMessage",
-                            TaskCategory::Other,
-                            runnable.forget()));
+      SystemGroup::Dispatch(TaskCategory::Other, runnable.forget()));
   }
 }
 
 } // namespace indexedDB
 } // namespace dom
 } // namespace mozilla
--- a/dom/interfaces/payments/nsIPaymentActionResponse.idl
+++ b/dom/interfaces/payments/nsIPaymentActionResponse.idl
@@ -1,15 +1,57 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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 "nsISupports.idl"
 #include "nsIVariant.idl"
+#include "nsIPaymentAddress.idl"
+
+[builtinclass, scriptable, uuid(2a338575-c688-40ee-a157-7488ab292ef2)]
+interface nsIPaymentResponseData: nsISupports
+{
+  const uint32_t GENERAL_RESPONSE = 0;
+  const uint32_t BASICCARD_RESPONSE = 1;
+
+  readonly attribute uint32_t type;
+  void init(in uint32_t aType);
+};
+
+[builtinclass, scriptable, uuid(b986773e-2b30-4ed2-b8fe-6a96631c8000)]
+interface nsIGeneralResponseData : nsIPaymentResponseData
+{
+  readonly attribute AString data;
+
+  [implicit_jscontext]
+  void initData(in jsval aData);
+};
+
+/*
+  Since PaymentAddress is an no constructor interface type, UI code can not
+  easy create PaymentAddress by calling new PaymentAddress().
+  Unfortunately, BasicCardResponse has a PaymentAddress attribute, billingAddress
+  , it means UI can not create BsaicCardResponse by calling the init() with a
+  given JSObject directly, because PaymentAddress creation in JS code is hard.
+  To let UI code can create BasicCardResponse easier, nsIBasicCardResponse is
+  provided for UI by passing the raw data of BasicCardResponse,
+*/
+[builtinclass, scriptable, uuid(0d55a5e6-d185-44f0-b992-a8e1321e4bce)]
+interface nsIBasicCardResponseData : nsIPaymentResponseData
+{
+  readonly attribute AString data;
+
+  void initData(in AString aCardholderName,
+                in AString aCardNumber,
+                in AString aExpiryMonth,
+                in AString aExpiryYear,
+                in AString aCardSecurityCode,
+                in nsIPaymentAddress billingAddress);
+};
 
 [builtinclass, scriptable, uuid(a607c095-ef60-4a9b-a3d0-0506c60728b3)]
 interface nsIPaymentActionResponse : nsISupports
 {
   /*
    *  Align type to nsIPaymentActionRequest types,
    *  where 1 is for payment request creation.
    *  the action expects no response from UI module.
@@ -89,17 +131,17 @@ interface nsIPaymentShowActionResponse :
   readonly attribute AString payerPhone;
 
   /*
    *  Initialize function for this response.
    */
   void init(in AString aRequestId,
             in uint32_t aAcceptStatus,
             in AString aMethodName,
-            in AString aData,
+            in nsIPaymentResponseData aData,
             in AString aPayerName,
             in AString aPayerEmail,
             in AString aPayerPhone);
 
   /*
    *  Check if the payment is accpeted
    */
   bool isAccepted();
@@ -137,16 +179,26 @@ interface nsIPaymentCompleteActionRespon
 
   /*
    *  Check if the UI is finished.
    */
   bool isCompleted();
 };
 
 %{C++
+#define NS_GENERAL_RESPONSE_DATA_CID \
+  { 0xb986773e, 0x2b30, 0x4ed2, { 0xb8, 0xfe, 0x6a, 0x96, 0x63, 0x1c, 0x80, 0x00 } }
+#define NS_GENERAL_RESPONSE_DATA_CONTRACT_ID \
+  "@mozilla.org/dom/payments/general-response-data;1"
+
+#define NS_BASICCARD_RESPONSE_DATA_CID \
+  { 0x0d55a5e6, 0xd185, 0x44f0, { 0xb9, 0x92, 0xa8, 0xe1, 0x32, 0x1e, 0x4b, 0xce } }
+#define NS_BASICCARD_RESPONSE_DATA_CONTRACT_ID \
+  "@mozilla.org/dom/payments/basiccard-response-data;1"
+
 #define NS_PAYMENT_CANMAKE_ACTION_RESPONSE_CID \
   { 0x52fc3f9f, 0xc0cb, 0x4874, { 0xb3, 0xd4, 0xee, 0x4b, 0x6e, 0x9c, 0xbe, 0x9c } }
 #define NS_PAYMENT_CANMAKE_ACTION_RESPONSE_CONTRACT_ID \
   "@mozilla.org/dom/payments/payment-canmake-action-response;1"
 
 #define NS_PAYMENT_SHOW_ACTION_RESPONSE_CID \
   { 0x184385cb, 0x2d35, 0x4b99, { 0xa9, 0xa3, 0x7c, 0x78, 0x0b, 0xf6, 0x6b, 0x9b } }
 #define NS_PAYMENT_SHOW_ACTION_RESPONSE_CONTRACT_ID \
--- a/dom/interfaces/payments/nsIPaymentRequest.idl
+++ b/dom/interfaces/payments/nsIPaymentRequest.idl
@@ -7,17 +7,18 @@
 #include "nsIVariant.idl"
 
 interface nsIArray;
 
 [scriptable, builtinclass, uuid(2fe296cc-d917-4820-b492-aa42df23f9b4)]
 interface nsIPaymentMethodData : nsISupports
 {
   readonly attribute AString supportedMethods;
-  readonly attribute AString data;
+  [implicit_jscontext]
+  readonly attribute jsval data;
 };
 
 [scriptable, builtinclass, uuid(d22a6f5f-767b-4fea-bf92-68b0b8003eba)]
 interface nsIPaymentCurrencyAmount : nsISupports
 {
   readonly attribute AString currency;
   readonly attribute AString value;
 };
@@ -31,17 +32,18 @@ interface nsIPaymentItem : nsISupports
 };
 
 [scriptable, builtinclass, uuid(74259861-c318-40e8-b3d5-518e701bed80)]
 interface nsIPaymentDetailsModifier : nsISupports
 {
   readonly attribute AString supportedMethods;
   readonly attribute nsIPaymentItem total;
   readonly attribute nsIArray additionalDisplayItems;
-  readonly attribute AString data;
+  [implicit_jscontext]
+  readonly attribute jsval data;
 };
 
 [scriptable, builtinclass, uuid(68341551-3605-4381-b936-41e830aa88fb)]
 interface nsIPaymentShippingOption : nsISupports
 {
   readonly attribute AString id;
   readonly attribute AString label;
   readonly attribute nsIPaymentCurrencyAmount amount;
--- a/dom/ipc/ProcessPriorityManager.cpp
+++ b/dom/ipc/ProcessPriorityManager.cpp
@@ -13,16 +13,17 @@
 #include "mozilla/IntegerPrintfMacros.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
 #include "mozilla/Unused.h"
 #include "mozilla/Logging.h"
 #include "nsPrintfCString.h"
 #include "nsXULAppAPI.h"
 #include "nsIFrameLoader.h"
+#include "nsINamed.h"
 #include "nsIObserverService.h"
 #include "StaticPtr.h"
 #include "nsIMozBrowserFrame.h"
 #include "nsIObserver.h"
 #include "nsITimer.h"
 #include "nsIPropertyBag2.h"
 #include "nsComponentManagerUtils.h"
 #include "nsCRT.h"
@@ -221,16 +222,17 @@ private:
 /**
  * This class manages the priority of one particular process.  It is
  * main-process only.
  */
 class ParticularProcessPriorityManager final
   : public WakeLockObserver
   , public nsIObserver
   , public nsITimerCallback
+  , public nsINamed
   , public nsSupportsWeakReference
 {
   ~ParticularProcessPriorityManager();
 public:
   explicit ParticularProcessPriorityManager(ContentParent* aContentParent);
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSIOBSERVER
@@ -269,16 +271,22 @@ public:
   void ResetPriority();
   void ResetPriorityNow();
   void SetPriorityNow(ProcessPriority aPriority);
 
   void TabActivityChanged(TabParent* aTabParent, bool aIsActive);
 
   void ShutDown();
 
+  NS_IMETHOD GetName(nsACString& aName) override
+  {
+    aName.AssignLiteral("ParticularProcessPriorityManager");
+    return NS_OK;
+  }
+
 private:
   static uint32_t sBackgroundPerceivableGracePeriodMS;
   static uint32_t sBackgroundGracePeriodMS;
 
   void FireTestOnlyObserverNotification(
     const char* aTopic,
     const nsACString& aData = EmptyCString());
 
@@ -556,17 +564,18 @@ ProcessPriorityManagerImpl::TabActivityC
   }
 
   pppm->TabActivityChanged(aTabParent, aIsActive);
 }
 
 NS_IMPL_ISUPPORTS(ParticularProcessPriorityManager,
                   nsIObserver,
                   nsITimerCallback,
-                  nsISupportsWeakReference);
+                  nsISupportsWeakReference,
+                  nsINamed);
 
 ParticularProcessPriorityManager::ParticularProcessPriorityManager(
   ContentParent* aContentParent)
   : mContentParent(aContentParent)
   , mChildID(aContentParent->ChildID())
   , mPriority(PROCESS_PRIORITY_UNKNOWN)
   , mHoldsCPUWakeLock(false)
   , mHoldsHighPriorityWakeLock(false)
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -3539,24 +3539,23 @@ TabChildGlobal::GetPrincipal()
 JSObject*
 TabChildGlobal::GetGlobalJSObject()
 {
   NS_ENSURE_TRUE(mTabChild, nullptr);
   return mTabChild->GetGlobal();
 }
 
 nsresult
-TabChildGlobal::Dispatch(const char* aName,
-                         TaskCategory aCategory,
+TabChildGlobal::Dispatch(TaskCategory aCategory,
                          already_AddRefed<nsIRunnable>&& aRunnable)
 {
   if (mTabChild && mTabChild->TabGroup()) {
-    return mTabChild->TabGroup()->Dispatch(aName, aCategory, Move(aRunnable));
+    return mTabChild->TabGroup()->Dispatch(aCategory, Move(aRunnable));
   }
-  return DispatcherTrait::Dispatch(aName, aCategory, Move(aRunnable));
+  return DispatcherTrait::Dispatch(aCategory, Move(aRunnable));
 }
 
 nsISerialEventTarget*
 TabChildGlobal::EventTargetFor(TaskCategory aCategory) const
 {
   if (mTabChild && mTabChild->TabGroup()) {
     return mTabChild->TabGroup()->EventTargetFor(aCategory);
   }
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -147,18 +147,17 @@ public:
   virtual JSObject* GetGlobalJSObject() override;
 
   virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto) override
   {
     MOZ_CRASH("TabChildGlobal doesn't use DOM bindings!");
   }
 
   // Dispatch a runnable related to the global.
-  virtual nsresult Dispatch(const char* aName,
-                            mozilla::TaskCategory aCategory,
+  virtual nsresult Dispatch(mozilla::TaskCategory aCategory,
                             already_AddRefed<nsIRunnable>&& aRunnable) override;
 
   virtual nsISerialEventTarget*
   EventTargetFor(mozilla::TaskCategory aCategory) const override;
 
   virtual AbstractThread*
   AbstractMainThreadFor(mozilla::TaskCategory aCategory) override;
 
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -168,16 +168,17 @@ TabParent::TabParent(nsIContentParent* a
 #ifdef DEBUG
   , mActiveSupressDisplayportCount(0)
 #endif
   , mLayerTreeEpoch(0)
   , mPreserveLayers(false)
   , mHasPresented(false)
   , mHasBeforeUnload(false)
   , mIsReadyToHandleInputEvents(false)
+  , mIsMouseEnterIntoWidgetEventSuppressed(false)
 {
   MOZ_ASSERT(aManager);
 }
 
 TabParent::~TabParent()
 {
 }
 
@@ -1087,17 +1088,17 @@ TabParent::SendKeyEvent(const nsAString&
   }
   Unused << PBrowserParent::SendKeyEvent(nsString(aType), aKeyCode, aCharCode,
                                          aModifiers, aPreventDefault);
 }
 
 void
 TabParent::SendRealMouseEvent(WidgetMouseEvent& aEvent)
 {
-  if (mIsDestroyed || !mIsReadyToHandleInputEvents) {
+  if (mIsDestroyed) {
     return;
   }
   aEvent.mRefPoint += GetChildProcessOffset();
 
   nsCOMPtr<nsIWidget> widget = GetWidget();
   if (widget) {
     // When we mouseenter the tab, the tab's cursor should
     // become the current cursor.  When we mouseexit, we stop.
@@ -1108,21 +1109,43 @@ TabParent::SendRealMouseEvent(WidgetMous
                           mCustomCursorHotspotX, mCustomCursorHotspotY);
       } else if (mCursor != nsCursor(-1)) {
         widget->SetCursor(mCursor);
       }
     } else if (eMouseExitFromWidget == aEvent.mMessage) {
       mTabSetsCursor = false;
     }
   }
+  if (!mIsReadyToHandleInputEvents) {
+    if (eMouseEnterIntoWidget == aEvent.mMessage) {
+      MOZ_ASSERT(!mIsMouseEnterIntoWidgetEventSuppressed);
+      mIsMouseEnterIntoWidgetEventSuppressed = true;
+    } else if (eMouseExitFromWidget == aEvent.mMessage) {
+      MOZ_ASSERT(mIsMouseEnterIntoWidgetEventSuppressed);
+      mIsMouseEnterIntoWidgetEventSuppressed = false;
+    }
+    return;
+  }
 
   ScrollableLayerGuid guid;
   uint64_t blockId;
   ApzAwareEventRoutingToChild(&guid, &blockId, nullptr);
 
+  if (mIsMouseEnterIntoWidgetEventSuppressed) {
+    // In the case that the TabParent suppressed the eMouseEnterWidget event due
+    // to its corresponding TabChild wasn't ready to handle it, we have to
+    // resend it when the TabChild is ready.
+    mIsMouseEnterIntoWidgetEventSuppressed = false;
+    WidgetMouseEvent localEvent(aEvent);
+    localEvent.mMessage = eMouseEnterIntoWidget;
+    DebugOnly<bool> ret = SendRealMouseButtonEvent(localEvent, guid, blockId);
+    NS_WARNING_ASSERTION(ret, "SendRealMouseButtonEvent(eMouseEnterIntoWidget) failed");
+    MOZ_ASSERT(!ret || localEvent.HasBeenPostedToRemoteProcess());
+  }
+
   if (eMouseMove == aEvent.mMessage) {
     if (aEvent.mReason == WidgetMouseEvent::eSynthesized) {
       DebugOnly<bool> ret = SendSynthMouseMoveEvent(aEvent, guid, blockId);
       NS_WARNING_ASSERTION(ret, "SendSynthMouseMoveEvent() failed");
       MOZ_ASSERT(!ret || aEvent.HasBeenPostedToRemoteProcess());
       return;
     }
     DebugOnly<bool> ret = SendRealMouseMoveEvent(aEvent, guid, blockId);
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -781,16 +781,21 @@ private:
 
   // True if at least one window hosted in the TabChild has added a
   // beforeunload event listener.
   bool mHasBeforeUnload;
 
   // True when the remote browser is created and ready to handle input events.
   bool mIsReadyToHandleInputEvents;
 
+  // True if we suppress the eMouseEnterIntoWidget event due to the TabChild was
+  // not ready to handle it. We will resend it when the next time we fire a
+  // mouse event and the TabChild is ready.
+  bool mIsMouseEnterIntoWidgetEventSuppressed;
+
 public:
   static TabParent* GetTabParentFromLayersId(uint64_t aLayersId);
 };
 
 struct MOZ_STACK_CLASS TabParent::AutoUseNewTab final
 {
 public:
   AutoUseNewTab(TabParent* aNewTab, nsCString* aURLToLoad)
--- a/dom/locales/en-US/chrome/dom/dom.properties
+++ b/dom/locales/en-US/chrome/dom/dom.properties
@@ -182,17 +182,17 @@ UseOfDOM3LoadMethodWarning=Use of docume
 # LOCALIZATION NOTE: Do not translate "window.showModalDialog()" or "window.open()"
 ShowModalDialogWarning=Use of window.showModalDialog() is deprecated. Use window.open() instead. For more help https://developer.mozilla.org/en-US/docs/Web/API/Window.open
 # LOCALIZATION NOTE: Do not translate "window._content" or "window.content"
 Window_ContentWarning=window._content is deprecated.  Please use window.content instead.
 # LOCALIZATION NOTE: Do not translate "XMLHttpRequest"
 SyncXMLHttpRequestWarning=Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user’s experience. For more help http://xhr.spec.whatwg.org/
 ImplicitMetaViewportTagFallback=No meta-viewport tag found. Please explicitly specify one to prevent unexpected behavioural changes in future versions. For more help https://developer.mozilla.org/en/docs/Mozilla/Mobile/Viewport_meta_tag
 # LOCALIZATION NOTE: Do not translate "window.controllers/Controllers"
-Window_ControllersWarning=window.controllers/Controllers is deprecated. Do not use it for UA detection.
+Window_Cc_ontrollersWarning=window.controllers/Controllers is deprecated. Do not use it for UA detection.
 ImportXULIntoContentWarning=Importing XUL nodes into a content document is deprecated. This functionality may be removed soon.
 XMLDocumentLoadPrincipalMismatch=Use of document.load forbidden on Documents that come from other Windows. Only the Window in which a Document was created is allowed to call .load on that Document. Preferably, use XMLHttpRequest instead.
 # LOCALIZATION NOTE: Do not translate "IndexedDB".
 IndexedDBTransactionAbortNavigation=An IndexedDB transaction that was not yet complete has been aborted due to page navigation.
 # LOCALIZATION NOTE: Do not translate Will-change, %1$S,%2$S are numbers.
 IgnoringWillChangeOverBudgetWarning=Will-change memory consumption is too high. Budget limit is the document surface area multiplied by %1$S (%2$S px). Occurrences of will-change over the budget will be ignored.
 # LOCALIZATION NOTE: Do not translate "Worker".
 HittingMaxWorkersPerDomain2=A Worker could not be started immediately because other documents in the same origin are already using the maximum number of workers. The Worker is now queued and will be started after some of the other workers have completed.
--- a/dom/media/DecoderDoctorDiagnostics.cpp
+++ b/dom/media/DecoderDoctorDiagnostics.cpp
@@ -821,22 +821,16 @@ DecoderDoctorDocumentWatcher::Notify(nsI
 
 NS_IMETHODIMP
 DecoderDoctorDocumentWatcher::GetName(nsACString& aName)
 {
   aName.AssignASCII("DecoderDoctorDocumentWatcher_timer");
   return NS_OK;
 }
 
-NS_IMETHODIMP
-DecoderDoctorDocumentWatcher::SetName(const char* aName)
-{
-  return NS_ERROR_NOT_IMPLEMENTED;
-}
-
 void
 DecoderDoctorDiagnostics::StoreFormatDiagnostics(nsIDocument* aDocument,
                                                  const nsAString& aFormat,
                                                  bool aCanPlay,
                                                  const char* aCallSite)
 {
   MOZ_ASSERT(NS_IsMainThread());
   // Make sure we only store once.
--- a/dom/media/FileBlockCache.cpp
+++ b/dom/media/FileBlockCache.cpp
@@ -201,18 +201,17 @@ FileBlockCache::Close()
                              // nsIThread::Shutdown()
                              // can cause events to run before it completes,
                              // which could end up
                              // opening more streams, while the media cache is
                              // shutting down and
                              // releasing memory etc!
                              nsCOMPtr<nsIRunnable> event =
                                new ShutdownThreadEvent(thread);
-                             SystemGroup::Dispatch("ShutdownThreadEvent",
-                                                   TaskCategory::Other,
+                             SystemGroup::Dispatch(TaskCategory::Other,
                                                    event.forget());
                            }),
     NS_DISPATCH_NORMAL);
   NS_ENSURE_SUCCESS_VOID(rv);
 }
 
 template<typename Container, typename Value>
 bool
--- a/dom/media/MediaCache.cpp
+++ b/dom/media/MediaCache.cpp
@@ -1504,19 +1504,17 @@ MediaCache::QueueUpdate()
                "Queuing an update while we're in an update");
   if (mUpdateQueued)
     return;
   mUpdateQueued = true;
   // XXX MediaCache does updates when decoders are still running at
   // shutdown and get freed in the final cycle-collector cleanup.  So
   // don't leak a runnable in that case.
   nsCOMPtr<nsIRunnable> event = new UpdateEvent(this);
-  SystemGroup::Dispatch("MediaCache::UpdateEvent",
-                        TaskCategory::Other,
-                        event.forget());
+  SystemGroup::Dispatch(TaskCategory::Other, event.forget());
 }
 
 void
 MediaCache::QueueSuspendedStatusUpdate(int64_t aResourceID)
 {
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
   if (!mSuspendedStatusToNotify.Contains(aResourceID)) {
     mSuspendedStatusToNotify.AppendElement(aResourceID);
--- a/dom/media/MediaDevices.cpp
+++ b/dom/media/MediaDevices.cpp
@@ -5,46 +5,53 @@
 #include "mozilla/dom/MediaDevices.h"
 #include "mozilla/dom/MediaStreamBinding.h"
 #include "mozilla/dom/MediaDeviceInfo.h"
 #include "mozilla/dom/MediaDevicesBinding.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/MediaManager.h"
 #include "MediaTrackConstraints.h"
 #include "nsIEventTarget.h"
+#include "nsINamed.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIPermissionManager.h"
 #include "nsPIDOMWindow.h"
 #include "nsQueryObject.h"
 
 #define DEVICECHANGE_HOLD_TIME_IN_MS 1000
 
 namespace mozilla {
 namespace dom {
 
-class FuzzTimerCallBack final : public nsITimerCallback
+class FuzzTimerCallBack final : public nsITimerCallback, public nsINamed
 {
   ~FuzzTimerCallBack() {}
 
 public:
   explicit FuzzTimerCallBack(MediaDevices* aMediaDevices) : mMediaDevices(aMediaDevices) {}
 
   NS_DECL_ISUPPORTS
 
   NS_IMETHOD Notify(nsITimer* aTimer) final
   {
     mMediaDevices->DispatchTrustedEvent(NS_LITERAL_STRING("devicechange"));
     return NS_OK;
   }
 
+  NS_IMETHOD GetName(nsACString& aName) override
+  {
+    aName.AssignLiteral("FuzzTimerCallBack");
+    return NS_OK;
+  }
+
 private:
   nsCOMPtr<MediaDevices> mMediaDevices;
 };
 
-NS_IMPL_ISUPPORTS(FuzzTimerCallBack, nsITimerCallback)
+NS_IMPL_ISUPPORTS(FuzzTimerCallBack, nsITimerCallback, nsINamed)
 
 class MediaDevices::GumResolver : public nsIDOMGetUserMediaSuccessCallback
 {
 public:
   NS_DECL_ISUPPORTS
 
   explicit GumResolver(Promise* aPromise) : mPromise(aPromise) {}
 
--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -112,17 +112,16 @@ private:
   GlobalAllocPolicy& mPolicy; // reference to a singleton object.
 };
 
 GlobalAllocPolicy::GlobalAllocPolicy()
   : mMonitor("DecoderAllocPolicy::mMonitor")
   , mDecoderLimit(MediaPrefs::MediaDecoderLimit())
 {
   SystemGroup::Dispatch(
-    "GlobalAllocPolicy::ClearOnShutdown",
     TaskCategory::Other,
     NS_NewRunnableFunction("GlobalAllocPolicy::GlobalAllocPolicy", [this]() {
       ClearOnShutdown(this, ShutdownPhase::ShutdownThreads);
     }));
 }
 
 GlobalAllocPolicy::~GlobalAllocPolicy()
 {
--- a/dom/media/MediaQueue.h
+++ b/dom/media/MediaQueue.h
@@ -1,17 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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 !defined(MediaQueue_h_)
 #define MediaQueue_h_
 
-#include "mozilla/ReentrantMonitor.h"
+#include "mozilla/RecursiveMutex.h"
 #include "mozilla/TaskQueue.h"
 
 #include "nsDeque.h"
 #include "MediaEventSource.h"
 #include "TimeUnits.h"
 
 namespace mozilla {
 
@@ -24,103 +24,103 @@ class MediaQueueDeallocator : public nsD
   }
 };
 
 template <class T>
 class MediaQueue : private nsDeque {
 public:
   MediaQueue()
     : nsDeque(new MediaQueueDeallocator<T>()),
-      mReentrantMonitor("mediaqueue"),
+      mRecursiveMutex("mediaqueue"),
       mEndOfStream(false)
   {}
 
   ~MediaQueue() {
     Reset();
   }
 
   inline size_t GetSize() const {
-    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+    RecursiveMutexAutoLock lock(mRecursiveMutex);
     return nsDeque::GetSize();
   }
 
   inline void Push(T* aItem) {
-    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+    RecursiveMutexAutoLock lock(mRecursiveMutex);
     MOZ_ASSERT(!mEndOfStream);
     MOZ_ASSERT(aItem);
     NS_ADDREF(aItem);
     MOZ_ASSERT(aItem->GetEndTime() >= aItem->mTime);
     nsDeque::Push(aItem);
     mPushEvent.Notify(RefPtr<T>(aItem));
   }
 
   inline already_AddRefed<T> PopFront() {
-    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+    RecursiveMutexAutoLock lock(mRecursiveMutex);
     RefPtr<T> rv = dont_AddRef(static_cast<T*>(nsDeque::PopFront()));
     if (rv) {
       mPopEvent.Notify(rv);
     }
     return rv.forget();
   }
 
   inline RefPtr<T> PeekFront() const {
-    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+    RecursiveMutexAutoLock lock(mRecursiveMutex);
     return static_cast<T*>(nsDeque::PeekFront());
   }
 
   void Reset() {
-    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+    RecursiveMutexAutoLock lock(mRecursiveMutex);
     while (GetSize() > 0) {
       RefPtr<T> x = dont_AddRef(static_cast<T*>(nsDeque::PopFront()));
     }
     mEndOfStream = false;
   }
 
   bool AtEndOfStream() const {
-    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+    RecursiveMutexAutoLock lock(mRecursiveMutex);
     return GetSize() == 0 && mEndOfStream;
   }
 
   // Returns true if the media queue has had its last item added to it.
   // This happens when the media stream has been completely decoded. Note this
   // does not mean that the corresponding stream has finished playback.
   bool IsFinished() const {
-    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+    RecursiveMutexAutoLock lock(mRecursiveMutex);
     return mEndOfStream;
   }
 
   // Informs the media queue that it won't be receiving any more items.
   void Finish() {
-    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+    RecursiveMutexAutoLock lock(mRecursiveMutex);
     if (!mEndOfStream) {
       mEndOfStream = true;
       mFinishEvent.Notify();
     }
   }
 
   // Returns the approximate number of microseconds of items in the queue.
   int64_t Duration() {
-    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+    RecursiveMutexAutoLock lock(mRecursiveMutex);
     if (GetSize() == 0) {
       return 0;
     }
     T* last = static_cast<T*>(nsDeque::Peek());
     T* first = static_cast<T*>(nsDeque::PeekFront());
     return (last->GetEndTime() - first->mTime).ToMicroseconds();
   }
 
   void LockedForEach(nsDequeFunctor& aFunctor) const {
-    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+    RecursiveMutexAutoLock lock(mRecursiveMutex);
     ForEach(aFunctor);
   }
 
   // Extracts elements from the queue into aResult, in order.
   // Elements whose start time is before aTime are ignored.
   void GetElementsAfter(int64_t aTime, nsTArray<RefPtr<T>>* aResult) {
-    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+    RecursiveMutexAutoLock lock(mRecursiveMutex);
     if (GetSize() == 0)
       return;
     size_t i;
     for (i = GetSize() - 1; i > 0; --i) {
       T* v = static_cast<T*>(ObjectAt(i));
       if (v->GetEndTime().ToMicroseconds() < aTime)
         break;
     }
@@ -133,24 +133,24 @@ public:
   }
 
   void GetElementsAfter(const media::TimeUnit& aTime,
                         nsTArray<RefPtr<T>>* aResult) {
     GetElementsAfter(aTime.ToMicroseconds(), aResult);
   }
 
   void GetFirstElements(uint32_t aMaxElements, nsTArray<RefPtr<T>>* aResult) {
-    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+    RecursiveMutexAutoLock lock(mRecursiveMutex);
     for (size_t i = 0; i < aMaxElements && i < GetSize(); ++i) {
       *aResult->AppendElement() = static_cast<T*>(ObjectAt(i));
     }
   }
 
   uint32_t FrameCount() {
-    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+    RecursiveMutexAutoLock lock(mRecursiveMutex);
     uint32_t frames = 0;
     for (size_t i = 0; i < GetSize(); ++i) {
       T* v = static_cast<T*>(ObjectAt(i));
       frames += v->mFrames;
     }
     return frames;
   }
 
@@ -162,17 +162,17 @@ public:
     return mPushEvent;
   }
 
   MediaEventSource<void>& FinishEvent() {
     return mFinishEvent;
   }
 
 private:
-  mutable ReentrantMonitor mReentrantMonitor;
+  mutable RecursiveMutex mRecursiveMutex;
   MediaEventProducer<RefPtr<T>> mPopEvent;
   MediaEventProducer<RefPtr<T>> mPushEvent;
   MediaEventProducer<void> mFinishEvent;
   // True when we've decoded the last frame of data in the
   // bitstream for which we're queueing frame data.
   bool mEndOfStream;
 };
 
--- a/dom/media/MediaResource.cpp
+++ b/dom/media/MediaResource.cpp
@@ -59,17 +59,16 @@ void
 MediaResource::Destroy()
 {
   // Ensures we only delete the MediaResource on the main thread.
   if (NS_IsMainThread()) {
     delete this;
     return;
   }
   nsresult rv = SystemGroup::Dispatch(
-    "MediaResource::Destroy",
     TaskCategory::Other,
     NewNonOwningRunnableMethod(
       "MediaResource::Destroy", this, &MediaResource::Destroy));
   MOZ_ALWAYS_SUCCEEDS(rv);
 }
 
 NS_IMPL_ADDREF(MediaResource)
 NS_IMPL_RELEASE_WITH_DESTROY(MediaResource, Destroy())
--- a/dom/media/MediaStreamGraph.cpp
+++ b/dom/media/MediaStreamGraph.cpp
@@ -1526,16 +1526,22 @@ MediaStreamGraphImpl::Notify(nsITimer* a
   MonitorAutoLock lock(mMonitor);
   NS_ASSERTION(!mForceShutdownTicket, "MediaStreamGraph took too long to shut down!");
   // Sigh, graph took too long to shut down.  Stop blocking system
   // shutdown and hope all is well.
   mForceShutdownTicket = nullptr;
   return NS_OK;
 }
 
+NS_IMETHODIMP
+MediaStreamGraphImpl::GetName(nsACString& aName)
+{
+  aName.AssignLiteral("MediaStreamGraphImpl");
+  return NS_OK;
+}
 
 /* static */ StaticRefPtr<nsIAsyncShutdownBlocker> gMediaStreamGraphShutdownBlocker;
 
 namespace {
 
 class MediaStreamGraphShutDownRunnable : public Runnable {
 public:
   explicit MediaStreamGraphShutDownRunnable(MediaStreamGraphImpl* aGraph)
@@ -3618,17 +3624,18 @@ MediaStreamGraph::DestroyNonRealtimeInst
 
   if (!graph->mNonRealtimeProcessing) {
     // Start the graph, but don't produce anything
     graph->StartNonRealtimeProcessing(0);
   }
   graph->ForceShutDown(nullptr);
 }
 
-NS_IMPL_ISUPPORTS(MediaStreamGraphImpl, nsIMemoryReporter, nsITimerCallback)
+NS_IMPL_ISUPPORTS(MediaStreamGraphImpl, nsIMemoryReporter, nsITimerCallback,
+                  nsINamed)
 
 NS_IMETHODIMP
 MediaStreamGraphImpl::CollectReports(nsIHandleReportCallback* aHandleReport,
                                      nsISupports* aData, bool aAnonymize)
 {
   if (mLifecycleState >= LIFECYCLE_WAITING_FOR_THREAD_SHUTDOWN) {
     // Shutting down, nothing to report.
     FinishCollectReports(aHandleReport, aData, nsTArray<AudioNodeSizes>());
--- a/dom/media/MediaStreamGraphImpl.h
+++ b/dom/media/MediaStreamGraphImpl.h
@@ -9,16 +9,17 @@
 #include "MediaStreamGraph.h"
 
 #include "nsDataHashtable.h"
 
 #include "nsITimer.h"
 #include "mozilla/Monitor.h"
 #include "mozilla/TimeStamp.h"
 #include "nsIMemoryReporter.h"
+#include "nsINamed.h"
 #include "nsIThread.h"
 #include "nsIRunnable.h"
 #include "nsIAsyncShutdown.h"
 #include "Latency.h"
 #include "mozilla/Services.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/WeakPtr.h"
 #include "GraphDriver.h"
@@ -94,22 +95,24 @@ public:
  * be able to friend it.
  *
  * There can be multiple MediaStreamGraph per process: one per AudioChannel.
  * Additionaly, each OfflineAudioContext object creates its own MediaStreamGraph
  * object too.
  */
 class MediaStreamGraphImpl : public MediaStreamGraph,
                              public nsIMemoryReporter,
-                             public nsITimerCallback
+                             public nsITimerCallback,
+                             public nsINamed
 {
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIMEMORYREPORTER
   NS_DECL_NSITIMERCALLBACK
+  NS_DECL_NSINAMED
 
   /**
    * Use aGraphDriverRequested with SYSTEM_THREAD_DRIVER or AUDIO_THREAD_DRIVER
    * to create a MediaStreamGraph which provides support for real-time audio
    * and/or video.  Set it to OFFLINE_THREAD_DRIVER in order to create a
    * non-realtime instance which just churns through its inputs and produces
    * output.  Those objects currently only support audio, and are used to
    * implement OfflineAudioContext.  They do not support MediaStream inputs.
--- a/dom/media/TextTrack.cpp
+++ b/dom/media/TextTrack.cpp
@@ -332,17 +332,16 @@ void
 TextTrack::DispatchAsyncTrustedEvent(const nsString& aEventName)
 {
   nsPIDOMWindowInner* win = GetOwner();
   if (!win) {
     return;
   }
   RefPtr<TextTrack> self = this;
   nsGlobalWindow::Cast(win)->Dispatch(
-    "TextTrack::DispatchAsyncTrustedEvent",
     TaskCategory::Other,
     NS_NewRunnableFunction(
       "dom::TextTrack::DispatchAsyncTrustedEvent",
       [self, aEventName]() { self->DispatchTrustedEvent(aEventName); }));
 }
 
 bool
 TextTrack::IsLoaded()
--- a/dom/media/TextTrackList.cpp
+++ b/dom/media/TextTrackList.cpp
@@ -188,19 +188,17 @@ TextTrackList::CreateAndDispatchChangeEv
 
     mPendingTextTrackChange = true;
     RefPtr<Event> event = NS_NewDOMEvent(this, nullptr, nullptr);
 
     event->InitEvent(NS_LITERAL_STRING("change"), false, false);
     event->SetTrusted(true);
 
     nsCOMPtr<nsIRunnable> eventRunner = new ChangeEventRunner(this, event);
-    nsGlobalWindow::Cast(win)->Dispatch(
-      "TextTrackList::CreateAndDispatchChangeEvent", TaskCategory::Other,
-      eventRunner.forget());
+    nsGlobalWindow::Cast(win)->Dispatch(TaskCategory::Other, eventRunner.forget());
   }
 }
 
 void
 TextTrackList::CreateAndDispatchTrackEventRunner(TextTrack* aTrack,
                                                  const nsAString& aEventName)
 {
   DebugOnly<nsresult> rv;
--- a/dom/media/VideoUtils.cpp
+++ b/dom/media/VideoUtils.cpp
@@ -336,16 +336,23 @@ SimpleTimer::Notify(nsITimer *timer) {
   RefPtr<SimpleTimer> deathGrip(this);
   if (mTask) {
     mTask->Run();
     mTask = nullptr;
   }
   return NS_OK;
 }
 
+NS_IMETHODIMP
+SimpleTimer::GetName(nsACString& aName)
+{
+  aName.AssignLiteral("SimpleTimer");
+  return NS_OK;
+}
+
 nsresult
 SimpleTimer::Init(nsIRunnable* aTask, uint32_t aTimeoutMs, nsIEventTarget* aTarget)
 {
   nsresult rv;
 
   // Get target thread first, so we don't have to cancel the timer if it fails.
   nsCOMPtr<nsIEventTarget> target;
   if (aTarget) {
@@ -373,17 +380,17 @@ SimpleTimer::Init(nsIRunnable* aTask, ui
     return rv;
   }
 
   mTimer = timer.forget();
   mTask = aTask;
   return NS_OK;
 }
 
-NS_IMPL_ISUPPORTS(SimpleTimer, nsITimerCallback)
+NS_IMPL_ISUPPORTS(SimpleTimer, nsITimerCallback, nsINamed)
 
 already_AddRefed<SimpleTimer>
 SimpleTimer::Create(nsIRunnable* aTask, uint32_t aTimeoutMs, nsIEventTarget* aTarget)
 {
   RefPtr<SimpleTimer> t(new SimpleTimer());
   if (NS_FAILED(t->Init(aTask, aTimeoutMs, aTarget))) {
     return nullptr;
   }
@@ -392,17 +399,17 @@ SimpleTimer::Create(nsIRunnable* aTask, 
 
 void
 LogToBrowserConsole(const nsAString& aMsg)
 {
   if (!NS_IsMainThread()) {
     nsString msg(aMsg);
     nsCOMPtr<nsIRunnable> task = NS_NewRunnableFunction(
       "LogToBrowserConsole", [msg]() { LogToBrowserConsole(msg); });
-    SystemGroup::Dispatch("LogToBrowserConsole", TaskCategory::Other, task.forget());
+    SystemGroup::Dispatch(TaskCategory::Other, task.forget());
     return;
   }
   nsCOMPtr<nsIConsoleService> console(
     do_GetService("@mozilla.org/consoleservice;1"));
   if (!console) {
     NS_WARNING("Failed to log message to console.");
     return;
   }
--- a/dom/media/VideoUtils.h
+++ b/dom/media/VideoUtils.h
@@ -15,16 +15,17 @@
 #include "mozilla/Attributes.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/MozPromise.h"
 #include "mozilla/ReentrantMonitor.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/UniquePtr.h"
 #include "nsAutoPtr.h"
 #include "nsCOMPtr.h"
+#include "nsINamed.h"
 #include "nsIThread.h"
 #include "nsITimer.h"
 #include "nsRect.h"
 #include "nsSize.h"
 #include "nsThreadUtils.h"
 #include "prtime.h"
 
 using mozilla::CheckedInt64;
@@ -279,20 +280,21 @@ RefPtr<GenericPromise> InvokeUntil(Work 
     }
   };
 
   Helper::Iteration(p, aWork, aCondition);
   return p.forget();
 }
 
 // Simple timer to run a runnable after a timeout.
-class SimpleTimer : public nsITimerCallback
+class SimpleTimer : public nsITimerCallback, public nsINamed
 {
 public:
   NS_DECL_ISUPPORTS
+  NS_DECL_NSINAMED
 
   // Create a new timer to run aTask after aTimeoutMs milliseconds
   // on thread aTarget. If aTarget is null, task is run on the main thread.
   static already_AddRefed<SimpleTimer> Create(nsIRunnable* aTask,
                                               uint32_t aTimeoutMs,
                                               nsIEventTarget* aTarget = nullptr);
   void Cancel();
 
--- a/dom/media/gmp/GMPCrashHelper.cpp
+++ b/dom/media/gmp/GMPCrashHelper.cpp
@@ -14,17 +14,16 @@ NS_IMPL_RELEASE_WITH_DESTROY(GMPCrashHel
 
 void
 GMPCrashHelper::Destroy()
 {
   if (NS_IsMainThread()) {
     delete this;
   } else {
     // Don't addref, as then we'd end up releasing after the detele runs!
-    SystemGroup::Dispatch("GMPCrashHelper::Destroy",
-                          TaskCategory::Other,
+    SystemGroup::Dispatch(TaskCategory::Other,
                           NewNonOwningRunnableMethod("GMPCrashHelper::Destroy",
                                                      this,
                                                      &GMPCrashHelper::Destroy));
   }
 }
 
 } // namespace mozilla
--- a/dom/media/gmp/GMPServiceChild.cpp
+++ b/dom/media/gmp/GMPServiceChild.cpp
@@ -392,17 +392,17 @@ GeckoMediaPluginServiceChild::GetService
     if (!contentChild) {
       return GetServiceChildPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
     }
     MozPromiseHolder<GetServiceChildPromise>* holder = mGetServiceChildPromises.AppendElement();
     RefPtr<GetServiceChildPromise> promise = holder->Ensure(__func__);
     if (mGetServiceChildPromises.Length() == 1) {
       nsCOMPtr<nsIRunnable> r = WrapRunnable(
         contentChild, &dom::ContentChild::SendCreateGMPService);
-      SystemGroup::Dispatch("SendCreateGMPService", TaskCategory::Other, r.forget());
+      SystemGroup::Dispatch(TaskCategory::Other, r.forget());
     }
     return promise;
   }
   return GetServiceChildPromise::CreateAndResolve(mServiceChild.get(), __func__);
 }
 
 void
 GeckoMediaPluginServiceChild::SetServiceChild(UniquePtr<GMPServiceChild>&& aServiceChild)
--- a/dom/media/gtest/GMPTestMonitor.h
+++ b/dom/media/gtest/GMPTestMonitor.h
@@ -30,17 +30,16 @@ private:
     MOZ_ASSERT(NS_IsMainThread());
     mFinished = true;
   }
 
 public:
   void SetFinished()
   {
     mozilla::SystemGroup::Dispatch(
-      "GMPTestMonitor::SetFinished",
       mozilla::TaskCategory::Other,
       mozilla::NewNonOwningRunnableMethod(
         "GMPTestMonitor::MarkFinished", this, &GMPTestMonitor::MarkFinished));
   }
 
 private:
   bool mFinished;
 };
--- a/dom/media/gtest/TestGMPCrossOrigin.cpp
+++ b/dom/media/gtest/TestGMPCrossOrigin.cpp
@@ -423,17 +423,17 @@ private:
 NS_IMPL_ISUPPORTS(ClearGMPStorageTask, nsIRunnable, nsIObserver)
 
 static void
 ClearGMPStorage(already_AddRefed<nsIRunnable> aContinuation,
                 nsIThread* aTarget, PRTime aSince = -1)
 {
   RefPtr<ClearGMPStorageTask> task(
     new ClearGMPStorageTask(Move(aContinuation), aTarget, aSince));
-  SystemGroup::Dispatch("ClearGMPStorage", TaskCategory::Other, task.forget());
+  SystemGroup::Dispatch(TaskCategory::Other, task.forget());
 }
 
 static void
 SimulatePBModeExit()
 {
   // SystemGroup::EventTargetFor() doesn't support NS_DISPATCH_SYNC.
   NS_DispatchToMainThread(new NotifyObserversTask("last-pb-context-exited"), NS_DISPATCH_SYNC);
 }
@@ -794,18 +794,17 @@ class GMPStorageTest : public GMPDecrypt
 
     UniquePtr<NodeInfo> siteInfo(
         new NodeInfo(NS_LITERAL_CSTRING("http://example1.com"),
                      pattern));
     // Collect nodeIds that are expected to remain for later comparison.
     EnumerateGMPStorageDir(NS_LITERAL_CSTRING("id"),
                            NodeIdCollector(siteInfo.get()));
     // Invoke "Forget this site" on the main thread.
-    SystemGroup::Dispatch("TestForgetThisSite_Forget",
-                          TaskCategory::Other,
+    SystemGroup::Dispatch(TaskCategory::Other,
                           NewRunnableMethod<UniquePtr<NodeInfo>&&>(
                             "GMPStorageTest::TestForgetThisSite_Forget",
                             this,
                             &GMPStorageTest::TestForgetThisSite_Forget,
                             Move(siteInfo)));
   }
 
   void TestForgetThisSite_Forget(UniquePtr<NodeInfo>&& aSiteInfo) {
@@ -1214,17 +1213,17 @@ class GMPStorageTest : public GMPDecrypt
       return;
     }
     EXPECT_FALSE(mNodeId.IsEmpty());
     RefPtr<GMPShutdownObserver> task(new GMPShutdownObserver(
       NewRunnableMethod(
         "GMPStorageTest::Shutdown", this, &GMPStorageTest::Shutdown),
       Move(aContinuation),
       mNodeId));
-    SystemGroup::Dispatch("GMPShutdownObserver", TaskCategory::Other, task.forget());
+    SystemGroup::Dispatch(TaskCategory::Other, task.forget());
   }
 
   void Shutdown() {
     if (mDecryptor) {
       mDecryptor->Close();
       mDecryptor = nullptr;
       mNodeId = EmptyCString();
     }
@@ -1233,17 +1232,17 @@ class GMPStorageTest : public GMPDecrypt
   void Dummy() {
   }
 
   void SetFinished() {
     mFinished = true;
     Shutdown();
     nsCOMPtr<nsIRunnable> task =
       NewRunnableMethod("GMPStorageTest::Dummy", this, &GMPStorageTest::Dummy);
-    SystemGroup::Dispatch("GMPStorageTest::Dummy", TaskCategory::Other, task.forget());
+    SystemGroup::Dispatch(TaskCategory::Other, task.forget());
   }
 
   void SessionMessage(const nsCString& aSessionId,
                       mozilla::dom::MediaKeyMessageType aMessageType,
                       const nsTArray<uint8_t>& aMessage) override
   {
     MonitorAutoLock mon(mMonitor);
 
--- a/dom/media/imagecapture/CaptureTask.cpp
+++ b/dom/media/imagecapture/CaptureTask.cpp
@@ -189,14 +189,12 @@ CaptureTask::PostTrackEndEvent()
     }
 
   protected:
     RefPtr<CaptureTask> mTask;
   };
 
   IC_LOG("Got MediaStream track removed or finished event.");
   nsCOMPtr<nsIRunnable> event = new TrackEndRunnable(this);
-  SystemGroup::Dispatch("CaptureTask::TaskComplete",
-                        TaskCategory::Other,
-                        event.forget());
+  SystemGroup::Dispatch(TaskCategory::Other, event.forget());
 }
 
 } // namespace mozilla
--- a/dom/media/mediasource/AutoTaskQueue.h
+++ b/dom/media/mediasource/AutoTaskQueue.h
@@ -57,16 +57,16 @@ public:
 
 private:
   ~AutoTaskQueue()
   {
     RefPtr<TaskQueue> taskqueue = mTaskQueue;
     nsCOMPtr<nsIRunnable> task =
       NS_NewRunnableFunction("AutoTaskQueue::~AutoTaskQueue",
                              [taskqueue]() { taskqueue->BeginShutdown(); });
-    SystemGroup::Dispatch("~AutoTaskQueue", TaskCategory::Other, task.forget());
+    SystemGroup::Dispatch(TaskCategory::Other, task.forget());
   }
   RefPtr<TaskQueue> mTaskQueue;
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/media/ogg/OggDemuxer.cpp
+++ b/dom/media/ogg/OggDemuxer.cpp
@@ -137,19 +137,17 @@ OggDemuxer::~OggDemuxer()
                 ("OggDemuxer(%p)::%s: Reporting telemetry "
                  "MEDIA_OGG_LOADED_IS_CHAINED=%d",
                  ptr,
                  __func__,
                  isChained));
         Telemetry::Accumulate(
           Telemetry::HistogramID::MEDIA_OGG_LOADED_IS_CHAINED, isChained);
       });
-    SystemGroup::Dispatch("~OggDemuxer::report_telemetry",
-                          TaskCategory::Other,
-                          task.forget());
+    SystemGroup::Dispatch(TaskCategory::Other, task.forget());
   }
 }
 
 void
 OggDemuxer::SetChainingEvents(TimedMetadataEventProducer* aMetadataEvent,
                               MediaEventProducer<void>* aOnSeekableEvent)
 {
   mTimedMetadataEvent = aMetadataEvent;
--- a/dom/media/platforms/wmf/WMFAudioMFTManager.cpp
+++ b/dom/media/platforms/wmf/WMFAudioMFTManager.cpp
@@ -252,19 +252,17 @@ WMFAudioMFTManager::Output(int64_t aStre
 
   if (!sample) {
     LOG("Audio MFTDecoder returned success but null output.");
     nsCOMPtr<nsIRunnable> task = NS_NewRunnableFunction("WMFAudioMFTManager::Output",
                                                         []() -> void {
       LOG("Reporting telemetry AUDIO_MFT_OUTPUT_NULL_SAMPLES");
       Telemetry::Accumulate(Telemetry::HistogramID::AUDIO_MFT_OUTPUT_NULL_SAMPLES, 1);
     });
-    SystemGroup::Dispatch("WMFAudioMFTManager::Output()::report_telemetry",
-                          TaskCategory::Other,
-                          task.forget());
+    SystemGroup::Dispatch(TaskCategory::Other, task.forget());
     return E_FAIL;
   }
 
   RefPtr<IMFMediaBuffer> buffer;
   hr = sample->ConvertToContiguousBuffer(getter_AddRefs(buffer));
   NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
 
   BYTE* data = nullptr; // Note: *data will be owned by the IMFMediaBuffer, we
--- a/dom/media/platforms/wmf/WMFMediaDataDecoder.cpp
+++ b/dom/media/platforms/wmf/WMFMediaDataDecoder.cpp
@@ -62,19 +62,17 @@ SendTelemetry(unsigned long hr)
   }
 
   nsCOMPtr<nsIRunnable> runnable = NS_NewRunnableFunction(
     "SendTelemetry",
     [sample] {
       Telemetry::Accumulate(Telemetry::MEDIA_WMF_DECODE_ERROR, sample);
     });
 
-  SystemGroup::Dispatch("WMFMediaDataDecoder::SendTelemetry",
-                        TaskCategory::Other,
-                        runnable.forget());
+  SystemGroup::Dispatch(TaskCategory::Other, runnable.forget());
 }
 
 RefPtr<ShutdownPromise>
 WMFMediaDataDecoder::Shutdown()
 {
   MOZ_DIAGNOSTIC_ASSERT(!mIsShutDown);
 
   mIsShutDown = true;
--- a/dom/media/platforms/wmf/WMFVideoMFTManager.cpp
+++ b/dom/media/platforms/wmf/WMFVideoMFTManager.cpp
@@ -88,17 +88,17 @@ public:
   }
 private:
   nsAutoPtr<T> mObject;
 };
 
 template<class T>
 void DeleteOnMainThread(nsAutoPtr<T>& aObject) {
   nsCOMPtr<nsIRunnable> r = new DeleteObjectTask<T>(aObject);
-  SystemGroup::Dispatch("VideoUtils::DeleteObjectTask", TaskCategory::Other, r.forget());
+  SystemGroup::Dispatch(TaskCategory::Other, r.forget());
 }
 
 LayersBackend
 GetCompositorBackendType(layers::KnowsCompositor* aKnowsCompositor)
 {
   if (aKnowsCompositor) {
     return aKnowsCompositor->GetCompositorBackendType();
   }
@@ -155,19 +155,17 @@ WMFVideoMFTManager::~WMFVideoMFTManager(
   nsCOMPtr<nsIRunnable> task = NS_NewRunnableFunction("WMFVideoMFTManager::~WMFVideoMFTManager",
                                                       [=]() -> void {
     LOG(nsPrintfCString("Reporting telemetry VIDEO_MFT_OUTPUT_NULL_SAMPLES=%d",
                         telemetry)
         .get());
     Telemetry::Accumulate(Telemetry::HistogramID::VIDEO_MFT_OUTPUT_NULL_SAMPLES,
                           telemetry);
   });
-  SystemGroup::Dispatch("~WMFVideoMFTManager::report_telemetry",
-                        TaskCategory::Other,
-                        task.forget());
+  SystemGroup::Dispatch(TaskCategory::Other, task.forget());
 }
 
 const GUID&
 WMFVideoMFTManager::GetMFTGUID()
 {
   MOZ_ASSERT(mStreamType != Unknown);
   switch (mStreamType) {
     case H264: return CLSID_CMSH264DecoderMFT;
--- a/dom/media/webaudio/MediaBufferDecoder.cpp
+++ b/dom/media/webaudio/MediaBufferDecoder.cpp
@@ -264,19 +264,17 @@ MediaDecodeTask::OnMetadataRead(Metadata
   nsCOMPtr<nsIRunnable> task = NS_NewRunnableFunction(
     "MediaDecodeTask::OnMetadataRead", [codec]() -> void {
       MOZ_ASSERT(!codec.IsEmpty());
       MOZ_LOG(gMediaDecoderLog,
               LogLevel::Debug,
               ("Telemetry (WebAudio) MEDIA_CODEC_USED= '%s'", codec.get()));
       Telemetry::Accumulate(Telemetry::HistogramID::MEDIA_CODEC_USED, codec);
     });
-  SystemGroup::Dispatch("MediaDecodeTask::OnMetadataRead()::report_telemetry",
-                        TaskCategory::Other,
-                        task.forget());
+  SystemGroup::Dispatch(TaskCategory::Other, task.forget());
 
   RequestSample();
 }
 
 void
 MediaDecodeTask::OnMetadataNotRead(const MediaResult& aReason)
 {
   mDecoderReader->Shutdown();
--- a/dom/media/webaudio/WebAudioUtils.cpp
+++ b/dom/media/webaudio/WebAudioUtils.cpp
@@ -99,18 +99,17 @@ WebAudioUtils::LogToDeveloperConsole(uin
   // This implementation is derived from dom/media/VideoUtils.cpp, but we
   // use a windowID so that the message is delivered to the developer console.
   // It is similar to ContentUtils::ReportToConsole, but also works off main
   // thread.
   if (!NS_IsMainThread()) {
     nsCOMPtr<nsIRunnable> task = NS_NewRunnableFunction(
       "dom::WebAudioUtils::LogToDeveloperConsole",
       [aWindowID, aKey] { LogToDeveloperConsole(aWindowID, aKey); });
-    SystemGroup::Dispatch("dom::WebAudioUtils::LogToDeveloperConsole",
-        TaskCategory::Other, task.forget());
+    SystemGroup::Dispatch(TaskCategory::Other, task.forget());
     return;
   }
 
   nsCOMPtr<nsIConsoleService> console(
     do_GetService("@mozilla.org/consoleservice;1"));
   if (!console) {
     NS_WARNING("Failed to log message to console.");
     return;
--- a/dom/media/webrtc/MediaEngineDefault.cpp
+++ b/dom/media/webrtc/MediaEngineDefault.cpp
@@ -27,17 +27,17 @@
 #endif
 
 #define AUDIO_RATE mozilla::MediaEngine::DEFAULT_SAMPLE_RATE
 #define DEFAULT_AUDIO_TIMER_MS 10
 namespace mozilla {
 
 using namespace mozilla::gfx;
 
-NS_IMPL_ISUPPORTS(MediaEngineDefaultVideoSource, nsITimerCallback)
+NS_IMPL_ISUPPORTS(MediaEngineDefaultVideoSource, nsITimerCallback, nsINamed)
 /**
  * Default video source.
  */
 
 MediaEngineDefaultVideoSource::MediaEngineDefaultVideoSource()
 #ifdef MOZ_WEBRTC
   : MediaEngineCameraVideoSource("FakeVideo.Monitor")
 #else
@@ -281,16 +281,23 @@ MediaEngineDefaultVideoSource::Notify(ns
   MonitorAutoLock lock(mMonitor);
 
   // implicitly releases last image
   mImage = ycbcr_image.forget();
 
   return NS_OK;
 }
 
+NS_IMETHODIMP
+MediaEngineDefaultVideoSource::GetName(nsACString& aName)
+{
+  aName.AssignLiteral("MediaEngineDefaultVideoSource");
+  return NS_OK;
+}
+
 void
 MediaEngineDefaultVideoSource::NotifyPull(MediaStreamGraph* aGraph,
                                           SourceMediaStream *aSource,
                                           TrackID aID,
                                           StreamTime aDesiredTime,
                                           const PrincipalHandle& aPrincipalHandle)
 {
   // AddTrack takes ownership of segment
--- a/dom/media/webrtc/MediaEngineDefault.h
+++ b/dom/media/webrtc/MediaEngineDefault.h
@@ -1,15 +1,16 @@
 /* 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 MEDIAENGINEDEFAULT_H_
 #define MEDIAENGINEDEFAULT_H_
 
+#include "nsINamed.h"
 #include "nsITimer.h"
 
 #include "nsAutoPtr.h"
 #include "nsCOMPtr.h"
 #include "DOMMediaStream.h"
 #include "nsComponentManagerUtils.h"
 #include "mozilla/Monitor.h"
 
@@ -31,16 +32,17 @@ class ImageContainer;
 } // namespace layers
 
 class MediaEngineDefault;
 
 /**
  * The default implementation of the MediaEngine interface.
  */
 class MediaEngineDefaultVideoSource : public nsITimerCallback,
+                                      public nsINamed,
 #ifdef MOZ_WEBRTC
                                       public MediaEngineCameraVideoSource
 #else
                                       public MediaEngineVideoSource
 #endif
 {
 public:
   MediaEngineDefaultVideoSource();
@@ -82,16 +84,17 @@ public:
 
   nsresult TakePhoto(MediaEnginePhotoCallback* aCallback) override
   {
     return NS_ERROR_NOT_IMPLEMENTED;
   }
 
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSITIMERCALLBACK
+  NS_DECL_NSINAMED
 
 protected:
   ~MediaEngineDefaultVideoSource();
 
   friend class MediaEngineDefault;
 
   TrackID mTrackID;
   nsCOMPtr<nsITimer> mTimer;
--- a/dom/messagechannel/MessagePort.cpp
+++ b/dom/messagechannel/MessagePort.cpp
@@ -557,17 +557,17 @@ MessagePort::Dispatch()
 
   RefPtr<SharedMessagePortMessage> data = mMessages.ElementAt(0);
   mMessages.RemoveElementAt(0);
 
   mPostMessageRunnable = new PostMessageRunnable(this, data);
 
   nsCOMPtr<nsIGlobalObject> global = GetOwnerGlobal();
   if (NS_IsMainThread() && global) {
-    MOZ_ALWAYS_SUCCEEDS(global->Dispatch("MessagePortMessage", TaskCategory::Other, do_AddRef(mPostMessageRunnable)));
+    MOZ_ALWAYS_SUCCEEDS(global->Dispatch(TaskCategory::Other, do_AddRef(mPostMessageRunnable)));
     return;
   }
 
   MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(mPostMessageRunnable));
 }
 
 void
 MessagePort::Close()
--- a/dom/notification/Notification.cpp
+++ b/dom/notification/Notification.cpp
@@ -221,17 +221,18 @@ public:
     rv = notificationStorage->Get(mOrigin, mTag, mCallback);
     //XXXnsm Is it guaranteed mCallback will be called in case of failure?
     Unused << NS_WARN_IF(NS_FAILED(rv));
     return rv;
   }
 };
 
 class NotificationPermissionRequest : public nsIContentPermissionRequest,
-                                      public nsIRunnable
+                                      public nsIRunnable,
+                                      public nsINamed
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_NSICONTENTPERMISSIONREQUEST
   NS_DECL_NSIRUNNABLE
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(NotificationPermissionRequest,
                                            nsIContentPermissionRequest)
 
@@ -242,16 +243,22 @@ public:
       mPermission(NotificationPermission::Default),
       mPromise(aPromise),
       mCallback(aCallback)
   {
     MOZ_ASSERT(aPromise);
     mRequester = new nsContentPermissionRequester(mWindow);
   }
 
+  NS_IMETHOD GetName(nsACString& aName) override
+  {
+    aName.AssignLiteral("NotificationPermissionRequest");
+    return NS_OK;
+  }
+
 protected:
   virtual ~NotificationPermissionRequest() {}
 
   nsresult ResolvePromise();
   nsresult DispatchResolvePromise();
   nsCOMPtr<nsIPrincipal> mPrincipal;
   nsCOMPtr<nsPIDOMWindowInner> mWindow;
   NotificationPermission mPermission;
@@ -523,16 +530,17 @@ protected:
 uint32_t Notification::sCount = 0;
 
 NS_IMPL_CYCLE_COLLECTION(NotificationPermissionRequest, mWindow, mPromise,
                                                         mCallback)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(NotificationPermissionRequest)
   NS_INTERFACE_MAP_ENTRY(nsIContentPermissionRequest)
   NS_INTERFACE_MAP_ENTRY(nsIRunnable)
+  NS_INTERFACE_MAP_ENTRY(nsINamed)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentPermissionRequest)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(NotificationPermissionRequest)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(NotificationPermissionRequest)
 
 NS_IMETHODIMP
 NotificationPermissionRequest::Run()
@@ -1792,18 +1800,17 @@ Notification::RequestPermission(const Gl
   }
   NotificationPermissionCallback* permissionCallback = nullptr;
   if (aCallback.WasPassed()) {
     permissionCallback = &aCallback.Value();
   }
   nsCOMPtr<nsIRunnable> request =
     new NotificationPermissionRequest(principal, window, promise, permissionCallback);
 
-  global->Dispatch("Notification::RequestPermission", TaskCategory::Other,
-                   request.forget());
+  global->Dispatch(TaskCategory::Other, request.forget());
 
   return promise.forget();
 }
 
 // static
 NotificationPermission
 Notification::GetPermission(const GlobalObject& aGlobal, ErrorResult& aRv)
 {
@@ -1990,18 +1997,17 @@ Notification::Get(nsPIDOMWindowInner* aW
   }
 
   nsCOMPtr<nsINotificationStorageCallback> callback =
     new NotificationStorageCallback(global, aScope, promise);
 
   RefPtr<NotificationGetRunnable> r =
     new NotificationGetRunnable(origin, aFilter.mTag, callback);
 
-  aRv = global->Dispatch("Notification::Get", TaskCategory::Other,
-                         r.forget());
+  aRv = global->Dispatch(TaskCategory::Other, r.forget());
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   return promise.forget();
 }
 
 already_AddRefed<Promise>
new file mode 100644
--- /dev/null
+++ b/dom/payments/BasicCardPayment.cpp
@@ -0,0 +1,390 @@
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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 "BasicCardPayment.h"
+#include "PaymentAddress.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "nsArrayUtils.h"
+#include "nsISupportsPrimitives.h"
+#include "nsCharSeparatedTokenizer.h"
+#include "nsDataHashtable.h"
+
+namespace mozilla {
+namespace dom {
+#ifndef PaymentBasicCardMacros
+#define PaymentBasicCardMacros
+
+#define AMEX NS_LITERAL_STRING("amex")
+#define CARTEBANCAIRE NS_LITERAL_STRING("cartebancaire")
+#define DINERS NS_LITERAL_STRING("diners")
+#define DISCOVER NS_LITERAL_STRING("discover")
+#define JCB NS_LITERAL_STRING("jcb")
+#define MASTERCARD NS_LITERAL_STRING("mastercard")
+#define MIR NS_LITERAL_STRING("mir")
+#define UNIONPAY NS_LITERAL_STRING("unionpay")
+#define VISA NS_LITERAL_STRING("visa")
+
+#define CardholderName NS_LITERAL_STRING("cardholderName")
+#define CardNumber NS_LITERAL_STRING("cardNumber")
+#define ExpiryMonth NS_LITERAL_STRING("expiryMonth")
+#define ExpiryYear NS_LITERAL_STRING("expiryYear")
+#define CardSecurityCode NS_LITERAL_STRING("cardSecurityCode")
+
+#define Country NS_LITERAL_STRING("country")
+#define AddressLine NS_LITERAL_STRING("addressLine")
+#define Region NS_LITERAL_STRING("region")
+#define City NS_LITERAL_STRING("city")
+#define DependentLocality NS_LITERAL_STRING("dependentLocality")
+#define PostalCode NS_LITERAL_STRING("postalCode")
+#define SortingCode NS_LITERAL_STRING("sortingCode")
+#define LanguageCode NS_LITERAL_STRING("languageCode")
+#define Organization NS_LITERAL_STRING("organization")
+#define Recipient NS_LITERAL_STRING("recipient")
+#define Phone NS_LITERAL_STRING("phone")
+
+#define PropertySpliter NS_LITERAL_STRING(";")
+#define KeyValueSpliter NS_LITERAL_STRING(":")
+#define AddressLineSpliter NS_LITERAL_STRING("%")
+
+#define EncodeBasicCardProperty(aPropertyName, aPropertyValue, aResult)        \
+  do {                                                                         \
+    if (!(aPropertyValue).IsEmpty()) {                                         \
+      (aResult) += (aPropertyName)                                             \
+                 + KeyValueSpliter                                             \
+                 + (aPropertyValue)                                            \
+                 + PropertySpliter;                                            \
+    }                                                                          \
+  } while(0)
+
+#define EncodeAddressProperty(aAddress, aPropertyName, aResult)                \
+  do {                                                                         \
+    nsAutoString propertyValue;                                                \
+    NS_ENSURE_SUCCESS((aAddress)->Get##aPropertyName(propertyValue),           \
+                                                     NS_ERROR_FAILURE);        \
+    EncodeBasicCardProperty((aPropertyName) ,propertyValue , (aResult));       \
+  } while(0)
+
+#define DecodeBasicCardProperty(aPropertyName, aPropertyValue,                 \
+                                aMatchPropertyName, aResponse)                 \
+  do {                                                                         \
+    if ((aPropertyName).Equals((aMatchPropertyName))) {                        \
+      (aResponse).m##aMatchPropertyName.Construct();                           \
+      (aResponse).m##aMatchPropertyName.Value() = (aPropertyValue);            \
+    }                                                                          \
+  } while(0)
+
+#define DecodeAddressProperty(aPropertyName, aPropertyValue,                   \
+                              aMatchPropertyName, aMatchPropertyValue)         \
+  do {                                                                         \
+    if ((aPropertyName).Equals((aMatchPropertyName))) {                        \
+      (aMatchPropertyValue) = (aPropertyValue);                                \
+    }                                                                          \
+  } while(0)
+
+#endif
+
+namespace {
+
+bool IsValidNetwork(const nsAString& aNetwork)
+{
+  return AMEX.Equals(aNetwork) ||
+         CARTEBANCAIRE.Equals(aNetwork) ||
+         DINERS.Equals(aNetwork) ||
+         DISCOVER.Equals(aNetwork) ||
+         JCB.Equals(aNetwork) ||
+         MASTERCARD.Equals(aNetwork) ||
+         MIR.Equals(aNetwork) ||
+         UNIONPAY.Equals(aNetwork) ||
+         VISA.Equals(aNetwork);
+}
+
+bool IsBasicCardKey(const nsAString& aKey)
+{
+  return CardholderName.Equals(aKey) ||
+         CardNumber.Equals(aKey) ||
+         ExpiryMonth.Equals(aKey) ||
+         ExpiryYear.Equals(aKey) ||
+         CardSecurityCode.Equals(aKey);
+}
+
+bool IsAddressKey(const nsAString& aKey)
+{
+  return Country.Equals(aKey) ||
+         AddressLine.Equals(aKey) ||
+         Region.Equals(aKey) ||
+         City.Equals(aKey) ||
+         DependentLocality.Equals(aKey) ||
+         PostalCode.Equals(aKey) ||
+         SortingCode.Equals(aKey) ||
+         LanguageCode.Equals(aKey) ||
+         Organization.Equals(aKey) ||
+         Recipient.Equals(aKey) ||
+         Phone.Equals(aKey);
+}
+
+} // end of namespace
+
+
+StaticRefPtr<BasicCardService> gBasicCardService;
+
+already_AddRefed<BasicCardService>
+BasicCardService::GetService()
+{
+  if (!gBasicCardService) {
+    gBasicCardService = new BasicCardService();
+    ClearOnShutdown(&gBasicCardService);
+  }
+  RefPtr<BasicCardService> service = gBasicCardService;
+  return service.forget();
+}
+
+bool
+BasicCardService::IsBasicCardPayment(const nsAString& aSupportedMethods)
+{
+  return aSupportedMethods.Equals(NS_LITERAL_STRING("basic-card"));
+}
+
+bool
+BasicCardService::IsValidBasicCardRequest(JSContext* aCx,
+                                          JSObject* aData,
+                                          nsAString& aErrorMsg)
+{
+  if (!aData) {
+    return true;
+  }
+  JS::RootedValue data(aCx, JS::ObjectValue(*aData));
+
+  BasicCardRequest request;
+  if (!request.Init(aCx, data)) {
+    aErrorMsg.AssignLiteral("Fail to convert methodData.data to BasicCardRequest.");
+    return false;
+  }
+
+  if (request.mSupportedNetworks.WasPassed()) {
+    for (const nsString& network : request.mSupportedNetworks.Value()) {
+      if (!IsValidNetwork(network)) {
+        aErrorMsg.Assign(network + NS_LITERAL_STRING(" is not an valid network."));
+        return false;
+      }
+    }
+  }
+  return true;
+}
+
+bool
+BasicCardService::IsValidExpiryMonth(const nsAString& aExpiryMonth)
+{
+  // ExpiryMonth can only be
+  //   1. empty string
+  //   2. 01 ~ 12
+  if (aExpiryMonth.IsEmpty()) {
+    return true;
+  }
+  if (aExpiryMonth.Length() != 2) {
+    return false;
+  }
+  // can only be 00 ~ 09
+  if (aExpiryMonth.CharAt(0) == '0') {
+    if (aExpiryMonth.CharAt(1) < '0' || aExpiryMonth.CharAt(1) > '9') {
+      return false;
+    }
+    return true;
+  }
+  // can only be 11 or 12
+  if (aExpiryMonth.CharAt(0) == '1') {
+    if (aExpiryMonth.CharAt(1) != '1' && aExpiryMonth.CharAt(1) != '2') {
+      return false;
+    }
+    return true;
+  }
+  return false;
+}
+
+bool
+BasicCardService::IsValidExpiryYear(const nsAString& aExpiryYear)
+{
+  // ExpiryYear can only be
+  //   1. empty string
+  //   2. 0000 ~ 9999
+  if (!aExpiryYear.IsEmpty()) {
+    if (aExpiryYear.Length() != 4) {
+      return false;
+    }
+    for (uint32_t index = 0; index < 4; ++index) {
+      if (aExpiryYear.CharAt(index) < '0' ||
+          aExpiryYear.CharAt(index) > '9') {
+        return false;
+      }
+    }
+  }
+  return true;
+}
+
+nsresult
+BasicCardService::EncodeBasicCardData(const nsAString& aCardholderName,
+                                      const nsAString& aCardNumber,
+                                      const nsAString& aExpiryMonth,
+                                      const nsAString& aExpiryYear,
+                                      const nsAString& aCardSecurityCode,
+                                      nsIPaymentAddress* aBillingAddress,
+                                      nsAString& aResult)
+{
+  // aBillingAddress can be nullptr
+  if (aCardNumber.IsEmpty()) {
+    return NS_ERROR_FAILURE;
+  }
+  EncodeBasicCardProperty(CardholderName, aCardholderName, aResult);
+  EncodeBasicCardProperty(CardNumber, aCardNumber, aResult);
+  EncodeBasicCardProperty(ExpiryMonth, aExpiryMonth, aResult);
+  EncodeBasicCardProperty(ExpiryYear, aExpiryYear, aResult);
+  EncodeBasicCardProperty(CardSecurityCode, aCardSecurityCode, aResult);
+  if (!aBillingAddress) {
+    return NS_OK;
+  }
+  EncodeAddressProperty(aBillingAddress, Country, aResult);
+  nsCOMPtr<nsIArray> addressLine;
+  NS_ENSURE_SUCCESS(aBillingAddress->GetAddressLine(getter_AddRefs(addressLine)),
+                                                    NS_ERROR_FAILURE);
+  uint32_t length;
+  nsAutoString addressLineString;
+  NS_ENSURE_SUCCESS(addressLine->GetLength(&length), NS_ERROR_FAILURE);
+  for (uint32_t index = 0; index < length; ++index) {
+    nsCOMPtr<nsISupportsString> address = do_QueryElementAt(addressLine, index);
+    MOZ_ASSERT(address);
+    nsAutoString addressString;
+    NS_ENSURE_SUCCESS(address->GetData(addressString), NS_ERROR_FAILURE);
+    addressLineString += addressString + AddressLineSpliter;
+  }
+  EncodeBasicCardProperty(AddressLine ,addressLineString , aResult);
+  EncodeAddressProperty(aBillingAddress, Region, aResult);
+  EncodeAddressProperty(aBillingAddress, City, aResult);
+  EncodeAddressProperty(aBillingAddress, DependentLocality, aResult);
+  EncodeAddressProperty(aBillingAddress, PostalCode, aResult);
+  EncodeAddressProperty(aBillingAddress, SortingCode, aResult);
+  EncodeAddressProperty(aBillingAddress, LanguageCode, aResult);
+  EncodeAddressProperty(aBillingAddress, Organization, aResult);
+  EncodeAddressProperty(aBillingAddress, Recipient, aResult);
+  EncodeAddressProperty(aBillingAddress, Phone, aResult);
+  return NS_OK;
+}
+
+nsresult
+BasicCardService::DecodeBasicCardData(const nsAString& aData,
+                                      nsPIDOMWindowInner* aWindow,
+                                      BasicCardResponse& aResponse)
+{
+  // aWindow can be nullptr
+  bool isBillingAddressPassed = false;
+  nsTArray<nsString> addressLine;
+  nsAutoString country;
+  nsAutoString region;
+  nsAutoString city;
+  nsAutoString dependentLocality;
+  nsAutoString postalCode;
+  nsAutoString sortingCode;
+  nsAutoString languageCode;
+  nsAutoString organization;
+  nsAutoString recipient;
+  nsAutoString phone;
+
+  nsCharSeparatedTokenizer propertyTokenizer(aData, PropertySpliter.CharAt(0));
+  while (propertyTokenizer.hasMoreTokens()) {
+    nsDependentSubstring property = propertyTokenizer.nextToken();
+    nsCharSeparatedTokenizer keyValueTokenizer(property, KeyValueSpliter.CharAt(0));
+    MOZ_ASSERT(keyValueTokenizer.hasMoreTokens());
+    nsDependentSubstring key = keyValueTokenizer.nextToken();
+    nsDependentSubstring value = keyValueTokenizer.nextToken();
+    if (IsAddressKey(key) && !isBillingAddressPassed) {
+      isBillingAddressPassed = true;
+    }
+    if (!IsAddressKey(key) && !IsBasicCardKey(key)) {
+      return NS_ERROR_FAILURE;
+    }
+
+    if (key.Equals(CardNumber)) {
+      aResponse.mCardNumber = (value);
+    }
+
+    DecodeBasicCardProperty(key, value, CardholderName, aResponse);
+    DecodeBasicCardProperty(key, value, ExpiryMonth, aResponse);
+    DecodeBasicCardProperty(key, value, ExpiryYear, aResponse);
+    DecodeBasicCardProperty(key, value, CardSecurityCode, aResponse);
+
+    DecodeAddressProperty(key, value, Country, country);
+    DecodeAddressProperty(key, value, Region, region);
+    DecodeAddressProperty(key, value, City, city);
+    DecodeAddressProperty(key, value, DependentLocality, dependentLocality);
+    DecodeAddressProperty(key, value, PostalCode, postalCode);
+    DecodeAddressProperty(key, value, SortingCode, sortingCode);
+    DecodeAddressProperty(key, value, LanguageCode, languageCode);
+    DecodeAddressProperty(key, value, Organization, organization);
+    DecodeAddressProperty(key, value, Recipient, recipient);
+    DecodeAddressProperty(key, value, Phone, phone);
+
+    if ((key).Equals(AddressLine)) {
+      nsCharSeparatedTokenizer addressTokenizer(value, AddressLineSpliter.CharAt(0));
+      while (addressTokenizer.hasMoreTokens()) {
+        addressLine.AppendElement(addressTokenizer.nextToken());
+      }
+    }
+  }
+  if (isBillingAddressPassed) {
+    aResponse.mBillingAddress.Construct();
+    aResponse.mBillingAddress.Value() = new PaymentAddress(aWindow,
+                                                           country,
+                                                           addressLine,
+                                                           region,
+                                                           city,
+                                                           dependentLocality,
+                                                           postalCode,
+                                                           sortingCode,
+                                                           languageCode,
+                                                           organization,
+                                                           recipient,
+                                                           phone);
+  }
+  return NS_OK;
+}
+
+#ifdef PaymentBasicCardMacros
+#undef PaymentBasicCardMacros
+#undef EncodeBasicCardProperty
+#undef EncodeAddressProperty
+#undef DecodeBasicCardProperty
+#undef DecodeBasicCardCardNumber
+#undef DecodeAddressProperty
+#undef DecodeAddressLine
+#undef AMEX
+#undef CARTEBANCAIRE
+#undef DINERS
+#undef DISCOVER
+#undef JCB
+#undef MASTERCARD
+#undef MIR
+#undef UNIONPAY
+#undef VISA
+#undef CardholderName
+#undef CardNumber
+#undef ExpiryMonth
+#undef ExpiryYear
+#undef CardSecurityCode
+#undef Country
+#undef AddressLine
+#undef Region
+#undef City
+#undef DependentLocality
+#undef PostalCode
+#undef SortingCode
+#undef LanguageCode
+#undef Organization
+#undef Recipient
+#undef Phone
+#undef PropertySpliter
+#undef KeyValueSpliter
+#undef AddressLineSpliter
+#endif
+
+} // end of namespace dom
+} // end of namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/payments/BasicCardPayment.h
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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_BasicCardPayment_h
+#define mozilla_dom_BasicCardPayment_h
+
+#include "mozilla/dom/BasicCardPaymentBinding.h"
+#include "nsPIDOMWindow.h"
+#include "nsIPaymentAddress.h"
+#include "nsTArray.h"
+
+namespace mozilla {
+namespace dom {
+
+class BasicCardService final
+{
+public:
+  NS_INLINE_DECL_REFCOUNTING(BasicCardService)
+
+  static already_AddRefed<BasicCardService> GetService();
+
+  bool IsBasicCardPayment(const nsAString& aSupportedMethods);
+  bool IsValidBasicCardRequest(JSContext* aCx, JSObject* aData, nsAString& aErrorMsg);
+  bool IsValidExpiryMonth(const nsAString& aExpiryMonth);
+  bool IsValidExpiryYear(const nsAString& aExpiryYear);
+
+/*
+  To let BasicCardResponse using the same data type with non-BasicCard response
+  in IPC transferring, following two methods is used to Encode/Decode the raw
+  data of BasicCardResponse.
+*/
+  nsresult EncodeBasicCardData(const nsAString& aCardholderName,
+                               const nsAString& aCardNumber,
+                               const nsAString& aExpiryMonth,
+                               const nsAString& aExpiryYear,
+                               const nsAString& aCardSecurityCode,
+                               nsIPaymentAddress* aBillingAddress,
+                               nsAString& aResult);
+
+  nsresult DecodeBasicCardData(const nsAString& aData,
+                               nsPIDOMWindowInner* aWindow,
+                               BasicCardResponse& aResponse);
+private:
+  BasicCardService() = default;
+  ~BasicCardService() = default;
+};
+
+} // end of namespace dom
+} // end of namespace mozilla
+
+#endif
--- a/dom/payments/PaymentActionResponse.cpp
+++ b/dom/payments/PaymentActionResponse.cpp
@@ -1,20 +1,130 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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 "PaymentActionResponse.h"
-#include "nsIRunnable.h"
+#include "PaymentRequestUtils.h"
+#include "BasicCardPayment.h"
 
 namespace mozilla {
 namespace dom {
 
+/* PaymentResponseData */
+
+NS_IMPL_ISUPPORTS(PaymentResponseData, nsIPaymentResponseData)
+
+NS_IMETHODIMP
+PaymentResponseData::GetType(uint32_t* aType)
+{
+  NS_ENSURE_ARG_POINTER(aType);
+  *aType = mType;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+PaymentResponseData::Init(const uint32_t aType)
+{
+  if (aType != nsIPaymentResponseData::GENERAL_RESPONSE &&
+      aType != nsIPaymentResponseData::BASICCARD_RESPONSE) {
+    return NS_ERROR_FAILURE;
+  }
+  mType = aType;
+  return NS_OK;
+}
+
+/* GeneralResponseData */
+
+NS_IMPL_ISUPPORTS_INHERITED(GeneralResponseData,
+                            PaymentResponseData,
+                            nsIGeneralResponseData)
+
+GeneralResponseData::GeneralResponseData()
+  : mData(NS_LITERAL_STRING("{}"))
+{
+  Init(nsIPaymentResponseData::GENERAL_RESPONSE);
+}
+
+NS_IMETHODIMP
+GeneralResponseData::GetData(nsAString& aData)
+{
+  aData = mData;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+GeneralResponseData::InitData(JS::HandleValue aValue, JSContext* aCx)
+{
+  if (aValue.isNullOrUndefined()) {
+    return NS_ERROR_FAILURE;
+  }
+  nsresult rv = SerializeFromJSVal(aCx, aValue, mData);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+  return NS_OK;
+}
+
+/* BasicCardResponseData */
+
+NS_IMPL_ISUPPORTS_INHERITED(BasicCardResponseData,
+                            PaymentResponseData,
+                            nsIBasicCardResponseData)
+
+BasicCardResponseData::BasicCardResponseData()
+{
+  Init(nsIPaymentResponseData::BASICCARD_RESPONSE);
+}
+
+NS_IMETHODIMP
+BasicCardResponseData::GetData(nsAString& aData)
+{
+  aData = mData;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+BasicCardResponseData::InitData(const nsAString& aCardholderName,
+                                const nsAString& aCardNumber,
+                                const nsAString& aExpiryMonth,
+                                const nsAString& aExpiryYear,
+                                const nsAString& aCardSecurityCode,
+                                nsIPaymentAddress* aBillingAddress)
+{
+  // cardNumber is a required attribute, cannot be empty;
+  if (aCardNumber.IsEmpty()) {
+    return NS_ERROR_FAILURE;
+  }
+
+  RefPtr<BasicCardService> service = BasicCardService::GetService();
+  MOZ_ASSERT(service);
+
+  if (!service->IsValidExpiryMonth(aExpiryMonth)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  if (!service->IsValidExpiryYear(aExpiryYear)) {
+    return NS_ERROR_FAILURE;
+  }
+  nsresult rv = service->EncodeBasicCardData(aCardholderName,
+                                             aCardNumber,
+                                             aExpiryMonth,
+                                             aExpiryYear,
+                                             aCardSecurityCode,
+                                             aBillingAddress,
+                                             mData);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+  return NS_OK;
+}
+
 /* PaymentActionResponse */
 
 NS_IMPL_ISUPPORTS(PaymentActionResponse,
                   nsIPaymentActionResponse)
 
 PaymentActionResponse::PaymentActionResponse()
   : mRequestId(EmptyString())
   , mType(nsIPaymentActionResponse::NO_TYPE)
@@ -116,25 +226,59 @@ PaymentShowActionResponse::GetPayerPhone
   aPayerPhone = mPayerPhone;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 PaymentShowActionResponse::Init(const nsAString& aRequestId,
                                 const uint32_t aAcceptStatus,
                                 const nsAString& aMethodName,
-                                const nsAString& aData,
+                                nsIPaymentResponseData* aData,
                                 const nsAString& aPayerName,
                                 const nsAString& aPayerEmail,
                                 const nsAString& aPayerPhone)
 {
+  NS_ENSURE_ARG_POINTER(aData);
   mRequestId = aRequestId;
   mAcceptStatus = aAcceptStatus;
   mMethodName = aMethodName;
-  mData = aData;
+
+  RefPtr<BasicCardService> service = BasicCardService::GetService();
+  MOZ_ASSERT(service);
+  bool isBasicCardPayment = service->IsBasicCardPayment(mMethodName);
+
+  uint32_t responseType;
+  NS_ENSURE_SUCCESS(aData->GetType(&responseType), NS_ERROR_FAILURE);
+  switch (responseType) {
+    case nsIPaymentResponseData::GENERAL_RESPONSE: {
+      if (isBasicCardPayment) {
+        return NS_ERROR_FAILURE;
+      }
+      nsCOMPtr<nsIGeneralResponseData> data = do_QueryInterface(aData);
+      MOZ_ASSERT(data);
+      NS_ENSURE_SUCCESS(data->GetData(mData), NS_ERROR_FAILURE);
+      break;
+    }
+    case nsIPaymentResponseData::BASICCARD_RESPONSE: {
+      if (!isBasicCardPayment) {
+        return NS_ERROR_FAILURE;
+      }
+      nsCOMPtr<nsIBasicCardResponseData> data = do_QueryInterface(aData);
+      MOZ_ASSERT(data);
+      NS_ENSURE_SUCCESS(data->GetData(mData), NS_ERROR_FAILURE);
+      break;
+    }
+    default: {
+      return NS_ERROR_FAILURE;
+    }
+  }
+  if (mData.IsEmpty()) {
+    return NS_ERROR_FAILURE;
+  }
+
   mPayerName = aPayerName;
   mPayerEmail = aPayerEmail;
   mPayerPhone = aPayerPhone;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 PaymentShowActionResponse::IsAccepted(bool* aIsAccepted)
--- a/dom/payments/PaymentActionResponse.h
+++ b/dom/payments/PaymentActionResponse.h
@@ -9,16 +9,63 @@
 
 #include "nsIPaymentActionResponse.h"
 
 namespace mozilla {
 namespace dom {
 
 class PaymentRequestParent;
 
+class PaymentResponseData : public nsIPaymentResponseData
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIPAYMENTRESPONSEDATA
+
+  PaymentResponseData() = default;
+
+protected:
+  virtual ~PaymentResponseData() = default;
+
+  uint32_t mType;
+};
+
+class GeneralResponseData final : public PaymentResponseData
+                                , public nsIGeneralResponseData
+{
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_FORWARD_NSIPAYMENTRESPONSEDATA(PaymentResponseData::)
+  NS_DECL_NSIGENERALRESPONSEDATA
+
+  GeneralResponseData();
+
+private:
+  ~GeneralResponseData() = default;
+
+  nsString mData;
+};
+
+class BasicCardResponseData final : public nsIBasicCardResponseData
+                                  , public PaymentResponseData
+{
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_FORWARD_NSIPAYMENTRESPONSEDATA(PaymentResponseData::)
+  NS_DECL_NSIBASICCARDRESPONSEDATA
+
+  BasicCardResponseData();
+
+private:
+  ~BasicCardResponseData() = default;
+
+  nsString mData;
+  nsCOMPtr<nsIPaymentAddress> mBillingAddress;
+};
+
 class PaymentActionResponse : public nsIPaymentActionResponse
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIPAYMENTACTIONRESPONSE
 
   PaymentActionResponse();
 
--- a/dom/payments/PaymentRequest.cpp
+++ b/dom/payments/PaymentRequest.cpp
@@ -3,16 +3,17 @@
 /* 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/Element.h"
 #include "mozilla/dom/PaymentRequest.h"
 #include "mozilla/dom/PaymentResponse.h"
 #include "nsContentUtils.h"
+#include "BasicCardPayment.h"
 #include "PaymentRequestManager.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(PaymentRequest)
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(PaymentRequest,
@@ -47,30 +48,44 @@ NS_IMPL_RELEASE_INHERITED(PaymentRequest
 
 bool
 PaymentRequest::PrefEnabled(JSContext* aCx, JSObject* aObj)
 {
   return Preferences::GetBool("dom.payments.request.enabled");
 }
 
 bool
-PaymentRequest::IsValidMethodData(const Sequence<PaymentMethodData>& aMethodData,
+PaymentRequest::IsValidMethodData(JSContext* aCx,
+                                  const Sequence<PaymentMethodData>& aMethodData,
                                   nsAString& aErrorMsg)
 {
   if (!aMethodData.Length()) {
     aErrorMsg.AssignLiteral("At least one payment method is required.");
     return false;
   }
 
   for (const PaymentMethodData& methodData : aMethodData) {
-    if (!methodData.mSupportedMethods.Length()) {
+    if (methodData.mSupportedMethods.IsEmpty()) {
       aErrorMsg.AssignLiteral(
-        "At least one payment method identifier is required.");
+        "Payment method identifier is required.");
       return false;
     }
+    RefPtr<BasicCardService> service = BasicCardService::GetService();
+    MOZ_ASSERT(service);
+    if (service->IsBasicCardPayment(methodData.mSupportedMethods)) {
+      if (!methodData.mData.WasPassed()) {
+        continue;
+      }
+      MOZ_ASSERT(aCx);
+      if (!service->IsValidBasicCardRequest(aCx,
+                                            methodData.mData.Value(),
+                                            aErrorMsg)) {
+        return false;
+      }
+    }
   }
 
   return true;
 }
 
 bool
 PaymentRequest::IsValidNumber(const nsAString& aItem,
                               const nsAString& aStr,
@@ -254,17 +269,19 @@ PaymentRequest::Constructor(const Global
         }
       }
     }
     node = parentNode;
   } while (node);
 
   // Check payment methods and details
   nsAutoString message;
-  if (!IsValidMethodData(aMethodData, message) ||
+  if (!IsValidMethodData(nsContentUtils::GetCurrentJSContext(),
+                         aMethodData,
+                         message) ||
       !IsValidDetailsInit(aDetails, message)) {
     aRv.ThrowTypeError<MSG_ILLEGAL_PR_CONSTRUCTOR>(message);
     return nullptr;
   }
 
   RefPtr<PaymentRequestManager> manager = PaymentRequestManager::GetSingleton();
   if (NS_WARN_IF(!manager)) {
     return nullptr;
--- a/dom/payments/PaymentRequest.h
+++ b/dom/payments/PaymentRequest.h
@@ -31,17 +31,18 @@ public:
   virtual JSObject* WrapObject(JSContext* aCx,
                                JS::Handle<JSObject*> aGivenProto) override;
 
   static already_AddRefed<PaymentRequest>
   CreatePaymentRequest(nsPIDOMWindowInner* aWindow, nsresult& aRv);
 
   static bool PrefEnabled(JSContext* aCx, JSObject* aObj);
 
-  static bool IsValidMethodData(const Sequence<PaymentMethodData>& aMethodData,
+  static bool IsValidMethodData(JSContext* aCx,
+                                const Sequence<PaymentMethodData>& aMethodData,
                                 nsAString& aErrorMsg);
 
   static bool
   IsValidNumber(const nsAString& aItem,
                 const nsAString& aStr,
                 nsAString& aErrorMsg);
   static bool
   IsNonNegativeNumber(const nsAString& aItem,
--- a/dom/payments/PaymentRequestData.cpp
+++ b/dom/payments/PaymentRequestData.cpp
@@ -39,19 +39,26 @@ PaymentMethodData::Create(const IPCPayme
 NS_IMETHODIMP
 PaymentMethodData::GetSupportedMethods(nsAString& aSupportedMethods)
 {
   aSupportedMethods = mSupportedMethods;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-PaymentMethodData::GetData(nsAString& aData)
+PaymentMethodData::GetData(JSContext* aCx, JS::MutableHandleValue aData)
 {
-  aData = mData;
+  if (mData.IsEmpty()) {
+    aData.set(JS::NullValue());
+    return NS_OK;
+  }
+  nsresult rv = DeserializeToJSValue(mData, aCx ,aData);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
   return NS_OK;
 }
 
 /* PaymentCurrencyAmount */
 
 NS_IMPL_ISUPPORTS(PaymentCurrencyAmount,
                   nsIPaymentCurrencyAmount)
 
@@ -217,19 +224,26 @@ PaymentDetailsModifier::GetAdditionalDis
 {
   NS_ENSURE_ARG_POINTER(aAdditionalDisplayItems);
   nsCOMPtr<nsIArray> additionalItems = mAdditionalDisplayItems;
   additionalItems.forget(aAdditionalDisplayItems);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-PaymentDetailsModifier::GetData(nsAString& aData)
+PaymentDetailsModifier::GetData(JSContext* aCx, JS::MutableHandleValue aData)
 {
-  aData = mData;
+  if (mData.IsEmpty()) {
+    aData.set(JS::NullValue());
+    return NS_OK;
+  }
+  nsresult rv = DeserializeToJSValue(mData, aCx ,aData);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
   return NS_OK;
 }
 
 /* PaymentShippingOption */
 
 NS_IMPL_ISUPPORTS(PaymentShippingOption,
                   nsIPaymentShippingOption)
 
--- a/dom/payments/PaymentRequestModule.cpp
+++ b/dom/payments/PaymentRequestModule.cpp
@@ -5,79 +5,91 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/ModuleUtils.h"
 #include "PaymentActionRequest.h"
 #include "PaymentActionResponse.h"
 #include "PaymentRequestData.h"
 #include "PaymentRequestService.h"
 
+using mozilla::dom::GeneralResponseData;
+using mozilla::dom::BasicCardResponseData;
 using mozilla::dom::PaymentActionRequest;
 using mozilla::dom::PaymentCreateActionRequest;
 using mozilla::dom::PaymentCompleteActionRequest;
 using mozilla::dom::PaymentUpdateActionRequest;
 using mozilla::dom::PaymentCanMakeActionResponse;
 using mozilla::dom::PaymentAbortActionResponse;
 using mozilla::dom::PaymentShowActionResponse;
 using mozilla::dom::PaymentCompleteActionResponse;
 using mozilla::dom::payments::PaymentAddress;
 using mozilla::dom::PaymentRequestService;
 
+NS_GENERIC_FACTORY_CONSTRUCTOR(GeneralResponseData)
+NS_GENERIC_FACTORY_CONSTRUCTOR(BasicCardResponseData)
 NS_GENERIC_FACTORY_CONSTRUCTOR(PaymentActionRequest)
 NS_GENERIC_FACTORY_CONSTRUCTOR(PaymentCreateActionRequest)
 NS_GENERIC_FACTORY_CONSTRUCTOR(PaymentCompleteActionRequest)
 NS_GENERIC_FACTORY_CONSTRUCTOR(PaymentUpdateActionRequest)
 NS_GENERIC_FACTORY_CONSTRUCTOR(PaymentCanMakeActionResponse)
 NS_GENERIC_FACTORY_CONSTRUCTOR(PaymentAbortActionResponse)
 NS_GENERIC_FACTORY_CONSTRUCTOR(PaymentShowActionResponse)
 NS_GENERIC_FACTORY_CONSTRUCTOR(PaymentCompleteActionResponse)
 NS_GENERIC_FACTORY_CONSTRUCTOR(PaymentAddress)
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(PaymentRequestService,
                                          PaymentRequestService::GetSingleton)
 
+NS_DEFINE_NAMED_CID(NS_GENERAL_RESPONSE_DATA_CID);
+NS_DEFINE_NAMED_CID(NS_BASICCARD_RESPONSE_DATA_CID);
 NS_DEFINE_NAMED_CID(NS_PAYMENT_ACTION_REQUEST_CID);
 NS_DEFINE_NAMED_CID(NS_PAYMENT_CREATE_ACTION_REQUEST_CID);
 NS_DEFINE_NAMED_CID(NS_PAYMENT_COMPLETE_ACTION_REQUEST_CID);
 NS_DEFINE_NAMED_CID(NS_PAYMENT_UPDATE_ACTION_REQUEST_CID);
 NS_DEFINE_NAMED_CID(NS_PAYMENT_CANMAKE_ACTION_RESPONSE_CID);
 NS_DEFINE_NAMED_CID(NS_PAYMENT_ABORT_ACTION_RESPONSE_CID);
 NS_DEFINE_NAMED_CID(NS_PAYMENT_SHOW_ACTION_RESPONSE_CID);
 NS_DEFINE_NAMED_CID(NS_PAYMENT_COMPLETE_ACTION_RESPONSE_CID);
 NS_DEFINE_NAMED_CID(NS_PAYMENT_ADDRESS_CID);
 NS_DEFINE_NAMED_CID(NS_PAYMENT_REQUEST_SERVICE_CID);
 
 static const mozilla::Module::CIDEntry kPaymentRequestCIDs[] = {
+  { &kNS_GENERAL_RESPONSE_DATA_CID, false, nullptr, GeneralResponseDataConstructor},
+  { &kNS_BASICCARD_RESPONSE_DATA_CID, false, nullptr, BasicCardResponseDataConstructor},
   { &kNS_PAYMENT_ACTION_REQUEST_CID, false, nullptr, PaymentActionRequestConstructor},
   { &kNS_PAYMENT_CREATE_ACTION_REQUEST_CID, false, nullptr, PaymentCreateActionRequestConstructor},
   { &kNS_PAYMENT_COMPLETE_ACTION_REQUEST_CID, false, nullptr, PaymentCompleteActionRequestConstructor},
   { &kNS_PAYMENT_UPDATE_ACTION_REQUEST_CID, false, nullptr, PaymentUpdateActionRequestConstructor},
   { &kNS_PAYMENT_CANMAKE_ACTION_RESPONSE_CID, false, nullptr, PaymentCanMakeActionResponseConstructor},
   { &kNS_PAYMENT_ABORT_ACTION_RESPONSE_CID, false, nullptr, PaymentAbortActionResponseConstructor},
   { &kNS_PAYMENT_SHOW_ACTION_RESPONSE_CID, false, nullptr, PaymentShowActionResponseConstructor},
   { &kNS_PAYMENT_COMPLETE_ACTION_RESPONSE_CID, false, nullptr, PaymentCompleteActionResponseConstructor},
   { &kNS_PAYMENT_ADDRESS_CID, false, nullptr, PaymentAddressConstructor},
   { &kNS_PAYMENT_REQUEST_SERVICE_CID, true, nullptr, PaymentRequestServiceConstructor },
   { nullptr }
 };
 
 static const mozilla::Module::ContractIDEntry kPaymentRequestContracts[] = {
+  { NS_GENERAL_RESPONSE_DATA_CONTRACT_ID, &kNS_GENERAL_RESPONSE_DATA_CID },
+  { NS_BASICCARD_RESPONSE_DATA_CONTRACT_ID, &kNS_BASICCARD_RESPONSE_DATA_CID },
   { NS_PAYMENT_ACTION_REQUEST_CONTRACT_ID, &kNS_PAYMENT_ACTION_REQUEST_CID },
   { NS_PAYMENT_CREATE_ACTION_REQUEST_CONTRACT_ID, &kNS_PAYMENT_CREATE_ACTION_REQUEST_CID },
   { NS_PAYMENT_COMPLETE_ACTION_REQUEST_CONTRACT_ID, &kNS_PAYMENT_COMPLETE_ACTION_REQUEST_CID },
   { NS_PAYMENT_UPDATE_ACTION_REQUEST_CONTRACT_ID, &kNS_PAYMENT_UPDATE_ACTION_REQUEST_CID },
   { NS_PAYMENT_CANMAKE_ACTION_RESPONSE_CONTRACT_ID, &kNS_PAYMENT_CANMAKE_ACTION_RESPONSE_CID },
   { NS_PAYMENT_ABORT_ACTION_RESPONSE_CONTRACT_ID, &kNS_PAYMENT_ABORT_ACTION_RESPONSE_CID },
   { NS_PAYMENT_SHOW_ACTION_RESPONSE_CONTRACT_ID, &kNS_PAYMENT_SHOW_ACTION_RESPONSE_CID },
   { NS_PAYMENT_COMPLETE_ACTION_RESPONSE_CONTRACT_ID, &kNS_PAYMENT_COMPLETE_ACTION_RESPONSE_CID },
   { NS_PAYMENT_ADDRESS_CONTRACT_ID, &kNS_PAYMENT_ADDRESS_CID },
   { NS_PAYMENT_REQUEST_SERVICE_CONTRACT_ID, &kNS_PAYMENT_REQUEST_SERVICE_CID },
   { nullptr }
 };
 
 static const mozilla::Module::CategoryEntry kPaymentRequestCategories[] = {
+  { "payment-request", "GeneralResponseData", NS_GENERAL_RESPONSE_DATA_CONTRACT_ID },
+  { "payment-request", "BasicCardResponseData", NS_BASICCARD_RESPONSE_DATA_CONTRACT_ID },
   { "payment-request", "PaymentActionRequest", NS_PAYMENT_ACTION_REQUEST_CONTRACT_ID },
   { "payment-request", "PaymentCreateActionRequest", NS_PAYMENT_CREATE_ACTION_REQUEST_CONTRACT_ID },
   { "payment-request", "PaymentCompleteActionRequest", NS_PAYMENT_COMPLETE_ACTION_REQUEST_CONTRACT_ID },
   { "payment-request", "PaymentUpdateActionRequest", NS_PAYMENT_UPDATE_ACTION_REQUEST_CONTRACT_ID },
   { "payment-request", "PaymentCanMakeActionResponse", NS_PAYMENT_CANMAKE_ACTION_RESPONSE_CONTRACT_ID },
   { "payment-request", "PaymentAbortActionResponse", NS_PAYMENT_ABORT_ACTION_RESPONSE_CONTRACT_ID },
   { "payment-request", "PaymentShowActionResponse", NS_PAYMENT_SHOW_ACTION_RESPONSE_CONTRACT_ID },
   { "payment-request", "PaymentCompleteActionResponse", NS_PAYMENT_COMPLETE_ACTION_RESPONSE_CONTRACT_ID },
--- a/dom/payments/PaymentRequestService.cpp
+++ b/dom/payments/PaymentRequestService.cpp
@@ -2,16 +2,17 @@
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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/ClearOnShutdown.h"
 #include "PaymentRequestData.h"
 #include "PaymentRequestService.h"
+#include "BasicCardPayment.h"
 
 namespace mozilla {
 namespace dom {
 
 StaticRefPtr<PaymentRequestService> gPaymentService;
 
 namespace {
 
@@ -295,41 +296,57 @@ PaymentRequestService::RequestPayment(ns
         new payments::PaymentRequest(tabId, requestId, methodData, details, options);
 
       if (!mRequestQueue.AppendElement(payment, mozilla::fallible)) {
         return NS_ERROR_OUT_OF_MEMORY;
       }
       break;
     }
     /*
-     *  TODO: 1. Check basic card support once the Basic Card Payment spec is
-     *           implemented.
-     *           https://www.w3.org/TR/payment-method-basic-card/
-     *        2. Check third party payment app support by traversing all
+     *  TODO: 1. Check third party payment app support by traversing all
      *           registered third party payment apps.
      */
     case nsIPaymentActionRequest::CANMAKE_ACTION: {
-      rv = CallTestingUIAction(requestId, type);
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return NS_ERROR_FAILURE;
+      if (IsBasicCardPayment(requestId)) {
+        nsCOMPtr<nsIPaymentCanMakeActionResponse> canMakeResponse =
+          do_CreateInstance(NS_PAYMENT_CANMAKE_ACTION_RESPONSE_CONTRACT_ID);
+        MOZ_ASSERT(canMakeResponse);
+        rv = canMakeResponse->Init(requestId, true);
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          return rv;
+        }
+        nsCOMPtr<nsIPaymentActionResponse> response = do_QueryInterface(canMakeResponse);
+        MOZ_ASSERT(response);
+        rv = RespondPayment(response);
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          return rv;
+        }
+      } else {
+        rv = CallTestingUIAction(requestId, type);
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          return NS_ERROR_FAILURE;
+        }
       }
       break;
     }
     /*
      *  TODO: Launch/inform payment UI here once the UI module is implemented.
      */
     case nsIPaymentActionRequest::SHOW_ACTION: {
       if (mShowingRequest) {
+        nsCOMPtr<nsIPaymentResponseData> responseData =
+          do_CreateInstance(NS_GENERAL_RESPONSE_DATA_CONTRACT_ID);
+        MOZ_ASSERT(responseData);
         nsCOMPtr<nsIPaymentShowActionResponse> showResponse =
           do_CreateInstance(NS_PAYMENT_SHOW_ACTION_RESPONSE_CONTRACT_ID);
         MOZ_ASSERT(showResponse);
         rv = showResponse->Init(requestId,
                                 nsIPaymentActionResponse::PAYMENT_REJECTED,
                                 EmptyString(),
-                                EmptyString(),
+                                responseData,
                                 EmptyString(),
                                 EmptyString(),
                                 EmptyString());
         nsCOMPtr<nsIPaymentActionResponse> response = do_QueryInterface(showResponse);
         MOZ_ASSERT(response);
         rv = RespondPayment(response);
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return rv;
@@ -514,10 +531,37 @@ PaymentRequestService::RemoveActionCallb
   nsCOMPtr<nsIPaymentActionCallback> callback;
   if (!mCallbackHashtable.Get(aRequestId, getter_AddRefs(callback))) {
     return NS_ERROR_FAILURE;
   }
   mCallbackHashtable.Remove(aRequestId);
   return NS_OK;
 }
 
+bool
+PaymentRequestService::IsBasicCardPayment(const nsAString& aRequestId)
+{
+  nsCOMPtr<nsIPaymentRequest> payment;
+  nsresult rv = GetPaymentRequestById(aRequestId, getter_AddRefs(payment));
+  NS_ENSURE_SUCCESS(rv, false);
+  nsCOMPtr<nsIArray> methods;
+  rv = payment->GetPaymentMethods(getter_AddRefs(methods));
+  NS_ENSURE_SUCCESS(rv, false);
+  uint32_t length;
+  rv = methods->GetLength(&length);
+  NS_ENSURE_SUCCESS(rv, false);
+  RefPtr<BasicCardService> service = BasicCardService::GetService();
+  MOZ_ASSERT(service);
+  for (uint32_t index = 0; index < length; ++index) {
+    nsCOMPtr<nsIPaymentMethodData> method = do_QueryElementAt(methods, index);
+    MOZ_ASSERT(method);
+    nsAutoString supportedMethods;
+    rv = method->GetSupportedMethods(supportedMethods);
+    NS_ENSURE_SUCCESS(rv, false);
+    if (service->IsBasicCardPayment(supportedMethods)) {
+      return true;
+    }
+  }
+  return false;
+}
+
 } // end of namespace dom
 } // end of namespace mozilla
--- a/dom/payments/PaymentRequestService.h
+++ b/dom/payments/PaymentRequestService.h
@@ -41,16 +41,19 @@ private:
                     nsIPaymentActionCallback* aCallback);
   nsresult
   RemoveActionCallback(const nsAString& aRequestId);
 
   // this method is only used for testing
   nsresult
   CallTestingUIAction(const nsAString& aRequestId, uint32_t aActionType);
 
+  bool
+  IsBasicCardPayment(const nsAString& aRequestId);
+
   FallibleTArray<nsCOMPtr<nsIPaymentRequest>> mRequestQueue;
 
   nsInterfaceHashtable<nsStringHashKey, nsIPaymentActionCallback> mCallbackHashtable;
 
   nsCOMPtr<nsIPaymentUIService> mTestingUIService;
 
   nsCOMPtr<nsIPaymentRequest> mShowingRequest;
 };
--- a/dom/payments/PaymentRequestUtils.cpp
+++ b/dom/payments/PaymentRequestUtils.cpp
@@ -9,27 +9,43 @@
 #include "nsIMutableArray.h"
 #include "nsISupportsPrimitives.h"
 #include "nsIJSON.h"
 
 namespace mozilla {
 namespace dom {
 
 nsresult
-SerializeFromJSObject(JSContext* aCx, JS::HandleObject aObject, nsAString& aSerializedObject) {
+SerializeFromJSObject(JSContext* aCx, JS::HandleObject aObject, nsAString& aSerializedObject)
+{
+  MOZ_ASSERT(aCx);
   nsCOMPtr<nsIJSON> serializer = do_CreateInstance("@mozilla.org/dom/json;1");
   if (NS_WARN_IF(!serializer)) {
     return NS_ERROR_FAILURE;
   }
   JS::RootedValue value(aCx, JS::ObjectValue(*aObject));
   return serializer->EncodeFromJSVal(value.address(), aCx, aSerializedObject);
 }
 
 nsresult
-DeserializeToJSObject(const nsAString& aSerializedObject, JSContext* aCx, JS::MutableHandleObject aObject) {
+SerializeFromJSVal(JSContext* aCx, JS::HandleValue aValue, nsAString& aSerializedValue)
+{
+  MOZ_ASSERT(aCx);
+  nsCOMPtr<nsIJSON> serializer = do_CreateInstance("@mozilla.org/dom/json;1");
+  if (NS_WARN_IF(!serializer)) {
+    return NS_ERROR_FAILURE;
+  }
+  JS::RootedValue value(aCx, aValue.get());
+  return serializer->EncodeFromJSVal(value.address(), aCx, aSerializedValue);
+}
+
+nsresult
+DeserializeToJSObject(const nsAString& aSerializedObject, JSContext* aCx, JS::MutableHandleObject aObject)
+{
+  MOZ_ASSERT(aCx);
   nsCOMPtr<nsIJSON> deserializer = do_CreateInstance("@mozilla.org/dom/json;1");
   if (NS_WARN_IF(!deserializer)) {
     return NS_ERROR_FAILURE;
   }
   JS::RootedValue value(aCx);
   JS::MutableHandleValue handleVal(&value);
   nsresult rv = deserializer->DecodeToJSVal(aSerializedObject, aCx, handleVal);
   if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -38,10 +54,25 @@ DeserializeToJSObject(const nsAString& a
   if (value.isObject()) {
     aObject.set(&value.toObject());
   } else {
     aObject.set(nullptr);
   }
   return NS_OK;
 }
 
+nsresult
+DeserializeToJSValue(const nsAString& aSerializedObject, JSContext* aCx, JS::MutableHandleValue aValue)
+{
+  MOZ_ASSERT(aCx);
+  nsCOMPtr<nsIJSON> deserializer = do_CreateInstance("@mozilla.org/dom/json;1");
+  if (NS_WARN_IF(!deserializer)) {
+    return NS_ERROR_FAILURE;
+  }
+  nsresult rv = deserializer->DecodeToJSVal(aSerializedObject, aCx, aValue);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+  return NS_OK;
+}
+
 } // end of namespace dom
 } // end of namespace mozilla
--- a/dom/payments/PaymentRequestUtils.h
+++ b/dom/payments/PaymentRequestUtils.h
@@ -14,16 +14,26 @@ namespace mozilla {
 namespace dom {
 
 nsresult
 SerializeFromJSObject(JSContext* aCx,
                       JS::HandleObject aObject,
                       nsAString& aSerializedObject);
 
 nsresult
+SerializeFromJSVal(JSContext* aCx,
+                   JS::HandleValue aValue,
+                   nsAString& aSerializedValue);
+
+nsresult
 DeserializeToJSObject(const nsAString& aSerializedObject,
                       JSContext* aCx,
                       JS::MutableHandleObject aObject);
 
+nsresult
+DeserializeToJSValue(const nsAString& aSerializedObject,
+                     JSContext* aCx,
+                     JS::MutableHandleValue aValue);
+
 } // end of namespace dom
 } // end of namespace mozilla
 
 #endif
--- a/dom/payments/PaymentResponse.cpp
+++ b/dom/payments/PaymentResponse.cpp
@@ -1,15 +1,18 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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/PaymentResponse.h"
+#include "mozilla/dom/BasicCardPaymentBinding.h"
+#include "BasicCardPayment.h"
+#include "PaymentAddress.h"
 #include "PaymentRequestUtils.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(PaymentResponse, mOwner,
                                       mShippingAddress, mPromise)
 
@@ -68,17 +71,35 @@ void
 PaymentResponse::GetMethodName(nsString& aRetVal) const
 {
   aRetVal = mMethodName;
 }
 
 void
 PaymentResponse::GetDetails(JSContext* aCx, JS::MutableHandle<JSObject*> aRetVal) const
 {
-  DeserializeToJSObject(mDetails, aCx, aRetVal);
+  RefPtr<BasicCardService> service = BasicCardService::GetService();
+  MOZ_ASSERT(service);
+  if (!service->IsBasicCardPayment(mMethodName)) {
+    DeserializeToJSObject(mDetails, aCx, aRetVal);
+  } else {
+    BasicCardResponse response;
+    nsresult rv = service->DecodeBasicCardData(mDetails, mOwner, response);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return;
+    }
+
+    MOZ_ASSERT(aCx);
+    JS::RootedValue value(aCx);
+    JS::MutableHandleValue handleValue(&value);
+    if (NS_WARN_IF(!response.ToObjectInternal(aCx, handleValue))) {
+      return;
+    }
+    aRetVal.set(&handleValue.toObject());
+  }
 }
 
 void
 PaymentResponse::GetShippingOption(nsString& aRetVal) const
 {
   aRetVal = mShippingOption;
 }
 
--- a/dom/payments/moz.build
+++ b/dom/payments/moz.build
@@ -17,16 +17,17 @@ EXPORTS.mozilla.dom += [
     'PaymentAddress.h',
     'PaymentRequest.h',
     'PaymentRequestManager.h',
     'PaymentRequestUpdateEvent.h',
     'PaymentResponse.h',
 ]
 
 UNIFIED_SOURCES += [
+    'BasicCardPayment.cpp',
     'PaymentActionRequest.cpp',
     'PaymentActionResponse.cpp',
     'PaymentAddress.cpp',
     'PaymentRequest.cpp',
     'PaymentRequestData.cpp',
     'PaymentRequestManager.cpp',
     'PaymentRequestModule.cpp',
     'PaymentRequestService.cpp',
new file mode 100644
--- /dev/null
+++ b/dom/payments/test/BasiccardChromeScript.js
@@ -0,0 +1,213 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+
+const { XPCOMUtils } = Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+const paymentSrv = Cc["@mozilla.org/dom/payments/payment-request-service;1"].getService(Ci.nsIPaymentRequestService);
+
+function emitTestFail(message) {
+  sendAsyncMessage("test-fail", message);
+}
+
+const billingAddress = Cc["@mozilla.org/dom/payments/payment-address;1"].
+                           createInstance(Ci.nsIPaymentAddress);
+const addressLine = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
+const address = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString);
+address.data = "Easton Ave";
+addressLine.appendElement(address);
+billingAddress.init("USA",              // country
+                     addressLine,        // address line
+                     "CA",               // region
+                     "San Bruno",        // city
+                     "",                 // dependent locality
+                     "94066",            // postal code
+                     "123456",           // sorting code
+                     "en",               // language code
+                     "",                 // organization
+                     "Bill A. Pacheco",  // recipient
+                     "+14344413879"); // phone
+
+const basiccardResponseData = Cc["@mozilla.org/dom/payments/basiccard-response-data;1"].
+                                 createInstance(Ci.nsIBasicCardResponseData);
+
+const showResponse = Cc["@mozilla.org/dom/payments/payment-show-action-response;1"].
+                        createInstance(Ci.nsIPaymentShowActionResponse);
+
+function abortPaymentResponse(requestId) {
+  let abortResponse = Cc["@mozilla.org/dom/payments/payment-abort-action-response;1"].
+                         createInstance(Ci.nsIPaymentAbortActionResponse);
+  abortResponse.init(requestId, Ci.nsIPaymentActionResponse.ABORT_SUCCEEDED);
+  return abortResponse.QueryInterface(Ci.nsIPaymentActionResponse);
+}
+
+function completePaymentResponse(requestId) {
+  let completeResponse = Cc["@mozilla.org/dom/payments/payment-complete-action-response;1"].
+                            createInstance(Ci.nsIPaymentCompleteActionResponse);
+  completeResponse.init(requestId, Ci.nsIPaymentActionResponse.COMPLETE_SUCCEEDED);
+  return completeResponse;
+}
+
+const detailedResponseUI = {
+  canMakePayment: function(requestId) {
+    return null;
+  },
+  showPayment: function(requestId) {
+    try {
+      basiccardResponseData.initData("Bill A. Pacheco",  // cardholderName
+                                     "4916855166538720", // cardNumber
+                                     "01",               // expiryMonth
+                                     "2024",             // expiryYear
+                                     "180",              // cardSecurityCode
+                                     billingAddress);   // billingAddress
+    } catch (e) {
+      emitTestFail("Fail to initialize basic card response data.");
+    }
+    showResponse.init(requestId,
+                      Ci.nsIPaymentActionResponse.PAYMENT_ACCEPTED,
+                      "basic-card",         // payment method
+                      basiccardResponseData,// payment method data
+                      "Bill A. Pacheco",    // payer name
+                      "",                   // payer email
+                      "");                  // payer phone
+    return showResponse;
+  },
+  abortPayment: abortPaymentResponse,
+  completePayment: completePaymentResponse,
+  updatePayment: function(requestId) {
+    return null;
+  },
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPaymentUIService]),
+};
+
+const simpleResponseUI = {
+  canMakePayment: function(requestId) {
+    return null;
+  },
+  showPayment: function(requestId) {
+    try {
+      basiccardResponseData.initData("",                 // cardholderName
+                                     "4916855166538720", // cardNumber
+                                     "",                 // expiryMonth
+                                     "",                 // expiryYear
+                                     "",                 // cardSecurityCode
+                                     null);              // billingAddress
+    } catch (e) {
+      emitTestFail("Fail to initialize basic card response data.");
+    }
+    showResponse.init(requestId,
+                      Ci.nsIPaymentActionResponse.PAYMENT_ACCEPTED,
+                      "basic-card",         // payment method
+                      basiccardResponseData,// payment method data
+                      "Bill A. Pacheco",    // payer name
+                      "",                   // payer email
+                      "");                  // payer phone
+    return showResponse;
+  },
+  abortPayment: abortPaymentResponse,
+  completePayment: completePaymentResponse,
+  updatePayment: function(requestId) {
+    return null;
+  },
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPaymentUIService]),
+};
+
+addMessageListener("set-detailed-ui-service", function() {
+  paymentSrv.setTestingUIService(detailedResponseUI.QueryInterface(Ci.nsIPaymentUIService));
+});
+
+addMessageListener("set-simple-ui-service", function() {
+  paymentSrv.setTestingUIService(simpleResponseUI.QueryInterface(Ci.nsIPaymentUIService));
+});
+
+addMessageListener("error-response-test", function() {
+  // test empty cardNumber
+  try {
+    basiccardResponseData.initData("", "", "", "", "", null);
+    emitTestFail("BasicCardResponse should not be initialized with empty cardNumber.");
+  } catch (e) {
+    if (e.name != "NS_ERROR_FAILURE") {
+      emitTestFail("Empty cardNumber expected 'NS_ERROR_FAILURE', but got " + e.name + ".");
+    }
+  }
+
+  // test invalid expiryMonth 123
+  try {
+    basiccardResponseData.initData("", "4916855166538720", "123", "", "", null);
+    emitTestFail("BasicCardResponse should not be initialized with invalid expiryMonth '123'.");
+  } catch (e) {
+    if (e.name != "NS_ERROR_FAILURE") {
+      emitTestFail("expiryMonth 123 expected 'NS_ERROR_FAILURE', but got " + e.name + ".");
+    }
+  }
+  // test invalid expiryMonth 99
+  try {
+    basiccardResponseData.initData("", "4916855166538720", "99", "", "", null);
+    emitTestFail("BasicCardResponse should not be initialized with invalid expiryMonth '99'.");
+  } catch (e) {
+    if (e.name != "NS_ERROR_FAILURE") {
+      emitTestFail("expiryMonth 99 xpected 'NS_ERROR_FAILURE', but got " + e.name + ".");
+    }
+  }
+  // test invalid expiryMonth ab
+  try {
+    basiccardResponseData.initData("", "4916855166538720", "ab", "", "", null);
+    emitTestFail("BasicCardResponse should not be initialized with invalid expiryMonth 'ab'.");
+  } catch (e) {
+    if (e.name != "NS_ERROR_FAILURE") {
+      emitTestFail("expiryMonth ab expected 'NS_ERROR_FAILURE', but got " + e.name + ".");
+    }
+  }
+  // test invalid expiryYear abcd
+  try {
+    basiccardResponseData.initData("", "4916855166538720", "", "abcd", "", null);
+    emitTestFail("BasicCardResponse should not be initialized with invalid expiryYear 'abcd'.");
+  } catch (e) {
+    if (e.name != "NS_ERROR_FAILURE") {
+      emitTestFail("expiryYear abcd expected 'NS_ERROR_FAILURE', but got " + e.name + ".");
+    }
+  }
+  // test invalid expiryYear 11111
+  try {
+    basiccardResponseData.initData("", "4916855166538720", "", "11111", "", null);
+    emitTestFail("BasicCardResponse should not be initialized with invalid expiryYear '11111'.");
+  } catch (e) {
+    if (e.name != "NS_ERROR_FAILURE") {
+      emitTestFail("expiryYear 11111 expected 'NS_ERROR_FAILURE', but got " + e.name + ".");
+    }
+  }
+
+
+  const responseData = Cc["@mozilla.org/dom/payments/general-response-data;1"].
+                          createInstance(Ci.nsIGeneralResponseData);
+  try {
+    responseData.initData({});
+  } catch (e) {
+    emitTestFail("Fail to initialize response data with empty object.");
+  }
+
+  try {
+    showResponse.init("testid",
+                      Ci.nsIPaymentActionResponse.PAYMENT_ACCEPTED,
+                      "basic-card",         // payment method
+                      responseData,         // payment method data
+                      "Bill A. Pacheco",    // payer name
+                      "",                   // payer email
+                      "");                  // payer phone
+    emitTestFail("nsIPaymentShowActionResponse should not be initialized with basic-card method and nsIGeneralResponseData.");
+  } catch (e) {
+    if (e.name != "NS_ERROR_FAILURE") {
+      emitTestFail("ShowResponse init expected 'NS_ERROR_FAILURE', but got " + e.name + ".");
+    }
+  }
+  sendAsyncMessage("error-response-complete");
+});
+
+addMessageListener("teardown", function() {
+  paymentSrv.cleanup();
+  paymentSrv.setTestingUIService(null);
+  sendAsyncMessage("teardown-complete");
+});
--- a/dom/payments/test/ConstructorChromeScript.js
+++ b/dom/payments/test/ConstructorChromeScript.js
@@ -21,16 +21,19 @@ function checkSimplestRequest(payRequest
   const methodData = payRequest.paymentMethods.queryElementAt(0, Ci.nsIPaymentMethodData);
   if (!methodData) {
     emitTestFail("Fail to get payment methodData.");
   }
   const supportedMethod = methodData.supportedMethods;
   if (supportedMethod != "basic-card") {
     emitTestFail("supported method should be 'basic-card'.");
   }
+  if (methodData.data) {
+    emitTestFail("methodData.data should not exist.");
+  }
 
   // checking the passed PaymentDetails parameter
   const details = payRequest.paymentDetails;
   if (details.totalItem.label != "Total") {
     emitTestFail("total item's label should be 'Total'.");
   }
   if (details.totalItem.amount.currency != "USD") {
     emitTestFail("total item's currency should be 'USD'.");
@@ -77,18 +80,44 @@ function checkComplexRequest(payRequest)
   if (!methodData) {
     emitTestFail("Fail to get payment methodData.");
   }
   let supportedMethod = methodData.supportedMethods;
   if (supportedMethod != "basic-card") {
     emitTestFail("supported method should be 'basic-card'.");
   }
   const data = methodData.data;
-  if (data != "{\"supportedNetworks\":[\"unionpay\",\"visa\",\"mastercard\",\"amex\",\"discover\",\"diners\",\"jcb\",\"mir\"],\"supportedTypes\":[\"prepaid\",\"debit\",\"credit\"]}") {
-    emitTestFail("method data should be '{\"supportedNetworks\":[\"unionpay\",\"visa\",\"mastercard\",\"amex\",\"discover\",\"diners\",\"jcb\",\"mir\"],\"supportedTypes\":[\"prepaid\",\"debit\",\"credit\"]}', but got '" + data + "'.");
+  const supportedNetworks = data.supportedNetworks;
+  const expectedSupportedNetworks = ["unionpay", "visa", "mastercard", "amex",
+                                     "discover", "diners", "jcb", "mir"];
+  if (supportedNetworks.length !=  expectedSupportedNetworks.length) {
+    emitTestFail("supportedNetworks.length should be " +
+                 expectedSupportedNetworks.length +
+                 ", but got " + supportedNetworks.length + ".");
+  }
+  for (let idx = 0; idx < supportedNetworks.length; idx++) {
+    if (supportedNetworks[idx] != expectedSupportedNetworks[idx]) {
+      emitTestFail("supportedNetworks[" + idx + "] should be '" +
+                   expectedSupportedNetworks[idx] + "', but got '" +
+                   supportedNetworks[idx] + "'.");
+    }
+  }
+  const supportedTypes = data.supportedTypes;
+  const expectedSupportedTypes = ["prepaid", "debit", "credit"];
+  if (supportedTypes.length != expectedSupportedTypes.length) {
+    emitTestFail("supportedTypes.length should be '" +
+                 expectedSupportedTypes.length + "', but got '" +
+                 supportedTypes.length + "'.");
+  }
+  for (let idx = 0; idx < supportedTypes.length; idx++) {
+    if (supportedTypes[idx] != expectedSupportedTypes[idx]) {
+      emitTestFail("supportedTypes[" + idx + "] should be '" +
+                   expectedSupportedTypes[idx] + "', but got '" +
+                   supportedTypes[idx] + "'.");
+    }
   }
   // checking the passed PaymentDetails parameter
   const details = payRequest.paymentDetails;
   if (details.id != "payment details" ) {
     emitTestFail("details.id should be 'payment details'.");
   }
   if (details.totalItem.label != "Total") {
     emitTestFail("total item's label should be 'Total'.");
@@ -159,18 +188,19 @@ function checkComplexRequest(payRequest)
     emitTestFail("additional item's label should be 'basic-card discount'.");
   }
   if (additionalItem.amount.currency != "USD") {
     emitTestFail("additional item's currency should be 'USD'.");
   }
   if (additionalItem.amount.value != "-10.00") {
     emitTestFail("additional item's value should be '-10.00'.");
   }
-  if (modifier.data != "{\"discountProgramParticipantId\":\"86328764873265\"}") {
-    emitTestFail("modifier's data should be '{\"discountProgramParticipantId\":\"86328764873265\"}'.");
+  if (modifier.data.discountProgramParticipantId != "86328764873265") {
+    emitTestFail("modifier's data should be '86328764873265', but got '" +
+                 modifier.data.discountProgramParticipantId + "'.");
   }
 
   const shippingOptions = details.shippingOptions;
   if (!shippingOptions) {
     emitTestFail("details.shippingOptions should not be undefined.");
   }
   if (shippingOptions.length != 2) {
     emitTestFail("shippingOptions' length should be 2.");
@@ -222,16 +252,80 @@ function checkComplexRequest(payRequest)
   if (!paymentOptions.requestShipping) {
     emitTestFail("requestShipping option should be true.");
   }
   if (paymentOptions.shippingType != "shipping") {
     emitTestFail("shippingType option should be 'shipping'.")
   }
 }
 
+function checkNonBasicCardRequest(payRequest) {
+  if (payRequest.paymentMethods.length != 1) {
+    emitTestFail("paymentMethods' length should be 1.");
+  }
+
+  const methodData = payRequest.paymentMethods.queryElementAt(0, Ci.nsIPaymentMethodData);
+  if (!methodData) {
+    emitTestFail("Fail to get payment methodData.");
+  }
+  const supportedMethod = methodData.supportedMethods;
+  if (supportedMethod != "testing-payment-method") {
+    emitTestFail("supported method should be 'testing-payment-method'.");
+  }
+
+  const paymentId = methodData.data.paymentId;
+  if (paymentId != "P3892940") {
+    emitTestFail("methodData.data.paymentId should be 'P3892940', but got " + paymentId + ".");
+  }
+  const paymentType = methodData.data.paymentType;
+  if (paymentType != "prepaid") {
+    emitTestFail("methodData.data.paymentType should be 'prepaid', but got " + paymentType + ".");
+  }
+
+  // checking the passed PaymentDetails parameter
+  const details = payRequest.paymentDetails;
+  if (details.totalItem.label != "Total") {
+    emitTestFail("total item's label should be 'Total'.");
+  }
+  if (details.totalItem.amount.currency != "USD") {
+    emitTestFail("total item's currency should be 'USD'.");
+  }
+  if (details.totalItem.amount.value != "1.00") {
+    emitTestFail("total item's value should be '1.00'.");
+  }
+
+  if (details.displayItems) {
+    emitTestFail("details.displayItems should be undefined.");
+  }
+  if (details.modifiers) {
+    emitTestFail("details.displayItems should be undefined.");
+  }
+  if (details.shippingOptions) {
+    emitTestFail("details.shippingOptions should be undefined.");
+  }
+
+  // checking the default generated PaymentOptions parameter
+  const paymentOptions = payRequest.paymentOptions;
+  if (paymentOptions.requestPayerName) {
+    emitTestFail("requestPayerName option should be false.");
+  }
+  if (paymentOptions.requestPayerEmail) {
+    emitTestFail("requestPayerEmail option should be false.");
+  }
+  if (paymentOptions.requestPayerPhone) {
+    emitTestFail("requestPayerPhone option should be false.");
+  }
+  if (paymentOptions.requestShipping) {
+    emitTestFail("requestShipping option should be false.");
+  }
+  if (paymentOptions.shippingType != "shipping") {
+    emitTestFail("shippingType option should be 'shipping'.")
+  }
+}
+
 function checkDuplicateShippingOptionsRequest(payRequest) {
   if (payRequest.paymentMethods.length != 1) {
     emitTestFail("paymentMethods' length should be 1.");
   }
 
   const methodData = payRequest.paymentMethods.queryElementAt(0, Ci.nsIPaymentMethodData);
   if (!methodData) {
     emitTestFail("Fail to get payment methodData.");
@@ -317,16 +411,33 @@ function checkComplexRequestHandler() {
       break;
     }
     checkComplexRequest(payRequest);
   }
   paymentSrv.cleanup();
   sendAsyncMessage("check-complete");
 }
 
+function checkNonBasicCardRequestHandler() {
+  const paymentEnum = paymentSrv.enumerate();
+  if (!paymentEnum.hasMoreElements()) {
+    emitTestFail("PaymentRequestService should have at least one payment request.");
+  }
+  while (paymentEnum.hasMoreElements()) {
+    let payRequest = paymentEnum.getNext().QueryInterface(Ci.nsIPaymentRequest);
+    if (!payRequest) {
+      emitTestFail("Fail to get existing payment request.");
+      break;
+    }
+    checkNonBasicCardRequest(payRequest);
+  }
+  paymentSrv.cleanup();
+  sendAsyncMessage("check-complete");
+}
+
 function checkDuplicateShippingOptionsRequestHandler() {
   const paymentEnum = paymentSrv.enumerate();
   if (!paymentEnum.hasMoreElements()) {
     emitTestFail("PaymentRequestService should have at least one payment request.");
   }
   while (paymentEnum.hasMoreElements()) {
     let payRequest = paymentEnum.getNext().QueryInterface(Ci.nsIPaymentRequest);
     if (!payRequest) {
@@ -361,13 +472,14 @@ function checkMultipleRequestsHandler ()
   paymentSrv.cleanup();
   sendAsyncMessage("check-complete");
 }
 
 addMessageListener("check-simplest-request", checkSimplestRequestHandler);
 addMessageListener("check-complex-request", checkComplexRequestHandler);
 addMessageListener("check-duplicate-shipping-options-request", checkDuplicateShippingOptionsRequestHandler);
 addMessageListener("check-multiple-requests", checkMultipleRequestsHandler);
+addMessageListener("check-nonbasiccard-request", checkNonBasicCardRequestHandler);
 
 addMessageListener("teardown", function() {
   paymentSrv.cleanup();
   sendAsyncMessage("teardown-complete");
 });
--- a/dom/payments/test/ShowPaymentChromeScript.js
+++ b/dom/payments/test/ShowPaymentChromeScript.js
@@ -8,39 +8,40 @@ const { classes: Cc, interfaces: Ci, uti
 const { XPCOMUtils } = Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 const paymentSrv = Cc["@mozilla.org/dom/payments/payment-request-service;1"].getService(Ci.nsIPaymentRequestService);
 
 function emitTestFail(message) {
   sendAsyncMessage("test-fail", message);
 }
 
+const shippingAddress = Cc["@mozilla.org/dom/payments/payment-address;1"].
+                           createInstance(Ci.nsIPaymentAddress);
+const addressLine = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
+const address = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString);
+address.data = "Easton Ave";
+addressLine.appendElement(address);
+shippingAddress.init("USA",              // country
+                     addressLine,        // address line
+                     "CA",               // region
+                     "San Bruno",        // city
+                     "",                 // dependent locality
+                     "94066",            // postal code
+                     "123456",           // sorting code
+                     "en",               // language code
+                     "",                 // organization
+                     "Bill A. Pacheco",  // recipient
+                     "+1-434-441-3879"); // phone
+
 const NormalUIService = {
   shippingOptionChanged: false,
   canMakePayment: function(requestId) {
     return null;
   },
   showPayment: function(requestId) {
-    const shippingAddress = Cc["@mozilla.org/dom/payments/payment-address;1"].
-                            createInstance(Ci.nsIPaymentAddress);
-    const addressLine = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
-    const address = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString);
-    address.data = "Easton Ave";
-    addressLine.appendElement(address);
-    shippingAddress.init("USA",              // country
-                         addressLine,        // address line
-                         "CA",               // region
-                         "San Bruno",        // city
-                         "",                 // dependent locality
-                         "94066",            // postal code
-                         "123456",           // sorting code
-                         "en",               // language code
-                         "",                 // organization
-                         "Bill A. Pacheco",  // recipient
-                         "+1-434-441-3879"); // phone
     paymentSrv.changeShippingAddress(requestId, shippingAddress);
     return null;
   },
   abortPayment: function(requestId) {
     return null;
   },
   completePayment: function(requestId) {
     let completeResponse = Cc["@mozilla.org/dom/payments/payment-complete-action-response;1"].
@@ -62,46 +63,63 @@ const NormalUIService = {
       let shippingOption = shippingOptions.queryElementAt(0, Ci.nsIPaymentShippingOption);
       if (shippingOption.selected) {
         emitTestFail(shippingOption.label + " should not be selected.");
       }
       shippingOption = shippingOptions.queryElementAt(1, Ci.nsIPaymentShippingOption);
       if (!shippingOption.selected) {
         emitTestFail(shippingOption.label + " should be selected.");
       }
-      const paymentData = "{\"cardholderName\":\"Bill A. Pacheco\",\"cardNumber\":\"4024007191304152\",\"expiryMonth\":\"05\",\"expiryYear\":\"2019\",\"cardSecurityCode\":\"024\"}";
+
+      const showResponseData = Cc["@mozilla.org/dom/payments/general-response-data;1"].
+                                  createInstance(Ci.nsIGeneralResponseData);
+
+      try {
+        showResponseData.initData({ paymentToken: "6880281f-0df3-4b8e-916f-66575e2457c1",});
+      } catch (e) {
+        emitTestFail("Fail to initialize response data with { paymentToken: \"6880281f-0df3-4b8e-916f-66575e2457c1\",}");
+      }
+
       showResponse = Cc["@mozilla.org/dom/payments/payment-show-action-response;1"].
                      createInstance(Ci.nsIPaymentShowActionResponse);
       showResponse.init(requestId,
                         Ci.nsIPaymentActionResponse.PAYMENT_ACCEPTED,
-                        "basic-card",               // payment method
-                        paymentData,                // payment method data
+                        "testing-payment-method",   // payment method
+                        showResponseData,           // payment method data
                         "Bill A. Pacheco",          // payer name
                         "",                         // payer email
                         "");                        // payer phone
     }
     return showResponse;
   },
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIPaymentUIService]),
 };
 
 const RejectUIService = {
   canMakePayment: function(requestId) {
     return null;
   },
   showPayment: function(requestId) {
+    const responseData = Cc["@mozilla.org/dom/payments/general-response-data;1"].
+                            createInstance(Ci.nsIGeneralResponseData);
+
+    try {
+      responseData.initData({});
+    } catch (e) {
+      emitTestFail("Fail to initialize response data with empty object.");
+    }
     const showResponse = Cc["@mozilla.org/dom/payments/payment-show-action-response;1"].
-                         createInstance(Ci.nsIPaymentShowActionResponse);
+                            createInstance(Ci.nsIPaymentShowActionResponse);
     showResponse.init(requestId,
                       Ci.nsIPaymentActionResponse.PAYMENT_REJECTED,
-                      "",       // payment method
-                      "",       // payment method data
-                      "",       // payer name
-                      "",       // payer email
-                      "");      // payer phone
+                      "",                 // payment method
+                      responseData,       // payment method data
+                      "",                 // payer name
+                      "",                 // payer email
+                      "");                // payer phone
 
     return showResponse;
   },
   abortPayment: function(requestId) {
     return null;
   },
   completePayment: function(requestId) {
     return null;
@@ -129,40 +147,78 @@ const ErrorUIService = {
   updatePayment: function(requestId) {
     let payRequest = paymentSrv.getPaymentRequestById(requestId);
     if (!payRequest) {
       emitTestFail("Fail to get existing payment request.");
     }
     if (payRequest.paymentDetails.error != "Update with Error") {
       emitTestFail("details.error should be 'Update with Error', but got " + payRequest.paymentDetails.error + ".");
     }
+    const responseData = Cc["@mozilla.org/dom/payments/general-response-data;1"].
+                            createInstance(Ci.nsIGeneralResponseData);
+    try {
+      responseData.initData({});
+    } catch (e) {
+      emitTestFail("Fail to initialize response data with empty object.");
+    }
     const showResponse = Cc["@mozilla.org/dom/payments/payment-show-action-response;1"].
-                         createInstance(Ci.nsIPaymentShowActionResponse);
+                            createInstance(Ci.nsIPaymentShowActionResponse);
     showResponse.init(requestId,
                       Ci.nsIPaymentActionResponse.PAYMENT_REJECTED,
-                      "",       // payment method
-                      "",       // payment method data
-                      "",       // payer name
-                      "",       // payer email
-                      "");      // payer phone
+                      "",                 // payment method
+                      responseData,       // payment method data
+                      "",                 // payer name
+                      "",                 // payer email
+                      "");                // payer phone
+
     return showResponse;
   },
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIPaymentUIService]),
 
 };
 
+function testNullDetailsResponseHandler() {
+  const showResponseData = Cc["@mozilla.org/dom/payments/general-response-data;1"].
+                              createInstance(Ci.nsIGeneralResponseData);
+  try {
+    showResponseData.initData(null);
+    emitTestFail("nsIGeneralResponseData can not be initialized with null object.");
+  } catch (e) {
+    if (e.name != "NS_ERROR_FAILURE") {
+      emitTestFail("Expected 'NS_ERROR_FAILURE' when initializing nsIGeneralResponseData with null object, but got " + e.name + ".");
+    }
+  }
+  const showResponse = Cc["@mozilla.org/dom/payments/payment-show-action-response;1"].
+                          createInstance(Ci.nsIPaymentShowActionResponse);
+  try {
+    showResponse.init("test request id",
+                      Ci.nsIPaymentActionResponse.PAYMENT_ACCEPTED,
+                      "testing-payment-method",   // payment method
+                      showResponseData,           // payment method data
+                      "Bill A. Pacheco",          // payer name
+                      "",                         // payer email
+                      "");                        // payer phone
+  } catch (e) {
+    emitTestFail("Unexpected error " + e.name + " when initializing nsIPaymentShowActionResponse.");
+  }
+  sendAsyncMessage("test-null-details-response-complete");
+}
+
 addMessageListener("set-normal-ui-service", function() {
   paymentSrv.setTestingUIService(NormalUIService.QueryInterface(Ci.nsIPaymentUIService));
 });
 
 addMessageListener("set-reject-ui-service", function() {
   paymentSrv.setTestingUIService(RejectUIService.QueryInterface(Ci.nsIPaymentUIService));
 });
 
 addMessageListener("set-update-with-error-ui-service", function() {
   paymentSrv.setTestingUIService(ErrorUIService.QueryInterface(Ci.nsIPaymentUIService));
 })
 
+addMessageListener("test-null-details-response", testNullDetailsResponseHandler);
+
+
 addMessageListener("teardown", function() {
   paymentSrv.cleanup();
   paymentSrv.setTestingUIService(null);
   sendAsyncMessage('teardown-complete');
 });
--- a/dom/payments/test/head.js
+++ b/dom/payments/test/head.js
@@ -3,17 +3,17 @@ const kTestRoot = getRootDirectory(gTest
 
 function checkSimplePayment(aSimplePayment) {
   // checking the passed PaymentMethods parameter
   is(aSimplePayment.paymentMethods.length, 1, "paymentMethods' length should be 1.");
 
   const methodData = aSimplePayment.paymentMethods.queryElementAt(0, Ci.nsIPaymentMethodData);
   ok(methodData, "Fail to get payment methodData.");
   is(methodData.supportedMethods, "MyPay", "supported method should be 'MyPay'.");
-  is(methodData.data, "", "method data should be empty");
+  ok(!methodData.data, "methodData.data should not exist.");
 
   // checking the passed PaymentDetails parameter
   const details = aSimplePayment.paymentDetails;
   is(details.id, "simple details", "details.id should be 'simple details'.");
   is(details.totalItem.label, "Donation", "total item's label should be 'Donation'.");
   is(details.totalItem.amount.currency, "USD", "total item's currency should be 'USD'.");
   is(details.totalItem.amount.value, "55.00", "total item's value should be '55.00'.");
 
@@ -32,17 +32,17 @@ function checkSimplePayment(aSimplePayme
 
 function checkComplexPayment(aPayment) {
   // checking the passed PaymentMethods parameter
   is(aPayment.paymentMethods.length, 1, "paymentMethods' length should be 1.");
 
   const methodData = aPayment.paymentMethods.queryElementAt(0, Ci.nsIPaymentMethodData);
   ok(methodData, "Fail to get payment methodData.");
   is(methodData.supportedMethods, "MyPay", "supported method should be 'MyPay'.");
-  is(methodData.data, "", "method data should be empty");
+  ok(!methodData.data, "methodData.data should not exist.");
 
   // checking the passed PaymentDetails parameter
   const details = aPayment.paymentDetails;
   is(details.id, "complex details", "details.id should be 'complex details'.");
   is(details.totalItem.label, "Donation", "total item's label should be 'Donation'.");
   is(details.totalItem.amount.currency, "USD", "total item's currency should be 'USD'.");
   is(details.totalItem.amount.value, "55.00", "total item's value should be '55.00'.");
 
@@ -67,18 +67,19 @@ function checkComplexPayment(aPayment) {
   is(modifier.total.amount.value, "45.00", "modifier's total value should be '45.00'.");
 
   const additionalItems = modifier.additionalDisplayItems;
   is(additionalItems.length, "1", "additionalDisplayItems' length should be 1.");
   const additionalItem = additionalItems.queryElementAt(0, Ci.nsIPaymentItem);
   is(additionalItem.label, "MyPay discount", "additional item's label should be 'MyPay discount'.");
   is(additionalItem.amount.currency, "USD", "additional item's currency should be 'USD'.");
   is(additionalItem.amount.value, "-10.00", "additional item's value should be '-10.00'.");
-  is(modifier.data, "{\"discountProgramParticipantId\":\"86328764873265\"}",
-     "modifier's data should be '{\"discountProgramParticipantId\":\"86328764873265\"}'.");
+  ok(modifier.data, "moidifer.data should exist.");
+  is(modifier.data.discountProgramParticipantId, "86328764873265",
+     "modifier.data.discountProgramParticipantId should be '86328764873265'.");
 
   const shippingOptions = details.shippingOptions;
   is(shippingOptions.length, 2, "shippingOptions' length should be 2.");
 
   let shippingOption = shippingOptions.queryElementAt(0, Ci.nsIPaymentShippingOption);
   is(shippingOption.id, "NormalShipping", "1st shippingOption's id should be 'NoramlShpping'.");
   is(shippingOption.label, "NormalShipping", "1st shippingOption's lable should be 'NormalShipping'.");
   is(shippingOption.amount.currency, "USD", "1st shippingOption's amount currency should be 'USD'.");
@@ -102,18 +103,18 @@ function checkComplexPayment(aPayment) {
 }
 
 function checkDupShippingOptionsPayment(aPayment) {
   // checking the passed PaymentMethods parameter
   is(aPayment.paymentMethods.length, 1, "paymentMethods' length should be 1.");
 
   const methodData = aPayment.paymentMethods.queryElementAt(0, Ci.nsIPaymentMethodData);
   ok(methodData, "Fail to get payment methodData.");
-  is(methodData.supportedMethods, "MyPay", "modifier's supported method name should be 'MyPay'.");
-  is(methodData.data, "", "method data should be empty");
+  is(methodData.supportedMethods, "MyPay", "methodData.supportedMethod name should be 'MyPay'.");
+  ok(!methodData.data, "methodData.data should not exist.");
 
   // checking the passed PaymentDetails parameter
   const details = aPayment.paymentDetails;
   is(details.id, "duplicate shipping options details", "details.id should be 'duplicate shipping options details'.");
   is(details.totalItem.label, "Donation", "total item's label should be 'Donation'.");
   is(details.totalItem.amount.currency, "USD", "total item's currency should be 'USD'.");
   is(details.totalItem.amount.value, "55.00", "total item's value should be '55.00'.");
 
--- a/dom/payments/test/mochitest.ini
+++ b/dom/payments/test/mochitest.ini
@@ -1,16 +1,18 @@
 [DEFAULT]
 # skip-if !e10s will be removed once non-e10s is supported
 skip-if = !e10s
 scheme = https
 support-files =
   simple_payment_request.html
+  BasiccardChromeScript.js
   CanMakePaymentChromeScript.js
   ConstructorChromeScript.js
   ShowPaymentChromeScript.js
 
 [test_abortPayment.html]
+[test_basiccard.html]
 [test_canMakePayment.html]
 [test_constructor.html]
 [test_showPayment.html]
 [test_validate_decimal_value.html]
 [test_payment-request-in-iframe.html]
new file mode 100644
--- /dev/null
+++ b/dom/payments/test/test_basiccard.html
@@ -0,0 +1,298 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1375345
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 1375345</title>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript">
+
+  "use strict";
+  SimpleTest.waitForExplicitFinish();
+
+  var gUrl = SimpleTest.getTestFileURL('BasiccardChromeScript.js');
+  var gScript = SpecialPowers.loadChromeScript(gUrl);
+
+  function testFailHandler(message) {
+    ok(false, message);
+  }
+  gScript.addMessageListener("test-fail", testFailHandler);
+
+  const errorTypesMethods = [{
+    supportedMethods: "basic-card",
+    data: {
+      supportedTypes: ["myType"],
+    },
+  }];
+
+  const errorNetworksMethods = [{
+    supportedMethods: "basic-card",
+    data: {
+      supportedNetworks: ["myNetwork"],
+    },
+  }];
+
+  const nullDataMethods = [{
+    supportedMethods: "basic-card",
+  }];
+
+  const emptyDataMethods = [{
+    supportedMethods: "basic-card",
+    data: {},
+  }];
+
+  const unconvertableDataMethods = [{
+    supportedMethods: "basic-card",
+    data: "unconvertable data",
+  }];
+
+  const defaultMethods = [{
+    supportedMethods: "basic-card",
+    data: {
+      supportedNetworks: ["unionpay", "visa", "mastercard", "amex", "discover",
+                          "diners", "jcb", "mir",
+      ],
+      supportedTypes: ["prepaid", "debit", "credit"],
+    },
+  }];
+  const defaultDetails = {
+    id: "test payment",
+    total: {
+      label: "Total",
+      amount: {
+        currency: "USD",
+        value: "1.00"
+      }
+    },
+    shippingOptions: [
+      {
+        id: "NormalShipping",
+        label: "NormalShipping",
+        amount: {
+          currency: "USD",
+          value: "10.00"
+        },
+        selected: true,
+      },
+      {
+        id: "FastShipping",
+        label: "FastShipping",
+        amount: {
+          currency: "USD",
+          value: "30.00"
+        },
+        selected: false,
+      },
+    ],
+  };
+
+  const defaultOptions = {
+    requestPayerName: true,
+    requestPayerEmail: false,
+    reqeustPayerPhone: false,
+    requestShipping: true,
+    shippingType: "shipping"
+  };
+
+  function testBasicCardRequestWithErrorTypes() {
+    return new Promise((resolve, reject) => {
+      try {
+        const payRequest = new PaymentRequest(errorTypesMethods, defaultDetails, defaultOptions);
+        ok(false, "Expected 'TypeError', but got success construction.");
+        resolve();
+      } catch (e) {
+        is(e.name, "TypeError", "Expected TypeError, but got " + e.name);
+        resolve();
+      }
+    });
+  }
+
+  function testBasicCardRequestWithErrorNetworks() {
+    return new Promise((resolve, reject) => {
+      try {
+        const payRequest = new PaymentRequest(errorNetworksMethods, defaultDetails, defaultOptions);
+        ok(false, "Expected 'TypeError', but got success construction.");
+        resolve();
+      } catch (e) {
+        is(e.name, "TypeError", "Expected TypeError, but got " + e.name);
+        resolve();
+      }
+    });
+  }
+
+  function testBasicCardRequestWithUnconvertableData() {
+    return new Promise((resolve, reject) => {
+      try {
+        const payRequest = new PaymentRequest(unconvertableDataMethods, defaultDetails, defaultOptions);
+        ok(false, "Expected 'TypeError', but got success construction.");
+        resolve();
+      } catch (e) {
+        is(e.name, "TypeError", "Expected TypeError, but got " + e.name);
+        resolve();
+      }
+    });
+  }
+
+  function testBasicCardRequestWithNullData() {
+    return new Promise((resolve, reject) => {
+      try {
+        const payRequest = new PaymentRequest(nullDataMethods, defaultDetails, defaultOptions);
+        ok(payRequest, "PaymentRequest should be constructed with null data BasicCardRequest.");
+        resolve();
+      } catch (e) {
+        ok(false, "Unexpected error: " + e.name);
+        resolve();
+      }
+    });
+  }
+
+  function testBasicCardRequestWithEmptyData() {
+    return new Promise((resolve, reject) => {
+      try {
+        const payRequest = new PaymentRequest(emptyDataMethods, defaultDetails, defaultOptions);
+        ok(payRequest, "PaymentRequest should be constructed with empty data BasicCardRequest.");
+        resolve();
+      } catch (e) {
+        ok(false, "Unexpected error: " + e.name);
+        resolve();
+      }
+    });
+  }
+
+  function testCanMakePaymentWithBasicCardRequest() {
+    return new Promise((resolve, reject) => {
+      const payRequest = new PaymentRequest(defaultMethods, defaultDetails, defaultOptions);
+      payRequest.canMakePayment().then( result => {
+        ok(result, "Should be resolved with true, but got false.");
+        resolve();
+      }).catch (e => {
+        ok(false, "Unexpected error: " + e.name);
+        resolve();
+      });
+    });
+  }
+
+  function testBasicCardSimpleResponse() {
+    gScript.sendAsyncMessage("set-simple-ui-service");
+    return new Promise((resolve, reject) => {
+      const payRequest = new PaymentRequest(defaultMethods, defaultDetails, defaultOptions);
+      payRequest.show().then(response => {
+        ok(response.details, "basiccard response should exists.");
+        ok(!response.details.cardholderName, "response.details.cardholderName should not exist.");
+        is(response.details.cardNumber, "4916855166538720", "response.details.cardNumber should be '4916855166538720'.");
+        ok(!response.details.expiryMonth, "response.details.expiryMonth should not exist.");
+        ok(!response.details.expiryYear, "response.details.expiryYear should be '2024'.");
+        ok(!response.details.cardSecurityCode, "response.details.cardSecurityCode should not exist.");
+        ok(!response.details.billingAddress, "response.details.billingAddress should not exist.");
+        response.complete("success").then(() =>{
+          resolve();
+        }).catch(e => {
+          ok(false, "Unexpected error: " + e.name);
+          resolve();
+        });
+      }).catch( e => {
+        ok(false, "Unexpected error: " + e.name);
+        resolve();
+      });
+    });
+  }
+
+  function testBasicCardDetailedResponse() {
+    gScript.sendAsyncMessage("set-detailed-ui-service");
+    return new Promise((resolve, reject) => {
+      const payRequest = new PaymentRequest(defaultMethods, defaultDetails, defaultOptions);
+      payRequest.show().then(response => {
+        ok(response.details, "basiccard response should exists.");
+        is(response.details.cardholderName, "Bill A. Pacheco", "response.details.cardholderName should be 'Bill A. Pacheco'.");
+        is(response.details.cardNumber, "4916855166538720", "response.details.cardNumber should be '4916855166538720'.");
+        is(response.details.expiryMonth, "01", "response.details.expiryMonth should be '01'.");
+        is(response.details.expiryYear, "2024", "response.details.expiryYear should be '2024'.");
+        is(response.details.cardSecurityCode, "180", "response.details.cardSecurityCode should be '180'.");
+        const billingAddress = response.details.billingAddress;
+        is(billingAddress.country, "USA", "country should be 'USA'.");
+        is(billingAddress.addressLine.length, 1, "addressLine.length should be 1.");
+        is(billingAddress.addressLine[0], "Easton Ave", "addressLine[0] should be 'Easton Ave'.");
+        is(billingAddress.region, "CA", "region should be 'CA'.");
+        is(billingAddress.city, "San Bruno", "city should be 'San Bruno'.");
+        is(billingAddress.dependentLocality, "", "dependentLocality should be empty.");
+        is(billingAddress.postalCode, "94066", "postalCode should be '94066'.");
+        is(billingAddress.sortingCode, "123456", "sortingCode should be '123456'.");
+        is(billingAddress.languageCode, "en", "languageCode should be 'en'.");
+        is(billingAddress.organization, "", "organization should be empty." );
+        is(billingAddress.recipient, "Bill A. Pacheco", "recipient should be 'Bill A. Pacheco'.");
+        is(billingAddress.phone, "+14344413879", "phone should be '+14344413879'.");
+        response.complete("success").then(() =>{
+          resolve();
+        }).catch(e => {
+          ok(false, "Unexpected error: " + e.name);
+          resolve();
+        });
+      }).catch( e => {
+        ok(false, "Unexpected error: " + e.name);
+        resolve();
+      });
+    });
+  }
+
+  function testBasicCardErrorResponse() {
+    return new Promise((resolve, reject) => {
+      gScript.addMessageListener("error-response-complete",
+                                 function errorResponseCompleteHandler() {
+        gScript.removeMessageListener("error-response-complete",
+                                      errorResponseCompleteHandler);
+        resolve();
+      });
+      gScript.sendAsyncMessage("error-response-test");
+    });
+  }
+
+  function teardown() {
+    gScript.addMessageListener("teardown-complete", function teardownCompleteHandler() {
+      gScript.removeMessageListener("teardown-complete", teardownCompleteHandler);
+      gScript.removeMessageListener("test-fail", testFailHandler)
+      gScript.destroy();
+      SimpleTest.finish();
+    });
+    gScript.sendAsyncMessage("teardown");
+  }
+
+  function runTests() {
+    testBasicCardRequestWithErrorTypes()
+    .then(testBasicCardRequestWithErrorNetworks)
+    .then(testBasicCardRequestWithUnconvertableData)
+    .then(testBasicCardRequestWithNullData)
+    .then(testBasicCardRequestWithEmptyData)
+    .then(testCanMakePaymentWithBasicCardRequest)
+    .then(testBasicCardSimpleResponse)
+    .then(testBasicCardDetailedResponse)
+    .then(testBasicCardErrorResponse)
+    .then(teardown)
+    .catch( e => {
+      ok(false, "Unexpected error: " + e.name);
+      SimpleTest.finish();
+    });
+  }
+
+  window.addEventListener('load', function() {
+    SpecialPowers.pushPrefEnv({
+      'set': [
+        ['dom.payments.request.enabled', true],
+      ]
+    }, runTests);
+  });
+
+  </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1375345">Mozilla Bug 1375345</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
--- a/dom/payments/test/test_constructor.html
+++ b/dom/payments/test/test_constructor.html
@@ -39,16 +39,24 @@ https://bugzilla.mozilla.org/show_bug.cg
     data: {
       supportedNetworks: ['unionpay', 'visa', 'mastercard', 'amex', 'discover',
                           'diners', 'jcb', 'mir',
       ],
       supportedTypes: ['prepaid', 'debit', 'credit'],
     },
   }];
 
+  const nonBasicCardMethods = [{
+    supportedMethods: "testing-payment-method",
+    data: {
+      paymentId: "P3892940",
+      paymentType: "prepaid",
+    },
+  }];
+
   const complexDetails = {
     id: "payment details",
     total: {
       label: "Total",
       amount: {
         currency: "USD",
         value: "100.00"
       }
@@ -172,16 +180,28 @@ https://bugzilla.mozilla.org/show_bug.cg
       gScript.addMessageListener("check-complete", function checkCompleteHandler() {
         gScript.removeMessageListener("check-complete", checkCompleteHandler);
         resolve();
       });
       gScript.sendAsyncMessage("check-complex-request");
     });
   }
 
+  function testWithNonBasicCardMethods() {
+    return new Promise((resolve, reject) => {
+      const payRequest = new PaymentRequest(nonBasicCardMethods, simplestDetails);
+      ok(payRequest, "PaymentRequest should be created");
+      gScript.addMessageListener("check-complete", function checkCompleteHandler() {
+        gScript.removeMessageListener("check-complete", checkCompleteHandler);
+        resolve();
+      });
+      gScript.sendAsyncMessage("check-nonbasiccard-request");
+    });
+  }
+
   function testWithDuplicateShippingOptionsParameters() {
     return new Promise((resolve, reject) => {
       const payRequest = new PaymentRequest(simplestMethods, duplicateShippingOptionsDetails);
       ok(payRequest, "PaymentRequest should be created");
       gScript.addMessageListener("check-complete", function checkCompleteHandler() {
         gScript.removeMessageListener("check-complete", checkCompleteHandler);
         resolve();
       });
@@ -213,16 +233,17 @@ https://bugzilla.mozilla.org/show_bug.cg
       SimpleTest.finish();
     });
     gScript.sendAsyncMessage("teardown");
   }
 
   function runTests() {
     testWithSimplestParameters()
     .then(testWithComplexParameters)
+    .then(testWithNonBasicCardMethods)
     .then(testWithDuplicateShippingOptionsParameters)
     .then(testMultipleRequests)
     .then(teardown)
     .catch( e => {
       ok(false, "Unexpected error: " + e.name);
       SimpleTest.finish();
     });
   }
--- a/dom/payments/test/test_showPayment.html
+++ b/dom/payments/test/test_showPayment.html
@@ -24,16 +24,18 @@ https://bugzilla.mozilla.org/show_bug.cg
   const defaultMethods = [{
     supportedMethods: "basic-card",
     data: { 
       supportedNetworks: ['unionpay', 'visa', 'mastercard', 'amex', 'discover',
                           'diners', 'jcb', 'mir',
       ],
       supportedTypes: ['prepaid', 'debit', 'credit'],
     },
+  }, {
+    supportedMethods: "testing-payment-method",
   }];
   const defaultDetails = {
     id: "test payment",
     total: {
       label: "Total",
       amount: {
         currency: "USD",
         value: "1.00"
@@ -155,22 +157,18 @@ https://bugzilla.mozilla.org/show_bug.cg
       payRequest.addEventListener("shippingaddresschange", event => {
         event.updateWith(updateWithShippingAddress());
       });
       payRequest.addEventListener("shippingoptionchange", event => {
         event.updateWith(updateWithShippingOption());
       });
       payRequest.show().then(response => {
         is(response.requestId, "test payment", "response.requestId should be 'test payment'.");
-        is(response.methodName, "basic-card", "response.methodName should be 'basic-card'.");
-        is(response.details.cardholderName, "Bill A. Pacheco", "response.details.cardholderName should be 'Bill A. Pacheco'.");
-        is(response.details.cardNumber, "4024007191304152", "response.details.cardNumber should be '4024007191304152'.");
-        is(response.details.expiryMonth, "05", "response.details.expiryMonth should be '05'.");
-        is(response.details.expiryYear, "2019", "response.details.expiryYear should be '2019'.");
-        is(response.details.cardSecurityCode, "024", "response.details.cardSecurityCode should be '024'.");
+        is(response.methodName, "testing-payment-method", "response.methodName should be 'testing-payment-method'.");
+        is(response.details.paymentToken, "6880281f-0df3-4b8e-916f-66575e2457c1", "response.details.paymentToken should be '6880281f-0df3-4b8e-916f-66575e2457c1'.");
         is(response.shippingAddress.country, "USA", "response.shippingAddress.country should be 'USA'.");
         is(response.shippingAddress.addressLine.length, 1, "response.shippingAddress.addressLine.length should be 1.");
         is(response.shippingAddress.addressLine[0], "Easton Ave", "response.shippingAddress.addressLine[0] should be 'Easton Ave'.");
         is(response.shippingAddress.region, "CA", "response.shippingAddress.region should be 'CA'.");
         is(response.shippingAddress.city, "San Bruno", "response.shippingAddress.city should be 'San Bruno'.");
         is(response.shippingAddress.dependentLocality, "", "response.shippingAddress.dependentLocality should be empty.");
         is(response.shippingAddress.postalCode, "94066", "response.shippingAddress.postalCode should be '94066'.");
         is(response.shippingAddress.sortingCode, "123456", "response.shippingAddress.sortingCode should be '123456'.");
@@ -229,30 +227,43 @@ https://bugzilla.mozilla.org/show_bug.cg
         resolve();
       }).catch(e => {
         ok(false, "Unexpected error: " + e.name);
         resolve();
       });
     });
   }
 
+  function testNullDetailsResponse() {
+    return new Promise((resolve, reject) => {
+      gScript.addMessageListener("test-null-details-response-complete",
+                                 function nullDetailsResponseCompleteHandler() {
+        gScript.removeMessageListener("test-null-details-response-complete",
+                                      nullDetailsResponseCompleteHandler);
+        resolve();
+      });
+      gScript.sendAsyncMessage("test-null-details-response");
+    });
+  }
+
   function teardown() {
     gScript.addMessageListener("teardown-complete", function teardownCompleteHandler() {
       gScript.removeMessageListener("teardown-complete", teardownCompleteHandler);
       gScript.removeMessageListener("test-fail", testFailHandler)
       gScript.destroy();
       SimpleTest.finish();
     });
     gScript.sendAsyncMessage("teardown");
   }
 
   function runTests() {
     testRejectShow()
     .then(testShow)
     .then(testUpdateWithError)
+    .then(testNullDetailsResponse)
     .then(teardown)
     .catch( e => {
       ok(false, "Unexpected error: " + e.name);
       SimpleTest.finish();
     });
   }
 
   window.addEventListener('load', function() {
--- a/dom/payments/test/test_validate_decimal_value.html
+++ b/dom/payments/test/test_validate_decimal_value.html
@@ -50,17 +50,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 
   function testInvalidTotalAmounts() {
     return new Promise((resolve, reject) => {
       for (const amount of invalidTotalAmounts) {
         try {
           new PaymentRequest(
             [
               {
-                supportedMethods: ["basic-card"],
+                supportedMethods: "basic-card",
               },
             ],
             {
               total: {
                 label: "",
                 amount: {
                   currency: "USD",
                   value: amount,
@@ -83,17 +83,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 
   function testInvalidAmounts() {
      return new Promise((resolve, reject) => {
       for (const amount of invalidAmounts) {
         try {
           new PaymentRequest(
             [
               {
-                supportedMethods: ["basic-card"],
+                supportedMethods: "basic-card",
               },
             ],
             {
               total: {
                 label: "",
                 amount: {
                   currency: "USD",
                   value: "1.00",
--- a/dom/plugins/base/nsNPAPIPluginStreamListener.cpp
+++ b/dom/plugins/base/nsNPAPIPluginStreamListener.cpp
@@ -31,17 +31,17 @@ nsNPAPIStreamWrapper::~nsNPAPIStreamWrap
 {
   if (mOutputStream) {
     mOutputStream->Close();
   }
 }
 
 // nsNPAPIPluginStreamListener Methods
 NS_IMPL_ISUPPORTS(nsNPAPIPluginStreamListener,
-                  nsITimerCallback, nsIHTTPHeaderListener)
+                  nsITimerCallback, nsIHTTPHeaderListener, nsINamed)
 
 nsNPAPIPluginStreamListener::nsNPAPIPluginStreamListener(nsNPAPIPluginInstance* inst,
                                                          void* notifyData,
                                                          const char* aURL)
   : mStreamBuffer(nullptr)
   , mNotifyURL(aURL ? PL_strdup(aURL) : nullptr)
   , mInst(inst)
   , mStreamBufferSize(0)
@@ -769,16 +769,23 @@ nsNPAPIPluginStreamListener::Notify(nsIT
         StopDataPump();
       }
 
   MaybeRunStopBinding();
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsNPAPIPluginStreamListener::GetName(nsACString& aName)
+{
+  aName.AssignLiteral("nsNPAPIPluginStreamListener");
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsNPAPIPluginStreamListener::StatusLine(const char* line)
 {
   mResponseHeaders.Append(line);
   mResponseHeaders.Append('\n');
   return NS_OK;
 }
 
 NS_IMETHODIMP
--- a/dom/plugins/base/nsNPAPIPluginStreamListener.h
+++ b/dom/plugins/base/nsNPAPIPluginStreamListener.h
@@ -3,16 +3,17 @@
  * 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 nsNPAPIPluginStreamListener_h_
 #define nsNPAPIPluginStreamListener_h_
 
 #include "nscore.h"
 #include "nsIHTTPHeaderListener.h"
+#include "nsINamed.h"
 #include "nsIRequest.h"
 #include "nsITimer.h"
 #include "nsCOMArray.h"
 #include "nsIOutputStream.h"
 #include "nsIPluginInstanceOwner.h"
 #include "nsString.h"
 #include "nsIAsyncVerifyRedirectCallback.h"
 #include "mozilla/PluginLibrary.h"
@@ -36,25 +37,27 @@ public:
 
   NPStream                              mNPStream;
 protected:
   nsCOMPtr<nsIOutputStream>             mOutputStream; // only valid if not browser initiated
   nsNPAPIPluginStreamListener*          mStreamListener; // only valid if browser initiated
 };
 
 class nsNPAPIPluginStreamListener : public nsITimerCallback,
-                                    public nsIHTTPHeaderListener
+                                    public nsIHTTPHeaderListener,
+                                    public nsINamed
 {
 private:
   typedef mozilla::PluginLibrary PluginLibrary;
 
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSITIMERCALLBACK
   NS_DECL_NSIHTTPHEADERLISTENER
+  NS_DECL_NSINAMED
 
   nsNPAPIPluginStreamListener(nsNPAPIPluginInstance* inst, void* notifyData,
                               const char* aURL);
 
   nsresult OnStartBinding(nsPluginStreamListenerPeer* streamPeer);
   nsresult OnDataAvailable(nsPluginStreamListenerPeer* streamPeer,
                            nsIInputStream* input,
                            uint32_t length);
--- a/dom/plugins/base/nsPluginHost.cpp
+++ b/dom/plugins/base/nsPluginHost.cpp
@@ -315,17 +315,18 @@ nsPluginHost::~nsPluginHost()
   UnloadPlugins();
   sInst = nullptr;
 }
 
 NS_IMPL_ISUPPORTS(nsPluginHost,
                   nsIPluginHost,
                   nsIObserver,
                   nsITimerCallback,
-                  nsISupportsWeakReference)
+                  nsISupportsWeakReference,
+                  nsINamed)
 
 already_AddRefed<nsPluginHost>
 nsPluginHost::GetInst()
 {
   if (!sInst) {
     sInst = new nsPluginHost();
     if (!sInst)
       return nullptr;
@@ -3729,16 +3730,23 @@ NS_IMETHODIMP nsPluginHost::Notify(nsITi
       return NS_OK;
     }
     pluginTag = pluginTag->mNext;
   }
 
   return NS_ERROR_FAILURE;
 }
 
+NS_IMETHODIMP
+nsPluginHost::GetName(nsACString& aName)
+{
+  aName.AssignLiteral("nsPluginHost");
+  return NS_OK;
+}
+
 #ifdef XP_WIN
 // Re-enable any top level browser windows that were disabled by modal dialogs
 // displayed by the crashed plugin.
 static void
 CheckForDisabledWindows()
 {
   nsCOMPtr<nsIWindowMediator> wm(do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
   if (!wm)
--- a/dom/plugins/base/nsPluginHost.h
+++ b/dom/plugins/base/nsPluginHost.h
@@ -15,16 +15,17 @@
 #include "nsIPluginTag.h"
 #include "nsPluginsDir.h"
 #include "nsPluginDirServiceProvider.h"
 #include "nsWeakPtr.h"
 #include "nsIPrompt.h"
 #include "nsWeakReference.h"
 #include "MainThreadUtils.h"
 #include "nsTArray.h"
+#include "nsINamed.h"
 #include "nsTObserverArray.h"
 #include "nsITimer.h"
 #include "nsPluginTags.h"
 #include "nsIEffectiveTLDService.h"
 #include "nsIIDNService.h"
 #include "nsCRT.h"
 
 #ifdef XP_WIN
@@ -70,31 +71,33 @@ public:
 
   RefPtr<nsInvalidPluginTag> mPrev;
   RefPtr<nsInvalidPluginTag> mNext;
 };
 
 class nsPluginHost final : public nsIPluginHost,
                            public nsIObserver,
                            public nsITimerCallback,
-                           public nsSupportsWeakReference
+                           public nsSupportsWeakReference,
+                           public nsINamed
 {
   friend class nsPluginTag;
   friend class nsFakePluginTag;
   virtual ~nsPluginHost();
 
 public:
   nsPluginHost();
 
   static already_AddRefed<nsPluginHost> GetInst();
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSIPLUGINHOST
   NS_DECL_NSIOBSERVER
   NS_DECL_NSITIMERCALLBACK
+  NS_DECL_NSINAMED
 
   nsresult LoadPlugins();
   nsresult UnloadPlugins();
 
   nsresult SetUpPluginInstance(const nsACString &aMimeType,
                                nsIURI *aURL,
                                nsPluginInstanceOwner *aOwner);
 
--- a/dom/presentation/PresentationSessionInfo.cpp
+++ b/dom/presentation/PresentationSessionInfo.cpp
@@ -1150,17 +1150,18 @@ PresentationControllingInfo::NotifyData(
  *    receives the answer.)
  * 5. |NotifyTransportReady| of |nsIPresentationSessionTransportCallback| is
  *    called. The presentation session is ready to use, so notify the listener
  *    of CONNECTED state.
  */
 
 NS_IMPL_ISUPPORTS_INHERITED(PresentationPresentingInfo,
                             PresentationSessionInfo,
-                            nsITimerCallback)
+                            nsITimerCallback,
+                            nsINamed)
 
 nsresult
 PresentationPresentingInfo::Init(nsIPresentationControlChannel* aControlChannel)
 {
   PresentationSessionInfo::Init(aControlChannel);
 
   // Add a timer to prevent waiting indefinitely in case the receiver page fails
   // to become ready.
@@ -1537,16 +1538,24 @@ PresentationPresentingInfo::Notify(nsITi
 {
   MOZ_ASSERT(NS_IsMainThread());
   NS_WARNING("The receiver page fails to become ready before timeout.");
 
   mTimer = nullptr;
   return ReplyError(NS_ERROR_DOM_TIMEOUT_ERR);
 }
 
+// nsITimerCallback
+NS_IMETHODIMP
+PresentationPresentingInfo::GetName(nsACString& aName)
+{
+  aName.AssignLiteral("PresentationPresentingInfo");
+  return NS_OK;
+}
+
 // PromiseNativeHandler
 void
 PresentationPresentingInfo::ResolvedCallback(JSContext* aCx,
                                              JS::Handle<JS::Value> aValue)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (NS_WARN_IF(!aValue.isObject())) {
--- a/dom/presentation/PresentationSessionInfo.h
+++ b/dom/presentation/PresentationSessionInfo.h
@@ -9,16 +9,17 @@
 
 #include "base/process.h"
 #include "mozilla/dom/nsIContentParent.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/PromiseNativeHandler.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/RefPtr.h"
 #include "nsCOMPtr.h"
+#include "nsINamed.h"
 #include "nsINetworkInfoService.h"
 #include "nsIPresentationControlChannel.h"
 #include "nsIPresentationDevice.h"
 #include "nsIPresentationListener.h"
 #include "nsIPresentationService.h"
 #include "nsIPresentationSessionTransport.h"
 #include "nsIPresentationSessionTransportBuilder.h"
 #include "nsIServerSocket.h"
@@ -228,21 +229,23 @@ private:
   bool mIsReconnecting = false;
   bool mDoReconnectAfterClose = false;
 };
 
 // Session info with presenting browsing context (receiver side) behaviors.
 class PresentationPresentingInfo final : public PresentationSessionInfo
                                        , public PromiseNativeHandler
                                        , public nsITimerCallback
+                                       , public nsINamed
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIPRESENTATIONCONTROLCHANNELLISTENER
   NS_DECL_NSITIMERCALLBACK
+  NS_DECL_NSINAMED
 
   PresentationPresentingInfo(const nsAString& aUrl,
                              const nsAString& aSessionId,
                              nsIPresentationDevice* aDevice)
     : PresentationSessionInfo(aUrl,
                               aSessionId,
                               nsIPresentationService::ROLE_RECEIVER)
   {
--- a/dom/promise/PromiseDebugging.cpp
+++ b/dom/promise/PromiseDebugging.cpp
@@ -37,17 +37,17 @@ public:
 
   static void DispatchNeeded() {
     if (sDispatched.get()) {
       // An instance of `FlushRejections` has already been dispatched
       // and not run yet. No need to dispatch another one.
       return;
     }
     sDispatched.set(true);
-    SystemGroup::Dispatch("FlushRejections", TaskCategory::Other,
+    SystemGroup::Dispatch(TaskCategory::Other,
                           do_AddRef(new FlushRejections()));
   }
 
   static void FlushSync() {
     sDispatched.set(false);
 
     // Call the callbacks if necessary.
     // Note that these callbacks may in turn cause Promise to turn
--- a/dom/script/ScriptLoader.cpp
+++ b/dom/script/ScriptLoader.cpp
@@ -1497,18 +1497,17 @@ public:
   void SetToken(void* aToken) {
     MOZ_ASSERT(aToken && !mToken);
     mToken = aToken;
   }
 
   static void Dispatch(already_AddRefed<NotifyOffThreadScriptLoadCompletedRunnable>&& aSelf) {
     RefPtr<NotifyOffThreadScriptLoadCompletedRunnable> self = aSelf;
     RefPtr<DocGroup> docGroup = self->mDocGroup;
-    docGroup->Dispatch("NotifyOffThreadScriptLoadCompletedRunnable",
-                       TaskCategory::Other, self.forget());
+    docGroup->Dispatch(TaskCategory::Other, self.forget());
   }
 
   NS_DECL_NSIRUNNABLE
 };
 
 } /* anonymous namespace */
 
 nsresult
@@ -2416,17 +2415,17 @@ void
 ScriptLoader::ProcessPendingRequestsAsync()
 {
   if (HasPendingRequests()) {
     nsCOMPtr<nsIRunnable> task =
       NewRunnableMethod("dom::ScriptLoader::ProcessPendingRequests",
                         this,
                         &ScriptLoader::ProcessPendingRequests);
     if (mDocument) {
-      mDocument->Dispatch("ScriptLoader", TaskCategory::Other, task.forget());
+      mDocument->Dispatch(TaskCategory::Other, task.forget());
     } else {
       NS_DispatchToCurrentThread(task.forget());
     }
   }
 }
 
 void
 ScriptLoader::ProcessPendingRequests()
--- a/dom/security/nsCSPContext.cpp
+++ b/dom/security/nsCSPContext.cpp
@@ -1209,19 +1209,16 @@ nsCSPContext::AsyncReportViolation(nsISu
                                 aObserverSubject,
                                 aSourceFile,
                                 aScriptSample,
                                 aLineNum,
                                 this);
 
   if (XRE_IsContentProcess()) {
     if (mEventTarget) {
-      if (nsCOMPtr<nsINamed> named = do_QueryInterface(task)) {
-        named->SetName("CSPReportSenderRunnable");
-      }
       mEventTarget->Dispatch(task.forget(), NS_DISPATCH_NORMAL);
       return NS_OK;
     }
   }
 
   NS_DispatchToMainThread(task.forget());
   return NS_OK;
 }
--- a/dom/smil/nsSMILTimedElement.cpp
+++ b/dom/smil/nsSMILTimedElement.cpp
@@ -2374,17 +2374,17 @@ nsSMILTimedElement::NotifyChangedInterva
 void
 nsSMILTimedElement::FireTimeEventAsync(EventMessage aMsg, int32_t aDetail)
 {
   if (!mAnimationElement)
     return;
 
   nsCOMPtr<nsIRunnable> event =
     new AsyncTimeEventRunner(mAnimationElement, aMsg, aDetail);
-  mAnimationElement->OwnerDoc()->Dispatch("AsyncTimeEventRunner", TaskCategory::Other,
+  mAnimationElement->OwnerDoc()->Dispatch(TaskCategory::Other,
                                           event.forget());
 }
 
 const nsSMILInstanceTime*
 nsSMILTimedElement::GetEffectiveBeginInstance() const
 {
   switch (mElementState)
   {
--- a/dom/storage/SessionStorageManager.cpp
+++ b/dom/storage/SessionStorageManager.cpp
@@ -162,17 +162,17 @@ SessionStorageManager::CheckStorage(nsIP
 
   if (!aPrincipal) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   nsAutoCString originKey;
   nsAutoCString originAttributes;
   nsresult rv = GenerateOriginKey(aPrincipal, originAttributes, originKey);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
+  if (NS_FAILED(rv)) {
     return rv;
   }
 
   *aRetval = false;
 
   OriginKeyHashTable* table;
   if (!mOATable.Get(originAttributes, &table)) {
     return NS_OK;
--- a/dom/storage/Storage.cpp
+++ b/dom/storage/Storage.cpp
@@ -130,15 +130,14 @@ Storage::NotifyChange(Storage* aStorage,
   // StorageNotifierService.
 
   RefPtr<StorageNotifierRunnable> r =
     new StorageNotifierRunnable(event, aStorageType, aIsPrivate);
 
   if (aImmediateDispatch) {
     Unused << r->Run();
   } else {
-    SystemGroup::Dispatch("Storage::NotifyChange", TaskCategory::Other,
-                          r.forget());
+    SystemGroup::Dispatch(TaskCategory::Other, r.forget());
   }
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/storage/StorageNotifierService.cpp
+++ b/dom/storage/StorageNotifierService.cpp
@@ -62,20 +62,38 @@ StorageNotifierService::Broadcast(Storag
   RefPtr<StorageEvent> event = aEvent;
 
   nsTObserverArray<RefPtr<StorageNotificationObserver>>::ForwardIterator
     iter(service->mObservers);
 
   while (iter.HasMore()) {
     RefPtr<StorageNotificationObserver> observer = iter.GetNext();
 
+    // Enforce that the source storage area's private browsing state matches
+    // this window's state.  These flag checks and their maintenance independent
+    // from the principal's OriginAttributes matter because chrome docshells
+    // that are part of private browsing windows can be private browsing without
+    // having their OriginAttributes set (because they have the system
+    // principal).
+    if (aPrivateBrowsing != observer->IsPrivateBrowsing()) {
+      continue;
+    }
+
+    // No reasons to continue if the principal of the event doesn't match with
+    // the window's one.
+    if (!StorageUtils::PrincipalsEqual(aEvent->GetPrincipal(),
+                                       observer->GetPrincipal())) {
+      continue;
+    }
+
     RefPtr<Runnable> r = NS_NewRunnableFunction(
       "StorageNotifierService::Broadcast",
       [observer, event, aStorageType, aPrivateBrowsing] () {
-        observer->ObserveStorageNotification(event, aStorageType, aPrivateBrowsing);
+        observer->ObserveStorageNotification(event, aStorageType,
+                                             aPrivateBrowsing);
       });
 
     if (aImmediateDispatch) {
       r->Run();
     } else {
       nsCOMPtr<nsIEventTarget> et = observer->GetEventTarget();
       if (et) {
         et->Dispatch(r.forget());
--- a/dom/storage/StorageNotifierService.h
+++ b/dom/storage/StorageNotifierService.h
@@ -19,16 +19,22 @@ class StorageNotificationObserver
 public:
   NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
 
   virtual void
   ObserveStorageNotification(StorageEvent* aEvent,
                              const char16_t* aStorageType,
                              bool aPrivateBrowsing) = 0;
 
+  virtual bool
+  IsPrivateBrowsing() const = 0;
+
+  virtual nsIPrincipal*
+  GetPrincipal() const = 0;
+
   virtual nsIEventTarget*
   GetEventTarget() const = 0;
 };
 
 class StorageNotifierService final
 {
 public:
   NS_INLINE_DECL_REFCOUNTING(StorageNotifierService)
--- a/dom/storage/moz.build
+++ b/dom/storage/moz.build
@@ -9,16 +9,17 @@ with Files("**"):
 
 EXPORTS.mozilla.dom += [
     'LocalStorage.h',
     'LocalStorageManager.h',
     'SessionStorageManager.h',
     'Storage.h',
     'StorageIPC.h',
     'StorageNotifierService.h',
+    'StorageUtils.h',
 ]
 
 UNIFIED_SOURCES += [
     'LocalStorage.cpp',
     'LocalStorageCache.cpp',
     'LocalStorageManager.cpp',
     'SessionStorage.cpp',
     'SessionStorageCache.cpp',
new file mode 100644
--- /dev/null
+++ b/dom/webidl/BasicCardPayment.webidl
@@ -0,0 +1,27 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/.
+ *
+ * The origin of this WebIDL file is
+ *   https://www.w3.org/TR/payment-request/#paymentrequest-interface
+ */
+enum BasicCardType {
+  "credit",
+  "debit",
+  "prepaid"
+};
+
+dictionary BasicCardRequest {
+  sequence<DOMString> supportedNetworks;
+  sequence<BasicCardType> supportedTypes;
+};
+
+dictionary BasicCardResponse {
+           DOMString cardholderName;
+  required DOMString cardNumber;
+           DOMString expiryMonth;
+           DOMString expiryYear;
+           DOMString cardSecurityCode;
+           PaymentAddress? billingAddress;
+};
--- a/dom/webidl/Document.webidl
+++ b/dom/webidl/Document.webidl
@@ -266,19 +266,21 @@ partial interface Document {
 //http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/custom/index.html#dfn-document-register
 partial interface Document {
     // this is deprecated from CustomElements v0
     [Throws, Func="CustomElementRegistry::IsCustomElementEnabled"]
     object registerElement(DOMString name, optional ElementRegistrationOptions options);
 };
 
 // http://dvcs.w3.org/hg/webperf/raw-file/tip/specs/PageVisibility/Overview.html#sec-document-interface
+// https://w3c.github.io/page-visibility/#extensions-to-the-document-interface
 partial interface Document {
   readonly attribute boolean hidden;
   readonly attribute VisibilityState visibilityState;
+           attribute EventHandler onvisibilitychange;
 };
 
 // http://dev.w3.org/csswg/cssom/#extensions-to-the-document-interface
 partial interface Document {
     [Constant]
     readonly attribute StyleSheetList styleSheets;
     attribute DOMString? selectedStyleSheetSet;
     readonly attribute DOMString? lastStyleSheetSet;
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -406,16 +406,17 @@ WEBIDL_FILES = [
     'AudioStreamTrack.webidl',
     'AudioTrack.webidl',
     'AudioTrackList.webidl',
     'AudioWorkletGlobalScope.webidl',
     'AutocompleteInfo.webidl',
     'BarProp.webidl',
     'BaseAudioContext.webidl',
     'BaseKeyframeTypes.webidl',
+    'BasicCardPayment.webidl',
     'BatteryManager.webidl',
     'BeforeUnloadEvent.webidl',
     'BiquadFilterNode.webidl',
     'Blob.webidl',
     'BoxObject.webidl',
     'BroadcastChannel.webidl',
     'BrowserElement.webidl',
     'BrowserElementDictionaries.webidl',
--- a/dom/workers/ScriptLoader.cpp
+++ b/dom/workers/ScriptLoader.cpp
@@ -612,22 +612,16 @@ private:
 
   NS_IMETHOD
   GetName(nsACString& aName) override
   {
     aName.AssignASCII("ScriptLoaderRunnable");
     return NS_OK;
   }
 
-  NS_IMETHOD
-  SetName(const char* aName) override
-  {
-    return NS_ERROR_NOT_IMPLEMENTED;
-  }
-
   void
   LoadingFinished(uint32_t aIndex, nsresult aRv)
   {
     AssertIsOnMainThread();
     MOZ_ASSERT(aIndex < mLoadInfos.Length());
     ScriptLoadInfo& loadInfo = mLoadInfos[aIndex];
 
     loadInfo.mLoadResult = aRv;
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -10,16 +10,17 @@
 #include "nsIConsoleService.h"
 #include "nsIDOMEventTarget.h"
 #include "nsIDocument.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIStreamLoader.h"
 #include "nsIHttpChannel.h"
 #include "nsIHttpChannelInternal.h"
 #include "nsIHttpHeaderVisitor.h"
+#include "nsINamed.h"
 #include "nsINetworkInterceptController.h"
 #include "nsIMutableArray.h"
 #include "nsIScriptError.h"
 #include "nsISimpleEnumerator.h"
 #include "nsITimer.h"
 #include "nsIUploadChannel2.h"
 #include "nsPIDOMWindow.h"
 #include "nsServiceManagerUtils.h"
@@ -4169,16 +4170,17 @@ ServiceWorkerManager::RemoveNavigationIn
     if (list->IsEmpty()) {
       list = nullptr;
       mNavigationInterceptions.Remove(aScope);
     }
   }
 }
 
 class UpdateTimerCallback final : public nsITimerCallback
+                                , public nsINamed
 {
   nsCOMPtr<nsIPrincipal> mPrincipal;
   const nsCString mScope;
 
   ~UpdateTimerCallback()
   {
   }
 
@@ -4202,20 +4204,27 @@ public:
       // shutting down, do nothing
       return NS_OK;
     }
 
     swm->UpdateTimerFired(mPrincipal, mScope);
     return NS_OK;
   }
 
+  NS_IMETHOD
+  GetName(nsACString& aName) override
+  {
+    aName.AssignLiteral("UpdateTimerCallback");
+    return NS_OK;
+  }
+
   NS_DECL_ISUPPORTS
 };
 
-NS_IMPL_ISUPPORTS(UpdateTimerCallback, nsITimerCallback)
+NS_IMPL_ISUPPORTS(UpdateTimerCallback, nsITimerCallback, nsINamed)
 
 bool
 ServiceWorkerManager::MayHaveActiveServiceWorkerInstance(ContentParent* aContent,
                                                          nsIPrincipal* aPrincipal)
 {
   AssertIsOnMainThread();
   MOZ_ASSERT(aPrincipal);
 
--- a/dom/workers/ServiceWorkerPrivate.cpp
+++ b/dom/workers/ServiceWorkerPrivate.cpp
@@ -6,16 +6,17 @@
 
 #include "ServiceWorkerPrivate.h"
 
 #include "ServiceWorkerManager.h"
 #include "ServiceWorkerWindowClient.h"
 #include "nsContentUtils.h"
 #include "nsIHttpChannelInternal.h"
 #include "nsIHttpHeaderVisitor.h"
+#include "nsINamed.h"
 #include "nsINetworkInterceptController.h"
 #include "nsIPushErrorReporter.h"
 #include "nsISupportsImpl.h"
 #include "nsITimedChannel.h"
 #include "nsIUploadChannel2.h"
 #include "nsNetUtil.h"
 #include "nsProxyRelease.h"
 #include "nsQueryObject.h"
@@ -1048,16 +1049,17 @@ ServiceWorkerPrivate::SendPushSubscripti
 
   return NS_OK;
 }
 
 namespace {
 
 class AllowWindowInteractionHandler final : public ExtendableEventCallback
                                           , public nsITimerCallback
+                                          , public nsINamed
                                           , public WorkerHolder
 {
   nsCOMPtr<nsITimer> mTimer;
 
   ~AllowWindowInteractionHandler()
   {
     // We must either fail to initialize or call ClearWindowAllowed.
     MOZ_DIAGNOSTIC_ASSERT(!mTimer);
@@ -1132,16 +1134,24 @@ class AllowWindowInteractionHandler fina
   NS_IMETHOD
   Notify(nsITimer* aTimer) override
   {
     MOZ_DIAGNOSTIC_ASSERT(mTimer == aTimer);
     ClearWindowAllowed(mWorkerPrivate);
     return NS_OK;
   }
 
+  // nsINamed virtual methods
+  NS_IMETHOD
+  GetName(nsACString& aName) override
+  {
+    aName.AssignLiteral("AllowWindowInteractionHandler");
+    return NS_OK;
+  }
+
   // WorkerHolder virtual methods
   bool
   Notify(Status aStatus) override
   {
     // We could try to hold the worker alive until the timer fires, but other
     // APIs are not likely to work in this partially shutdown state.  We might
     // as well let the worker thread exit.
     ClearWindowAllowed(mWorkerPrivate);
@@ -1159,17 +1169,17 @@ public:
   void
   FinishedWithResult(ExtendableEventResult /* aResult */) override
   {
     WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
     ClearWindowAllowed(workerPrivate);
   }
 };
 
-NS_IMPL_ISUPPORTS(AllowWindowInteractionHandler, nsITimerCallback)
+NS_IMPL_ISUPPORTS(AllowWindowInteractionHandler, nsITimerCallback, nsINamed)
 
 class SendNotificationEventRunnable final : public ExtendableEventWorkerRunnable
 {
   const nsString mEventName;
   const nsString mID;
   const nsString mTitle;
   const nsString mDir;
   const nsString mLang;
@@ -2014,16 +2024,17 @@ ServiceWorkerPrivate::IsIdle() const
 {
   AssertIsOnMainThread();
   return mTokenCount == 0 || (mTokenCount == 1 && mIdleKeepAliveToken);
 }
 
 namespace {
 
 class ServiceWorkerPrivateTimerCallback final : public nsITimerCallback
+                                              , public nsINamed
 {
 public:
   typedef void (ServiceWorkerPrivate::*Method)(nsITimer*);
 
   ServiceWorkerPrivateTimerCallback(ServiceWorkerPrivate* aServiceWorkerPrivate,
                                     Method aMethod)
     : mServiceWorkerPrivate(aServiceWorkerPrivate)
     , mMethod(aMethod)
@@ -2033,26 +2044,33 @@ public:
   NS_IMETHOD
   Notify(nsITimer* aTimer) override
   {
     (mServiceWorkerPrivate->*mMethod)(aTimer);
     mServiceWorkerPrivate = nullptr;
     return NS_OK;
   }
 
+  NS_IMETHOD
+  GetName(nsACString& aName) override
+  {
+    aName.AssignLiteral("ServiceWorkerPrivateTimerCallback");
+    return NS_OK;
+  }
+
 private:
   ~ServiceWorkerPrivateTimerCallback() = default;
 
   RefPtr<ServiceWorkerPrivate> mServiceWorkerPrivate;
   Method mMethod;
 
   NS_DECL_THREADSAFE_ISUPPORTS
 };
 
-NS_IMPL_ISUPPORTS(ServiceWorkerPrivateTimerCallback, nsITimerCallback);
+NS_IMPL_ISUPPORTS(ServiceWorkerPrivateTimerCallback, nsITimerCallback, nsINamed);
 
 } // anonymous namespace
 
 void
 ServiceWorkerPrivate::NoteIdleWorkerCallback(nsITimer* aTimer)
 {
   AssertIsOnMainThread();
 
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -11,16 +11,17 @@
 #include "nsIContentSecurityPolicy.h"
 #include "nsIConsoleService.h"
 #include "nsIDOMDOMException.h"
 #include "nsIDOMEvent.h"
 #include "nsIDocument.h"
 #include "nsIDocShell.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsIMemoryReporter.h"
+#include "nsINamed.h"
 #include "nsINetworkInterceptController.h"
 #include "nsIPermissionManager.h"
 #include "nsIScriptError.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIScriptTimeoutHandler.h"
 #include "nsITabChild.h"
 #include "nsITextToSubURI.h"
@@ -1206,17 +1207,18 @@ private:
 
     ReportError(aCx, parent, fireAtScope, aWorkerPrivate, mReport,
                 innerWindowId);
     return true;
   }
 };
 
 class TimerRunnable final : public WorkerRunnable,
-                            public nsITimerCallback
+                            public nsITimerCallback,
+                            public nsINamed
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
 
   explicit TimerRunnable(WorkerPrivate* aWorkerPrivate)
   : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
   { }
 
@@ -1242,19 +1244,27 @@ private:
     return aWorkerPrivate->RunExpiredTimeouts(aCx);
   }
 
   NS_IMETHOD
   Notify(nsITimer* aTimer) override
   {
     return Run();
   }
+
+  NS_IMETHOD
+  GetName(nsACString& aName) override
+  {
+    aName.AssignLiteral("TimerRunnable");
+    return NS_OK;
+  }
 };
 
-NS_IMPL_ISUPPORTS_INHERITED(TimerRunnable, WorkerRunnable, nsITimerCallback)
+NS_IMPL_ISUPPORTS_INHERITED(TimerRunnable, WorkerRunnable, nsITimerCallback,
+                            nsINamed)
 
 class DebuggerImmediateRunnable : public WorkerRunnable
 {
   RefPtr<dom::Function> mHandler;
 
 public:
   explicit DebuggerImmediateRunnable(WorkerPrivate* aWorkerPrivate,
                                      dom::Function& aHandler)
@@ -5196,21 +5206,17 @@ WorkerPrivate::DispatchToMainThread(nsIR
   nsCOMPtr<nsIRunnable> r = aRunnable;
   return DispatchToMainThread(r.forget(), aFlags);
 }
 
 nsresult
 WorkerPrivate::DispatchToMainThread(already_AddRefed<nsIRunnable> aRunnable,
                                     uint32_t aFlags)
 {
-  nsCOMPtr<nsIRunnable> runnable = aRunnable;
-  if (nsCOMPtr<nsINamed> named = do_QueryInterface(runnable)) {
-    named->SetName("WorkerRunnable");
-  }
-  return mMainThreadEventTarget->Dispatch(runnable.forget(), aFlags);
+  return mMainThreadEventTarget->Dispatch(Move(aRunnable), aFlags);
 }
 
 nsISerialEventTarget*
 WorkerPrivate::ControlEventTarget()
 {
   return mWorkerControlEventTarget;
 }
 
--- a/dom/workers/WorkerScope.cpp
+++ b/dom/workers/WorkerScope.cpp
@@ -489,17 +489,17 @@ WorkerGlobalScope::CreateImageBitmap(JSC
                                aRv);
   } else {
     aRv.Throw(NS_ERROR_TYPE_ERR);
     return nullptr;
   }
 }
 
 nsresult
-WorkerGlobalScope::Dispatch(const char* aName, TaskCategory aCategory,
+WorkerGlobalScope::Dispatch(TaskCategory aCategory,
                             already_AddRefed<nsIRunnable>&& aRunnable)
 {
   return EventTargetFor(aCategory)->Dispatch(Move(aRunnable),
                                              NS_DISPATCH_NORMAL);
 }
 
 nsISerialEventTarget*
 WorkerGlobalScope::EventTargetFor(TaskCategory aCategory) const
@@ -1075,17 +1075,17 @@ WorkerDebuggerGlobalScope::Dump(JSContex
 {
   WorkerGlobalScope* scope = mWorkerPrivate->GetOrCreateGlobalScope(aCx);
   if (scope) {
     scope->Dump(aString);
   }
 }
 
 nsresult
-WorkerDebuggerGlobalScope::Dispatch(const char* aName, TaskCategory aCategory,
+WorkerDebuggerGlobalScope::Dispatch(TaskCategory aCategory,
                                     already_AddRefed<nsIRunnable>&& aRunnable)
 {
   return EventTargetFor(aCategory)->Dispatch(Move(aRunnable),
                                              NS_DISPATCH_NORMAL);
 }
 
 nsISerialEventTarget*
 WorkerDebuggerGlobalScope::EventTargetFor(TaskCategory aCategory) const
--- a/dom/workers/WorkerScope.h
+++ b/dom/workers/WorkerScope.h
@@ -203,17 +203,17 @@ public:
   {
     MOZ_ASSERT(mWindowInteractionsAllowed > 0);
     mWindowInteractionsAllowed--;
   }
 
   // Override DispatchTrait API to target the worker thread.  Dispatch may
   // return failure if the worker thread is not alive.
   nsresult
-  Dispatch(const char* aName, TaskCategory aCategory,
+  Dispatch(TaskCategory aCategory,
            already_AddRefed<nsIRunnable>&& aRunnable) override;
 
   nsISerialEventTarget*
   EventTargetFor(TaskCategory aCategory) const override;
 
   AbstractThread*
   AbstractMainThreadFor(TaskCategory aCategory) override;
 };
@@ -416,17 +416,17 @@ public:
   }
 
   void
   Dump(JSContext* aCx, const Optional<nsAString>& aString) const;
 
   // Override DispatchTrait API to target the worker thread.  Dispatch may
   // return failure if the worker thread is not alive.
   nsresult
-  Dispatch(const char* aName, TaskCategory aCategory,
+  Dispatch(TaskCategory aCategory,
            already_AddRefed<nsIRunnable>&& aRunnable) override;
 
   nsISerialEventTarget*
   EventTargetFor(TaskCategory aCategory) const override;
 
   AbstractThread*
   AbstractMainThreadFor(TaskCategory aCategory) override;
 
--- a/dom/xhr/XMLHttpRequestMainThread.cpp
+++ b/dom/xhr/XMLHttpRequestMainThread.cpp
@@ -3949,31 +3949,26 @@ XMLHttpRequestMainThread::BlobStoreCompl
 
 NS_IMETHODIMP
 XMLHttpRequestMainThread::GetName(nsACString& aName)
 {
   aName.AssignLiteral("XMLHttpRequest");
   return NS_OK;
 }
 
-NS_IMETHODIMP
-XMLHttpRequestMainThread::SetName(const char* aName)
-{
-  return NS_ERROR_NOT_IMPLEMENTED;
-}
-
 // nsXMLHttpRequestXPCOMifier implementation
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXMLHttpRequestXPCOMifier)
   NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
   NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
   NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink)
   NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectCallback)
   NS_INTERFACE_MAP_ENTRY(nsIProgressEventSink)
   NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
   NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
+  NS_INTERFACE_MAP_ENTRY(nsINamed)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStreamListener)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXMLHttpRequestXPCOMifier)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXMLHttpRequestXPCOMifier)
 
 // Can't NS_IMPL_CYCLE_COLLECTION( because mXHR has ambiguous
 // inheritance from nsISupports.
--- a/dom/xhr/XMLHttpRequestMainThread.h
+++ b/dom/xhr/XMLHttpRequestMainThread.h
@@ -873,17 +873,18 @@ private:
 
 // A shim class designed to expose the non-DOM interfaces of
 // XMLHttpRequest via XPCOM stuff.
 class nsXMLHttpRequestXPCOMifier final : public nsIStreamListener,
                                          public nsIChannelEventSink,
                                          public nsIAsyncVerifyRedirectCallback,
                                          public nsIProgressEventSink,
                                          public nsIInterfaceRequestor,
-                                         public nsITimerCallback
+                                         public nsITimerCallback,
+                                         public nsINamed
 {
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsXMLHttpRequestXPCOMifier,
                                            nsIStreamListener)
 
   explicit nsXMLHttpRequestXPCOMifier(XMLHttpRequestMainThread* aXHR) :
     mXHR(aXHR)
   {
@@ -898,16 +899,17 @@ private:
 
 public:
   NS_FORWARD_NSISTREAMLISTENER(mXHR->)
   NS_FORWARD_NSIREQUESTOBSERVER(mXHR->)
   NS_FORWARD_NSICHANNELEVENTSINK(mXHR->)
   NS_FORWARD_NSIASYNCVERIFYREDIRECTCALLBACK(mXHR->)
   NS_FORWARD_NSIPROGRESSEVENTSINK(mXHR->)
   NS_FORWARD_NSITIMERCALLBACK(mXHR->)
+  NS_FORWARD_NSINAMED(mXHR->)
 
   NS_DECL_NSIINTERFACEREQUESTOR
 
 private:
   RefPtr<XMLHttpRequestMainThread> mXHR;
 };
 
 class nsXHRParseEndListener : public nsIDOMEventListener
--- a/dom/xml/nsXMLContentSink.cpp
+++ b/dom/xml/nsXMLContentSink.cpp
@@ -1093,18 +1093,17 @@ nsXMLContentSink::HandleEndElement(const
     }
     mNotifyLevel = stackLen - 1;
   }
   DidAddContent();
 
   if (content->IsSVGElement(nsGkAtoms::svg)) {
     FlushTags();
     nsCOMPtr<nsIRunnable> event = new nsHtml5SVGLoadDispatcher(content);
-    if (NS_FAILED(content->OwnerDoc()->Dispatch("nsHtml5SVGLoadDispatcher",
-                                                TaskCategory::Other,
+    if (NS_FAILED(content->OwnerDoc()->Dispatch(TaskCategory::Other,
                                                 event.forget()))) {
       NS_WARNING("failed to dispatch svg load dispatcher");
     }
   }
 
   return aInterruptable && NS_SUCCEEDED(result) ? DidProcessATokenImpl() :
                                                   result;
 }
--- a/editor/composer/nsComposerCommandsUpdater.cpp
+++ b/editor/composer/nsComposerCommandsUpdater.cpp
@@ -35,17 +35,18 @@ nsComposerCommandsUpdater::~nsComposerCo
 {
   // cancel any outstanding update timer
   if (mUpdateTimer) {
     mUpdateTimer->Cancel();
   }
 }
 
 NS_IMPL_ISUPPORTS(nsComposerCommandsUpdater, nsISelectionListener,
-                  nsIDocumentStateListener, nsITransactionListener, nsITimerCallback)
+                  nsIDocumentStateListener, nsITransactionListener,
+                  nsITimerCallback, nsINamed)
 
 #if 0
 #pragma mark -
 #endif
 
 NS_IMETHODIMP
 nsComposerCommandsUpdater::NotifyDocumentCreated()
 {
@@ -350,16 +351,23 @@ nsComposerCommandsUpdater::GetCommandUpd
 {
   nsCOMPtr<nsIDocShell> docShell = do_QueryReferent(mDocShell);
   NS_ENSURE_TRUE(docShell, nullptr);
   nsCOMPtr<nsICommandManager> manager = docShell->GetCommandManager();
   nsCOMPtr<nsPICommandUpdater> updater = do_QueryInterface(manager);
   return updater.forget();
 }
 
+NS_IMETHODIMP
+nsComposerCommandsUpdater::GetName(nsACString& aName)
+{
+  aName.AssignLiteral("nsComposerCommandsUpdater");
+  return NS_OK;
+}
+
 #if 0
 #pragma mark -
 #endif
 
 nsresult
 nsComposerCommandsUpdater::Notify(nsITimer *timer)
 {
   NS_ASSERTION(timer == mUpdateTimer.get(), "Hey, this ain't my timer!");
--- a/editor/composer/nsComposerCommandsUpdater.h
+++ b/editor/composer/nsComposerCommandsUpdater.h
@@ -7,32 +7,34 @@
 
 
 
 #ifndef nsComposerCommandsUpdater_h__
 #define nsComposerCommandsUpdater_h__
 
 #include "nsCOMPtr.h"                   // for already_AddRefed, nsCOMPtr
 #include "nsIDocumentStateListener.h"
+#include "nsINamed.h"
 #include "nsISelectionListener.h"
 #include "nsISupportsImpl.h"            // for NS_DECL_ISUPPORTS
 #include "nsITimer.h"                   // for NS_DECL_NSITIMERCALLBACK, etc
 #include "nsITransactionListener.h"     // for nsITransactionListener
 #include "nsIWeakReferenceUtils.h"      // for nsWeakPtr
 #include "nscore.h"                     // for NS_IMETHOD, nsresult, etc
 
 class nsPIDOMWindowOuter;
 class nsITransaction;
 class nsITransactionManager;
 class nsPICommandUpdater;
 
 class nsComposerCommandsUpdater : public nsISelectionListener,
                                   public nsIDocumentStateListener,
                                   public nsITransactionListener,
-                                  public nsITimerCallback
+                                  public nsITimerCallback,
+                                  public nsINamed
 {
 public:
 
                                   nsComposerCommandsUpdater();
 
   // nsISupports
   NS_DECL_ISUPPORTS
 
@@ -40,16 +42,19 @@ public:
   NS_DECL_NSISELECTIONLISTENER
 
   // nsIDocumentStateListener
   NS_DECL_NSIDOCUMENTSTATELISTENER
 
   // nsITimerCallback interfaces
   NS_DECL_NSITIMERCALLBACK
 
+  // nsINamed
+  NS_DECL_NSINAMED
+
   /** nsITransactionListener interfaces
     */
   NS_IMETHOD WillDo(nsITransactionManager *aManager, nsITransaction *aTransaction, bool *aInterrupt) override;
   NS_IMETHOD DidDo(nsITransactionManager *aManager, nsITransaction *aTransaction, nsresult aDoResult) override;
   NS_IMETHOD WillUndo(nsITransactionManager *aManager, nsITransaction *aTransaction, bool *aInterrupt) override;
   NS_IMETHOD DidUndo(nsITransactionManager *aManager, nsITransaction *aTransaction, nsresult aUndoResult) override;
   NS_IMETHOD WillRedo(nsITransactionManager *aManager, nsITransaction *aTransaction, bool *aInterrupt) override;
   NS_IMETHOD DidRedo(nsITransactionManager *aManager, nsITransaction *aTransaction, nsresult aRedoResult) override;
--- a/editor/composer/nsEditorSpellCheck.cpp
+++ b/editor/composer/nsEditorSpellCheck.cpp
@@ -405,18 +405,17 @@ nsEditorSpellCheck::InitSpellChecker(nsI
   // do not fail if UpdateCurrentDictionary fails because this method may
   // succeed later.
   rv = UpdateCurrentDictionary(aCallback);
   if (NS_FAILED(rv) && aCallback) {
     // However, if it does fail, we still need to call the callback since we
     // discard the failure.  Do it asynchronously so that the caller is always
     // guaranteed async behavior.
     RefPtr<CallbackCaller> caller = new CallbackCaller(aCallback);
-    rv = doc->Dispatch("nsEditorSpellCheck::CallbackCaller",
-                       TaskCategory::Other, caller.forget());
+    rv = doc->Dispatch(TaskCategory::Other, caller.forget());
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsEditorSpellCheck::GetNextMisspelledWord(char16_t **aNextMisspelledWord)
--- a/editor/libeditor/TextEditRules.cpp
+++ b/editor/libeditor/TextEditRules.cpp
@@ -99,16 +99,17 @@ TextEditRules::~TextEditRules()
   }
 }
 
 NS_IMPL_CYCLE_COLLECTION(TextEditRules, mBogusNode, mCachedSelectionNode)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TextEditRules)
   NS_INTERFACE_MAP_ENTRY(nsIEditRules)
   NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
+  NS_INTERFACE_MAP_ENTRY(nsINamed)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIEditRules)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(TextEditRules)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(TextEditRules)
 
 NS_IMETHODIMP
 TextEditRules::Init(TextEditor* aTextEditor)
@@ -1584,16 +1585,23 @@ TextEditRules::Notify(nsITimer* aTimer)
   // Check whether our text editor's password flag was changed before this
   // "hide password character" timer actually fires.
   nsresult rv = IsPasswordEditor() ? HideLastPWInput() : NS_OK;
   ASSERT_PASSWORD_LENGTHS_EQUAL();
   mLastLength = 0;
   return rv;
 }
 
+NS_IMETHODIMP
+TextEditRules::GetName(nsACString& aName)
+{
+  aName.AssignLiteral("TextEditRules");
+  return NS_OK;
+}
+
 nsresult
 TextEditRules::HideLastPWInput()
 {
   if (!mLastLength) {
     // Special case, we're trying to replace a range that no longer exists
     return NS_OK;
   }
 
--- a/editor/libeditor/TextEditRules.h
+++ b/editor/libeditor/TextEditRules.h
@@ -6,16 +6,17 @@
 #ifndef mozilla_TextEditRules_h
 #define mozilla_TextEditRules_h
 
 #include "mozilla/EditorBase.h"
 #include "nsCOMPtr.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsIEditRules.h"
 #include "nsIEditor.h"
+#include "nsINamed.h"
 #include "nsISupportsImpl.h"
 #include "nsITimer.h"
 #include "nsString.h"
 #include "nscore.h"
 
 class nsIDOMNode;
 
 namespace mozilla {
@@ -35,16 +36,17 @@ class Selection;
  *    directly.  Content nodes in document fragments that are not part of the
  *    document itself may be manipulated at will.  Operations on document
  *    fragments must <B>not</B> go through the editor.
  * 2. Selection must not be explicitly set by the rule method.
  *    Any manipulation of Selection must be done by the editor.
  */
 class TextEditRules : public nsIEditRules
                     , public nsITimerCallback
+                    , public nsINamed
 {
 public:
   typedef dom::Element Element;
   typedef dom::Selection Selection;
   typedef dom::Text Text;
   template<typename T> using OwningNonNull = OwningNonNull<T>;
 
   NS_DECL_NSITIMERCALLBACK
@@ -63,16 +65,19 @@ public:
                        nsIEditor::EDirection aDirection) override;
   NS_IMETHOD WillDoAction(Selection* aSelection, RulesInfo* aInfo,
                           bool* aCancel, bool* aHandled) override;
   NS_IMETHOD DidDoAction(Selection* aSelection, RulesInfo* aInfo,
                          nsresult aResult) override;
   NS_IMETHOD_(bool) DocumentIsEmpty() override;
   NS_IMETHOD DocumentModified() override;
 
+  // nsINamed methods
+  NS_DECL_NSINAMED
+
 protected:
   virtual ~TextEditRules();
 
 public:
   void ResetIMETextPWBuf();
 
   /**
    * Handles the newline characters either according to aNewLineHandling
--- a/extensions/pref/autoconfig/src/nsAutoConfig.cpp
+++ b/extensions/pref/autoconfig/src/nsAutoConfig.cpp
@@ -31,17 +31,19 @@ mozilla::LazyLogModule MCD("MCD");
 extern nsresult EvaluateAdminConfigScript(const char *js_buffer, size_t length,
                                           const char *filename,
                                           bool bGlobalContext,
                                           bool bCallbacks,
                                           bool skipFirstLine);
 
 // nsISupports Implementation
 
-NS_IMPL_ISUPPORTS(nsAutoConfig, nsIAutoConfig, nsITimerCallback, nsIStreamListener, nsIObserver, nsIRequestObserver, nsISupportsWeakReference)
+NS_IMPL_ISUPPORTS(nsAutoConfig, nsIAutoConfig, nsITimerCallback, nsIStreamListener,
+                  nsIObserver, nsIRequestObserver, nsISupportsWeakReference,
+                  nsINamed)
 
 nsAutoConfig::nsAutoConfig()
 {
 }
 
 nsresult nsAutoConfig::Init()
 {
     // member initializers and constructor code
@@ -167,16 +169,23 @@ nsAutoConfig::OnStopRequest(nsIRequest *
 
 // Notify method as a TimerCallBack function
 NS_IMETHODIMP nsAutoConfig::Notify(nsITimer *timer)
 {
     downloadAutoConfig();
     return NS_OK;
 }
 
+NS_IMETHODIMP
+nsAutoConfig::GetName(nsACString& aName)
+{
+  aName.AssignLiteral("nsAutoConfig");
+  return NS_OK;
+}
+
 /* Observe() is called twice: once at the instantiation time and other
    after the profile is set. It doesn't do anything but return NS_OK during the
    creation time. Second time it calls  downloadAutoConfig().
 */
 
 NS_IMETHODIMP nsAutoConfig::Observe(nsISupports *aSubject,
                                     const char *aTopic,
                                     const char16_t *someData)
--- a/extensions/pref/autoconfig/src/nsAutoConfig.h
+++ b/extensions/pref/autoconfig/src/nsAutoConfig.h
@@ -4,38 +4,41 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsAutoConfig_h
 #define nsAutoConfig_h
 
 #include "nsIAutoConfig.h"
 #include "nsITimer.h"
 #include "nsIFile.h"
+#include "nsINamed.h"
 #include "nsIObserver.h"
 #include "nsIStreamListener.h"
 #include "nsIPrefService.h"
 #include "nsIPrefBranch.h"
 #include "nsWeakReference.h"
 #include "nsString.h"
 
 class nsAutoConfig : public nsIAutoConfig,
                      public nsITimerCallback,
                      public nsIStreamListener,
                      public nsIObserver,
-                     public nsSupportsWeakReference
+                     public nsSupportsWeakReference,
+                     public nsINamed
 
 {
     public:
 
         NS_DECL_THREADSAFE_ISUPPORTS
         NS_DECL_NSIAUTOCONFIG
         NS_DECL_NSIREQUESTOBSERVER
         NS_DECL_NSISTREAMLISTENER
         NS_DECL_NSIOBSERVER
         NS_DECL_NSITIMERCALLBACK
+        NS_DECL_NSINAMED
 
         nsAutoConfig();
         nsresult Init();
 
     protected:
 
         virtual ~nsAutoConfig();
         nsresult downloadAutoConfig();
--- a/gfx/doc/README.webrender
+++ b/gfx/doc/README.webrender
@@ -24,18 +24,18 @@ For a debug webrender build:
     See also https://groups.google.com/forum/#!topic/mozilla.dev.servo/MbeMcqqO1fs
 
 
 What if you have to pull in an update to webrender itself?
 
 1) Update your graphics branch checkout to the latest code on the
    graphics branch
 2) Check out and update the webrender repo to the version you want
-3) Copy over the webrender and webrender_traits folders into gfx/. The best way
-   to do this is to simply delete the gfx/webrender and gfx/webrender_traits
+3) Copy over the webrender and webrender_api folders into gfx/. The best way
+   to do this is to simply delete the gfx/webrender and gfx/webrender_api
    folders and use |cp -R| to copy them in again from the webrender repo. Update
    the "latest commit" information at the bottom of this file with the version.
 4) If you need to modify webrender_bindings/Cargo.toml to include or remove
    features, do so now.
 4) Commit your changes to the graphics branch locally
 5) Run |mach vendor rust| to update the rust dependencies in third_party/rust
 6) Commit the vendored changes locally
 7) Build and test. You may need to make changes in bindings.rs or on
--- a/gfx/layers/CompositorTypes.h
+++ b/gfx/layers/CompositorTypes.h
@@ -247,17 +247,22 @@ struct TextureInfo
  * See ShadowLayerForwarder::OpenDescriptor for example.
  */
 enum class OpenMode : uint8_t {
   OPEN_NONE        = 0,
   OPEN_READ        = 0x1,
   OPEN_WRITE       = 0x2,
   OPEN_READ_WRITE  = OPEN_READ|OPEN_WRITE,
   OPEN_READ_ONLY   = OPEN_READ,
-  OPEN_WRITE_ONLY  = OPEN_WRITE
+  OPEN_WRITE_ONLY  = OPEN_WRITE,
+
+  // This is only used in conjunction with OMTP to indicate that the DrawTarget
+  // that is being borrowed will be painted asynchronously, and so will outlive
+  // the write lock.
+  OPEN_ASYNC_WRITE = 0x04
 };
 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(OpenMode)
 
 // The kinds of mask texture a shader can support
 // We rely on the items in this enum being sequential
 enum class MaskType : uint8_t {
   MaskNone = 0,   // no mask layer
   Mask,           // mask layer
--- a/gfx/layers/ImageContainer.cpp
+++ b/gfx/layers/ImageContainer.cpp
@@ -124,17 +124,17 @@ ImageContainerListener::ClearImageContai
 {
   MutexAutoLock lock(mLock);
   mImageContainer = nullptr;
 }
 
 already_AddRefed<ImageClient>
 ImageContainer::GetImageClient()
 {
-  ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+  RecursiveMutexAutoLock mon(mRecursiveMutex);
   EnsureImageClient();
   RefPtr<ImageClient> imageClient = mImageClient;
   return imageClient.forget();
 }
 
 void
 ImageContainer::EnsureImageClient()
 {
@@ -158,32 +158,32 @@ ImageContainer::EnsureImageClient()
       // is going to die anyway.
       mAsyncContainerHandle = CompositableHandle();
       mNotifyCompositeListener = nullptr;
     }
   }
 }
 
 ImageContainer::ImageContainer(Mode flag)
-: mReentrantMonitor("ImageContainer.mReentrantMonitor"),
+: mRecursiveMutex("ImageContainer.mRecursiveMutex"),
   mGenerationCounter(++sGenerationCounter),
   mPaintCount(0),
   mDroppedImageCount(0),
   mImageFactory(new ImageFactory()),
   mRecycleBin(new BufferRecycleBin()),
   mIsAsync(flag == ASYNCHRONOUS),
   mCurrentProducerID(-1)
 {
   if (flag == ASYNCHRONOUS) {
     EnsureImageClient();
   }
 }
 
 ImageContainer::ImageContainer(const CompositableHandle& aHandle)
-  : mReentrantMonitor("ImageContainer.mReentrantMonitor"),
+  : mRecursiveMutex("ImageContainer.mRecursiveMutex"),
   mGenerationCounter(++sGenerationCounter),
   mPaintCount(0),
   mDroppedImageCount(0),
   mImageFactory(nullptr),
   mRecycleBin(nullptr),
   mIsAsync(true),
   mAsyncContainerHandle(aHandle),
   mCurrentProducerID(-1)
@@ -201,39 +201,39 @@ ImageContainer::~ImageContainer()
       imageBridge->ForgetImageContainer(mAsyncContainerHandle);
     }
   }
 }
 
 RefPtr<PlanarYCbCrImage>
 ImageContainer::CreatePlanarYCbCrImage()
 {
-  ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+  RecursiveMutexAutoLock lock(mRecursiveMutex);
   EnsureImageClient();
   if (mImageClient && mImageClient->AsImageClientSingle()) {
     return new SharedPlanarYCbCrImage(mImageClient);
   }
   return mImageFactory->CreatePlanarYCbCrImage(mScaleHint, mRecycleBin);
 }
 
 RefPtr<SharedRGBImage>
 ImageContainer::CreateSharedRGBImage()
 {
-  ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+  RecursiveMutexAutoLock lock(mRecursiveMutex);
   EnsureImageClient();
   if (!mImageClient || !mImageClient->AsImageClientSingle()) {
     return nullptr;
   }
   return new SharedRGBImage(mImageClient);
 }
 
 void
 ImageContainer::SetCurrentImageInternal(const nsTArray<NonOwningImage>& aImages)
 {
-  ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+  RecursiveMutexAutoLock lock(mRecursiveMutex);
 
   mGenerationCounter = ++sGenerationCounter;
 
   if (!aImages.IsEmpty()) {
     NS_ASSERTION(mCurrentImages.IsEmpty() ||
                  mCurrentImages[0].mProducerID != aImages[0].mProducerID ||
                  mCurrentImages[0].mFrameID <= aImages[0].mFrameID,
                  "frame IDs shouldn't go backwards");
@@ -293,25 +293,25 @@ ImageContainer::SetCurrentImageInternal(
   }
 
   mCurrentImages.SwapElements(newImages);
 }
 
 void
 ImageContainer::ClearImagesFromImageBridge()
 {
-  ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+  RecursiveMutexAutoLock lock(mRecursiveMutex);
   SetCurrentImageInternal(nsTArray<NonOwningImage>());
 }
 
 void
 ImageContainer::SetCurrentImages(const nsTArray<NonOwningImage>& aImages)
 {
   MOZ_ASSERT(!aImages.IsEmpty());
-  ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+  RecursiveMutexAutoLock lock(mRecursiveMutex);
   if (mIsAsync) {
     if (RefPtr<ImageBridgeChild> imageBridge = ImageBridgeChild::GetSingleton()) {
       imageBridge->UpdateImageClient(this);
     }
   }
   SetCurrentImageInternal(aImages);
 }
 
@@ -322,24 +322,24 @@ ImageContainer::ClearAllImages()
     // Let ImageClient release all TextureClients. This doesn't return
     // until ImageBridge has called ClearCurrentImageFromImageBridge.
     if (RefPtr<ImageBridgeChild> imageBridge = ImageBridgeChild::GetSingleton()) {
       imageBridge->FlushAllImages(mImageClient, this);
     }
     return;
   }
 
-  ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+  RecursiveMutexAutoLock lock(mRecursiveMutex);
   SetCurrentImageInternal(nsTArray<NonOwningImage>());
 }
 
 void
 ImageContainer::ClearCachedResources()
 {
-  ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+  RecursiveMutexAutoLock lock(mRecursiveMutex);
   if (mImageClient && mImageClient->AsImageClientSingle()) {
     if (!mImageClient->HasTextureClientRecycler()) {
       return;
     }
     mImageClient->GetTextureClientRecycler()->ShrinkToMinimumSize();
     return;
   }
   return mRecycleBin->ClearRecycledBuffers();
@@ -373,49 +373,49 @@ CompositableHandle ImageContainer::GetAs
   NS_ASSERTION(mAsyncContainerHandle, "Should have a shared image ID");
   EnsureImageClient();
   return mAsyncContainerHandle;
 }
 
 bool
 ImageContainer::HasCurrentImage()
 {
-  ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+  RecursiveMutexAutoLock lock(mRecursiveMutex);
 
   return !mCurrentImages.IsEmpty();
 }
 
 void
 ImageContainer::GetCurrentImages(nsTArray<OwningImage>* aImages,
                                  uint32_t* aGenerationCounter)
 {
-  ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+  RecursiveMutexAutoLock lock(mRecursiveMutex);
 
   *aImages = mCurrentImages;
   if (aGenerationCounter) {
     *aGenerationCounter = mGenerationCounter;
   }
 }
 
 gfx::IntSize
 ImageContainer::GetCurrentSize()
 {
-  ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+  RecursiveMutexAutoLock lock(mRecursiveMutex);
 
   if (mCurrentImages.IsEmpty()) {
     return gfx::IntSize(0, 0);
   }
 
   return mCurrentImages[0].mImage->GetSize();
 }
 
 void
 ImageContainer::NotifyComposite(const ImageCompositeNotification& aNotification)
 {
-  ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+  RecursiveMutexAutoLock lock(mRecursiveMutex);
 
   // An image composition notification is sent the first time a particular
   // image is composited by an ImageHost. Thus, every time we receive such
   // a notification, a new image has been painted.
   ++mPaintCount;
 
   if (aNotification.producerID() == mCurrentProducerID) {
     uint32_t i;
--- a/gfx/layers/ImageContainer.h
+++ b/gfx/layers/ImageContainer.h
@@ -7,17 +7,17 @@
 #define GFX_IMAGECONTAINER_H
 
 #include <stdint.h>                     // for uint32_t, uint8_t, uint64_t
 #include <sys/types.h>                  // for int32_t
 #include "gfxTypes.h"
 #include "ImageTypes.h"                 // for ImageFormat, etc
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT_HELPER2
 #include "mozilla/Mutex.h"              // for Mutex
-#include "mozilla/ReentrantMonitor.h"   // for ReentrantMonitorAutoEnter, etc
+#include "mozilla/RecursiveMutex.h"     // for RecursiveMutex, etc
 #include "mozilla/TimeStamp.h"          // for TimeStamp
 #include "mozilla/gfx/Point.h"          // For IntSize
 #include "mozilla/layers/LayersTypes.h"  // for LayersBackend, etc
 #include "mozilla/layers/CompositorTypes.h"
 #include "mozilla/mozalloc.h"           // for operator delete, etc
 #include "nsAutoPtr.h"                  // for nsRefPtr, nsAutoArrayPtr, etc
 #include "nsAutoRef.h"                  // for nsCountedRef
 #include "nsCOMPtr.h"                   // for already_AddRefed
@@ -415,34 +415,34 @@ public:
     Image* mImage;
     TimeStamp mTimeStamp;
     FrameID mFrameID;
     ProducerID mProducerID;
   };
   /**
    * Set aImages as the list of timestamped to display. The Images must have
    * been created by this ImageContainer.
-   * Can be called on any thread. This method takes mReentrantMonitor
+   * Can be called on any thread. This method takes mRecursiveMutex
    * when accessing thread-shared state.
    * aImages must be non-empty. The first timestamp in the list may be
    * null but the others must not be, and the timestamps must increase.
    * Every element of aImages must have non-null mImage.
    * mFrameID can be zero, in which case you won't get meaningful
    * painted/dropped frame counts. Otherwise you should use a unique and
    * increasing ID for each decoded and submitted frame (but it's OK to
    * pass the same frame to SetCurrentImages).
    * mProducerID is a unique ID for the stream of images. A change in the
    * mProducerID means changing to a new mFrameID namespace. All frames in
    * aImages must have the same mProducerID.
    *
    * The Image data must not be modified after this method is called!
    * Note that this must not be called if ENABLE_ASYNC has not been set.
    *
    * The implementation calls CurrentImageChanged() while holding
-   * mReentrantMonitor.
+   * mRecursiveMutex.
    *
    * If this ImageContainer has an ImageClient for async video:
    * Schedule a task to send the image to the compositor using the
    * PImageBridge protcol without using the main thread.
    */
   void SetCurrentImages(const nsTArray<NonOwningImage>& aImages);
 
   /**
@@ -464,17 +464,17 @@ public:
    */
   void ClearImagesFromImageBridge();
 
   /**
    * Set an Image as the current image to display. The Image must have
    * been created by this ImageContainer.
    * Must be called on the main thread, within a layers transaction.
    *
-   * This method takes mReentrantMonitor
+   * This method takes mRecursiveMutex
    * when accessing thread-shared state.
    * aImage can be null. While it's null, nothing will be painted.
    *
    * The Image data must not be modified after this method is called!
    * Note that this must not be called if ENABLE_ASYNC been set.
    *
    * You won't get meaningful painted/dropped counts when using this method.
    */
@@ -495,17 +495,17 @@ public:
    * 0 is always an invalid ID for asynchronous image containers.
    *
    * Can be called from any thread.
    */
   CompositableHandle GetAsyncContainerHandle();
 
   /**
    * Returns if the container currently has an image.
-   * Can be called on any thread. This method takes mReentrantMonitor
+   * Can be called on any thread. This method takes mRecursiveMutex
    * when accessing thread-shared state.
    */
   bool HasCurrentImage();
 
   struct OwningImage {
     OwningImage() : mFrameID(0), mProducerID(0), mComposited(false) {}
     RefPtr<Image> mImage;
     TimeStamp mTimeStamp;
@@ -523,34 +523,34 @@ public:
    * If aGenerationCounter is non-null, sets *aGenerationCounter to a value
    * that's unique for this ImageContainer state.
    */
   void GetCurrentImages(nsTArray<OwningImage>* aImages,
                         uint32_t* aGenerationCounter = nullptr);
 
   /**
    * Returns the size of the image in pixels.
-   * Can be called on any thread. This method takes mReentrantMonitor when accessing
+   * Can be called on any thread. This method takes mRecursiveMutex when accessing
    * thread-shared state.
    */
   gfx::IntSize GetCurrentSize();
 
   /**
    * Sets a size that the image is expected to be rendered at.
    * This is a hint for image backends to optimize scaling.
    * Default implementation in this class is to ignore the hint.
-   * Can be called on any thread. This method takes mReentrantMonitor
+   * Can be called on any thread. This method takes mRecursiveMutex
    * when accessing thread-shared state.
    */
   void SetScaleHint(const gfx::IntSize& aScaleHint)
   { mScaleHint = aScaleHint; }
 
   void SetImageFactory(ImageFactory *aFactory)
   {
-    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+    RecursiveMutexAutoLock lock(mRecursiveMutex);
     mImageFactory = aFactory ? aFactory : new ImageFactory();
   }
 
   ImageFactory* GetImageFactory() const
   {
     return mImageFactory;
   }
 
@@ -564,40 +564,40 @@ public:
    * timestamp and when it was first composited. It's possible for the delay
    * to be negative if the first image in the list passed to SetCurrentImages
    * has a presentation timestamp greater than "now".
    * Returns 0 if the composited image had a null timestamp, or if no
    * image has been composited yet.
    */
   TimeDuration GetPaintDelay()
   {
-    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+    RecursiveMutexAutoLock lock(mRecursiveMutex);
     return mPaintDelay;
   }
 
   /**
    * Returns the number of images which have been contained in this container
    * and painted at least once.  Can be called from any thread.
    */
   uint32_t GetPaintCount() {
-    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+    RecursiveMutexAutoLock lock(mRecursiveMutex);
     return mPaintCount;
   }
 
   /**
    * An entry in the current image list "expires" when the entry has an
    * non-null timestamp, and in a SetCurrentImages call the new image list is
    * non-empty, the timestamp of the first new image is non-null and greater
    * than the timestamp associated with the image, and the first new image's
    * frameID is not the same as the entry's.
    * Every expired image that is never composited is counted as dropped.
    */
   uint32_t GetDroppedImageCount()
   {
-    ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+    RecursiveMutexAutoLock lock(mRecursiveMutex);
     return mDroppedImageCount;
   }
 
   void NotifyComposite(const ImageCompositeNotification& aNotification);
 
   ImageContainerListener* GetImageContainerListener()
   {
     return mNotifyCompositeListener;
@@ -611,53 +611,53 @@ public:
   already_AddRefed<ImageClient> GetImageClient();
 
   /**
    * Main thread only.
    */
   static ProducerID AllocateProducerID();
 
 private:
-  typedef mozilla::ReentrantMonitor ReentrantMonitor;
+  typedef mozilla::RecursiveMutex RecursiveMutex;
 
   // Private destructor, to discourage deletion outside of Release():
   ~ImageContainer();
 
   void SetCurrentImageInternal(const nsTArray<NonOwningImage>& aImages);
 
   // This is called to ensure we have an active image, this may not be true
   // when we're storing image information in a RemoteImageData structure.
   // NOTE: If we have remote data mRemoteDataMutex should be locked when
   // calling this function!
   void EnsureActiveImage();
 
   void EnsureImageClient();
 
-  // ReentrantMonitor to protect thread safe access to the "current
+  // RecursiveMutex to protect thread safe access to the "current
   // image", and any other state which is shared between threads.
-  ReentrantMonitor mReentrantMonitor;
+  RecursiveMutex mRecursiveMutex;
 
 #ifdef XP_WIN
   RefPtr<D3D11YCbCrRecycleAllocator> mD3D11YCbCrRecycleAllocator;
 #endif
 
   nsTArray<OwningImage> mCurrentImages;
 
   // Updates every time mActiveImage changes
   uint32_t mGenerationCounter;
 
   // Number of contained images that have been painted at least once.  It's up
   // to the ImageContainer implementation to ensure accesses to this are
   // threadsafe.
   uint32_t mPaintCount;
 
-  // See GetPaintDelay. Accessed only with mReentrantMonitor held.
+  // See GetPaintDelay. Accessed only with mRecursiveMutex held.
   TimeDuration mPaintDelay;
 
-  // See GetDroppedImageCount. Accessed only with mReentrantMonitor held.
+  // See GetDroppedImageCount. Accessed only with mRecursiveMutex held.
   uint32_t mDroppedImageCount;
 
   // This is the image factory used by this container, layer managers using
   // this container can set an alternative image factory that will be used to
   // create images for this container.
   RefPtr<ImageFactory> mImageFactory;
 
   gfx::IntSize mScaleHint;
--- a/gfx/layers/apz/util/APZEventState.cpp
+++ b/gfx/layers/apz/util/APZEventState.cpp
@@ -16,16 +16,17 @@
 #include "mozilla/Move.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/TouchEvents.h"
 #include "mozilla/layers/APZCCallbackHelper.h"
 #include "nsCOMPtr.h"
 #include "nsDocShell.h"
 #include "nsIDOMMouseEvent.h"
 #include "nsIDOMWindowUtils.h"
+#include "nsINamed.h"
 #include "nsIScrollableFrame.h"
 #include "nsIScrollbarMediator.h"
 #include "nsITimer.h"
 #include "nsIWeakReferenceUtils.h"
 #include "nsIWidget.h"
 #include "nsLayoutUtils.h"
 #include "nsQueryFrame.h"
 #include "TouchManager.h"
@@ -119,16 +120,17 @@ APZEventState::APZEventState(nsIWidget* 
     sActiveDurationMsSet = true;
   }
 }
 
 APZEventState::~APZEventState()
 {}
 
 class DelayedFireSingleTapEvent final : public nsITimerCallback
+                                      , public nsINamed
 {
 public:
   NS_DECL_ISUPPORTS
 
   DelayedFireSingleTapEvent(nsWeakPtr aWidget,
                             LayoutDevicePoint& aPoint,
                             Modifiers aModifiers,
                             int32_t aClickCount,
@@ -149,16 +151,23 @@ public:
     if (nsCOMPtr<nsIWidget> widget = do_QueryReferent(mWidget)) {
       widget::nsAutoRollup rollup(mTouchRollup.get());
       APZCCallbackHelper::FireSingleTapEvent(mPoint, mModifiers, mClickCount, widget);
     }
     mTimer = nullptr;
     return NS_OK;
   }
 
+  NS_IMETHOD
+  GetName(nsACString& aName) override
+  {
+    aName.AssignLiteral("DelayedFireSingleTapEvent");
+    return NS_OK;
+  }
+
   void ClearTimer() {
     mTimer = nullptr;
   }
 
 private:
   ~DelayedFireSingleTapEvent()
   {
   }
@@ -166,17 +175,17 @@ private:
   nsWeakPtr mWidget;
   LayoutDevicePoint mPoint;
   Modifiers mModifiers;
   int32_t mClickCount;
   nsCOMPtr<nsITimer> mTimer;
   RefPtr<nsIContent> mTouchRollup;
 };
 
-NS_IMPL_ISUPPORTS(DelayedFireSingleTapEvent, nsITimerCallback)
+NS_IMPL_ISUPPORTS(DelayedFireSingleTapEvent, nsITimerCallback, nsINamed)
 
 void
 APZEventState::ProcessSingleTap(const CSSPoint& aPoint,
                                 const CSSToLayoutDeviceScale& aScale,
                                 Modifiers aModifiers,
                                 const ScrollableLayerGuid& aGuid,
                                 int32_t aClickCount)
 {
--- a/gfx/layers/apz/util/APZThreadUtils.h
+++ b/gfx/layers/apz/util/APZThreadUtils.h
@@ -92,22 +92,16 @@ public:
   }
 
   NS_IMETHOD GetName(nsACString& aName) override
   {
     aName = mName;
     return NS_OK;
   }
 
-  NS_IMETHOD SetName(const char * aName) override
-  {
-    mName.Assign(aName);
-    return NS_OK;
-  }
-
 private:
   Function mFunction;
   nsCString mName;
 };
 
 // Convenience function for constructing a GenericNamedTimerCallback.
 // Returns a raw pointer, suitable for passing directly as an argument to
 // nsITimer::InitWithCallback(). The intention is to enable the following
--- a/gfx/layers/client/ClientPaintedLayer.cpp
+++ b/gfx/layers/client/ClientPaintedLayer.cpp
@@ -180,17 +180,17 @@ ClientPaintedLayer::PaintThebes(nsTArray
   if (didUpdate) {
     UpdateContentClient(state);
   }
 }
 
 bool
 ClientPaintedLayer::PaintOffMainThread()
 {
-  mContentClient->BeginPaint();
+  mContentClient->BeginAsyncPaint();
 
   uint32_t flags = GetPaintFlags();
 
   PaintState state = mContentClient->BeginPaintBuffer(this, flags);
   if (!UpdatePaintRegion(state)) {
     return false;
   }
 
--- a/gfx/layers/client/ContentClient.cpp
+++ b/gfx/layers/client/ContentClient.cpp
@@ -87,18 +87,25 @@ ContentClient::CreateContentClient(Compo
 
   if (useDoubleBuffering || gfxEnv::ForceDoubleBuffering()) {
     return MakeAndAddRef<ContentClientDoubleBuffered>(aForwarder);
   }
   return MakeAndAddRef<ContentClientSingleBuffered>(aForwarder);
 }
 
 void
+ContentClient::BeginAsyncPaint()
+{
+  mInAsyncPaint = true;
+}
+
+void
 ContentClient::EndPaint(nsTArray<ReadbackProcessor::Update>* aReadbackUpdates)
 {
+  mInAsyncPaint = false;
 }
 
 void
 ContentClient::PrintInfo(std::stringstream& aStream, const char* aPrefix)
 {
   aStream << aPrefix;
   aStream << nsPrintfCString("ContentClient (0x%p)", this).get();
 
@@ -234,16 +241,23 @@ ContentClientRemoteBuffer::BeginPaint()
     SetBufferProvider(mTextureClient);
   }
   if (mTextureClientOnWhite) {
     SetBufferProviderOnWhite(mTextureClientOnWhite);
   }
 }
 
 void
+ContentClientRemoteBuffer::BeginAsyncPaint()
+{
+  BeginPaint();
+  mInAsyncPaint = true;
+}
+
+void
 ContentClientRemoteBuffer::EndPaint(nsTArray<ReadbackProcessor::Update>* aReadbackUpdates)
 {
   MOZ_ASSERT(!mTextureClientOnWhite || !aReadbackUpdates || aReadbackUpdates->Length() == 0);
 
   // XXX: We might still not have a texture client if PaintThebes
   // decided we didn't need one yet because the region to draw was empty.
   SetBufferProvider(nullptr);
   SetBufferProviderOnWhite(nullptr);
@@ -344,24 +358,29 @@ ContentClientRemoteBuffer::CreateBuffer(
                                         RefPtr<gfx::DrawTarget>* aBlackDT,
                                         RefPtr<gfx::DrawTarget>* aWhiteDT)
 {
   BuildTextureClients(gfxPlatform::GetPlatform()->Optimal2DFormatForContent(aType), aRect, aFlags);
   if (!mTextureClient) {
     return;
   }
 
+  OpenMode mode = OpenMode::OPEN_READ_WRITE;
+  if (mInAsyncPaint) {
+    mode |= OpenMode::OPEN_ASYNC_WRITE;
+  }
+
   // We just created the textures and we are about to get their draw targets
   // so we have to lock them here.
-  DebugOnly<bool> locked = mTextureClient->Lock(OpenMode::OPEN_READ_WRITE);
+  DebugOnly<bool> locked = mTextureClient->Lock(mode);
   MOZ_ASSERT(locked, "Could not lock the TextureClient");
 
   *aBlackDT = mTextureClient->BorrowDrawTarget();
   if (aFlags & BUFFER_COMPONENT_ALPHA) {
-    locked = mTextureClientOnWhite->Lock(OpenMode::OPEN_READ_WRITE);
+    locked = mTextureClientOnWhite->Lock(mode);
     MOZ_ASSERT(locked, "Could not lock the second TextureClient for component alpha");
 
     *aWhiteDT = mTextureClientOnWhite->BorrowDrawTarget();
   }
 }
 
 nsIntRegion
 ContentClientRemoteBuffer::GetUpdatedRegion(const nsIntRegion& aRegionToDraw,
@@ -425,24 +444,28 @@ void
 ContentClientRemoteBuffer::SwapBuffers(const nsIntRegion& aFrontUpdatedRegion)
 {
   mFrontAndBackBufferDiffer = true;
 }
 
 bool
 ContentClientRemoteBuffer::LockBuffers()
 {
+  OpenMode mode = OpenMode::OPEN_READ_WRITE;
+  if (mInAsyncPaint) {
+    mode |= OpenMode::OPEN_ASYNC_WRITE;
+  }
   if (mTextureClient) {
-    bool locked = mTextureClient->Lock(OpenMode::OPEN_READ_WRITE);
+    bool locked = mTextureClient->Lock(mode);
     if (!locked) {
       return false;
     }
   }
   if (mTextureClientOnWhite) {
-    bool locked = mTextureClientOnWhite->Lock(OpenMode::OPEN_READ_WRITE);
+    bool locked = mTextureClientOnWhite->Lock(mode);
     if (!locked) {
       UnlockBuffers();
       return false;
     }
   }
   return true;
 }
 
@@ -548,16 +571,23 @@ ContentClientDoubleBuffered::BeginPaint(
     mBufferRect.MoveTo(mFrontBufferRect.TopLeft());
     mBufferRotation = nsIntPoint();
     return;
   }
   mBufferRect = mFrontBufferRect;
   mBufferRotation = mFrontBufferRotation;
 }
 
+void
+ContentClientDoubleBuffered::BeginAsyncPaint()
+{
+  BeginPaint();
+  mInAsyncPaint = true;
+}
+
 // Sync front/back buffers content
 // After executing, the new back buffer has the same (interesting) pixels as
 // the new front buffer, and mValidRegion et al. are correct wrt the new
 // back buffer (i.e. as they were for the old back buffer)
 void
 ContentClientDoubleBuffered::FinalizeFrame(const nsIntRegion& aRegionToDraw)
 {
   if (!mFrontAndBackBufferDiffer) {
--- a/gfx/layers/client/ContentClient.h
+++ b/gfx/layers/client/ContentClient.h
@@ -77,17 +77,18 @@ public:
   /**
    * Creates, configures, and returns a new content client. If necessary, a
    * message will be sent to the compositor to create a corresponding content
    * host.
    */
   static already_AddRefed<ContentClient> CreateContentClient(CompositableForwarder* aFwd);
 
   explicit ContentClient(CompositableForwarder* aForwarder)
-  : CompositableClient(aForwarder)
+  : CompositableClient(aForwarder),
+    mInAsyncPaint(false)
   {}
   virtual ~ContentClient()
   {}
 
   virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix);
 
   virtual void Clear() = 0;
   virtual RotatedContentBuffer::PaintState BeginPaintBuffer(PaintedLayer* aLayer,
@@ -98,17 +99,21 @@ public:
 
   // Called as part of the layers transation reply. Conveys data about our
   // buffer(s) from the compositor. If appropriate we should swap references
   // to our buffers.
   virtual void SwapBuffers(const nsIntRegion& aFrontUpdatedRegion) {}
 
   // call before and after painting into this content client
   virtual void BeginPaint() {}
+  virtual void BeginAsyncPaint();
   virtual void EndPaint(nsTArray<ReadbackProcessor::Update>* aReadbackUpdates = nullptr);
+
+protected:
+  bool mInAsyncPaint;
 };
 
 /**
  * A ContentClient for use with OMTC.
  */
 class ContentClientRemote : public ContentClient
 {
 public:
@@ -238,16 +243,17 @@ public:
    * Begin/End Paint map a gfxASurface from the texture client
    * into the buffer of RotatedBuffer. The surface is only
    * valid when the texture client is locked, so is mapped out
    * of RotatedContentBuffer when we are done painting.
    * None of the underlying buffer attributes (rect, rotation)
    * are affected by mapping/unmapping.
    */
   virtual void BeginPaint() override;
+  virtual void BeginAsyncPaint() override;
   virtual void EndPaint(nsTArray<ReadbackProcessor::Update>* aReadbackUpdates = nullptr) override;
 
   virtual void Updated(const nsIntRegion& aRegionToDraw,
                        const nsIntRegion& aVisibleRegion,
                        bool aDidSelfCopy) override;
 
   virtual void SwapBuffers(const nsIntRegion& aFrontUpdatedRegion) override;
 
@@ -342,16 +348,17 @@ public:
 
   virtual void Updated(const nsIntRegion& aRegionToDraw,
                        const nsIntRegion& aVisibleRegion,
                        bool aDidSelfCopy) override;
 
   virtual void SwapBuffers(const nsIntRegion& aFrontUpdatedRegion) override;
 
   virtual void BeginPaint() override;
+  virtual void BeginAsyncPaint() override;
 
   virtual void FinalizeFrame(const nsIntRegion& aRegionToDraw) override;
 
   virtual void EnsureBackBufferIfFrontBuffer() override;
 
   virtual TextureInfo GetTextureInfo() const override
   {
     return TextureInfo(CompositableType::CONTENT_DOUBLE, mTextureFlags);
--- a/gfx/layers/client/TextureClient.cpp
+++ b/gfx/layers/client/TextureClient.cpp
@@ -230,17 +230,17 @@ static void DestroyTextureData(TextureDa
                                bool aDeallocate, bool aMainThreadOnly)