Merge inbound to mozilla-central. a=merge
authorTiberius Oros <toros@mozilla.com>
Wed, 18 Jul 2018 00:55:33 +0300
changeset 468725 afa310dc89beeb4b7a9564d2c89ff32906f427ad
parent 468643 e9cd9d73e5a8f6fcb9223798453cdaa9f206399a (current diff)
parent 468724 5196c06ae5add254243c580b4489db7453809468 (diff)
child 468746 f6748ccdbfe961c0f0171b7c18ec6f167eed6a67
child 468790 7e6a5a6fbc6d0bd88178c837cec6a863537bda2e
push idunknown
push userunknown
push dateunknown
reviewersmerge
milestone63.0a1
first release with
nightly linux32
afa310dc89be / 63.0a1 / 20180717220130 / files
nightly linux64
afa310dc89be / 63.0a1 / 20180717220130 / files
nightly mac
afa310dc89be / 63.0a1 / 20180717220130 / files
nightly win32
afa310dc89be / 63.0a1 / 20180717220130 / files
nightly win64
afa310dc89be / 63.0a1 / 20180717220130 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to mozilla-central. a=merge
dom/base/nsDocument.cpp
modules/libpref/init/StaticPrefList.h
modules/libpref/init/all.js
servo/tests/unit/style/keyframes.rs
testing/web-platform/meta/content-security-policy/script-src/javascript-window-open-blocked.html.ini
testing/web-platform/meta/content-security-policy/script-src/script-src-report-only-policy-works-with-hash-policy.html.ini
testing/web-platform/meta/content-security-policy/securitypolicyviolation/blockeduri-eval.html.ini
testing/web-platform/meta/content-security-policy/securitypolicyviolation/blockeduri-inline.html.ini
testing/web-platform/meta/content-security-policy/securitypolicyviolation/style-sample-no-opt-in.html.ini
testing/web-platform/meta/content-security-policy/securitypolicyviolation/style-sample.html.ini
testing/web-platform/meta/content-security-policy/unsafe-hashes/javascript_src_denied_missing_unsafe_hashes-href.html.ini
testing/web-platform/meta/content-security-policy/unsafe-hashes/javascript_src_denied_missing_unsafe_hashes-href_blank.html.ini
testing/web-platform/meta/content-security-policy/unsafe-hashes/javascript_src_denied_missing_unsafe_hashes-window_open.html.ini
testing/web-platform/meta/content-security-policy/unsafe-hashes/javascript_src_denied_wrong_hash-href.html.ini
testing/web-platform/meta/content-security-policy/unsafe-hashes/javascript_src_denied_wrong_hash-href_blank.html.ini
testing/web-platform/meta/content-security-policy/unsafe-hashes/javascript_src_denied_wrong_hash-window_open.html.ini
testing/web-platform/meta/content-security-policy/unsafe-hashes/script_event_handlers_denied_wrong_hash.html.ini
testing/web-platform/meta/content-security-policy/unsafe-hashes/style_attribute_denied_wrong_hash.html.ini
--- a/browser/base/content/content.js
+++ b/browser/base/content/content.js
@@ -12,35 +12,39 @@
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 
 // TabChildGlobal
 var global = this;
 
 XPCOMUtils.defineLazyModuleGetters(this, {
   BlockedSiteContent: "resource:///modules/BlockedSiteContent.jsm",
-  BrowserUtils: "resource://gre/modules/BrowserUtils.jsm",
   ContentLinkHandler: "resource:///modules/ContentLinkHandler.jsm",
   ContentMetaHandler: "resource:///modules/ContentMetaHandler.jsm",
   ContentWebRTC: "resource:///modules/ContentWebRTC.jsm",
   LoginFormFactory: "resource://gre/modules/LoginManagerContent.jsm",
   InsecurePasswordUtils: "resource://gre/modules/InsecurePasswordUtils.jsm",
   PluginContent: "resource:///modules/PluginContent.jsm",
-  PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
   FormSubmitObserver: "resource:///modules/FormSubmitObserver.jsm",
   NetErrorContent: "resource:///modules/NetErrorContent.jsm",
   PageMetadata: "resource://gre/modules/PageMetadata.jsm",
   WebNavigationFrames: "resource://gre/modules/WebNavigationFrames.jsm",
   ContextMenu: "resource:///modules/ContextMenu.jsm",
 });
 
 XPCOMUtils.defineLazyProxy(this, "contextMenu", () => {
   return new ContextMenu(global);
 });
 
+XPCOMUtils.defineLazyProxy(this, "ClickEventHandler", () => {
+  let tmp = {};
+  ChromeUtils.import("resource:///modules/ClickEventHandler.jsm", tmp);
+  return new tmp.ClickEventHandler(global);
+});
+
 XPCOMUtils.defineLazyGetter(this, "LoginManagerContent", () => {
   let tmp = {};
   ChromeUtils.import("resource://gre/modules/LoginManagerContent.jsm", tmp);
   tmp.LoginManagerContent.setupEventListeners(global);
   return tmp.LoginManagerContent;
 });
 
 XPCOMUtils.defineLazyProxy(this, "formSubmitObserver", () => {
@@ -81,16 +85,17 @@ addEventListener("DOMInputPasswordAdded"
 addEventListener("DOMAutoComplete", function(event) {
   LoginManagerContent.onUsernameInput(event);
 });
 
 var AboutBlockedSiteListener = {
   init(chromeGlobal) {
     addMessageListener("DeceptiveBlockedDetails", this);
     chromeGlobal.addEventListener("AboutBlockedLoaded", this, false, true);
+    this.init = null;
   },
 
   get isBlockedSite() {
     return content.document.documentURI.startsWith("about:blocked");
   },
 
   receiveMessage(msg) {
     if (!this.isBlockedSite) {
@@ -109,24 +114,25 @@ var AboutBlockedSiteListener = {
       return;
     }
 
     BlockedSiteContent.handleEvent(global, aEvent);
   },
 };
 AboutBlockedSiteListener.init(this);
 
-var AboutNetAndCertErrorListener = {
+this.AboutNetAndCertErrorListener = {
   init(chromeGlobal) {
     addMessageListener("CertErrorDetails", this);
     addMessageListener("Browser:CaptivePortalFreed", this);
     chromeGlobal.addEventListener("AboutNetErrorLoad", this, false, true);
     chromeGlobal.addEventListener("AboutNetErrorOpenCaptivePortal", this, false, true);
     chromeGlobal.addEventListener("AboutNetErrorSetAutomatic", this, false, true);
     chromeGlobal.addEventListener("AboutNetErrorResetPreferences", this, false, true);
+    this.init = null;
   },
 
   isAboutNetError(doc) {
     return doc.documentURI.startsWith("about:neterror");
   },
 
   isAboutCertError(doc) {
     return doc.documentURI.startsWith("about:certerror");
@@ -164,161 +170,17 @@ var AboutNetAndCertErrorListener = {
       return;
     }
 
     NetErrorContent.handleEvent(global, aEvent);
   },
 };
 AboutNetAndCertErrorListener.init(this);
 
-var ClickEventHandler = {
-  init: function init() {
-    Services.els.addSystemEventListener(global, "click", this, true);
-  },
-
-  handleEvent(event) {
-    if (!event.isTrusted || event.defaultPrevented || event.button == 2) {
-      return;
-    }
-
-    let originalTarget = event.originalTarget;
-    let ownerDoc = originalTarget.ownerDocument;
-    if (!ownerDoc) {
-      return;
-    }
-
-    // Handle click events from about pages
-    if (event.button == 0) {
-      if (AboutNetAndCertErrorListener.isAboutCertError(ownerDoc)) {
-        NetErrorContent.onCertError(global, originalTarget, ownerDoc.defaultView);
-        return;
-      } else if (ownerDoc.documentURI.startsWith("about:blocked")) {
-        BlockedSiteContent.onAboutBlocked(global, originalTarget, ownerDoc);
-        return;
-      } else if (AboutNetAndCertErrorListener.isAboutNetError(ownerDoc)) {
-        NetErrorContent.onAboutNetError(global, event, ownerDoc.documentURI);
-        return;
-      }
-    }
-
-    let [href, node, principal] = this._hrefAndLinkNodeForClickEvent(event);
-
-    // get referrer attribute from clicked link and parse it
-    // if per element referrer is enabled, the element referrer overrules
-    // the document wide referrer
-    let referrerPolicy = ownerDoc.referrerPolicy;
-    if (node) {
-      let referrerAttrValue = Services.netUtils.parseAttributePolicyString(node.
-                              getAttribute("referrerpolicy"));
-      if (referrerAttrValue !== Ci.nsIHttpChannel.REFERRER_POLICY_UNSET) {
-        referrerPolicy = referrerAttrValue;
-      }
-    }
-
-    let frameOuterWindowID = WebNavigationFrames.getFrameId(ownerDoc.defaultView);
-
-    let json = { button: event.button, shiftKey: event.shiftKey,
-                 ctrlKey: event.ctrlKey, metaKey: event.metaKey,
-                 altKey: event.altKey, href: null, title: null,
-                 frameOuterWindowID, referrerPolicy,
-                 triggeringPrincipal: principal,
-                 originAttributes: principal ? principal.originAttributes : {},
-                 isContentWindowPrivate: PrivateBrowsingUtils.isContentWindowPrivate(ownerDoc.defaultView)};
-
-    if (href) {
-      try {
-        BrowserUtils.urlSecurityCheck(href, principal);
-      } catch (e) {
-        return;
-      }
-
-      json.href = href;
-      if (node) {
-        json.title = node.getAttribute("title");
-      }
-      json.noReferrer = BrowserUtils.linkHasNoReferrer(node);
-
-      // Check if the link needs to be opened with mixed content allowed.
-      // Only when the owner doc has |mixedContentChannel| and the same origin
-      // should we allow mixed content.
-      json.allowMixedContent = false;
-      let docshell = ownerDoc.docShell;
-      if (docShell.mixedContentChannel) {
-        const sm = Services.scriptSecurityManager;
-        try {
-          let targetURI = Services.io.newURI(href);
-          sm.checkSameOriginURI(docshell.mixedContentChannel.URI, targetURI, false);
-          json.allowMixedContent = true;
-        } catch (e) {}
-      }
-      json.originPrincipal = ownerDoc.nodePrincipal;
-      json.triggeringPrincipal = ownerDoc.nodePrincipal;
-
-      sendAsyncMessage("Content:Click", json);
-      return;
-    }
-
-    // This might be middle mouse navigation.
-    if (event.button == 1) {
-      sendAsyncMessage("Content:Click", json);
-    }
-  },
-
-  /**
-   * Extracts linkNode and href for the current click target.
-   *
-   * @param event
-   *        The click event.
-   * @return [href, linkNode, linkPrincipal].
-   *
-   * @note linkNode will be null if the click wasn't on an anchor
-   *       element. This includes SVG links, because callers expect |node|
-   *       to behave like an <a> element, which SVG links (XLink) don't.
-   */
-  _hrefAndLinkNodeForClickEvent(event) {
-    function isHTMLLink(aNode) {
-      // Be consistent with what nsContextMenu.js does.
-      return ((aNode instanceof content.HTMLAnchorElement && aNode.href) ||
-              (aNode instanceof content.HTMLAreaElement && aNode.href) ||
-              aNode instanceof content.HTMLLinkElement);
-    }
-
-    let node = event.target;
-    while (node && !isHTMLLink(node)) {
-      node = node.parentNode;
-    }
-
-    if (node)
-      return [node.href, node, node.ownerDocument.nodePrincipal];
-
-    // If there is no linkNode, try simple XLink.
-    let href, baseURI;
-    node = event.target;
-    while (node && !href) {
-      if (node.nodeType == content.Node.ELEMENT_NODE &&
-          (node.localName == "a" ||
-           node.namespaceURI == "http://www.w3.org/1998/Math/MathML")) {
-        href = node.getAttribute("href") ||
-               node.getAttributeNS("http://www.w3.org/1999/xlink", "href");
-        if (href) {
-          baseURI = node.ownerDocument.baseURIObject;
-          break;
-        }
-      }
-      node = node.parentNode;
-    }
-
-    // In case of XLink, we don't return the node we got href from since
-    // callers expect <a>-like elements.
-    // Note: makeURI() will throw if aUri is not a valid URI.
-    return [href ? Services.io.newURI(href, null, baseURI).spec : null, null,
-            node && node.ownerDocument.nodePrincipal];
-  }
-};
-ClickEventHandler.init();
+Services.els.addSystemEventListener(global, "click", ClickEventHandler, true);
 
 new ContentLinkHandler(this);
 ContentMetaHandler.init(this);
 
 var PluginContentStub = {
   EVENTS: [
     "PluginCrashed",
     "PluginOutdated",
@@ -351,16 +213,17 @@ var PluginContentStub = {
 
     for (let event of this.EVENTS) {
       addEventListener(event, this, true);
     }
     for (let msg of this.MESSAGES) {
       addMessageListener(msg, this);
     }
     Services.obs.addObserver(this, "decoder-doctor-notification");
+    this.init = null;
   },
 
   uninit() {
     Services.obs.removeObserver(this, "decoder-doctor-notification");
   },
 
   observe(subject, topic, data) {
     return this.pluginContent.observe(subject, topic, data);
@@ -396,16 +259,17 @@ addMessageListener("rtcpeer:Deny", Conte
 addMessageListener("webrtc:Allow", ContentWebRTCShim);
 addMessageListener("webrtc:Deny", ContentWebRTCShim);
 addMessageListener("webrtc:StopSharing", ContentWebRTCShim);
 
 var PageMetadataMessenger = {
   init() {
     addMessageListener("PageMetadata:GetPageData", this);
     addMessageListener("PageMetadata:GetMicroformats", this);
+    this.init = null;
   },
   receiveMessage(message) {
     switch (message.name) {
       case "PageMetadata:GetPageData": {
         let target = contextMenu.getTarget(message);
         let result = PageMetadata.getData(content.document, target);
         sendAsyncMessage("PageMetadata:PageDataResult", result);
         break;
--- a/browser/base/content/tab-content.js
+++ b/browser/base/content/tab-content.js
@@ -89,16 +89,17 @@ var LightweightThemeChildListenerStub = 
     }
     return this._childListener;
   },
 
   init() {
     addEventListener("LightweightTheme:Support", this, false, true);
     addMessageListener("LightweightTheme:Update", this);
     sendAsyncMessage("LightweightTheme:Request");
+    this.init = null;
   },
 
   handleEvent(event) {
     return this.childListener.handleEvent(event);
   },
 
   receiveMessage(msg) {
     return this.childListener.receiveMessage(msg);
@@ -116,16 +117,17 @@ var AboutReaderListener = {
 
   init() {
     addEventListener("AboutReaderContentLoaded", this, false, true);
     addEventListener("DOMContentLoaded", this, false);
     addEventListener("pageshow", this, false);
     addEventListener("pagehide", this, false);
     addMessageListener("Reader:ToggleReaderMode", this);
     addMessageListener("Reader:PushState", this);
+    this.init = null;
   },
 
   receiveMessage(message) {
     switch (message.name) {
       case "Reader:ToggleReaderMode":
         if (!this.isAboutReader) {
           this._articlePromise = ReaderMode.parseDocument(content.document).catch(Cu.reportError);
           ReaderMode.enterReaderMode(docShell, content);
@@ -259,16 +261,17 @@ var ContentSearchMediator = {
     "about:home",
     "about:newtab",
     "about:welcome",
   ]),
 
   init(chromeGlobal) {
     chromeGlobal.addEventListener("ContentSearchClient", this, true, true);
     addMessageListener("ContentSearch", this);
+    this.init = null;
   },
 
   handleEvent(event) {
     if (this._contentWhitelisted) {
       this._sendMsg(event.detail.type, event.detail.data);
     }
   },
 
@@ -394,16 +397,17 @@ var DOMFullscreenHandler = {
   init() {
     addMessageListener("DOMFullscreen:Entered", this);
     addMessageListener("DOMFullscreen:CleanUp", this);
     addEventListener("MozDOMFullscreen:Request", this);
     addEventListener("MozDOMFullscreen:Entered", this);
     addEventListener("MozDOMFullscreen:NewOrigin", this);
     addEventListener("MozDOMFullscreen:Exit", this);
     addEventListener("MozDOMFullscreen:Exited", this);
+    this.init = null;
   },
 
   get _windowUtils() {
     if (!content) {
       return null;
     }
     return content.QueryInterface(Ci.nsIInterfaceRequestor)
                   .getInterface(Ci.nsIDOMWindowUtils);
@@ -479,16 +483,17 @@ var DOMFullscreenHandler = {
     }
   }
 };
 DOMFullscreenHandler.init();
 
 var UserContextIdNotifier = {
   init() {
     addEventListener("DOMWindowCreated", this);
+    this.init = null;
   },
 
   uninit() {
     removeEventListener("DOMWindowCreated", this);
   },
 
   handleEvent(aEvent) {
     // When the window is created, we want to inform the tabbrowser about
--- a/browser/base/content/test/performance/browser_startup_content.js
+++ b/browser/base/content/test/performance/browser_startup_content.js
@@ -40,17 +40,16 @@ const whitelist = {
     "resource://gre/modules/Services.jsm", // bug 1464542
     "resource://gre/modules/Timer.jsm",
     "resource://gre/modules/XPCOMUtils.jsm",
 
     // Logging related
     "resource://gre/modules/Log.jsm",
 
     // Session store
-    "resource:///modules/sessionstore/ContentRestore.jsm",
     "resource://gre/modules/sessionstore/SessionHistory.jsm",
 
     // Forms and passwords
     "resource://formautofill/FormAutofill.jsm",
     "resource://formautofill/FormAutofillContent.jsm",
 
     // Browser front-end
     "resource:///modules/ContentLinkHandler.jsm",
--- a/browser/components/sessionstore/content/content-sessionStore.js
+++ b/browser/components/sessionstore/content/content-sessionStore.js
@@ -15,28 +15,34 @@ ChromeUtils.defineModuleGetter(this, "Te
 
 function debug(msg) {
   Services.console.logStringMessage("SessionStoreContent: " + msg);
 }
 
 ChromeUtils.defineModuleGetter(this, "FormData",
   "resource://gre/modules/FormData.jsm");
 
+ChromeUtils.defineModuleGetter(this, "ContentRestore",
+  "resource:///modules/sessionstore/ContentRestore.jsm");
 ChromeUtils.defineModuleGetter(this, "DocShellCapabilities",
   "resource:///modules/sessionstore/DocShellCapabilities.jsm");
 ChromeUtils.defineModuleGetter(this, "ScrollPosition",
   "resource://gre/modules/ScrollPosition.jsm");
 ChromeUtils.defineModuleGetter(this, "SessionHistory",
   "resource://gre/modules/sessionstore/SessionHistory.jsm");
 ChromeUtils.defineModuleGetter(this, "SessionStorage",
   "resource:///modules/sessionstore/SessionStorage.jsm");
 
-ChromeUtils.import("resource:///modules/sessionstore/ContentRestore.jsm", this);
+var contentRestoreInitialized = false;
+
 XPCOMUtils.defineLazyGetter(this, "gContentRestore",
-                            () => { return new ContentRestore(this); });
+                            () => {
+                              contentRestoreInitialized = true;
+                              return new ContentRestore(this);
+                            });
 
 ChromeUtils.defineModuleGetter(this, "Utils",
   "resource://gre/modules/sessionstore/Utils.jsm");
 const ssu = Cc["@mozilla.org/browser/sessionstore/utils;1"]
               .getService(Ci.nsISessionStoreUtils);
 
 // The current epoch.
 var gCurrentEpoch = 0;
@@ -154,19 +160,21 @@ var EventListener = {
         // point; listen for the custom event dispatched from AboutReader.jsm.
         content.addEventListener("AboutReaderContentReady", this);
         return;
       }
 
       content.removeEventListener("AboutReaderContentReady", this);
     }
 
-    // Restore the form data and scroll position. If we're not currently
-    // restoring a tab state then this call will simply be a noop.
-    gContentRestore.restoreDocument();
+    if (contentRestoreInitialized) {
+      // Restore the form data and scroll position. If we're not currently
+      // restoring a tab state then this call will simply be a noop.
+      gContentRestore.restoreDocument();
+    }
   }
 };
 
 /**
  * Listens for and handles messages sent by the session store service.
  */
 var MessageListener = {
 
@@ -957,15 +965,17 @@ addEventListener("unload", () => {
   // event to be fired.
   handleRevivedTab();
 
   // Remove all registered nsIObservers.
   SessionStorageListener.uninit();
   SessionHistoryListener.uninit();
   MessageQueue.uninit();
 
-  // Remove progress listeners.
-  gContentRestore.resetRestore();
+  if (contentRestoreInitialized) {
+    // Remove progress listeners.
+    gContentRestore.resetRestore();
+  }
 
   // We don't need to take care of any StateChangeNotifier observers as they
   // will die with the content script. The same goes for the privacy transition
   // observer that will die with the docShell when the tab is closed.
 });
copy from browser/base/content/content.js
copy to browser/modules/ClickEventHandler.jsm
--- a/browser/base/content/content.js
+++ b/browser/modules/ClickEventHandler.jsm
@@ -1,205 +1,57 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
 
-/* This content script should work in any browser or iframe and should not
- * depend on the frame being contained in tabbrowser. */
+var EXPORTED_SYMBOLS = ["ClickEventHandler"];
 
-/* eslint-env mozilla/frame-script */
 /* eslint no-unused-vars: ["error", {args: "none"}] */
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 
-// TabChildGlobal
-var global = this;
-
-XPCOMUtils.defineLazyModuleGetters(this, {
-  BlockedSiteContent: "resource:///modules/BlockedSiteContent.jsm",
-  BrowserUtils: "resource://gre/modules/BrowserUtils.jsm",
-  ContentLinkHandler: "resource:///modules/ContentLinkHandler.jsm",
-  ContentMetaHandler: "resource:///modules/ContentMetaHandler.jsm",
-  ContentWebRTC: "resource:///modules/ContentWebRTC.jsm",
-  LoginFormFactory: "resource://gre/modules/LoginManagerContent.jsm",
-  InsecurePasswordUtils: "resource://gre/modules/InsecurePasswordUtils.jsm",
-  PluginContent: "resource:///modules/PluginContent.jsm",
-  PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
-  FormSubmitObserver: "resource:///modules/FormSubmitObserver.jsm",
-  NetErrorContent: "resource:///modules/NetErrorContent.jsm",
-  PageMetadata: "resource://gre/modules/PageMetadata.jsm",
-  WebNavigationFrames: "resource://gre/modules/WebNavigationFrames.jsm",
-  ContextMenu: "resource:///modules/ContextMenu.jsm",
-});
-
-XPCOMUtils.defineLazyProxy(this, "contextMenu", () => {
-  return new ContextMenu(global);
-});
-
-XPCOMUtils.defineLazyGetter(this, "LoginManagerContent", () => {
-  let tmp = {};
-  ChromeUtils.import("resource://gre/modules/LoginManagerContent.jsm", tmp);
-  tmp.LoginManagerContent.setupEventListeners(global);
-  return tmp.LoginManagerContent;
-});
-
-XPCOMUtils.defineLazyProxy(this, "formSubmitObserver", () => {
-  return new FormSubmitObserver(content, this);
-}, {
-  // stub QI
-  QueryInterface: ChromeUtils.generateQI([Ci.nsIFormSubmitObserver, Ci.nsISupportsWeakReference])
-});
-
-XPCOMUtils.defineLazyProxy(this, "PageInfoListener",
-                           "resource:///modules/PageInfoListener.jsm");
-
-XPCOMUtils.defineLazyProxy(this, "LightWeightThemeWebInstallListener",
-                           "resource:///modules/LightWeightThemeWebInstallListener.jsm");
-
-Services.els.addSystemEventListener(global, "contextmenu", contextMenu, false);
-
-Services.obs.addObserver(formSubmitObserver, "invalidformsubmit", true);
-
-addMessageListener("PageInfo:getData", PageInfoListener);
-
-// NOTE: Much of this logic is duplicated in BrowserCLH.js for Android.
-addMessageListener("RemoteLogins:fillForm", function(message) {
-  // intercept if ContextMenu.jsm had sent a plain object for remote targets
-  message.objects.inputElement = contextMenu.getTarget(message, "inputElement");
-  LoginManagerContent.receiveMessage(message, content);
-});
-addEventListener("DOMFormHasPassword", function(event) {
-  LoginManagerContent.onDOMFormHasPassword(event, content);
-  let formLike = LoginFormFactory.createFromForm(event.originalTarget);
-  InsecurePasswordUtils.reportInsecurePasswords(formLike);
-});
-addEventListener("DOMInputPasswordAdded", function(event) {
-  LoginManagerContent.onDOMInputPasswordAdded(event, content);
-  let formLike = LoginFormFactory.createFromField(event.originalTarget);
-  InsecurePasswordUtils.reportInsecurePasswords(formLike);
-});
-addEventListener("DOMAutoComplete", function(event) {
-  LoginManagerContent.onUsernameInput(event);
-});
-
-var AboutBlockedSiteListener = {
-  init(chromeGlobal) {
-    addMessageListener("DeceptiveBlockedDetails", this);
-    chromeGlobal.addEventListener("AboutBlockedLoaded", this, false, true);
-  },
-
-  get isBlockedSite() {
-    return content.document.documentURI.startsWith("about:blocked");
-  },
+ChromeUtils.defineModuleGetter(this, "BlockedSiteContent",
+                               "resource:///modules/BlockedSiteContent.jsm");
+ChromeUtils.defineModuleGetter(this, "BrowserUtils",
+                               "resource://gre/modules/BrowserUtils.jsm");
+ChromeUtils.defineModuleGetter(this, "NetErrorContent",
+                               "resource:///modules/NetErrorContent.jsm");
+ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
+                               "resource://gre/modules/PrivateBrowsingUtils.jsm");
+ChromeUtils.defineModuleGetter(this, "WebNavigationFrames",
+                               "resource://gre/modules/WebNavigationFrames.jsm");
 
-  receiveMessage(msg) {
-    if (!this.isBlockedSite) {
-      return;
-    }
-
-    BlockedSiteContent.receiveMessage(global, msg);
-  },
-
-  handleEvent(aEvent) {
-    if (!this.isBlockedSite) {
-      return;
-    }
-
-    if (aEvent.type != "AboutBlockedLoaded") {
-      return;
-    }
-
-    BlockedSiteContent.handleEvent(global, aEvent);
-  },
-};
-AboutBlockedSiteListener.init(this);
-
-var AboutNetAndCertErrorListener = {
-  init(chromeGlobal) {
-    addMessageListener("CertErrorDetails", this);
-    addMessageListener("Browser:CaptivePortalFreed", this);
-    chromeGlobal.addEventListener("AboutNetErrorLoad", this, false, true);
-    chromeGlobal.addEventListener("AboutNetErrorOpenCaptivePortal", this, false, true);
-    chromeGlobal.addEventListener("AboutNetErrorSetAutomatic", this, false, true);
-    chromeGlobal.addEventListener("AboutNetErrorResetPreferences", this, false, true);
-  },
-
-  isAboutNetError(doc) {
-    return doc.documentURI.startsWith("about:neterror");
-  },
-
-  isAboutCertError(doc) {
-    return doc.documentURI.startsWith("about:certerror");
-  },
-
-  receiveMessage(msg) {
-    if (msg.name == "CertErrorDetails") {
-      let frameDocShell = WebNavigationFrames.findDocShell(msg.data.frameId, docShell);
-      // We need nsIWebNavigation to access docShell.document.
-      frameDocShell && frameDocShell.QueryInterface(Ci.nsIWebNavigation);
-      if (!frameDocShell || !this.isAboutCertError(frameDocShell.document)) {
-        return;
-      }
-
-      NetErrorContent.onCertErrorDetails(global, msg, frameDocShell);
-    } else if (msg.name == "Browser:CaptivePortalFreed") {
-      // TODO: This check is not correct for frames.
-      if (!this.isAboutCertError(content.document)) {
-        return;
-      }
-
-      this.onCaptivePortalFreed(msg);
-    }
-  },
-
-  onCaptivePortalFreed(msg) {
-    content.dispatchEvent(new content.CustomEvent("AboutNetErrorCaptivePortalFreed"));
-  },
-
-  handleEvent(aEvent) {
-    // Documents have a null ownerDocument.
-    let doc = aEvent.originalTarget.ownerDocument || aEvent.originalTarget;
-
-    if (!this.isAboutNetError(doc) && !this.isAboutCertError(doc)) {
-      return;
-    }
-
-    NetErrorContent.handleEvent(global, aEvent);
-  },
-};
-AboutNetAndCertErrorListener.init(this);
-
-var ClickEventHandler = {
-  init: function init() {
-    Services.els.addSystemEventListener(global, "click", this, true);
-  },
+class ClickEventHandler {
+  constructor(mm) {
+    this.mm = mm;
+  }
 
   handleEvent(event) {
     if (!event.isTrusted || event.defaultPrevented || event.button == 2) {
       return;
     }
 
     let originalTarget = event.originalTarget;
     let ownerDoc = originalTarget.ownerDocument;
     if (!ownerDoc) {
       return;
     }
 
     // Handle click events from about pages
     if (event.button == 0) {
-      if (AboutNetAndCertErrorListener.isAboutCertError(ownerDoc)) {
-        NetErrorContent.onCertError(global, originalTarget, ownerDoc.defaultView);
+      if (this.mm.AboutNetAndCertErrorListener.isAboutCertError(ownerDoc)) {
+        NetErrorContent.onCertError(this.mm, originalTarget, ownerDoc.defaultView);
         return;
       } else if (ownerDoc.documentURI.startsWith("about:blocked")) {
-        BlockedSiteContent.onAboutBlocked(global, originalTarget, ownerDoc);
+        BlockedSiteContent.onAboutBlocked(this.mm, originalTarget, ownerDoc);
         return;
-      } else if (AboutNetAndCertErrorListener.isAboutNetError(ownerDoc)) {
-        NetErrorContent.onAboutNetError(global, event, ownerDoc.documentURI);
+      } else if (this.mm.AboutNetAndCertErrorListener.isAboutNetError(ownerDoc)) {
+        NetErrorContent.onAboutNetError(this.mm, event, ownerDoc.documentURI);
         return;
       }
     }
 
     let [href, node, principal] = this._hrefAndLinkNodeForClickEvent(event);
 
     // get referrer attribute from clicked link and parse it
     // if per element referrer is enabled, the element referrer overrules
@@ -236,49 +88,50 @@ var ClickEventHandler = {
       }
       json.noReferrer = BrowserUtils.linkHasNoReferrer(node);
 
       // Check if the link needs to be opened with mixed content allowed.
       // Only when the owner doc has |mixedContentChannel| and the same origin
       // should we allow mixed content.
       json.allowMixedContent = false;
       let docshell = ownerDoc.docShell;
-      if (docShell.mixedContentChannel) {
+      if (this.mm.docShell.mixedContentChannel) {
         const sm = Services.scriptSecurityManager;
         try {
           let targetURI = Services.io.newURI(href);
           sm.checkSameOriginURI(docshell.mixedContentChannel.URI, targetURI, false);
           json.allowMixedContent = true;
         } catch (e) {}
       }
       json.originPrincipal = ownerDoc.nodePrincipal;
       json.triggeringPrincipal = ownerDoc.nodePrincipal;
 
-      sendAsyncMessage("Content:Click", json);
+      this.mm.sendAsyncMessage("Content:Click", json);
       return;
     }
 
     // This might be middle mouse navigation.
     if (event.button == 1) {
-      sendAsyncMessage("Content:Click", json);
+      this.mm.sendAsyncMessage("Content:Click", json);
     }
-  },
+  }
 
   /**
    * Extracts linkNode and href for the current click target.
    *
    * @param event
    *        The click event.
    * @return [href, linkNode, linkPrincipal].
    *
    * @note linkNode will be null if the click wasn't on an anchor
    *       element. This includes SVG links, because callers expect |node|
    *       to behave like an <a> element, which SVG links (XLink) don't.
    */
   _hrefAndLinkNodeForClickEvent(event) {
+    let {content} = this.mm;
     function isHTMLLink(aNode) {
       // Be consistent with what nsContextMenu.js does.
       return ((aNode instanceof content.HTMLAnchorElement && aNode.href) ||
               (aNode instanceof content.HTMLAreaElement && aNode.href) ||
               aNode instanceof content.HTMLLinkElement);
     }
 
     let node = event.target;
@@ -307,226 +160,9 @@ var ClickEventHandler = {
     }
 
     // In case of XLink, we don't return the node we got href from since
     // callers expect <a>-like elements.
     // Note: makeURI() will throw if aUri is not a valid URI.
     return [href ? Services.io.newURI(href, null, baseURI).spec : null, null,
             node && node.ownerDocument.nodePrincipal];
   }
-};
-ClickEventHandler.init();
-
-new ContentLinkHandler(this);
-ContentMetaHandler.init(this);
-
-var PluginContentStub = {
-  EVENTS: [
-    "PluginCrashed",
-    "PluginOutdated",
-    "PluginInstantiated",
-    "PluginRemoved",
-    "HiddenPlugin",
-  ],
-
-  MESSAGES: [
-    "BrowserPlugins:ActivatePlugins",
-    "BrowserPlugins:NotificationShown",
-    "BrowserPlugins:ContextMenuCommand",
-    "BrowserPlugins:NPAPIPluginProcessCrashed",
-    "BrowserPlugins:CrashReportSubmitted",
-    "BrowserPlugins:Test:ClearCrashData",
-  ],
-
-  _pluginContent: null,
-  get pluginContent() {
-    if (!this._pluginContent) {
-      this._pluginContent = new PluginContent(global);
-    }
-    return this._pluginContent;
-  },
-
-  init() {
-    addEventListener("unload", this);
-
-    addEventListener("PluginBindingAttached", this, true, true);
-
-    for (let event of this.EVENTS) {
-      addEventListener(event, this, true);
-    }
-    for (let msg of this.MESSAGES) {
-      addMessageListener(msg, this);
-    }
-    Services.obs.addObserver(this, "decoder-doctor-notification");
-  },
-
-  uninit() {
-    Services.obs.removeObserver(this, "decoder-doctor-notification");
-  },
-
-  observe(subject, topic, data) {
-    return this.pluginContent.observe(subject, topic, data);
-  },
-
-  handleEvent(event) {
-    if (event.type === "unload") {
-      return this.uninit();
-    }
-    return this.pluginContent.handleEvent(event);
-  },
-
-  receiveMessage(msg) {
-    return this.pluginContent.receiveMessage(msg);
-  },
-};
-
-PluginContentStub.init();
-
-// This is a temporary hack to prevent regressions (bug 1471327).
-void content;
-
-addEventListener("DOMWindowFocus", function(event) {
-  sendAsyncMessage("DOMWindowFocus", {});
-}, false);
-
-// We use this shim so that ContentWebRTC.jsm will not be loaded until
-// it is actually needed.
-var ContentWebRTCShim = message => ContentWebRTC.receiveMessage(message);
-
-addMessageListener("rtcpeer:Allow", ContentWebRTCShim);
-addMessageListener("rtcpeer:Deny", ContentWebRTCShim);
-addMessageListener("webrtc:Allow", ContentWebRTCShim);
-addMessageListener("webrtc:Deny", ContentWebRTCShim);
-addMessageListener("webrtc:StopSharing", ContentWebRTCShim);
-
-var PageMetadataMessenger = {
-  init() {
-    addMessageListener("PageMetadata:GetPageData", this);
-    addMessageListener("PageMetadata:GetMicroformats", this);
-  },
-  receiveMessage(message) {
-    switch (message.name) {
-      case "PageMetadata:GetPageData": {
-        let target = contextMenu.getTarget(message);
-        let result = PageMetadata.getData(content.document, target);
-        sendAsyncMessage("PageMetadata:PageDataResult", result);
-        break;
-      }
-      case "PageMetadata:GetMicroformats": {
-        let target = contextMenu.getTarget(message);
-        let result = PageMetadata.getMicroformats(content.document, target);
-        sendAsyncMessage("PageMetadata:MicroformatsResult", result);
-        break;
-      }
-    }
-  }
-};
-PageMetadataMessenger.init();
-
-addEventListener("InstallBrowserTheme", LightWeightThemeWebInstallListener, false, true);
-addEventListener("PreviewBrowserTheme", LightWeightThemeWebInstallListener, false, true);
-addEventListener("ResetBrowserThemePreview", LightWeightThemeWebInstallListener, false, true);
-
-let OfflineApps = {
-  _docId: 0,
-  _docIdMap: new Map(),
-
-  _docManifestSet: new Set(),
-
-  _observerAdded: false,
-  registerWindow(aWindow) {
-    if (!this._observerAdded) {
-      this._observerAdded = true;
-      Services.obs.addObserver(this, "offline-cache-update-completed", true);
-    }
-    let manifestURI = this._getManifestURI(aWindow);
-    this._docManifestSet.add(manifestURI.spec);
-  },
-
-  handleEvent(event) {
-    if (event.type == "MozApplicationManifest") {
-      this.offlineAppRequested(event.originalTarget.defaultView);
-    }
-  },
-
-  _getManifestURI(aWindow) {
-    if (!aWindow.document.documentElement)
-      return null;
-
-    var attr = aWindow.document.documentElement.getAttribute("manifest");
-    if (!attr)
-      return null;
-
-    try {
-      return Services.io.newURI(attr, aWindow.document.characterSet,
-                                Services.io.newURI(aWindow.location.href));
-    } catch (e) {
-      return null;
-    }
-  },
-
-  offlineAppRequested(aContentWindow) {
-    this.registerWindow(aContentWindow);
-    if (!Services.prefs.getBoolPref("browser.offline-apps.notify")) {
-      return;
-    }
-
-    let currentURI = aContentWindow.document.documentURIObject;
-    // don't bother showing UI if the user has already made a decision
-    if (Services.perms.testExactPermission(currentURI, "offline-app") != Services.perms.UNKNOWN_ACTION)
-      return;
-
-    try {
-      if (Services.prefs.getBoolPref("offline-apps.allow_by_default")) {
-        // all pages can use offline capabilities, no need to ask the user
-        return;
-      }
-    } catch (e) {
-      // this pref isn't set by default, ignore failures
-    }
-    let docId = ++this._docId;
-    this._docIdMap.set(docId, Cu.getWeakReference(aContentWindow.document));
-    sendAsyncMessage("OfflineApps:RequestPermission", {
-      uri: currentURI.spec,
-      docId,
-    });
-  },
-
-  _startFetching(aDocument) {
-    if (!aDocument.documentElement)
-      return;
-
-    let manifestURI = this._getManifestURI(aDocument.defaultView);
-    if (!manifestURI)
-      return;
-
-    var updateService = Cc["@mozilla.org/offlinecacheupdate-service;1"].
-                        getService(Ci.nsIOfflineCacheUpdateService);
-    updateService.scheduleUpdate(manifestURI, aDocument.documentURIObject,
-                                 aDocument.nodePrincipal, aDocument.defaultView);
-  },
-
-  receiveMessage(aMessage) {
-    if (aMessage.name == "OfflineApps:StartFetching") {
-      let doc = this._docIdMap.get(aMessage.data.docId);
-      doc = doc && doc.get();
-      if (doc) {
-        this._startFetching(doc);
-      }
-      this._docIdMap.delete(aMessage.data.docId);
-    }
-  },
-
-  observe(aSubject, aTopic, aState) {
-    if (aTopic == "offline-cache-update-completed") {
-      let cacheUpdate = aSubject.QueryInterface(Ci.nsIOfflineCacheUpdate);
-      let uri = cacheUpdate.manifestURI;
-      if (uri && this._docManifestSet.has(uri.spec)) {
-        sendAsyncMessage("OfflineApps:CheckUsage", {uri: uri.spec});
-      }
-    }
-  },
-  QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver,
-                                          Ci.nsISupportsWeakReference]),
-};
-
-addEventListener("MozApplicationManifest", OfflineApps, false);
-addMessageListener("OfflineApps:StartFetching", OfflineApps);
+}
--- a/browser/modules/moz.build
+++ b/browser/modules/moz.build
@@ -140,16 +140,17 @@ XPCSHELL_TESTS_MANIFESTS += ['test/unit/
 EXTRA_JS_MODULES += [
     'AboutNewTab.jsm',
     'AsyncTabSwitcher.jsm',
     'AttributionCode.jsm',
     'BlockedSiteContent.jsm',
     'BrowserErrorReporter.jsm',
     'BrowserUsageTelemetry.jsm',
     'BrowserWindowTracker.jsm',
+    'ClickEventHandler.jsm',
     'ContentClick.jsm',
     'ContentCrashHandlers.jsm',
     'ContentLinkHandler.jsm',
     'ContentMetaHandler.jsm',
     'ContentObservers.js',
     'ContentSearch.jsm',
     'ContentWebRTC.jsm',
     'ContextMenu.jsm',
--- a/caps/nsScriptSecurityManager.cpp
+++ b/caps/nsScriptSecurityManager.cpp
@@ -41,17 +41,16 @@
 #include "nsIFileURL.h"
 #include "nsIZipReader.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsPIDOMWindow.h"
 #include "nsIDocShell.h"
 #include "nsIPrompt.h"
 #include "nsIWindowWatcher.h"
 #include "nsIConsoleService.h"
-#include "nsIObserverService.h"
 #include "nsIOService.h"
 #include "nsIContent.h"
 #include "nsDOMJSUtils.h"
 #include "nsAboutProtocolUtils.h"
 #include "nsIClassInfo.h"
 #include "nsIURIFixup.h"
 #include "nsCDefaultURIFixup.h"
 #include "nsIChromeRegistry.h"
@@ -456,18 +455,17 @@ nsScriptSecurityManager::IsSystemPrincip
 /////////////////////////////
 // nsScriptSecurityManager //
 /////////////////////////////
 
 ////////////////////////////////////
 // Methods implementing ISupports //
 ////////////////////////////////////
 NS_IMPL_ISUPPORTS(nsScriptSecurityManager,
-                  nsIScriptSecurityManager,
-                  nsIObserver)
+                  nsIScriptSecurityManager)
 
 ///////////////////////////////////////////////////
 // Methods implementing nsIScriptSecurityManager //
 ///////////////////////////////////////////////////
 
 ///////////////// Security Checks /////////////////
 
 bool
@@ -1357,39 +1355,28 @@ nsScriptSecurityManager::CanGetService(J
     nsAutoCString errorMsg("Permission denied to get service. CID=");
     char cidStr[NSID_LENGTH];
     aCID.ToProvidedString(cidStr);
     errorMsg.Append(cidStr);
     SetPendingExceptionASCII(cx, errorMsg.get());
     return NS_ERROR_DOM_XPCONNECT_ACCESS_DENIED;
 }
 
-/////////////////////////////////////
-// Method implementing nsIObserver //
-/////////////////////////////////////
 const char sJSEnabledPrefName[] = "javascript.enabled";
 const char sFileOriginPolicyPrefName[] =
     "security.fileuri.strict_origin_policy";
 
 static const char* kObservedPrefs[] = {
   sJSEnabledPrefName,
   sFileOriginPolicyPrefName,
   "capability.policy.",
   nullptr
 };
 
 
-NS_IMETHODIMP
-nsScriptSecurityManager::Observe(nsISupports* aObject, const char* aTopic,
-                                 const char16_t* aMessage)
-{
-    ScriptSecurityPrefChanged();
-    return NS_OK;
-}
-
 /////////////////////////////////////////////
 // Constructor, Destructor, Initialization //
 /////////////////////////////////////////////
 nsScriptSecurityManager::nsScriptSecurityManager(void)
     : mPrefInitialized(false)
     , mIsJavaScriptEnabled(false)
 {
     static_assert(sizeof(intptr_t) == sizeof(void*),
@@ -1426,17 +1413,20 @@ nsresult nsScriptSecurityManager::Init()
 
     return NS_OK;
 }
 
 static StaticRefPtr<nsScriptSecurityManager> gScriptSecMan;
 
 nsScriptSecurityManager::~nsScriptSecurityManager(void)
 {
-    Preferences::RemoveObservers(this, kObservedPrefs);
+    Preferences::UnregisterPrefixCallbacks(
+       PREF_CHANGE_METHOD(nsScriptSecurityManager::ScriptSecurityPrefChanged),
+       kObservedPrefs,
+       this);
     if (mDomainPolicy) {
         mDomainPolicy->Deactivate();
     }
     // ContentChild might hold a reference to the domain policy,
     // and it might release it only after the security manager is
     // gone. But we can still assert this for the main process.
     MOZ_ASSERT_IF(XRE_IsParentProcess(),
                   !mDomainPolicy);
@@ -1506,17 +1496,17 @@ uint32_t SkipUntil(const nsCString& str,
 {
     while (base < str.Length() && !Predicate::Test(str[base])) {
         ++base;
     }
     return base;
 }
 
 inline void
-nsScriptSecurityManager::ScriptSecurityPrefChanged()
+nsScriptSecurityManager::ScriptSecurityPrefChanged(const char* aPref)
 {
     MOZ_ASSERT(mPrefInitialized);
     mIsJavaScriptEnabled =
         Preferences::GetBool(sJSEnabledPrefName, mIsJavaScriptEnabled);
     sStrictFileOriginPolicy =
         Preferences::GetBool(sFileOriginPolicyPrefName, false);
     mFileURIWhitelist.reset();
 }
@@ -1563,17 +1553,20 @@ nsScriptSecurityManager::InitPrefs()
     NS_ENSURE_TRUE(branch, NS_ERROR_FAILURE);
 
     mPrefInitialized = true;
 
     // Set the initial value of the "javascript.enabled" prefs
     ScriptSecurityPrefChanged();
 
     // set observer callbacks in case the value of the prefs change
-    Preferences::AddStrongObservers(this, kObservedPrefs);
+    Preferences::RegisterPrefixCallbacks(
+       PREF_CHANGE_METHOD(nsScriptSecurityManager::ScriptSecurityPrefChanged),
+       kObservedPrefs,
+       this);
 
     OriginAttributes::InitPrefs();
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsScriptSecurityManager::GetDomainPolicyActive(bool *aRv)
--- a/caps/nsScriptSecurityManager.h
+++ b/caps/nsScriptSecurityManager.h
@@ -7,17 +7,16 @@
 #ifndef nsScriptSecurityManager_h__
 #define nsScriptSecurityManager_h__
 
 #include "nsIScriptSecurityManager.h"
 
 #include "mozilla/Maybe.h"
 #include "nsIPrincipal.h"
 #include "nsCOMPtr.h"
-#include "nsIObserver.h"
 #include "nsServiceManagerUtils.h"
 #include "nsStringFwd.h"
 #include "plstr.h"
 #include "js/TypeDecls.h"
 
 #include <stdint.h>
 
 class nsIIOService;
@@ -30,27 +29,25 @@ class OriginAttributes;
 
 /////////////////////////////
 // nsScriptSecurityManager //
 /////////////////////////////
 #define NS_SCRIPTSECURITYMANAGER_CID \
 { 0x7ee2a4c0, 0x4b93, 0x17d3, \
 { 0xba, 0x18, 0x00, 0x60, 0xb0, 0xf1, 0x99, 0xa2 }}
 
-class nsScriptSecurityManager final : public nsIScriptSecurityManager,
-                                      public nsIObserver
+class nsScriptSecurityManager final : public nsIScriptSecurityManager
 {
 public:
     static void Shutdown();
 
     NS_DEFINE_STATIC_CID_ACCESSOR(NS_SCRIPTSECURITYMANAGER_CID)
 
     NS_DECL_ISUPPORTS
     NS_DECL_NSISCRIPTSECURITYMANAGER
-    NS_DECL_NSIOBSERVER
 
     static nsScriptSecurityManager*
     GetScriptSecurityManager();
 
     // Invoked exactly once, by XPConnect.
     static void InitStatics();
 
     static already_AddRefed<SystemPrincipal>
@@ -94,18 +91,18 @@ private:
     JSPrincipalsSubsume(JSPrincipals *first, JSPrincipals *second);
 
     nsresult
     Init();
 
     nsresult
     InitPrefs();
 
-    inline void
-    ScriptSecurityPrefChanged();
+    void
+    ScriptSecurityPrefChanged(const char* aPref = nullptr);
 
     inline void
     AddSitesToFileURIWhitelist(const nsCString& aSiteList);
 
     nsresult GetChannelResultPrincipal(nsIChannel* aChannel,
                                        nsIPrincipal** aPrincipal,
                                        bool aIgnoreSandboxing);
 
--- a/devtools/client/debugger/new/README.mozilla
+++ b/devtools/client/debugger/new/README.mozilla
@@ -1,13 +1,13 @@
 This is the debugger.html project output.
 See https://github.com/devtools-html/debugger.html
 
-Version 71
+Version 72
 
-Comparison: https://github.com/devtools-html/debugger.html/compare/release-70...release-71
+Comparison: https://github.com/devtools-html/debugger.html/compare/release-71...release-72
 
 Packages:
 - babel-plugin-transform-es2015-modules-commonjs @6.26.2
 - babel-preset-react @6.24.1
 - react @16.4.1
 - react-dom @16.4.1
 - webpack @3.11.0
--- a/devtools/client/debugger/new/dist/debugger.css
+++ b/devtools/client/debugger/new/dist/debugger.css
@@ -2701,16 +2701,48 @@ button.jump-definition {
   left: 0px;
   --editor-footer-height: 24px;
 }
 
 html[dir="rtl"] .editor-mount {
   direction: ltr;
 }
 
+.theme-light {
+  --gutter-hover-background-color: #dde1e4;
+}
+
+.theme-dark {
+  --gutter-hover-background-color: #414141;
+}
+
+:not(.empty-line):not(.new-breakpoint)
+  > .CodeMirror-gutter-wrapper:hover
+  > .CodeMirror-linenumber {
+  height: 13px;
+  color: var(--theme-body-color);
+  /* Add 1px offset to the background to match it
+    with the actual breakpoint image dimensions */
+  background: linear-gradient(to bottom, transparent 1px, var(--gutter-hover-background-color) 0);
+}
+
+:not(.empty-line):not(.new-breakpoint)
+  > .CodeMirror-gutter-wrapper:hover
+  > .CodeMirror-linenumber::after {
+    content: '';
+    position: absolute;
+    top: 1px;
+    height: 12px;
+    width: 9px;
+    background-color: var(--gutter-hover-background-color);
+    mask: url("chrome://devtools/skin/images/debugger/breakpoint.svg") no-repeat;
+    mask-size: auto 12px;
+    mask-position: right;
+  }
+
 .editor-wrapper .breakpoints {
   position: absolute;
   top: 0;
   left: 0;
 }
 
 .function-search {
   max-height: 300px;
@@ -2748,16 +2780,21 @@ html[dir="rtl"] .editor-mount {
   width: 20px;
   padding: 0px 5px;
   margin: 0px 4px;
   border-radius: 5px;
   border-color: blue;
   border: 1px solid #00b6ff;
 }
 
+.editor .breakpoint {
+    position: absolute;
+    right: -2px;
+}
+
 .editor.new-breakpoint.folding-enabled svg {
   right: -16px;
 }
 
 .new-breakpoint.has-condition svg {
   fill: var(--theme-graphs-yellow);
 }
 
--- a/devtools/client/debugger/new/dist/parser-worker.js
+++ b/devtools/client/debugger/new/dist/parser-worker.js
@@ -1938,19 +1938,19 @@ var _findOutOfScopeLocations2 = _interop
 var _steps = __webpack_require__(1625);
 
 var _validate = __webpack_require__(1629);
 
 var _frameworks = __webpack_require__(1703);
 
 var _pausePoints = __webpack_require__(3612);
 
-var _mapOriginalExpression = __webpack_require__(3613);
-
-var _mapOriginalExpression2 = _interopRequireDefault(_mapOriginalExpression);
+var _mapExpression = __webpack_require__(3755);
+
+var _mapExpression2 = _interopRequireDefault(_mapExpression);
 
 var _devtoolsUtils = __webpack_require__(1363);
 
 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
 const { workerHandler } = _devtoolsUtils.workerUtils; /* 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/>. */
@@ -1964,17 +1964,17 @@ self.onmessage = workerHandler({
   clearASTs: _ast.clearASTs,
   hasSource: _sources.hasSource,
   setSource: _sources.setSource,
   clearSources: _sources.clearSources,
   getNextStep: _steps.getNextStep,
   hasSyntaxError: _validate.hasSyntaxError,
   getFramework: _frameworks.getFramework,
   getPausePoints: _pausePoints.getPausePoints,
-  mapOriginalExpression: _mapOriginalExpression2.default
+  mapExpression: _mapExpression2.default
 });
 
 /***/ }),
 
 /***/ 1620:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
@@ -25381,16 +25381,180 @@ function streamingWorkerHandler(publicIn
 module.exports = {
   WorkerDispatcher,
   workerHandler,
   streamingWorkerHandler
 };
 
 /***/ }),
 
+/***/ 3755:
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+exports.default = mapExpression;
+
+var _mapOriginalExpression = __webpack_require__(3613);
+
+var _mapOriginalExpression2 = _interopRequireDefault(_mapOriginalExpression);
+
+var _mapBindings = __webpack_require__(3756);
+
+var _mapBindings2 = _interopRequireDefault(_mapBindings);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+/* 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/>. */
+
+function mapExpression(expression, mappings, bindings, shouldMapBindings = true) {
+  let originalExpression = expression;
+  if (mappings) {
+    originalExpression = (0, _mapOriginalExpression2.default)(expression, mappings);
+  }
+
+  let safeExpression = originalExpression;
+  if (shouldMapBindings) {
+    safeExpression = (0, _mapBindings2.default)(originalExpression, bindings);
+  }
+
+  return safeExpression;
+}
+
+/***/ }),
+
+/***/ 3756:
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+exports.default = mapExpressionBindings;
+
+var _ast = __webpack_require__(1375);
+
+var _generator = __webpack_require__(2365);
+
+var _generator2 = _interopRequireDefault(_generator);
+
+var _types = __webpack_require__(2268);
+
+var t = _interopRequireWildcard(_types);
+
+function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+// translates new bindings `var a = 3` into `self.a = 3`
+// and existing bindings `var a = 3` into `a = 3` for re-assignments
+function globalizeDeclaration(node, bindings) {
+  return node.declarations.map(declaration => {
+    const identifier = bindings.includes(declaration.id.name) ? declaration.id : t.memberExpression(t.identifier("self"), declaration.id);
+
+    return t.expressionStatement(t.assignmentExpression("=", identifier, declaration.init));
+  });
+}
+
+// translates new bindings `a = 3` into `self.a = 3`
+// and keeps assignments the same for existing bindings.
+/* 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/>. */
+
+function globalizeAssignment(node, bindings) {
+  if (bindings.includes(node.left.name)) {
+    return node;
+  }
+
+  const identifier = t.memberExpression(t.identifier("self"), node.left);
+  return t.assignmentExpression(node.operator, identifier, node.right);
+}
+
+function isTopLevel(ancestors) {
+  return ancestors.filter(ancestor => ancestor.key == "body").length == 1;
+}
+
+function replaceNode(ancestors, node) {
+  const parent = ancestors[ancestors.length - 1];
+
+  if (typeof parent.index === "number") {
+    if (Array.isArray(node)) {
+      parent.node[parent.key].splice(parent.index, 1, ...node);
+    } else {
+      parent.node[parent.key][parent.index] = node;
+    }
+  } else {
+    parent.node[parent.key] = node;
+  }
+}
+
+function hasDestructuring(node) {
+  return node.declarations.some(declaration => t.isPattern(declaration.id));
+}
+
+function mapExpressionBindings(expression, bindings = []) {
+  const ast = (0, _ast.parseScript)(expression);
+  let shouldUpdate = true;
+  t.traverse(ast, (node, ancestors) => {
+    const parent = ancestors[ancestors.length - 1];
+
+    if (t.isWithStatement(node)) {
+      shouldUpdate = false;
+      return;
+    }
+
+    if (!isTopLevel(ancestors)) {
+      return;
+    }
+
+    if (t.isAssignmentExpression(node)) {
+      if (t.isIdentifier(node.left)) {
+        const newNode = globalizeAssignment(node, bindings);
+        return replaceNode(ancestors, newNode);
+      }
+
+      if (t.isPattern(node.left)) {
+        shouldUpdate = false;
+        return;
+      }
+    }
+
+    if (!t.isVariableDeclaration(node)) {
+      return;
+    }
+
+    if (hasDestructuring(node)) {
+      shouldUpdate = false;
+      return;
+    }
+
+    if (!t.isForStatement(parent.node)) {
+      const newNodes = globalizeDeclaration(node, bindings);
+      replaceNode(ancestors, newNodes);
+    }
+  });
+
+  if (!shouldUpdate) {
+    return expression;
+  }
+
+  return (0, _generator2.default)(ast).code;
+}
+
+/***/ }),
+
 /***/ 398:
 /***/ (function(module, exports, __webpack_require__) {
 
 /* WEBPACK VAR INJECTION */(function(module) {var root = __webpack_require__(8);
 
 /** Detect free variable `exports`. */
 var freeExports = typeof exports == 'object' && exports && !exports.nodeType && exports;
 
--- a/devtools/client/debugger/new/dist/vendors.js
+++ b/devtools/client/debugger/new/dist/vendors.js
@@ -8379,40 +8379,43 @@ var transition = _interopRequireWildcard
 var _reselect = __webpack_require__(993);
 
 var reselect = _interopRequireWildcard(_reselect);
 
 var _url = __webpack_require__(334);
 
 var url = _interopRequireWildcard(_url);
 
-var _lodashMove = __webpack_require__(3751);
-
-var lodashMove = _interopRequireWildcard(_lodashMove);
-
 var _classnames = __webpack_require__(175);
 
 var _classnames2 = _interopRequireDefault(_classnames);
 
 var _devtoolsSplitter = __webpack_require__(1440);
 
 var _devtoolsSplitter2 = _interopRequireDefault(_devtoolsSplitter);
 
+var _lodashMove = __webpack_require__(3751);
+
+var _lodashMove2 = _interopRequireDefault(_lodashMove);
+
 var _Svg = __webpack_require__(1359);
 
 var _Svg2 = _interopRequireDefault(_Svg);
 
 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
 function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
 
 // We cannot directly export literals containing special characters
 // (eg. "my-module/Test") which is why they are nested in "vendored".
 // The keys of the vendored object should match the module names
 // !!! Should remain synchronized with .babel/transform-mc.js !!!
+
+
+// Modules imported without destructuring
 /* 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/>. */
 
 /**
  * Vendors.js is a file used to bundle and expose all dependencies needed to run
  * the transpiled debugger modules when running in Firefox.
  *
@@ -8430,27 +8433,25 @@ const vendored = exports.vendored = {
   "devtools-components": devtoolsComponents,
   "devtools-config": devtoolsConfig,
   "devtools-contextmenu": devtoolsContextmenu,
   "devtools-environment": devtoolsEnvironment,
   "devtools-modules": devtoolsModules,
   "devtools-splitter": _devtoolsSplitter2.default,
   "devtools-utils": devtoolsUtils,
   "fuzzaldrin-plus": fuzzaldrinPlus,
-  "lodash-move": lodashMove,
+  "lodash-move": _lodashMove2.default,
   "react-transition-group/Transition": transition,
   reselect,
   // Svg is required via relative paths, so the key is not imported path.
   // See .babel/transform-mc.js
   Svg: _Svg2.default,
   url
 };
 
-// Modules imported without destructuring
-
 /***/ }),
 
 /***/ 3750:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
--- a/devtools/client/debugger/new/src/actions/expressions.js
+++ b/devtools/client/debugger/new/src/actions/expressions.js
@@ -15,16 +15,18 @@ exports.getMappedExpression = getMappedE
 var _selectors = require("../selectors/index");
 
 var _promise = require("./utils/middleware/promise");
 
 var _devtoolsSourceMap = require("devtools/client/shared/source-map/index.js");
 
 var _expressions = require("../utils/expressions");
 
+var _prefs = require("../utils/prefs");
+
 var _parser = require("../workers/parser/index");
 
 var parser = _interopRequireWildcard(_parser);
 
 function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } }
 
 /* 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
@@ -210,16 +212,17 @@ function evaluateExpression(expression) 
 function getMappedExpression(expression) {
   return async function ({
     dispatch,
     getState,
     client,
     sourceMaps
   }) {
     const mappings = (0, _selectors.getSelectedScopeMappings)(getState());
+    const bindings = (0, _selectors.getSelectedFrameBindings)(getState());
 
-    if (!mappings) {
+    if (!mappings && !bindings) {
       return expression;
     }
 
-    return parser.mapOriginalExpression(expression, mappings);
+    return parser.mapExpression(expression, mappings, bindings, _prefs.features.mapExpressionBindings);
   };
 }
\ No newline at end of file
--- a/devtools/client/debugger/new/src/actions/file-search.js
+++ b/devtools/client/debugger/new/src/actions/file-search.js
@@ -85,25 +85,30 @@ function updateSearchResults(characterIn
 function searchContents(query, editor) {
   return async ({
     getState,
     dispatch
   }) => {
     const modifiers = (0, _selectors.getFileSearchModifiers)(getState());
     const selectedSource = (0, _selectors.getSelectedSource)(getState());
 
-    if (!query || !editor || !selectedSource || !selectedSource.text || !modifiers) {
+    if (!editor || !selectedSource || !selectedSource.text || !modifiers) {
       return;
     }
 
     const ctx = {
       ed: editor,
       cm: editor.codeMirror
     };
 
+    if (!query) {
+      (0, _editor.clearSearch)(ctx.cm, query);
+      return;
+    }
+
     const _modifiers = modifiers.toJS();
 
     const matches = await (0, _search.getMatches)(query, selectedSource.text, _modifiers);
     const res = (0, _editor.find)(ctx, query, true, _modifiers);
 
     if (!res) {
       return;
     }
--- a/devtools/client/debugger/new/src/actions/sources/loadSourceText.js
+++ b/devtools/client/debugger/new/src/actions/sources/loadSourceText.js
@@ -80,17 +80,17 @@ function loadSourceText(source) {
 
     const deferred = (0, _defer2.default)();
     requests.set(id, deferred.promise);
     telemetry.start(loadSourceHistogram, source);
 
     try {
       await dispatch({
         type: "LOAD_SOURCE_TEXT",
-        sourceId: id,
+        sourceId: source.id,
         [_promise.PROMISE]: loadSource(source, {
           sourceMaps,
           client
         })
       });
     } catch (e) {
       deferred.resolve();
       requests.delete(id);
--- a/devtools/client/debugger/new/src/components/Editor/Footer.js
+++ b/devtools/client/debugger/new/src/components/Editor/Footer.js
@@ -5,26 +5,28 @@ Object.defineProperty(exports, "__esModu
 });
 
 var _react = require("devtools/client/shared/vendor/react");
 
 var _react2 = _interopRequireDefault(_react);
 
 var _reactRedux = require("devtools/client/shared/vendor/react-redux");
 
+var _devtoolsSourceMap = require("devtools/client/shared/source-map/index.js");
+
+var _classnames = require("devtools/client/debugger/new/dist/vendors").vendored["classnames"];
+
+var _classnames2 = _interopRequireDefault(_classnames);
+
 var _actions = require("../../actions/index");
 
 var _actions2 = _interopRequireDefault(_actions);
 
 var _selectors = require("../../selectors/index");
 
-var _classnames = require("devtools/client/debugger/new/dist/vendors").vendored["classnames"];
-
-var _classnames2 = _interopRequireDefault(_classnames);
-
 var _prefs = require("../../utils/prefs");
 
 var _source = require("../../utils/source");
 
 var _sources = require("../../reducers/sources");
 
 var _editor = require("../../utils/editor/index");
 
@@ -143,17 +145,17 @@ class SourceFooter extends _react.PureCo
 
   renderSourceSummary() {
     const {
       mappedSource,
       jumpToMappedLocation,
       selectedSource
     } = this.props;
 
-    if (!mappedSource) {
+    if (!mappedSource || !(0, _devtoolsSourceMap.isOriginalId)(selectedSource.id)) {
       return null;
     }
 
     const filename = (0, _source.getFilename)(mappedSource);
     const tooltip = L10N.getFormatStr("sourceFooter.mappedSourceTooltip", filename);
     const title = L10N.getFormatStr("sourceFooter.mappedSource", filename);
     const mappedSourceLocation = {
       sourceId: selectedSource.id,
--- a/devtools/client/debugger/new/src/components/Editor/index.js
+++ b/devtools/client/debugger/new/src/components/Editor/index.js
@@ -406,25 +406,17 @@ class Editor extends _react.PureComponen
     const {
       selectedLocation,
       selectedSource
     } = this.props;
     const {
       editor
     } = this.state;
 
-    if (!nextProps.selectedSource || !editor || !nextProps.selectedLocation) {
-      return false;
-    }
-
-    if (!(0, _source.isLoaded)(nextProps.selectedSource)) {
-      return false;
-    }
-
-    if (!nextProps.selectedLocation.line) {
+    if (!editor || !nextProps.selectedSource || !nextProps.selectedLocation || !(0, _source.isLoaded)(nextProps.selectedSource)) {
       return false;
     }
 
     const isFirstLoad = (!selectedSource || !(0, _source.isLoaded)(selectedSource)) && (0, _source.isLoaded)(nextProps.selectedSource);
     const locationChanged = selectedLocation !== nextProps.selectedLocation;
     return isFirstLoad || locationChanged;
   }
 
--- a/devtools/client/debugger/new/src/reducers/pause.js
+++ b/devtools/client/debugger/new/src/reducers/pause.js
@@ -16,16 +16,17 @@ exports.getIsWaitingOnBreak = getIsWaiti
 exports.getShouldPauseOnExceptions = getShouldPauseOnExceptions;
 exports.getShouldPauseOnCaughtExceptions = getShouldPauseOnCaughtExceptions;
 exports.getCanRewind = getCanRewind;
 exports.getExtra = getExtra;
 exports.getFrames = getFrames;
 exports.getGeneratedFrameScope = getGeneratedFrameScope;
 exports.getOriginalFrameScope = getOriginalFrameScope;
 exports.getFrameScopes = getFrameScopes;
+exports.getSelectedFrameBindings = getSelectedFrameBindings;
 exports.getFrameScope = getFrameScope;
 exports.getSelectedScope = getSelectedScope;
 exports.getSelectedScopeMappings = getSelectedScopeMappings;
 exports.getSelectedFrameId = getSelectedFrameId;
 exports.getSelectedComponentIndex = getSelectedComponentIndex;
 exports.getTopFrame = getTopFrame;
 exports.getDebuggeeUrl = getDebuggeeUrl;
 exports.getSkipPausing = getSkipPausing;
@@ -376,16 +377,43 @@ function getOriginalFrameScope(state, so
 
   return null;
 }
 
 function getFrameScopes(state) {
   return state.pause.frameScopes;
 }
 
+function getSelectedFrameBindings(state) {
+  const scopes = getFrameScopes(state);
+  const selectedFrameId = getSelectedFrameId(state);
+
+  if (!scopes || !selectedFrameId) {
+    return null;
+  }
+
+  const frameScope = scopes.generated[selectedFrameId];
+
+  if (!frameScope || frameScope.pending) {
+    return;
+  }
+
+  let currentScope = frameScope.scope;
+  let frameBindings = [];
+
+  while (currentScope && currentScope.type != "object") {
+    const bindings = Object.keys(currentScope.bindings.variables);
+    const args = [].concat(...currentScope.bindings.arguments.map(argument => Object.keys(argument)));
+    frameBindings = [...frameBindings, ...bindings, ...args];
+    currentScope = currentScope.parent;
+  }
+
+  return frameBindings;
+}
+
 function getFrameScope(state, sourceId, frameId) {
   return getOriginalFrameScope(state, sourceId, frameId) || getGeneratedFrameScope(state, frameId);
 }
 
 function getSelectedScope(state) {
   const sourceRecord = (0, _sources.getSelectedSource)(state);
   const frameId = getSelectedFrameId(state);
   const {
--- a/devtools/client/debugger/new/src/reducers/sources.js
+++ b/devtools/client/debugger/new/src/reducers/sources.js
@@ -21,24 +21,28 @@ exports.getSourceInSources = getSourceIn
 exports.getSources = getSources;
 exports.getSourceList = getSourceList;
 exports.getSourceCount = getSourceCount;
 
 var _reselect = require("devtools/client/debugger/new/dist/vendors").vendored["reselect"];
 
 var _lodashMove = require("devtools/client/debugger/new/dist/vendors").vendored["lodash-move"];
 
+var _lodashMove2 = _interopRequireDefault(_lodashMove);
+
 var _source = require("../utils/source");
 
 var _devtoolsSourceMap = require("devtools/client/shared/source-map/index.js");
 
 var _lodash = require("devtools/client/shared/vendor/lodash");
 
 var _prefs = require("../utils/prefs");
 
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
 /* 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/>. */
 
 /**
  * Sources reducer
  * @module reducers/sources
  */
@@ -251,17 +255,17 @@ function restoreTabs() {
 
 
 function updateTabList(tabs, url, newIndex) {
   const currentIndex = tabs.indexOf(url);
 
   if (currentIndex === -1) {
     tabs = [url, ...tabs];
   } else if (newIndex !== undefined) {
-    tabs = (0, _lodashMove.move)(tabs, currentIndex, newIndex);
+    tabs = (0, _lodashMove2.default)(tabs, currentIndex, newIndex);
   }
 
   _prefs.prefs.tabs = tabs;
   return tabs;
 }
 
 function updateBlackBoxList(url, isBlackBoxed) {
   const tabs = getBlackBoxList();
--- a/devtools/client/debugger/new/src/utils/editor/source-search.js
+++ b/devtools/client/debugger/new/src/utils/editor/source-search.js
@@ -2,16 +2,17 @@
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
 exports.buildQuery = undefined;
 exports.getMatchIndex = getMatchIndex;
 exports.searchSourceForHighlight = searchSourceForHighlight;
 exports.removeOverlay = removeOverlay;
+exports.clearSearch = clearSearch;
 exports.find = find;
 exports.findNext = findNext;
 exports.findPrev = findPrev;
 
 var _buildQuery = require("../../workers/search/build-query");
 
 var _buildQuery2 = _interopRequireDefault(_buildQuery);
 
--- a/devtools/client/debugger/new/src/utils/prefs.js
+++ b/devtools/client/debugger/new/src/utils/prefs.js
@@ -61,16 +61,17 @@ if (isDevelopment()) {
   pref("devtools.debugger.features.outline", true);
   pref("devtools.debugger.features.column-breakpoints", true);
   pref("devtools.debugger.features.replay", true);
   pref("devtools.debugger.features.pause-points", true);
   pref("devtools.debugger.features.skip-pausing", false);
   pref("devtools.debugger.features.component-pane", false);
   pref("devtools.debugger.features.skip-pausing", true);
   pref("devtools.debugger.features.autocomplete-expressions", false);
+  pref("devtools.debugger.features.map-expression-bindings", true);
 }
 
 const prefs = exports.prefs = new PrefsHelper("devtools", {
   alphabetizeOutline: ["Bool", "debugger.alphabetize-outline"],
   autoPrettyPrint: ["Bool", "debugger.auto-pretty-print"],
   clientSourceMapsEnabled: ["Bool", "source-map.client-service.enabled"],
   pauseOnExceptions: ["Bool", "debugger.pause-on-exceptions"],
   pauseOnCaughtExceptions: ["Bool", "debugger.pause-on-caught-exceptions"],
@@ -108,17 +109,17 @@ const features = exports.features = new 
   workers: ["Bool", "workers"],
   codeCoverage: ["Bool", "code-coverage"],
   eventListeners: ["Bool", "event-listeners"],
   outline: ["Bool", "outline"],
   codeFolding: ["Bool", "code-folding"],
   replay: ["Bool", "replay"],
   pausePoints: ["Bool", "pause-points"],
   skipPausing: ["Bool", "skip-pausing"],
-  componentPane: ["Bool", "component-pane"],
-  autocompleteExpression: ["Bool", "autocomplete-expressions"]
+  autocompleteExpression: ["Bool", "autocomplete-expressions"],
+  mapExpressionBindings: ["Bool", "map-expression-bindings"]
 });
 
 if (prefs.debuggerPrefsSchemaVersion !== prefsSchemaVersion) {
   // clear pending Breakpoints
   prefs.pendingBreakpoints = {};
   prefs.debuggerPrefsSchemaVersion = prefsSchemaVersion;
 }
\ No newline at end of file
--- a/devtools/client/debugger/new/src/workers/parser/index.js
+++ b/devtools/client/debugger/new/src/workers/parser/index.js
@@ -1,14 +1,14 @@
 "use strict";
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
-exports.replaceOriginalVariableName = exports.getPausePoints = exports.getFramework = exports.mapOriginalExpression = exports.hasSyntaxError = exports.clearSources = exports.setSource = exports.hasSource = exports.getNextStep = exports.clearASTs = exports.clearScopes = exports.clearSymbols = exports.findOutOfScopeLocations = exports.getScopes = exports.getSymbols = exports.getClosestExpression = exports.stop = exports.start = undefined;
+exports.replaceOriginalVariableName = exports.getPausePoints = exports.getFramework = exports.mapExpression = exports.hasSyntaxError = exports.clearSources = exports.setSource = exports.hasSource = exports.getNextStep = exports.clearASTs = exports.clearScopes = exports.clearSymbols = exports.findOutOfScopeLocations = exports.getScopes = exports.getSymbols = exports.getClosestExpression = exports.stop = exports.start = undefined;
 
 var _devtoolsUtils = require("devtools/client/debugger/new/dist/vendors").vendored["devtools-utils"];
 
 /* 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/>. */
 const {
   WorkerDispatcher
@@ -23,12 +23,12 @@ const findOutOfScopeLocations = exports.
 const clearSymbols = exports.clearSymbols = dispatcher.task("clearSymbols");
 const clearScopes = exports.clearScopes = dispatcher.task("clearScopes");
 const clearASTs = exports.clearASTs = dispatcher.task("clearASTs");
 const getNextStep = exports.getNextStep = dispatcher.task("getNextStep");
 const hasSource = exports.hasSource = dispatcher.task("hasSource");
 const setSource = exports.setSource = dispatcher.task("setSource");
 const clearSources = exports.clearSources = dispatcher.task("clearSources");
 const hasSyntaxError = exports.hasSyntaxError = dispatcher.task("hasSyntaxError");
-const mapOriginalExpression = exports.mapOriginalExpression = dispatcher.task("mapOriginalExpression");
+const mapExpression = exports.mapExpression = dispatcher.task("mapExpression");
 const getFramework = exports.getFramework = dispatcher.task("getFramework");
 const getPausePoints = exports.getPausePoints = dispatcher.task("getPausePoints");
 const replaceOriginalVariableName = exports.replaceOriginalVariableName = dispatcher.task("replaceOriginalVariableName");
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/src/workers/parser/mapBindings.js
@@ -0,0 +1,114 @@
+"use strict";
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+exports.default = mapExpressionBindings;
+
+var _ast = require("./utils/ast");
+
+var _generator = require("@babel/generator/index");
+
+var _generator2 = _interopRequireDefault(_generator);
+
+var _types = require("@babel/types/index");
+
+var t = _interopRequireWildcard(_types);
+
+function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } }
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+/* 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/>. */
+// translates new bindings `var a = 3` into `self.a = 3`
+// and existing bindings `var a = 3` into `a = 3` for re-assignments
+function globalizeDeclaration(node, bindings) {
+  return node.declarations.map(declaration => {
+    const identifier = bindings.includes(declaration.id.name) ? declaration.id : t.memberExpression(t.identifier("self"), declaration.id);
+    return t.expressionStatement(t.assignmentExpression("=", identifier, declaration.init));
+  });
+} // translates new bindings `a = 3` into `self.a = 3`
+// and keeps assignments the same for existing bindings.
+
+
+function globalizeAssignment(node, bindings) {
+  if (bindings.includes(node.left.name)) {
+    return node;
+  }
+
+  const identifier = t.memberExpression(t.identifier("self"), node.left);
+  return t.assignmentExpression(node.operator, identifier, node.right);
+}
+
+function isTopLevel(ancestors) {
+  return ancestors.filter(ancestor => ancestor.key == "body").length == 1;
+}
+
+function replaceNode(ancestors, node) {
+  const parent = ancestors[ancestors.length - 1];
+
+  if (typeof parent.index === "number") {
+    if (Array.isArray(node)) {
+      parent.node[parent.key].splice(parent.index, 1, ...node);
+    } else {
+      parent.node[parent.key][parent.index] = node;
+    }
+  } else {
+    parent.node[parent.key] = node;
+  }
+}
+
+function hasDestructuring(node) {
+  return node.declarations.some(declaration => t.isPattern(declaration.id));
+}
+
+function mapExpressionBindings(expression, bindings = []) {
+  const ast = (0, _ast.parseScript)(expression);
+  let shouldUpdate = true;
+  t.traverse(ast, (node, ancestors) => {
+    const parent = ancestors[ancestors.length - 1];
+
+    if (t.isWithStatement(node)) {
+      shouldUpdate = false;
+      return;
+    }
+
+    if (!isTopLevel(ancestors)) {
+      return;
+    }
+
+    if (t.isAssignmentExpression(node)) {
+      if (t.isIdentifier(node.left)) {
+        const newNode = globalizeAssignment(node, bindings);
+        return replaceNode(ancestors, newNode);
+      }
+
+      if (t.isPattern(node.left)) {
+        shouldUpdate = false;
+        return;
+      }
+    }
+
+    if (!t.isVariableDeclaration(node)) {
+      return;
+    }
+
+    if (hasDestructuring(node)) {
+      shouldUpdate = false;
+      return;
+    }
+
+    if (!t.isForStatement(parent.node)) {
+      const newNodes = globalizeDeclaration(node, bindings);
+      replaceNode(ancestors, newNodes);
+    }
+  });
+
+  if (!shouldUpdate) {
+    return expression;
+  }
+
+  return (0, _generator2.default)(ast).code;
+}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/src/workers/parser/mapExpression.js
@@ -0,0 +1,35 @@
+"use strict";
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
+exports.default = mapExpression;
+
+var _mapOriginalExpression = require("./mapOriginalExpression");
+
+var _mapOriginalExpression2 = _interopRequireDefault(_mapOriginalExpression);
+
+var _mapBindings = require("./mapBindings");
+
+var _mapBindings2 = _interopRequireDefault(_mapBindings);
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+/* 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/>. */
+function mapExpression(expression, mappings, bindings, shouldMapBindings = true) {
+  let originalExpression = expression;
+
+  if (mappings) {
+    originalExpression = (0, _mapOriginalExpression2.default)(expression, mappings);
+  }
+
+  let safeExpression = originalExpression;
+
+  if (shouldMapBindings) {
+    safeExpression = (0, _mapBindings2.default)(originalExpression, bindings);
+  }
+
+  return safeExpression;
+}
\ No newline at end of file
--- a/devtools/client/debugger/new/src/workers/parser/moz.build
+++ b/devtools/client/debugger/new/src/workers/parser/moz.build
@@ -8,15 +8,17 @@ DIRS += [
     'utils',
 ]
 
 DevToolsModules(
     'findOutOfScopeLocations.js',
     'frameworks.js',
     'getSymbols.js',
     'index.js',
+    'mapBindings.js',
+    'mapExpression.js',
     'mapOriginalExpression.js',
     'pausePoints.js',
     'sources.js',
     'steps.js',
     'validate.js',
     'worker.js',
 )
--- a/devtools/client/debugger/new/src/workers/parser/worker.js
+++ b/devtools/client/debugger/new/src/workers/parser/worker.js
@@ -17,19 +17,19 @@ var _findOutOfScopeLocations2 = _interop
 var _steps = require("./steps");
 
 var _validate = require("./validate");
 
 var _frameworks = require("./frameworks");
 
 var _pausePoints = require("./pausePoints");
 
-var _mapOriginalExpression = require("./mapOriginalExpression");
+var _mapExpression = require("./mapExpression");
 
-var _mapOriginalExpression2 = _interopRequireDefault(_mapOriginalExpression);
+var _mapExpression2 = _interopRequireDefault(_mapExpression);
 
 var _devtoolsUtils = require("devtools/client/debugger/new/dist/vendors").vendored["devtools-utils"];
 
 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
 /* 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/>. */
@@ -45,10 +45,10 @@ self.onmessage = workerHandler({
   clearASTs: _ast.clearASTs,
   hasSource: _sources.hasSource,
   setSource: _sources.setSource,
   clearSources: _sources.clearSources,
   getNextStep: _steps.getNextStep,
   hasSyntaxError: _validate.hasSyntaxError,
   getFramework: _frameworks.getFramework,
   getPausePoints: _pausePoints.getPausePoints,
-  mapOriginalExpression: _mapOriginalExpression2.default
+  mapExpression: _mapExpression2.default
 });
\ No newline at end of file
--- a/devtools/client/debugger/new/test/mochitest/browser.ini
+++ b/devtools/client/debugger/new/test/mochitest/browser.ini
@@ -119,16 +119,17 @@ support-files =
   examples/doc-minified2.html
   examples/doc-on-load.html
   examples/doc-preview.html
   examples/doc-sourcemaps.html
   examples/doc-sourcemaps2.html
   examples/doc-sourcemaps3.html
   examples/doc-sourcemap-bogus.html
   examples/doc-sources.html
+  examples/doc-strict.html
   examples/doc-pause-points.html
   examples/doc-return-values.html
   examples/doc-wasm-sourcemaps.html
   examples/asm.js
   examples/async.js
   examples/bogus-map.js
   examples/entry.js
   examples/exceptions.js
@@ -169,16 +170,17 @@ skip-if = !e10s || verify # This test is
 [browser_dbg-breakpoint-skipping.js]
 [browser_dbg-call-stack.js]
 [browser_dbg-scopes.js]
 [browser_dbg-chrome-create.js]
 skip-if = (verify && !debug && (os == 'linux'))
 [browser_dbg-chrome-debugging.js]
 [browser_dbg-console.js]
 [browser_dbg-console-link.js]
+[browser_dbg-console-map-bindings.js]
 [browser_dbg-content-script-sources.js]
 skip-if = (os == "win" && ccov) # Bug 1424154
 [browser_dbg-debugger-buttons.js]
 [browser_dbg-editor-gutter.js]
 [browser_dbg-editor-select.js]
 [browser_dbg-editor-highlight.js]
 [browser_dbg-ember-quickstart.js]
 [browser_dbg-expressions.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-console-map-bindings.js
@@ -0,0 +1,45 @@
+// Return a promise with a reference to jsterm, opening the split
+// console if necessary.  This cleans up the split console pref so
+// it won't pollute other tests.
+function getSplitConsole(dbg) {
+  const { toolbox, win } = dbg;
+
+  if (!win) {
+    win = toolbox.win;
+  }
+
+  if (!toolbox.splitConsole) {
+    pressKey(dbg, "Escape");
+  }
+
+  return new Promise(resolve => {
+    toolbox.getPanelWhenReady("webconsole").then(() => {
+      ok(toolbox.splitConsole, "Split console is shown.");
+      let jsterm = toolbox.getPanel("webconsole").hud.jsterm;
+      resolve(jsterm);
+    });
+  });
+}
+
+async function evaluate(dbg, expression) {
+  const { toolbox } = dbg;
+  let jsterm = toolbox.getPanel("webconsole").hud.jsterm;
+  const msg = await jsterm.execute(expression);
+  return msg.innerText;
+}
+
+add_task(async function() {
+  Services.prefs.setBoolPref("devtools.toolbox.splitconsoleEnabled", true);
+  const dbg = await initDebugger("doc-strict.html");
+
+  await getSplitConsole(dbg);
+  ok(dbg.toolbox.splitConsole, "Split console is shown.");
+
+  invokeInTab("strict", 2);
+
+  await waitForPaused(dbg);
+  const msg = await evaluate(dbg, "var c = 3");
+  const msg2 = await evaluate(dbg, "c");
+
+  is(msg2, "3");
+});
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-preview-source-maps.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-preview-source-maps.js
@@ -44,17 +44,17 @@ add_task(async function() {
   await addBreakpoint(dbg, "times2", 2);
 
   invokeInTab("keepMeAlive");
   await waitForPaused(dbg);
   await waitForSelectedSource(dbg, "times2");
 
   info(`Test previewing in the original location`);
   await assertPreviews(dbg, [
-    { line: 2, column: 10, result: 4, expression: "x" }
+    { line: 2, column: 10, result: 4, expression: "x;" }
   ]);
 
   info(`Test previewing in the generated location`);
   await dbg.actions.jumpToMappedSelectedLocation();
   await waitForSelectedSource(dbg, "bundle.js");
   await assertPreviews(dbg, [
     { line: 70, column: 11, result: 4, expression: "x" }
   ]);
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-sourcemapped-preview.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-sourcemapped-preview.js
@@ -23,29 +23,29 @@ async function breakpointPreviews(dbg, f
   ok(true, `Ran tests for ${fixture} at line ${line} column ${column}`);
 }
 
 function testForOf(dbg) {
   return breakpointPreviews(dbg, "babel-for-of", { line: 5, column: 4 }, [
     {
       line: 5,
       column: 7,
-      expression: "doThing",
+      expression: "doThing;",
       result: "doThing(arg)"
     },
     {
       line: 5,
       column: 13,
-      expression: "x",
+      expression: "x;",
       result: "1"
     },
     {
       line: 8,
       column: 16,
-      expression: "doThing",
+      expression: "doThing;",
       result: "doThing(arg)"
     }
   ]);
 }
 
 function testShadowing(dbg) {
   return breakpointPreviews(
     dbg,
@@ -54,17 +54,17 @@ function testShadowing(dbg) {
     [
       // These aren't what the user would expect, but we test them anyway since
       // they reflect what this actually returns. These shadowed bindings read
       // the binding closest to the current frame's scope even though their
       // actual value is different.
       {
         line: 2,
         column: 9,
-        expression: "aVar",
+        expression: "aVar;",
         result: '"var3"'
       },
       {
         line: 3,
         column: 9,
         expression: "_aLet2;",
         result: '"let3"'
       },
@@ -72,17 +72,17 @@ function testShadowing(dbg) {
         line: 4,
         column: 11,
         expression: "_aConst2;",
         result: '"const3"'
       },
       {
         line: 10,
         column: 11,
-        expression: "aVar",
+        expression: "aVar;",
         result: '"var3"'
       },
       {
         line: 11,
         column: 11,
         expression: "_aLet2;",
         result: '"let3"'
       },
@@ -92,17 +92,17 @@ function testShadowing(dbg) {
         expression: "_aConst2;",
         result: '"const3"'
       },
 
       // These actually result in the values the user would expect.
       {
         line: 14,
         column: 13,
-        expression: "aVar",
+        expression: "aVar;",
         result: '"var3"'
       },
       {
         line: 15,
         column: 13,
         expression: "_aLet2;",
         result: '"let3"'
       },
@@ -140,17 +140,17 @@ function testImportedBindings(dbg) {
       line: 25,
       column: 16,
       expression: "_mod3.aNamed;",
       result: '"a-named"'
     },
     {
       line: 26,
       column: 16,
-      expression: "aNamespace",
+      expression: "aNamespace;",
       fields: [["aNamed", "a-named"], ["default", "a-default"]]
     },
     {
       line: 31,
       column: 20,
       expression: "_mod7.default;",
       result: '"a-default2"'
     },
@@ -170,17 +170,17 @@ function testImportedBindings(dbg) {
       line: 34,
       column: 20,
       expression: "_mod8.aNamed2;",
       result: '"a-named2"'
     },
     {
       line: 35,
       column: 20,
-      expression: "aNamespace2",
+      expression: "aNamespace2;",
       fields: [["aNamed", "a-named2"], ["default", "a-default2"]]
     }
   ]);
 }
 
 add_task(async function() {
   await pushPref("devtools.debugger.features.map-scopes", true);
   const dbg = await initDebugger("doc-sourcemapped.html");
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-tabs-pretty-print.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-tabs-pretty-print.js
@@ -10,9 +10,12 @@ add_task(async function() {
   clickElement(dbg, "prettyPrintButton");
   await waitForSource(dbg, "math.min.js:formatted");
   // Test reloading the debugger
   await waitForSelectedSource(dbg, "math.min.js:formatted");
   await reload(dbg);
 
   await waitForSelectedSource(dbg, "math.min.js:formatted");
   ok(true, "Pretty printed source is selected on reload");
+
+  const breakpointTab = findElementWithSelector(dbg, ".source-tab.active .filename");
+  is(breakpointTab.textContent, "math.min.js", ":formatted does not display in tab label");
 });
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/test/mochitest/examples/doc-strict.html
@@ -0,0 +1,20 @@
+ <!-- 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/. -->
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8"/>
+    <title>Debugger test page</title>
+  </head>
+
+  <body>
+    <script>
+      function strict(a) {
+        "use strict"
+        var b = 2;
+        debugger;
+      }
+    </script>
+  </body>
+</html>
--- a/devtools/client/debugger/test/mochitest/browser.ini
+++ b/devtools/client/debugger/test/mochitest/browser.ini
@@ -267,17 +267,17 @@ skip-if = e10s && debug
 [browser_dbg_console-eval.js]
 uses-unsafe-cpows = true
 skip-if = e10s && debug
 [browser_dbg_console-named-eval.js]
 uses-unsafe-cpows = true
 skip-if = e10s && debug
 [browser_dbg_server-conditional-bp-01.js]
 uses-unsafe-cpows = true
-skip-if = e10s && debug
+skip-if = e10s && debug || (os == "linux") # Bug 1468669
 [browser_dbg_server-conditional-bp-02.js]
 uses-unsafe-cpows = true
 skip-if = e10s && debug
 [browser_dbg_server-conditional-bp-03.js]
 uses-unsafe-cpows = true
 skip-if = e10s && debug
 [browser_dbg_server-conditional-bp-04.js]
 uses-unsafe-cpows = true
--- a/devtools/client/inspector/markup/views/slotted-node-editor.js
+++ b/devtools/client/inspector/markup/views/slotted-node-editor.js
@@ -7,17 +7,17 @@
 const {LocalizationHelper} = require("devtools/shared/l10n");
 const INSPECTOR_L10N =
       new LocalizationHelper("devtools/client/locales/inspector.properties");
 
 function SlottedNodeEditor(container, node) {
   this.container = container;
   this.markup = this.container.markup;
   this.buildMarkup();
-  this.tag.textContent = "→ <" + node.nodeName.toLowerCase() + ">";
+  this.tag.textContent = "<" + node.nodeName.toLowerCase() + ">";
 
   // Make the "tag" part of this editor focusable.
   this.tag.setAttribute("tabindex", "-1");
 }
 
 SlottedNodeEditor.prototype = {
   buildMarkup: function() {
     const doc = this.markup.doc;
@@ -26,17 +26,17 @@ SlottedNodeEditor.prototype = {
     this.elt.classList.add("editor");
 
     this.tag = doc.createElement("span");
     this.tag.classList.add("tag");
     this.elt.appendChild(this.tag);
 
     this.revealLink = doc.createElement("span");
     this.revealLink.classList.add("reveal-link");
-    this.revealLink.textContent = INSPECTOR_L10N.getStr("markupView.revealLink.label");
+    this.revealLink.title = INSPECTOR_L10N.getStr("markupView.revealLink.tooltip");
     this.elt.appendChild(this.revealLink);
   },
 
   destroy: function() {
     // We might be already destroyed.
     if (!this.elt) {
       return;
     }
--- a/devtools/client/inspector/shared/highlighters-overlay.js
+++ b/devtools/client/inspector/shared/highlighters-overlay.js
@@ -280,18 +280,19 @@ class HighlightersOverlay {
     }
 
     this._toggleRuleViewIcon(node, false, ".ruleview-flex");
 
     await this.highlighters.FlexboxHighlighter.hide();
 
     // Emit the NodeFront of the flexbox container element that the flexbox highlighter
     // was hidden for.
-    this.emit("flexbox-highlighter-hidden", this.flexboxHighlighterShown);
+    const nodeFront = this.flexboxHighlighterShown;
     this.flexboxHighlighterShown = null;
+    this.emit("flexbox-highlighter-hidden", nodeFront);
 
     // Erase flexbox highlighter state.
     this.state.flexbox = null;
   }
 
   /**
    * Create a grid highlighter settings object for the provided nodeFront.
    *
--- a/devtools/client/jar.mn
+++ b/devtools/client/jar.mn
@@ -252,22 +252,24 @@ devtools.jar:
     skin/images/security-state-secure.svg (themes/images/security-state-secure.svg)
     skin/images/security-state-weak.svg (themes/images/security-state-weak.svg)
     skin/images/diff.svg (themes/images/diff.svg)
     skin/images/import.svg (themes/images/import.svg)
     skin/images/pane-collapse.svg (themes/images/pane-collapse.svg)
     skin/images/pane-expand.svg (themes/images/pane-expand.svg)
     skin/images/help.svg (themes/images/help.svg)
     skin/images/read-only.svg (themes/images/read-only.svg)
+    skin/images/reveal.svg (themes/images/reveal.svg)
 
     # Debugger
     skin/images/debugger/angular.svg (themes/images/debugger/angular.svg)
     skin/images/debugger/arrow.svg (themes/images/debugger/arrow.svg)
     skin/images/debugger/back.svg (themes/images/debugger/back.svg)
     skin/images/debugger/blackBox.svg (themes/images/debugger/blackBox.svg)
+    skin/images/debugger/breakpoint.svg (themes/images/debugger/breakpoint.svg)
     skin/images/debugger/close.svg (themes/images/debugger/close.svg)
     skin/images/debugger/coffeescript.svg (themes/images/debugger/coffeescript.svg)
     skin/images/debugger/disable-pausing.svg (themes/images/debugger/disable-pausing.svg)
     skin/images/debugger/domain.svg (themes/images/debugger/domain.svg)
     skin/images/debugger/extension.svg (themes/images/debugger/extension.svg)
     skin/images/debugger/file.svg (themes/images/debugger/file.svg)
     skin/images/debugger/folder.svg (themes/images/debugger/folder.svg)
     skin/images/debugger/forward.svg (themes/images/debugger/forward.svg)
--- a/devtools/client/locales/en-US/inspector.properties
+++ b/devtools/client/locales/en-US/inspector.properties
@@ -78,21 +78,22 @@ markupView.display.contents.tooltiptext2=This element doesn’t produce a specific box by itself, but renders its contents.
 # the markup view.
 markupView.event.tooltiptext=Event listener
 
 # LOCALIZATION NOTE (markupView.newAttribute.label)
 # This is used to speak the New Attribute button when editing a tag
 # and a screen reader user tabs to it. This string is not visible onscreen.
 markupView.newAttribute.label=New attribute
 
-# LOCALIZATION NOTE (markupView.revealLink.label)
-# Used in the markup view when displaying elements inserted in <slot> nodes in a custom
-# component. On hover, a link with this label will be shown to select the corresponding
-# non-slotted container. (test with dom.webcomponents.shadowdom.enabled set to true)
-markupView.revealLink.label=reveal
+# LOCALIZATION NOTE (markupView.revealLink.tooltip)
+# Used as a tooltip for an icon in the markup view when displaying elements inserted in
+# <slot> nodes in a custom  component. When clicking on the icon, the corresponding
+# non-slotted container will be selected
+# (test with dom.webcomponents.shadowdom.enabled set to true)
+markupView.revealLink.tooltip=Reveal
 
 #LOCALIZATION NOTE: Used in the image preview tooltip when the image could not be loaded
 previewTooltip.image.brokenImage=Could not load the image
 
 # LOCALIZATION NOTE: Used in color picker tooltip when the eyedropper is disabled for
 # non-HTML documents
 eyedropper.disabled.title=Unavailable in non-HTML documents
 
--- a/devtools/client/preferences/debugger.js
+++ b/devtools/client/preferences/debugger.js
@@ -61,8 +61,9 @@ pref("devtools.debugger.features.event-l
 pref("devtools.debugger.features.code-folding", false);
 pref("devtools.debugger.features.outline", true);
 pref("devtools.debugger.features.replay", false);
 pref("devtools.debugger.features.pause-points", true);
 pref("devtools.debugger.features.component-stack", false);
 pref("devtools.debugger.features.async-stepping", true);
 pref("devtools.debugger.features.skip-pausing", true);
 pref("devtools.debugger.features.autocomplete-expressions", false);
+pref("devtools.debugger.features.map-expression-bindings", true);
new file mode 100644
--- /dev/null
+++ b/devtools/client/themes/images/debugger/breakpoint.svg
@@ -0,0 +1,6 @@
+<!-- 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/. -->
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 60 12">
+  <path id="base-path" d="M53.9,0H1C0.4,0,0,0.4,0,1v10c0,0.6,0.4,1,1,1h52.9c0.6,0,1.2-0.3,1.5-0.7L60,6l-4.4-5.3C55,0.3,54.5,0,53.9,0z"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/devtools/client/themes/images/reveal.svg
@@ -0,0 +1,10 @@
+<!-- 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/. -->
+
+<svg width="16" height="8" xmlns="http://www.w3.org/2000/svg" stroke="context-stroke" fill="none" stroke-linecap="round">
+  <g transform="rotate(90 8,8)">
+    <path d="M5.5 3.5l2 2M5.5 7.5l2-2"/>
+    <path d="M7 5.5H4.006c-1.012 0-1.995 1.017-2.011 2.024-.005.023-.005 1.347 0 3.971" stroke-linejoin="round"/>
+  </g>
+</svg>
\ No newline at end of file
--- a/devtools/client/themes/markup.css
+++ b/devtools/client/themes/markup.css
@@ -327,27 +327,24 @@ ul.children + .tag-line::before {
   padding-left: 16px;
 }
 
 .styleinspector-propertyeditor {
   border: 1px solid #CCC;
 }
 
 .reveal-link {
-  margin-inline-start: 10px;
   cursor: pointer;
-  display: none;
-}
+  background: url("chrome://devtools/skin/images/reveal.svg") no-repeat;
+  display: inline-block;
+  width: 16px;
+  height: 8px;
 
-.reveal-link:hover {
-  text-decoration: underline
-}
-
-.tag-line:hover .reveal-link {
-  display: inline;
+  -moz-context-properties: stroke;
+  stroke: currentColor;
 }
 
 /* Draw a circle next to nodes that have a pseudo class lock.
    Center vertically with the 1.4em line height on .tag-line */
 .child.pseudoclass-locked::before {
   content: "";
   background: var(--theme-highlight-lightorange);
   border-radius: 50%;
--- a/docshell/shistory/nsSHistory.cpp
+++ b/docshell/shistory/nsSHistory.cpp
@@ -150,33 +150,40 @@ enum HistCmd
 class nsSHistoryObserver final : public nsIObserver
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIOBSERVER
 
   nsSHistoryObserver() {}
 
+  void PrefChanged(const char* aPref);
+
 protected:
   ~nsSHistoryObserver() {}
 };
 
 StaticRefPtr<nsSHistoryObserver> gObserver;
 
 NS_IMPL_ISUPPORTS(nsSHistoryObserver, nsIObserver)
 
+void
+nsSHistoryObserver::PrefChanged(const char* aPref)
+{
+  nsSHistory::UpdatePrefs();
+  nsSHistory::GloballyEvictContentViewers();
+
+}
+
 NS_IMETHODIMP
 nsSHistoryObserver::Observe(nsISupports* aSubject, const char* aTopic,
                             const char16_t* aData)
 {
-  if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
-    nsSHistory::UpdatePrefs();
-    nsSHistory::GloballyEvictContentViewers();
-  } else if (!strcmp(aTopic, "cacheservice:empty-cache") ||
-             !strcmp(aTopic, "memory-pressure")) {
+  if (!strcmp(aTopic, "cacheservice:empty-cache") ||
+      !strcmp(aTopic, "memory-pressure")) {
     nsSHistory::GloballyEvictAllContentViewers();
   }
 
   return NS_OK;
 }
 
 namespace {
 
@@ -346,17 +353,19 @@ nsSHistory::Startup()
   if (gHistoryMaxSize < defaultHistoryMaxSize) {
     gHistoryMaxSize = defaultHistoryMaxSize;
   }
 
   // Allow the user to override the max total number of cached viewers,
   // but keep the per SHistory cached viewer limit constant
   if (!gObserver) {
     gObserver = new nsSHistoryObserver();
-    Preferences::AddStrongObservers(gObserver, kObservedPrefs);
+    Preferences::RegisterCallbacks(
+        PREF_CHANGE_METHOD(nsSHistoryObserver::PrefChanged),
+        kObservedPrefs, gObserver.get());
 
     nsCOMPtr<nsIObserverService> obsSvc =
       mozilla::services::GetObserverService();
     if (obsSvc) {
       // Observe empty-cache notifications so tahat clearing the disk/memory
       // cache will also evict all content viewers.
       obsSvc->AddObserver(gObserver, "cacheservice:empty-cache", false);
 
@@ -368,17 +377,20 @@ nsSHistory::Startup()
   return NS_OK;
 }
 
 // static
 void
 nsSHistory::Shutdown()
 {
   if (gObserver) {
-    Preferences::RemoveObservers(gObserver, kObservedPrefs);
+    Preferences::UnregisterCallbacks(
+        PREF_CHANGE_METHOD(nsSHistoryObserver::PrefChanged),
+        kObservedPrefs, gObserver.get());
+
     nsCOMPtr<nsIObserverService> obsSvc =
       mozilla::services::GetObserverService();
     if (obsSvc) {
       obsSvc->RemoveObserver(gObserver, "cacheservice:empty-cache");
       obsSvc->RemoveObserver(gObserver, "memory-pressure");
     }
     gObserver = nullptr;
   }
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -8850,28 +8850,27 @@ nsContentUtils::IsTrackingResourceWindow
     do_QueryInterface(document->GetChannel());
   if (!httpChannel) {
     return false;
   }
 
   return httpChannel->GetIsTrackingResource();
 }
 
-// static public
-bool
-nsContentUtils::StorageDisabledByAntiTracking(nsPIDOMWindowInner* aWindow,
-                                              nsIChannel* aChannel,
-                                              nsIURI* aURI)
+static bool
+StorageDisabledByAntiTrackingInternal(nsPIDOMWindowInner* aWindow,
+                                      nsIChannel* aChannel,
+                                      nsIURI* aURI)
 {
   if (!StaticPrefs::privacy_restrict3rdpartystorage_enabled()) {
     return false;
   }
 
   // Let's check if this is a 3rd party context.
-  if (!IsThirdPartyWindowOrChannel(aWindow, aChannel, aURI)) {
+  if (!nsContentUtils::IsThirdPartyWindowOrChannel(aWindow, aChannel, aURI)) {
     return false;
   }
 
   if (aWindow) {
     nsIURI* documentURI = aURI ? aURI : aWindow->GetDocumentURI();
     if (documentURI &&
         AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor(aWindow,
                                                                 documentURI)) {
@@ -8899,16 +8898,43 @@ nsContentUtils::StorageDisabledByAntiTra
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return false;
   }
 
   return AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor(httpChannel,
                                                                  uri);
 }
 
+// static public
+bool
+nsContentUtils::StorageDisabledByAntiTracking(nsPIDOMWindowInner* aWindow,
+                                              nsIChannel* aChannel,
+                                              nsIURI* aURI)
+{
+  bool disabled =
+    StorageDisabledByAntiTrackingInternal(aWindow, aChannel, aURI);
+  if (disabled &&
+      StaticPrefs::privacy_restrict3rdpartystorage_ui_enabled()) {
+    nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil = services::GetThirdPartyUtil();
+    if (!thirdPartyUtil) {
+      return false;
+    }
+
+    nsCOMPtr<mozIDOMWindowProxy> win;
+    nsresult rv = thirdPartyUtil->GetTopWindowForChannel(aChannel,
+                                                         getter_AddRefs(win));
+    NS_ENSURE_SUCCESS(rv, false);
+    auto* pwin = nsPIDOMWindowOuter::From(win);
+
+    pwin->NotifyContentBlockingState(
+      nsIWebProgressListener::STATE_BLOCKED_TRACKING_COOKIES, aChannel);
+  }
+  return disabled;
+}
+
 // static, private
 nsContentUtils::StorageAccess
 nsContentUtils::InternalStorageAllowedForPrincipal(nsIPrincipal* aPrincipal,
                                                    nsPIDOMWindowInner* aWindow,
                                                    nsIURI* aURI,
                                                    nsIChannel* aChannel)
 {
   MOZ_ASSERT(aPrincipal);
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -12597,64 +12597,63 @@ ArrayContainsTable(const nsTArray<nsCStr
       return true;
     }
   }
   return false;
 }
 
 namespace {
 
+static const char* gCallbackPrefs[] = {
+  // We only need to register string-typed preferences.
+  "urlclassifier.flashAllowTable",
+  "urlclassifier.flashAllowExceptTable",
+  "urlclassifier.flashTable",
+  "urlclassifier.flashExceptTable",
+  "urlclassifier.flashSubDocTable",
+  "urlclassifier.flashSubDocExceptTable",
+  nullptr,
+};
+
 // An object to store all preferences we need for flash blocking feature.
 struct PrefStore
 {
   PrefStore()
     : mFlashBlockEnabled(false)
     , mPluginsHttpOnly(false)
   {
     Preferences::AddBoolVarCache(&mFlashBlockEnabled,
                                  "plugins.flashBlock.enabled");
     Preferences::AddBoolVarCache(&mPluginsHttpOnly,
                                  "plugins.http_https_only");
 
-    // We only need to register string-typed preferences.
-    Preferences::RegisterCallback(UpdateStringPrefs, "urlclassifier.flashAllowTable", this);
-    Preferences::RegisterCallback(UpdateStringPrefs, "urlclassifier.flashAllowExceptTable", this);
-    Preferences::RegisterCallback(UpdateStringPrefs, "urlclassifier.flashTable", this);
-    Preferences::RegisterCallback(UpdateStringPrefs, "urlclassifier.flashExceptTable", this);
-    Preferences::RegisterCallback(UpdateStringPrefs, "urlclassifier.flashSubDocTable", this);
-    Preferences::RegisterCallback(UpdateStringPrefs, "urlclassifier.flashSubDocExceptTable", this);
+    Preferences::RegisterCallbacks(
+      PREF_CHANGE_METHOD(PrefStore::UpdateStringPrefs),
+      gCallbackPrefs, this);
 
     UpdateStringPrefs();
   }
 
   ~PrefStore()
   {
-    Preferences::UnregisterCallback(UpdateStringPrefs, "urlclassifier.flashAllowTable", this);
-    Preferences::UnregisterCallback(UpdateStringPrefs, "urlclassifier.flashAllowExceptTable", this);
-    Preferences::UnregisterCallback(UpdateStringPrefs, "urlclassifier.flashTable", this);
-    Preferences::UnregisterCallback(UpdateStringPrefs, "urlclassifier.flashExceptTable", this);
-    Preferences::UnregisterCallback(UpdateStringPrefs, "urlclassifier.flashSubDocTable", this);
-    Preferences::UnregisterCallback(UpdateStringPrefs, "urlclassifier.flashSubDocExceptTable", this);
-  }
-
-  void UpdateStringPrefs()
+    Preferences::UnregisterCallbacks(
+      PREF_CHANGE_METHOD(PrefStore::UpdateStringPrefs),
+      gCallbackPrefs, this);
+  }
+
+  void UpdateStringPrefs(const char* aPref = nullptr)
   {
     Preferences::GetCString("urlclassifier.flashAllowTable", mAllowTables);
     Preferences::GetCString("urlclassifier.flashAllowExceptTable", mAllowExceptionsTables);
     Preferences::GetCString("urlclassifier.flashTable", mDenyTables);
     Preferences::GetCString("urlclassifier.flashExceptTable", mDenyExceptionsTables);
     Preferences::GetCString("urlclassifier.flashSubDocTable", mSubDocDenyTables);
     Preferences::GetCString("urlclassifier.flashSubDocExceptTable", mSubDocDenyExceptionsTables);
   }
 
-  static void UpdateStringPrefs(const char*, void* aClosure)
-  {
-    static_cast<PrefStore*>(aClosure)->UpdateStringPrefs();
-  }
-
   bool mFlashBlockEnabled;
   bool mPluginsHttpOnly;
 
   nsCString mAllowTables;
   nsCString mAllowExceptionsTables;
   nsCString mDenyTables;
   nsCString mDenyExceptionsTables;
   nsCString mSubDocDenyTables;
--- a/dom/base/nsFocusManager.cpp
+++ b/dom/base/nsFocusManager.cpp
@@ -183,17 +183,19 @@ static const char* kObservedPrefs[] = {
 };
 
 nsFocusManager::nsFocusManager()
   : mEventHandlingNeedsFlush(false)
 { }
 
 nsFocusManager::~nsFocusManager()
 {
-  Preferences::RemoveObservers(this, kObservedPrefs);
+  Preferences::UnregisterCallbacks(
+    PREF_CHANGE_METHOD(nsFocusManager::PrefChanged),
+    kObservedPrefs, this);
 
   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   if (obs) {
     obs->RemoveObserver(this, "xpcom-shutdown");
   }
 }
 
 // static
@@ -208,57 +210,63 @@ nsFocusManager::Init()
     Preferences::GetBool("accessibility.tabfocus_applies_to_xul",
                          nsIContent::sTabFocusModelAppliesToXUL);
 
   sMouseFocusesFormControl =
     Preferences::GetBool("accessibility.mouse_focuses_formcontrol", false);
 
   sTestMode = Preferences::GetBool("focusmanager.testmode", false);
 
-  Preferences::AddWeakObservers(fm, kObservedPrefs);
+  Preferences::RegisterCallbacks(
+    PREF_CHANGE_METHOD(nsFocusManager::PrefChanged),
+    kObservedPrefs, fm);
 
   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   if (obs) {
     obs->AddObserver(fm, "xpcom-shutdown", true);
   }
 
   return NS_OK;
 }
 
 // static
 void
 nsFocusManager::Shutdown()
 {
   NS_IF_RELEASE(sInstance);
 }
 
+void
+nsFocusManager::PrefChanged(const char* aPref)
+{
+  nsDependentCString pref(aPref);
+  if (pref.EqualsLiteral("accessibility.browsewithcaret")) {
+    UpdateCaretForCaretBrowsingMode();
+  }
+  else if (pref.EqualsLiteral("accessibility.tabfocus_applies_to_xul")) {
+    nsIContent::sTabFocusModelAppliesToXUL =
+      Preferences::GetBool("accessibility.tabfocus_applies_to_xul",
+                           nsIContent::sTabFocusModelAppliesToXUL);
+  }
+  else if (pref.EqualsLiteral("accessibility.mouse_focuses_formcontrol")) {
+    sMouseFocusesFormControl =
+      Preferences::GetBool("accessibility.mouse_focuses_formcontrol",
+                           false);
+  }
+  else if (pref.EqualsLiteral("focusmanager.testmode")) {
+    sTestMode = Preferences::GetBool("focusmanager.testmode", false);
+  }
+}
+
 NS_IMETHODIMP
 nsFocusManager::Observe(nsISupports *aSubject,
                         const char *aTopic,
                         const char16_t *aData)
 {
-  if (!nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
-    nsDependentString data(aData);
-    if (data.EqualsLiteral("accessibility.browsewithcaret")) {
-      UpdateCaretForCaretBrowsingMode();
-    }
-    else if (data.EqualsLiteral("accessibility.tabfocus_applies_to_xul")) {
-      nsIContent::sTabFocusModelAppliesToXUL =
-        Preferences::GetBool("accessibility.tabfocus_applies_to_xul",
-                             nsIContent::sTabFocusModelAppliesToXUL);
-    }
-    else if (data.EqualsLiteral("accessibility.mouse_focuses_formcontrol")) {
-      sMouseFocusesFormControl =
-        Preferences::GetBool("accessibility.mouse_focuses_formcontrol",
-                             false);
-    }
-    else if (data.EqualsLiteral("focusmanager.testmode")) {
-      sTestMode = Preferences::GetBool("focusmanager.testmode", false);
-    }
-  } else if (!nsCRT::strcmp(aTopic, "xpcom-shutdown")) {
+  if (!nsCRT::strcmp(aTopic, "xpcom-shutdown")) {
     mActiveWindow = nullptr;
     mFocusedWindow = nullptr;
     mFocusedElement = nullptr;
     mFirstBlurEvent = nullptr;
     mFirstFocusEvent = nullptr;
     mWindowBeingLowered = nullptr;
     mDelayedBlurFocusEvents.Clear();
     mMouseButtonEventHandlingDocument = nullptr;
--- a/dom/base/nsFocusManager.h
+++ b/dom/base/nsFocusManager.h
@@ -52,16 +52,18 @@ public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_NSIOBSERVER
   NS_DECL_NSIFOCUSMANAGER
 
   // called to initialize and stop the focus manager at startup and shutdown
   static nsresult Init();
   static void Shutdown();
 
+  void PrefChanged(const char* aPref);
+
   /**
    * Retrieve the single focus manager.
    */
   static nsFocusManager* GetFocusManager() { return sInstance; }
 
   /**
    * A faster version of nsIFocusManager::GetFocusedElement, returning a
    * raw Element pointer (instead of having AddRef-ed Element
--- a/dom/base/nsGlobalWindowOuter.cpp
+++ b/dom/base/nsGlobalWindowOuter.cpp
@@ -12,16 +12,18 @@
 
 // Local Includes
 #include "Navigator.h"
 #include "nsContentSecurityManager.h"
 #include "nsScreen.h"
 #include "nsHistory.h"
 #include "nsDOMNavigationTiming.h"
 #include "nsIDOMStorageManager.h"
+#include "nsISecureBrowserUI.h"
+#include "nsIWebProgressListener.h"
 #include "mozilla/AntiTrackingCommon.h"
 #include "mozilla/dom/EventTarget.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"
@@ -5337,16 +5339,86 @@ nsGlobalWindowOuter::FirePopupBlockedEve
                                    NS_LITERAL_STRING("DOMPopupBlocked"),
                                    init);
 
   event->SetTrusted(true);
 
   aDoc->DispatchEvent(*event);
 }
 
+void
+nsGlobalWindowOuter::NotifyContentBlockingState(unsigned aState,
+                                                nsIChannel* aChannel)
+{
+  nsCOMPtr<nsIDocShell> docShell = GetDocShell();
+  if (!docShell) {
+    return;
+  }
+  nsCOMPtr<nsIDocument> doc = docShell->GetDocument();
+  NS_ENSURE_TRUE_VOID(doc);
+
+  // This event might come after the user has navigated to another page.
+  // To prevent showing the TrackingProtection UI on the wrong page, we need to
+  // check that the loading URI for the channel is the same as the URI currently
+  // loaded in the document.
+  if (!SameLoadingURI(doc, aChannel)) {
+    return;
+  }
+
+  // Notify nsIWebProgressListeners of this security event.
+  // Can be used to change the UI state.
+  nsresult rv = NS_OK;
+  nsCOMPtr<nsISecurityEventSink> eventSink = do_QueryInterface(docShell, &rv);
+  NS_ENSURE_SUCCESS_VOID(rv);
+  uint32_t state = 0;
+  nsCOMPtr<nsISecureBrowserUI> securityUI;
+  docShell->GetSecurityUI(getter_AddRefs(securityUI));
+  if (!securityUI) {
+    return;
+  }
+  securityUI->GetState(&state);
+  if (aState == nsIWebProgressListener::STATE_BLOCKED_TRACKING_CONTENT) {
+    doc->SetHasTrackingContentBlocked(true);
+  } else if (aState == nsIWebProgressListener::STATE_BLOCKED_TRACKING_COOKIES) {
+    doc->SetHasTrackingCookiesBlocked(true);
+  } else {
+    // Ignore nsIWebProgressListener::STATE_BLOCKED_UNSAFE_CONTENT;
+  }
+  state |= aState;
+
+  eventSink->OnSecurityChange(aChannel, state);
+}
+
+//static
+bool
+nsGlobalWindowOuter::SameLoadingURI(nsIDocument *aDoc, nsIChannel *aChannel)
+{
+  nsCOMPtr<nsIURI> docURI = aDoc->GetDocumentURI();
+  nsCOMPtr<nsILoadInfo> channelLoadInfo = aChannel->GetLoadInfo();
+  if (!channelLoadInfo || !docURI) {
+    return false;
+  }
+
+  nsCOMPtr<nsIPrincipal> channelLoadingPrincipal = channelLoadInfo->LoadingPrincipal();
+  if (!channelLoadingPrincipal) {
+    // TYPE_DOCUMENT loads will not have a channelLoadingPrincipal. But top level
+    // loads should not be blocked by Tracking Protection, so we will return
+    // false
+    return false;
+  }
+  nsCOMPtr<nsIURI> channelLoadingURI;
+  channelLoadingPrincipal->GetURI(getter_AddRefs(channelLoadingURI));
+  if (!channelLoadingURI) {
+    return false;
+  }
+  bool equals = false;
+  nsresult rv = docURI->EqualsExceptRef(channelLoadingURI, &equals);
+  return NS_SUCCEEDED(rv) && equals;
+}
+
 // static
 bool
 nsGlobalWindowOuter::CanSetProperty(const char *aPrefName)
 {
   // Chrome can set any property.
   if (nsContentUtils::LegacyIsCallerChromeOrNativeCode()) {
     return true;
   }
--- a/dom/base/nsGlobalWindowOuter.h
+++ b/dom/base/nsGlobalWindowOuter.h
@@ -486,16 +486,20 @@ public:
   bool IsTopLevelWindow();
 
   virtual void
   FirePopupBlockedEvent(nsIDocument* aDoc,
                         nsIURI* aPopupURI,
                         const nsAString& aPopupWindowName,
                         const nsAString& aPopupWindowFeatures) override;
 
+  virtual void
+  NotifyContentBlockingState(unsigned aState,
+                             nsIChannel* aChannel) override;
+
   virtual uint32_t GetSerial() override {
     return mSerial;
   }
 
   void AddSizeOfIncludingThis(nsWindowSizes& aWindowSizes) const;
 
   void AllowScriptsToClose()
   {
@@ -896,16 +900,19 @@ private:
                         bool aDoJSFixups,
                         bool aNavigate,
                         nsIArray *argv,
                         nsISupports *aExtraArgument,
                         nsDocShellLoadInfo* aLoadInfo,
                         bool aForceNoOpener,
                         nsPIDOMWindowOuter **aReturn);
 
+  // Checks that the channel was loaded by the URI currently loaded in aDoc
+  static bool SameLoadingURI(nsIDocument *aDoc, nsIChannel *aChannel);
+
 public:
   // Helper Functions
   already_AddRefed<nsIDocShellTreeOwner> GetTreeOwner();
   already_AddRefed<nsIBaseWindow> GetTreeOwnerWindow();
   already_AddRefed<nsIWebBrowserChrome> GetWebBrowserChrome();
   nsresult SecurityCheckURL(const char *aURL, nsIURI** aURI);
   bool IsPrivateBrowsing();
 
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -988,24 +988,40 @@ public:
    * Get tracking content blocked flag for this document.
    */
   bool GetHasTrackingContentBlocked()
   {
     return mHasTrackingContentBlocked;
   }
 
   /**
+   * Get tracking cookies blocked flag for this document.
+   */
+  bool GetHasTrackingCookiesBlocked()
+  {
+    return mHasTrackingCookiesBlocked;
+  }
+
+  /**
    * Set the tracking content blocked flag for this document.
    */
   void SetHasTrackingContentBlocked(bool aHasTrackingContentBlocked)
   {
     mHasTrackingContentBlocked = aHasTrackingContentBlocked;
   }
 
   /**
+   * Set the tracking cookies blocked flag for this document.
+   */
+  void SetHasTrackingCookiesBlocked(bool aHasTrackingCookiesBlocked)
+  {
+    mHasTrackingCookiesBlocked = aHasTrackingCookiesBlocked;
+  }
+
+  /**
    * Get tracking content loaded flag for this document.
    */
   bool GetHasTrackingContentLoaded()
   {
     return mHasTrackingContentLoaded;
   }
 
   /**
@@ -3900,16 +3916,19 @@ protected:
   bool mHasUnsafeEvalCSP : 1;
 
   // True if a document load has a CSP with unsafe-inline attached.
   bool mHasUnsafeInlineCSP : 1;
 
   // True if a document has blocked Tracking Content
   bool mHasTrackingContentBlocked : 1;
 
+  // True if a document has blocked Tracking Cookies
+  bool mHasTrackingCookiesBlocked : 1;
+
   // True if a document has loaded Tracking Content
   bool mHasTrackingContentLoaded : 1;
 
   // True if DisallowBFCaching has been called on this document.
   bool mBFCacheDisallowed : 1;
 
   bool mHasHadDefaultView : 1;
 
--- a/dom/base/nsNameSpaceManager.cpp
+++ b/dom/base/nsNameSpaceManager.cpp
@@ -58,20 +58,21 @@ bool nsNameSpaceManager::Init()
 #define REGISTER_NAMESPACE(uri, id) \
   rv = AddNameSpace(dont_AddRef(uri), id); \
   NS_ENSURE_SUCCESS(rv, false)
 
 #define REGISTER_DISABLED_NAMESPACE(uri, id) \
   rv = AddDisabledNameSpace(dont_AddRef(uri), id); \
   NS_ENSURE_SUCCESS(rv, false)
 
-  mozilla::Preferences::AddStrongObservers(this, kObservedPrefs);
-  mMathMLDisabled = mozilla::Preferences::GetBool(kPrefMathMLDisabled);
-  mSVGDisabled = mozilla::Preferences::GetBool(kPrefSVGDisabled);
+  mozilla::Preferences::RegisterCallbacks(
+    PREF_CHANGE_METHOD(nsNameSpaceManager::PrefChanged),
+    kObservedPrefs, this);
 
+  PrefChanged(nullptr);
 
   // Need to be ordered according to ID.
   MOZ_ASSERT(mURIArray.IsEmpty());
   REGISTER_NAMESPACE(nsGkAtoms::_empty, kNameSpaceID_None);
   REGISTER_NAMESPACE(nsGkAtoms::nsuri_xmlns, kNameSpaceID_XMLNS);
   REGISTER_NAMESPACE(nsGkAtoms::nsuri_xml, kNameSpaceID_XML);
   REGISTER_NAMESPACE(nsGkAtoms::nsuri_xhtml, kNameSpaceID_XHTML);
   REGISTER_NAMESPACE(nsGkAtoms::nsuri_xlink, kNameSpaceID_XLink);
@@ -269,20 +270,16 @@ nsNameSpaceManager::AddDisabledNameSpace
   MOZ_ASSERT(aNameSpaceID == (int32_t) mURIArray.Length());
   mURIArray.AppendElement(uri.forget());
   mDisabledURIToIDTable.Put(mURIArray.LastElement(), aNameSpaceID);
 
   return NS_OK;
 }
 
 // nsISupports
-NS_IMPL_ISUPPORTS(nsNameSpaceManager,
-                  nsIObserver)
+NS_IMPL_ISUPPORTS0(nsNameSpaceManager)
 
-// nsIObserver
-NS_IMETHODIMP
-nsNameSpaceManager::Observe(nsISupports* aObject, const char* aTopic,
-                            const char16_t* aMessage)
+void
+nsNameSpaceManager::PrefChanged(const char* aPref)
 {
   mMathMLDisabled = mozilla::Preferences::GetBool(kPrefMathMLDisabled);
   mSVGDisabled = mozilla::Preferences::GetBool(kPrefSVGDisabled);
-  return NS_OK;
 }
--- a/dom/base/nsNameSpaceManager.h
+++ b/dom/base/nsNameSpaceManager.h
@@ -6,17 +6,16 @@
 
 #ifndef nsNameSpaceManager_h___
 #define nsNameSpaceManager_h___
 
 #include "nsDataHashtable.h"
 #include "nsHashKeys.h"
 #include "nsAtom.h"
 #include "nsIDocument.h"
-#include "nsIObserver.h"
 #include "nsStringFwd.h"
 #include "nsTArray.h"
 
 #include "mozilla/StaticPtr.h"
 
 /**
  * The Name Space Manager tracks the association between a NameSpace
  * URI and the int32_t runtime id. Mappings between NameSpaces and
@@ -26,21 +25,20 @@
  * consistent accross the app. NameSpace IDs are only consistent at runtime
  * ie: they are not guaranteed to be consistent accross app sessions.
  *
  * The nsNameSpaceManager needs to have a live reference for as long as
  * the NameSpace IDs are needed.
  *
  */
 
-class nsNameSpaceManager final : public nsIObserver
+class nsNameSpaceManager final : public nsISupports
 {
 public:
   NS_DECL_ISUPPORTS
-  NS_DECL_NSIOBSERVER
   virtual nsresult RegisterNameSpace(const nsAString& aURI,
                                      int32_t& aNameSpaceID);
   nsresult RegisterNameSpace(already_AddRefed<nsAtom> aURI,
                              int32_t& aNameSpaceID);
 
   virtual nsresult GetNameSpaceURI(int32_t aNameSpaceID, nsAString& aURI);
 
   // Returns the atom for the namespace URI associated with the given ID. The
@@ -62,16 +60,18 @@ public:
 
   bool HasElementCreator(int32_t aNameSpaceID);
 
   static nsNameSpaceManager* GetInstance();
   bool mMathMLDisabled;
   bool mSVGDisabled;
 
 private:
+  void PrefChanged(const char* aPref);
+
   bool Init();
   nsresult AddNameSpace(already_AddRefed<nsAtom> aURI, const int32_t aNameSpaceID);
   nsresult AddDisabledNameSpace(already_AddRefed<nsAtom> aURI, const int32_t aNameSpaceID);
   ~nsNameSpaceManager() {};
 
   nsDataHashtable<nsRefPtrHashKey<nsAtom>, int32_t> mURIToIDTable;
   nsDataHashtable<nsRefPtrHashKey<nsAtom>, int32_t> mDisabledURIToIDTable;
   nsTArray<RefPtr<nsAtom>> mURIArray;
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -22,16 +22,17 @@
 #define DOM_WINDOW_FROZEN_TOPIC "dom-window-frozen"
 #define DOM_WINDOW_THAWED_TOPIC "dom-window-thawed"
 
 class nsDOMOfflineResourceList;
 class nsDOMWindowList;
 class nsGlobalWindowInner;
 class nsGlobalWindowOuter;
 class nsIArray;
+class nsIChannel;
 class nsIContent;
 class nsICSSDeclaration;
 class nsIDocShell;
 class nsDocShellLoadInfo;
 class nsIDocument;
 class nsIIdleObserver;
 class nsIPrincipal;
 class nsIScriptTimeoutHandler;
@@ -1094,16 +1095,20 @@ public:
    * Fire a popup blocked event on the document.
    */
   virtual void
   FirePopupBlockedEvent(nsIDocument* aDoc,
                         nsIURI* aPopupURI,
                         const nsAString& aPopupWindowName,
                         const nsAString& aPopupWindowFeatures) = 0;
 
+  virtual void
+  NotifyContentBlockingState(unsigned aState,
+                             nsIChannel* aChannel) = 0;
+
   // WebIDL-ish APIs
   void MarkUncollectableForCCGeneration(uint32_t aGeneration)
   {
     mMarkedCCGeneration = aGeneration;
   }
 
   uint32_t GetMarkedCCGeneration()
   {
--- a/dom/encoding/FallbackEncoding.cpp
+++ b/dom/encoding/FallbackEncoding.cpp
@@ -148,18 +148,17 @@ FallbackEncoding::Observe(nsISupports *a
 
 void
 FallbackEncoding::Initialize()
 {
   MOZ_ASSERT(!FallbackEncoding::sInstance,
              "Initializing pre-existing fallback cache.");
   FallbackEncoding::sInstance = new FallbackEncoding;
   Preferences::RegisterCallback(FallbackEncoding::PrefChanged,
-                                "intl.charset.fallback.override",
-                                nullptr);
+                                "intl.charset.fallback.override");
   Preferences::AddBoolVarCache(&sGuessFallbackFromTopLevelDomain,
                                "intl.charset.fallback.tld");
   Preferences::AddBoolVarCache(&sFallbackToUTF8ForFile,
                                "intl.charset.fallback.utf8_for_file");
 
   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   if (obs) {
     obs->AddObserver(sInstance, "intl:requested-locales-changed", true);
--- a/dom/indexedDB/ActorsParent.cpp
+++ b/dom/indexedDB/ActorsParent.cpp
@@ -25722,19 +25722,20 @@ NormalTransactionOp::SendSuccessResult()
     MOZ_ASSERT(maximalSizeFromPref > kMaxIDBMsgOverhead);
     const size_t kMaxMessageSize = maximalSizeFromPref - kMaxIDBMsgOverhead;
 
     RequestResponse response;
     size_t responseSize = kMaxMessageSize;
     GetResponse(response, &responseSize);
 
     if (responseSize >= kMaxMessageSize) {
-      nsPrintfCString("The serialized value is too large"
-                      " (size=%zu bytes, max=%zu bytes).",
-                      responseSize, kMaxMessageSize);
+      nsPrintfCString warning("The serialized value is too large"
+                              " (size=%zu bytes, max=%zu bytes).",
+                              responseSize, kMaxMessageSize);
+      NS_WARNING(warning.get());
       return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
     }
 
     MOZ_ASSERT(response.type() != RequestResponse::T__None);
 
     if (response.type() == RequestResponse::Tnsresult) {
       MOZ_ASSERT(NS_FAILED(response.get_nsresult()));
 
--- a/dom/indexedDB/IndexedDatabaseManager.cpp
+++ b/dom/indexedDB/IndexedDatabaseManager.cpp
@@ -242,22 +242,22 @@ private:
   void
   Finish();
 
   void
   UnblockOpen();
 };
 
 void
-AtomicBoolPrefChangedCallback(const char* aPrefName, void* aClosure)
+AtomicBoolPrefChangedCallback(const char* aPrefName, Atomic<bool>* aClosure)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aClosure);
 
-  *static_cast<Atomic<bool>*>(aClosure) = Preferences::GetBool(aPrefName);
+  *aClosure = Preferences::GetBool(aPrefName);
 }
 
 void
 DataThresholdPrefChangedCallback(const char* aPrefName, void* aClosure)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(!strcmp(aPrefName, kDataThresholdPref));
   MOZ_ASSERT(!aClosure);
--- a/dom/media/CubebUtils.cpp
+++ b/dom/media/CubebUtils.cpp
@@ -517,60 +517,63 @@ uint32_t GetCubebMSGLatencyInFrames(cube
   if (cubeb_get_min_latency(context, params, &latency_frames) != CUBEB_OK) {
     NS_WARNING("Could not get minimal latency from cubeb.");
     return sCubebMSGLatencyInFrames; // default 512
   }
   return latency_frames;
 #endif
 }
 
+static const char* gInitCallbackPrefs[] = {
+  PREF_VOLUME_SCALE,
+  PREF_CUBEB_LATENCY_PLAYBACK,
+  PREF_CUBEB_LATENCY_MSG,
+  PREF_CUBEB_BACKEND,
+  PREF_CUBEB_FORCE_NULL_CONTEXT,
+  PREF_CUBEB_SANDBOX,
+  PREF_AUDIOIPC_POOL_SIZE,
+  PREF_AUDIOIPC_STACK_SIZE,
+  nullptr,
+};
+static const char* gCallbackPrefs[] = {
+  PREF_CUBEB_FORCE_SAMPLE_RATE,
+  // We don't want to call the callback on startup, because the pref is the
+  // empty string by default ("", which means "logging disabled"). Because the
+  // logging can be enabled via environment variables (MOZ_LOG="module:5"),
+  // calling this callback on init would immediately re-disable the logging.
+  PREF_CUBEB_LOGGING_LEVEL,
+  nullptr,
+};
+
 void InitLibrary()
 {
-  Preferences::RegisterCallbackAndCall(PrefChanged, PREF_VOLUME_SCALE);
-  Preferences::RegisterCallbackAndCall(PrefChanged, PREF_CUBEB_LATENCY_PLAYBACK);
-  Preferences::RegisterCallbackAndCall(PrefChanged, PREF_CUBEB_LATENCY_MSG);
-  Preferences::RegisterCallback(PrefChanged, PREF_CUBEB_FORCE_SAMPLE_RATE);
-  Preferences::RegisterCallbackAndCall(PrefChanged, PREF_CUBEB_BACKEND);
-  Preferences::RegisterCallbackAndCall(PrefChanged, PREF_CUBEB_FORCE_NULL_CONTEXT);
-  Preferences::RegisterCallbackAndCall(PrefChanged, PREF_CUBEB_SANDBOX);
-  Preferences::RegisterCallbackAndCall(PrefChanged, PREF_AUDIOIPC_POOL_SIZE);
-  Preferences::RegisterCallbackAndCall(PrefChanged, PREF_AUDIOIPC_STACK_SIZE);
+  Preferences::RegisterCallbacksAndCall(PrefChanged, gInitCallbackPrefs);
+  Preferences::RegisterCallbacks(PrefChanged, gCallbackPrefs);
+
   if (MOZ_LOG_TEST(gCubebLog, LogLevel::Verbose)) {
     cubeb_set_log_callback(CUBEB_LOG_VERBOSE, CubebLogCallback);
   } else if (MOZ_LOG_TEST(gCubebLog, LogLevel::Error)) {
     cubeb_set_log_callback(CUBEB_LOG_NORMAL, CubebLogCallback);
   }
-  // We don't want to call the callback on startup, because the pref is the
-  // empty string by default ("", which means "logging disabled"). Because the
-  // logging can be enabled via environment variables (MOZ_LOG="module:5"),
-  // calling this callback on init would immediately re-disable the logging.
-  Preferences::RegisterCallback(PrefChanged, PREF_CUBEB_LOGGING_LEVEL);
+
 #ifndef MOZ_WIDGET_ANDROID
   AbstractThread::MainThread()->Dispatch(
     NS_NewRunnableFunction("CubebUtils::InitLibrary", &InitBrandName));
 #endif
 #ifdef MOZ_CUBEB_REMOTING
   if (sCubebSandbox && XRE_IsContentProcess()) {
     InitAudioIPCConnection();
   }
 #endif
 }
 
 void ShutdownLibrary()
 {
-  Preferences::UnregisterCallback(PrefChanged, PREF_VOLUME_SCALE);
-  Preferences::UnregisterCallback(PrefChanged, PREF_AUDIOIPC_STACK_SIZE);
-  Preferences::UnregisterCallback(PrefChanged, PREF_AUDIOIPC_POOL_SIZE);
-  Preferences::UnregisterCallback(PrefChanged, PREF_CUBEB_SANDBOX);
-  Preferences::UnregisterCallback(PrefChanged, PREF_CUBEB_BACKEND);
-  Preferences::UnregisterCallback(PrefChanged, PREF_CUBEB_LATENCY_PLAYBACK);
-  Preferences::UnregisterCallback(PrefChanged, PREF_CUBEB_FORCE_SAMPLE_RATE);
-  Preferences::UnregisterCallback(PrefChanged, PREF_CUBEB_LATENCY_MSG);
-  Preferences::UnregisterCallback(PrefChanged, PREF_CUBEB_LOGGING_LEVEL);
-  Preferences::UnregisterCallback(PrefChanged, PREF_CUBEB_FORCE_NULL_CONTEXT);
+  Preferences::UnregisterCallbacks(PrefChanged, gInitCallbackPrefs);
+  Preferences::UnregisterCallbacks(PrefChanged, gCallbackPrefs);
 
   StaticMutexAutoLock lock(sMutex);
   if (sCubebContext) {
     cubeb_destroy(sCubebContext);
     sCubebContext = nullptr;
   }
   sBrandName = nullptr;
   sCubebBackendName = nullptr;
--- a/dom/messagechannel/MessagePort.cpp
+++ b/dom/messagechannel/MessagePort.cpp
@@ -743,16 +743,18 @@ MessagePort::CloneAndDisentangle(Message
     MOZ_ASSERT(mUnshippedEntangledPort);
     MOZ_ASSERT(mMessagesForTheOtherPort.IsEmpty());
 
     RefPtr<MessagePort> port = std::move(mUnshippedEntangledPort);
 
     // Disconnect the entangled port and connect it to PBackground.
     if (!port->ConnectToPBackground()) {
       // We are probably shutting down. We cannot proceed.
+      mState = eStateDisentangled;
+      UpdateMustKeepAlive();
       return;
     }
 
     // In this case, we don't need to be connected to the PBackground service.
     if (mMessages.IsEmpty()) {
       aIdentifier.sequenceId() = mIdentifier->sequenceId();
 
       mState = eStateDisentangled;
--- a/dom/plugins/ipc/PluginModuleParent.cpp
+++ b/dom/plugins/ipc/PluginModuleParent.cpp
@@ -507,16 +507,26 @@ PluginModuleChromeParent::LoadModule(con
       FunctionBrokerParent::Create(std::move(brokerParentEnd));
     if (parent->mBrokerParent) {
       parent->SendInitPluginFunctionBroker(std::move(brokerChildEnd));
     }
 #endif
     return parent.forget();
 }
 
+static const char* gCallbackPrefs[] = {
+    kChildTimeoutPref,
+    kParentTimeoutPref,
+#ifdef XP_WIN
+    kHangUITimeoutPref,
+    kHangUIMinDisplayPref,
+#endif
+    nullptr,
+};
+
 void
 PluginModuleChromeParent::OnProcessLaunched(const bool aSucceeded)
 {
     if (!aSucceeded) {
         mShutdown = true;
         OnInitFailure();
         return;
     }
@@ -531,22 +541,18 @@ PluginModuleChromeParent::OnProcessLaunc
 
     // Request Windows message deferral behavior on our channel. This
     // applies to the top level and all sub plugin protocols since they
     // all share the same channel.
     GetIPCChannel()->SetChannelFlags(MessageChannel::REQUIRE_DEFERRED_MESSAGE_PROTECTION);
 
     TimeoutChanged(CHILD_TIMEOUT_PREF, this);
 
-    Preferences::RegisterCallback(TimeoutChanged, kChildTimeoutPref, this);
-    Preferences::RegisterCallback(TimeoutChanged, kParentTimeoutPref, this);
-#ifdef XP_WIN
-    Preferences::RegisterCallback(TimeoutChanged, kHangUITimeoutPref, this);
-    Preferences::RegisterCallback(TimeoutChanged, kHangUIMinDisplayPref, this);
-#endif
+    Preferences::RegisterCallbacks(TimeoutChanged, gCallbackPrefs,
+                                   static_cast<PluginModuleParent*>(this));
 
     RegisterSettingsCallbacks();
 
     // If this fails, we're having IPC troubles, and we're doomed anyways.
     if (!InitCrashReporter()) {
         mShutdown = true;
         Close();
         OnInitFailure();
@@ -621,22 +627,24 @@ PluginModuleParent::~PluginModuleParent(
         NP_Shutdown(&err);
     }
 }
 
 PluginModuleContentParent::PluginModuleContentParent()
   : PluginModuleParent(false)
   , mPluginId(0)
 {
-  Preferences::RegisterCallback(TimeoutChanged, kContentTimeoutPref, this);
+  Preferences::RegisterCallback(TimeoutChanged, kContentTimeoutPref,
+                                static_cast<PluginModuleParent*>(this));
 }
 
 PluginModuleContentParent::~PluginModuleContentParent()
 {
-    Preferences::UnregisterCallback(TimeoutChanged, kContentTimeoutPref, this);
+    Preferences::UnregisterCallback(TimeoutChanged, kContentTimeoutPref,
+                                    static_cast<PluginModuleParent*>(this));
 }
 
 PluginModuleChromeParent::PluginModuleChromeParent(const char* aFilePath,
                                                    uint32_t aPluginId,
                                                    int32_t aSandboxLevel)
   : PluginModuleParent(true)
   , mSubprocess(new PluginProcessParent(aFilePath))
   , mPluginId(aPluginId)
@@ -697,22 +705,20 @@ PluginModuleChromeParent::~PluginModuleC
     if (mFinishInitTask) {
         // mFinishInitTask will be deleted by the main thread message_loop
         mFinishInitTask->Cancel();
     }
 #endif
 
     UnregisterSettingsCallbacks();
 
-    Preferences::UnregisterCallback(TimeoutChanged, kChildTimeoutPref, this);
-    Preferences::UnregisterCallback(TimeoutChanged, kParentTimeoutPref, this);
+    Preferences::UnregisterCallbacks(TimeoutChanged, gCallbackPrefs,
+                                     static_cast<PluginModuleParent*>(this));
+
 #ifdef XP_WIN
-    Preferences::UnregisterCallback(TimeoutChanged, kHangUITimeoutPref, this);
-    Preferences::UnregisterCallback(TimeoutChanged, kHangUIMinDisplayPref, this);
-
     if (mHangUIParent) {
         delete mHangUIParent;
         mHangUIParent = nullptr;
     }
 #endif
 
     mozilla::BackgroundHangMonitor::UnregisterAnnotator(*this);
 }
@@ -763,43 +769,41 @@ void
 PluginModuleParent::SetChildTimeout(const int32_t aChildTimeout)
 {
     int32_t timeoutMs = (aChildTimeout > 0) ? (1000 * aChildTimeout) :
                       MessageChannel::kNoTimeout;
     SetReplyTimeoutMs(timeoutMs);
 }
 
 void
-PluginModuleParent::TimeoutChanged(const char* aPref, void* aModule)
+PluginModuleParent::TimeoutChanged(const char* aPref, PluginModuleParent* aModule)
 {
-    PluginModuleParent* module = static_cast<PluginModuleParent*>(aModule);
-
     NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 #ifndef XP_WIN
     if (!strcmp(aPref, kChildTimeoutPref)) {
-      MOZ_ASSERT(module->IsChrome());
+      MOZ_ASSERT(aModule->IsChrome());
       // The timeout value used by the parent for children
       int32_t timeoutSecs = Preferences::GetInt(kChildTimeoutPref, 0);
-      module->SetChildTimeout(timeoutSecs);
+      aModule->SetChildTimeout(timeoutSecs);
 #else
     if (!strcmp(aPref, kChildTimeoutPref) ||
         !strcmp(aPref, kHangUIMinDisplayPref) ||
         !strcmp(aPref, kHangUITimeoutPref)) {
-      MOZ_ASSERT(module->IsChrome());
-      static_cast<PluginModuleChromeParent*>(module)->EvaluateHangUIState(true);
+      MOZ_ASSERT(aModule->IsChrome());
+      static_cast<PluginModuleChromeParent*>(aModule)->EvaluateHangUIState(true);
 #endif // XP_WIN
     } else if (!strcmp(aPref, kParentTimeoutPref)) {
       // The timeout value used by the child for its parent
-      MOZ_ASSERT(module->IsChrome());
+      MOZ_ASSERT(aModule->IsChrome());
       int32_t timeoutSecs = Preferences::GetInt(kParentTimeoutPref, 0);
-      Unused << static_cast<PluginModuleChromeParent*>(module)->SendSetParentHangTimeout(timeoutSecs);
+      Unused << static_cast<PluginModuleChromeParent*>(aModule)->SendSetParentHangTimeout(timeoutSecs);
     } else if (!strcmp(aPref, kContentTimeoutPref)) {
-      MOZ_ASSERT(!module->IsChrome());
+      MOZ_ASSERT(!aModule->IsChrome());
       int32_t timeoutSecs = Preferences::GetInt(kContentTimeoutPref, 0);
-      module->SetChildTimeout(timeoutSecs);
+      aModule->SetChildTimeout(timeoutSecs);
     }
 }
 
 void
 PluginModuleChromeParent::CleanupFromTimeout(const bool aFromHangUI)
 {
     if (mShutdown) {
       return;
@@ -2073,20 +2077,19 @@ void
 PluginModuleChromeParent::CachedSettingChanged()
 {
     PluginSettings settings;
     GetSettings(&settings);
     Unused << SendSettingChanged(settings);
 }
 
 /* static */ void
-PluginModuleChromeParent::CachedSettingChanged(const char* aPref, void* aModule)
+PluginModuleChromeParent::CachedSettingChanged(const char* aPref, PluginModuleChromeParent* aModule)
 {
-    PluginModuleChromeParent *module = static_cast<PluginModuleChromeParent*>(aModule);
-    module->CachedSettingChanged();
+    aModule->CachedSettingChanged();
 }
 
 #if defined(XP_UNIX) && !defined(XP_MACOSX)
 nsresult
 PluginModuleParent::NP_Initialize(NPNetscapeFuncs* bFuncs, NPPluginFuncs* pFuncs, NPError* error)
 {
     PLUGIN_LOG_DEBUG_METHOD;
 
--- a/dom/plugins/ipc/PluginModuleParent.h
+++ b/dom/plugins/ipc/PluginModuleParent.h
@@ -164,17 +164,17 @@ protected:
 
     virtual mozilla::ipc::IPCResult
     AnswerNPN_SetValue_NPPVpluginRequiresAudioDeviceChanges(
                                         const bool& shouldRegister,
                                         NPError* result) override;
 
 protected:
     void SetChildTimeout(const int32_t aChildTimeout);
-    static void TimeoutChanged(const char* aPref, void* aModule);
+    static void TimeoutChanged(const char* aPref, PluginModuleParent* aModule);
 
     virtual void UpdatePluginTimeout() {}
 
     virtual mozilla::ipc::IPCResult RecvNotifyContentModuleDestroyed() override { return IPC_OK(); }
 
     virtual mozilla::ipc::IPCResult RecvReturnClearSiteData(const NPError& aRv,
                                                             const uint64_t& aCallbackId) override;
 
@@ -514,17 +514,17 @@ private:
 
     void RegisterSettingsCallbacks();
     void UnregisterSettingsCallbacks();
 
     bool InitCrashReporter();
 
     virtual mozilla::ipc::IPCResult RecvNotifyContentModuleDestroyed() override;
 
-    static void CachedSettingChanged(const char* aPref, void* aModule);
+    static void CachedSettingChanged(const char* aPref, PluginModuleChromeParent* aModule);
 
     virtual mozilla::ipc::IPCResult
     AnswerNPN_SetValue_NPPVpluginRequiresAudioDeviceChanges(
                                         const bool& shouldRegister,
                                         NPError* result) override;
 
     PluginProcessParent* mSubprocess;
     uint32_t mPluginId;
--- a/dom/security/nsCSPContext.cpp
+++ b/dom/security/nsCSPContext.cpp
@@ -478,17 +478,17 @@ nsCSPContext::reportInlineViolation(nsCo
   else {
     observerSubject = (aContentType == nsIContentPolicy::TYPE_SCRIPT)
                       ? NS_LITERAL_STRING(SCRIPT_HASH_VIOLATION_OBSERVER_TOPIC)
                       : NS_LITERAL_STRING(STYLE_HASH_VIOLATION_OBSERVER_TOPIC);
   }
 
   nsCOMPtr<nsISupportsCString> selfICString(do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID));
   if (selfICString) {
-    selfICString->SetData(nsDependentCString("self"));
+    selfICString->SetData(nsDependentCString("inline"));
   }
   nsCOMPtr<nsISupports> selfISupports(do_QueryInterface(selfICString));
 
   // use selfURI as the sourceFile
   nsAutoCString sourceFile;
   if (mSelfURI) {
     mSelfURI->GetSpec(sourceFile);
   }
@@ -667,17 +667,26 @@ nsCSPContext::LogViolationDetails(uint16
                                   const nsAString& aNonce,
                                   const nsAString& aContent)
 {
   for (uint32_t p = 0; p < mPolicies.Length(); p++) {
     NS_ASSERTION(mPolicies[p], "null pointer in nsTArray<nsCSPPolicy>");
 
     nsCOMPtr<nsISupportsCString> selfICString(do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID));
     if (selfICString) {
-      selfICString->SetData(nsDependentCString("self"));
+      if (aViolationType == nsIContentSecurityPolicy::VIOLATION_TYPE_EVAL) {
+        selfICString->SetData(nsDependentCString("eval"));
+      } else if (aViolationType == nsIContentSecurityPolicy::VIOLATION_TYPE_INLINE_SCRIPT ||
+                 aViolationType == nsIContentSecurityPolicy::VIOLATION_TYPE_INLINE_STYLE) {
+        selfICString->SetData(nsDependentCString("inline"));
+      } else {
+        // All the other types should have a URL, but just in case, let's use
+        // 'self' here.
+        selfICString->SetData(nsDependentCString("self"));
+      }
     }
     nsCOMPtr<nsISupports> selfISupports(do_QueryInterface(selfICString));
 
     switch (aViolationType) {
       CASE_CHECK_AND_REPORT(EVAL,              SCRIPT,     NS_LITERAL_STRING(""),
                             CSP_UNSAFE_EVAL,   EVAL_VIOLATION_OBSERVER_TOPIC);
       CASE_CHECK_AND_REPORT(INLINE_STYLE,      STYLESHEET, NS_LITERAL_STRING(""),
                             CSP_UNSAFE_INLINE, INLINE_STYLE_VIOLATION_OBSERVER_TOPIC);
@@ -876,16 +885,17 @@ StripURIForReporting(nsIURI* aURI,
 
   // 3) Return uri, with any fragment component removed.
   aURI->GetSpecIgnoringRef(outStrippedURI);
 }
 
 nsresult
 nsCSPContext::GatherSecurityPolicyViolationEventData(
   nsIURI* aBlockedURI,
+  const nsACString& aBlockedString,
   nsIURI* aOriginalURI,
   nsAString& aViolatedDirective,
   uint32_t aViolatedPolicyIndex,
   nsAString& aSourceFile,
   nsAString& aScriptSample,
   uint32_t aLineNum,
   uint32_t aColumnNum,
   mozilla::dom::SecurityPolicyViolationEventInit& aViolationEventInit)
@@ -904,16 +914,18 @@ nsCSPContext::GatherSecurityPolicyViolat
   // referrer
   aViolationEventInit.mReferrer = mReferrer;
 
   // blocked-uri
   if (aBlockedURI) {
     nsAutoCString reportBlockedURI;
     StripURIForReporting(aBlockedURI, mSelfURI, reportBlockedURI);
     aViolationEventInit.mBlockedURI = NS_ConvertUTF8toUTF16(reportBlockedURI);
+  } else {
+    aViolationEventInit.mBlockedURI = NS_ConvertUTF8toUTF16(aBlockedString);
   }
 
   // effective-directive
   // The name of the policy directive that was violated.
   aViolationEventInit.mEffectiveDirective = aViolatedDirective;
 
   // violated-directive
   // In CSP2, the policy directive that was violated, as it appears in the policy.
@@ -1284,18 +1296,25 @@ class CSPReportSenderRunnable final : pu
       MOZ_ASSERT(NS_IsMainThread());
 
       nsresult rv;
 
       // 0) prepare violation data
       mozilla::dom::SecurityPolicyViolationEventInit init;
       // mBlockedContentSource could be a URI or a string.
       nsCOMPtr<nsIURI> blockedURI = do_QueryInterface(mBlockedContentSource);
+      // if mBlockedContentSource is not a URI, it could be a string
+      nsCOMPtr<nsISupportsCString> blockedICString = do_QueryInterface(mBlockedContentSource);
+      nsAutoCString blockedDataStr;
+      if (blockedICString) {
+        blockedICString->GetData(blockedDataStr);
+      }
+
       rv = mCSPContext->GatherSecurityPolicyViolationEventData(
-        blockedURI, mOriginalURI,
+        blockedURI, blockedDataStr, mOriginalURI,
         mViolatedDirective, mViolatedPolicyIndex,
         mSourceFile, mScriptSample, mLineNum, mColumnNum,
         init);
       NS_ENSURE_SUCCESS(rv, rv);
 
       // 1) notify observers
       nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
       NS_ASSERTION(observerService, "needs observer service");
@@ -1303,34 +1322,28 @@ class CSPReportSenderRunnable final : pu
                                             CSP_VIOLATION_TOPIC,
                                             mViolatedDirective.get());
       NS_ENSURE_SUCCESS(rv, rv);
 
       // 2) send reports for the policy that was violated
       mCSPContext->SendReports(init, mViolatedPolicyIndex);
 
       // 3) log to console (one per policy violation)
-      // if mBlockedContentSource is not a URI, it could be a string
-      nsCOMPtr<nsISupportsCString> blockedString = do_QueryInterface(mBlockedContentSource);
-
-      nsCString blockedDataStr;
 
       if (blockedURI) {
         blockedURI->GetSpec(blockedDataStr);
         if (blockedDataStr.Length() > nsCSPContext::ScriptSampleMaxLength()) {
           bool isData = false;
           rv = blockedURI->SchemeIs("data", &isData);
           if (NS_SUCCEEDED(rv) && isData &&
               blockedDataStr.Length() > nsCSPContext::ScriptSampleMaxLength()) {
             blockedDataStr.Truncate(nsCSPContext::ScriptSampleMaxLength());
             blockedDataStr.Append(NS_ConvertUTF16toUTF8(nsContentUtils::GetLocalizedEllipsis()));
           }
         }
-      } else if (blockedString) {
-        blockedString->GetData(blockedDataStr);
       }
 
       if (blockedDataStr.Length() > 0) {
         nsString blockedDataChar16 = NS_ConvertUTF8toUTF16(blockedDataStr);
         const char16_t* params[] = { mViolatedDirective.get(),
                                      blockedDataChar16.get() };
         mCSPContext->logToConsole(mReportOnlyFlag ? "CSPROViolationWithURI" :
                                                     "CSPViolationWithURI",
--- a/dom/security/nsCSPContext.h
+++ b/dom/security/nsCSPContext.h
@@ -84,16 +84,17 @@ class nsCSPContext : public nsIContentSe
      *        source line number of the violation (if available)
      * @param aColumnNum
      *        source column number of the violation (if available)
      * @param aViolationEventInit
      *        The output
      */
     nsresult GatherSecurityPolicyViolationEventData(
       nsIURI* aBlockedURI,
+      const nsACString& aBlockedString,
       nsIURI* aOriginalURI,
       nsAString& aViolatedDirective,
       uint32_t aViolatedPolicyIndex,
       nsAString& aSourceFile,
       nsAString& aScriptSample,
       uint32_t aLineNum,
       uint32_t aColumnNum,
       mozilla::dom::SecurityPolicyViolationEventInit& aViolationEventInit);
--- a/dom/security/test/csp/test_report.html
+++ b/dom/security/test/csp/test_report.html
@@ -43,17 +43,17 @@ window.checkResults = function(reportObj
   //    * source-file
   // see http://www.w3.org/TR/CSP11/#violation-reports
   is(cspReport["document-uri"], docUri, "Incorrect document-uri");
 
   // we can not test for the whole referrer since it includes platform specific information
   ok(cspReport["referrer"].startsWith("http://mochi.test:8888/tests/dom/security/test/csp/test_report.html"),
      "Incorrect referrer");
 
-  is(cspReport["blocked-uri"], "", "Incorrect blocked-uri");
+  is(cspReport["blocked-uri"], "inline", "Incorrect blocked-uri");
 
   is(cspReport["violated-directive"], "default-src", "Incorrect violated-directive");
 
   is(cspReport["original-policy"], "default-src 'none' 'report-sample'; report-uri http://mochi.test:8888/foo.sjs",
      "Incorrect original-policy");
 
   is(cspReport["source-file"], docUri, "Incorrect source-file");
 
--- a/dom/security/test/unit/test_csp_reports.js
+++ b/dom/security/test/unit/test_csp_reports.js
@@ -98,33 +98,33 @@ function makeTest(id, expectedJSON, useR
 }
 
 function run_test() {
   var selfuri = NetUtil.newURI(REPORT_SERVER_URI +
                                ":" + REPORT_SERVER_PORT +
                                "/foo/self");
 
   // test that inline script violations cause a report.
-  makeTest(0, {"blocked-uri": ""}, false,
+  makeTest(0, {"blocked-uri": "inline"}, false,
       function(csp) {
         let inlineOK = true;
         inlineOK = csp.getAllowsInline(Ci.nsIContentPolicy.TYPE_SCRIPT,
                                        "", // aNonce
                                        false, // aParserCreated
                                        null, // aTriggeringElement
                                        "", // aContentOfPseudoScript
                                        0, // aLineNumber
                                        0); // aColumnNumber
 
         // this is not a report only policy, so it better block inline scripts
         Assert.ok(!inlineOK);
       });
 
   // test that eval violations cause a report.
-  makeTest(1, {"blocked-uri": "",
+  makeTest(1, {"blocked-uri": "eval",
                // JSON script-sample is UTF8 encoded
                "script-sample" : "\xc2\xa3\xc2\xa5\xc2\xb5\xe5\x8c\x97\xf0\xa0\x9d\xb9",
                "line-number": 1,
                "column-number": 2}, false,
       function(csp) {
         let evalOK = true, oReportViolation = {'value': false};
         evalOK = csp.getAllowsEval(oReportViolation);
 
@@ -151,33 +151,33 @@ function run_test() {
       function(csp) {
         // shouldLoad creates and sends out the report here.
         csp.shouldLoad(Ci.nsIContentPolicy.TYPE_SCRIPT,
                       NetUtil.newURI("http://blocked.test/foo.js"),
                       null, null, null, null);
       });
 
   // test that inline script violations cause a report in report-only policy
-  makeTest(3, {"blocked-uri": ""}, true,
+  makeTest(3, {"blocked-uri": "inline"}, true,
       function(csp) {
         let inlineOK = true;
         inlineOK = csp.getAllowsInline(Ci.nsIContentPolicy.TYPE_SCRIPT,
                                        "", // aNonce
                                        false, // aParserCreated
                                        null, // aTriggeringElement
                                        "", // aContentOfPseudoScript
                                        0, // aLineNumber
                                        0); // aColumnNumber
 
         // this is a report only policy, so it better allow inline scripts
         Assert.ok(inlineOK);
       });
 
   // test that eval violations cause a report in report-only policy
-  makeTest(4, {"blocked-uri": ""}, true,
+  makeTest(4, {"blocked-uri": "inline"}, true,
       function(csp) {
         let evalOK = true, oReportViolation = {'value': false};
         evalOK = csp.getAllowsEval(oReportViolation);
 
         // this is a report only policy, so it better allow eval
         Assert.ok(evalOK);
         // ... but still cause reports to go out
         Assert.ok(oReportViolation.value);
--- a/dom/storage/StorageDBUpdater.cpp
+++ b/dom/storage/StorageDBUpdater.cpp
@@ -173,17 +173,17 @@ GetOriginParticular::OnFunctionCall(
 {
   nsresult rv;
 
   nsAutoCString scope;
   rv = aFunctionArguments->GetUTF8String(0, scope);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsAutoCString suffix, origin;
-  ExtractOriginData(scope, suffix, origin);
+  ExtractOriginData extractor(scope, suffix, origin);
 
   nsCOMPtr<nsIWritableVariant> outVar(new nsVariant());
 
   switch (mParticular) {
   case EParticular::ORIGIN_ATTRIBUTES_SUFFIX:
     rv = outVar->SetAsAUTF8String(suffix);
     break;
   case EParticular::ORIGIN_KEY:
--- a/dom/xul/XULDocument.cpp
+++ b/dom/xul/XULDocument.cpp
@@ -2922,22 +2922,21 @@ XULDocument::IsDocumentRightToLeft()
 
 void
 XULDocument::ResetDocumentDirection()
 {
     DocumentStatesChanged(NS_DOCUMENT_STATE_RTL_LOCALE);
 }
 
 void
-XULDocument::DirectionChanged(const char* aPrefName, void* aData)
+XULDocument::DirectionChanged(const char* aPrefName, XULDocument* aDoc)
 {
   // Reset the direction and restyle the document if necessary.
-  XULDocument* doc = (XULDocument *)aData;
-  if (doc) {
-      doc->ResetDocumentDirection();
+  if (aDoc) {
+      aDoc->ResetDocumentDirection();
   }
 }
 
 nsIDocument::DocumentTheme
 XULDocument::GetDocumentLWTheme()
 {
     if (mDocLWTheme == Doc_Theme_Uninitialized) {
         mDocLWTheme = ThreadSafeGetDocumentLWTheme();
--- a/dom/xul/XULDocument.h
+++ b/dom/xul/XULDocument.h
@@ -204,17 +204,17 @@ protected:
 
     nsresult
     ExecuteOnBroadcastHandlerFor(Element* aBroadcaster,
                                  Element* aListener,
                                  nsAtom* aAttr);
 
     already_AddRefed<nsPIWindowRoot> GetWindowRoot();
 
-    static void DirectionChanged(const char* aPrefName, void* aData);
+    static void DirectionChanged(const char* aPrefName, XULDocument* aData);
 
     // pseudo constants
     static int32_t gRefCnt;
 
     static LazyLogModule gXULLog;
 
     nsresult
     Persist(mozilla::dom::Element* aElement,
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -448,39 +448,22 @@ SRGBOverrideObserver::Observe(nsISupport
 
 static const char* kObservedPrefs[] = {
     "gfx.downloadable_fonts.",
     "gfx.font_rendering.",
     BIDI_NUMERAL_PREF,
     nullptr
 };
 
-class FontPrefsObserver final : public nsIObserver
+static void
+FontPrefChanged(const char* aPref, void* aData)
 {
-    ~FontPrefsObserver() = default;
-public:
-    NS_DECL_ISUPPORTS
-    NS_DECL_NSIOBSERVER
-};
-
-NS_IMPL_ISUPPORTS(FontPrefsObserver, nsIObserver)
-
-NS_IMETHODIMP
-FontPrefsObserver::Observe(nsISupports *aSubject,
-                           const char *aTopic,
-                           const char16_t *someData)
-{
-    if (!someData) {
-        NS_ERROR("font pref observer code broken");
-        return NS_ERROR_UNEXPECTED;
-    }
+    MOZ_ASSERT(aPref);
     NS_ASSERTION(gfxPlatform::GetPlatform(), "the singleton instance has gone");
-    gfxPlatform::GetPlatform()->FontsPrefsChanged(NS_ConvertUTF16toUTF8(someData).get());
-
-    return NS_OK;
+    gfxPlatform::GetPlatform()->FontsPrefsChanged(aPref);
 }
 
 class MemoryPressureObserver final : public nsIObserver
 {
     ~MemoryPressureObserver() = default;
 public:
     NS_DECL_ISUPPORTS
     NS_DECL_NSIOBSERVER
@@ -818,18 +801,17 @@ gfxPlatform::Init()
     if (NS_FAILED(rv)) {
         MOZ_CRASH("Could not initialize gfxFontCache");
     }
 
     /* Create and register our CMS Override observer. */
     gPlatform->mSRGBOverrideObserver = new SRGBOverrideObserver();
     Preferences::AddWeakObserver(gPlatform->mSRGBOverrideObserver, GFX_PREF_CMS_FORCE_SRGB);
 
-    gPlatform->mFontPrefsObserver = new FontPrefsObserver();
-    Preferences::AddStrongObservers(gPlatform->mFontPrefsObserver, kObservedPrefs);
+    Preferences::RegisterPrefixCallbacks(FontPrefChanged, kObservedPrefs);
 
     GLContext::PlatformStartup();
 
     Preferences::RegisterCallbackAndCall(RecordingPrefChanged, "gfx.2d.recording");
 
     CreateCMSOutputProfile();
 
     // Listen to memory pressure event so we can purge DrawTarget caches
@@ -993,19 +975,17 @@ gfxPlatform::Shutdown()
     // Free the various non-null transforms and loaded profiles
     ShutdownCMS();
 
     /* Unregister our CMS Override callback. */
     NS_ASSERTION(gPlatform->mSRGBOverrideObserver, "mSRGBOverrideObserver has alreay gone");
     Preferences::RemoveObserver(gPlatform->mSRGBOverrideObserver, GFX_PREF_CMS_FORCE_SRGB);
     gPlatform->mSRGBOverrideObserver = nullptr;
 
-    NS_ASSERTION(gPlatform->mFontPrefsObserver, "mFontPrefsObserver has alreay gone");
-    Preferences::RemoveObservers(gPlatform->mFontPrefsObserver, kObservedPrefs);
-    gPlatform->mFontPrefsObserver = nullptr;
+    Preferences::UnregisterPrefixCallbacks(FontPrefChanged, kObservedPrefs);
 
     NS_ASSERTION(gPlatform->mMemoryPressureObserver, "mMemoryPressureObserver has already gone");
     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     if (obs) {
         obs->RemoveObserver(gPlatform->mMemoryPressureObserver, "memory-pressure");
     }
 
     gPlatform->mMemoryPressureObserver = nullptr;
--- a/gfx/thebes/gfxPlatform.h
+++ b/gfx/thebes/gfxPlatform.h
@@ -883,17 +883,16 @@ private:
     void InitGPUProcessPrefs();
     void InitOMTPConfig();
 
     static bool IsDXInterop2Blocked();
     static bool IsDXNV12Blocked();
 
     RefPtr<gfxASurface> mScreenReferenceSurface;
     nsCOMPtr<nsIObserver> mSRGBOverrideObserver;
-    nsCOMPtr<nsIObserver> mFontPrefsObserver;
     nsCOMPtr<nsIObserver> mMemoryPressureObserver;
 
     // The preferred draw target backend to use for canvas
     mozilla::gfx::BackendType mPreferredCanvasBackend;
     // The fallback draw target backend to use for canvas, if the preferred backend fails
     mozilla::gfx::BackendType mFallbackCanvasBackend;
     // The backend to use for content
     mozilla::gfx::BackendType mContentBackend;
--- a/gfx/thebes/gfxPlatformFontList.cpp
+++ b/gfx/thebes/gfxPlatformFontList.cpp
@@ -121,34 +121,40 @@ static_assert(MOZ_ARRAY_LENGTH(gPrefLang
 
 class gfxFontListPrefObserver final : public nsIObserver {
     ~gfxFontListPrefObserver() {}
 public:
     NS_DECL_ISUPPORTS
     NS_DECL_NSIOBSERVER
 };
 
+static void
+FontListPrefChanged(const char* aPref, void* aData = nullptr)
+{
+    // XXX this could be made to only clear out the cache for the prefs that were changed
+    // but it probably isn't that big a deal.
+    gfxPlatformFontList::PlatformFontList()->ClearLangGroupPrefFonts();
+    gfxFontCache::GetCache()->AgeAllGenerations();
+}
+
 static gfxFontListPrefObserver* gFontListPrefObserver = nullptr;
 
 NS_IMPL_ISUPPORTS(gfxFontListPrefObserver, nsIObserver)
 
 #define LOCALES_CHANGED_TOPIC "intl:system-locales-changed"
 
 NS_IMETHODIMP
 gfxFontListPrefObserver::Observe(nsISupports     *aSubject,
                                  const char      *aTopic,
                                  const char16_t *aData)
 {
-    NS_ASSERTION(!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) ||
-                 !strcmp(aTopic, LOCALES_CHANGED_TOPIC), "invalid topic");
-    // XXX this could be made to only clear out the cache for the prefs that were changed
-    // but it probably isn't that big a deal.
-    gfxPlatformFontList::PlatformFontList()->ClearLangGroupPrefFonts();
-    gfxFontCache::GetCache()->AgeAllGenerations();
-    if (XRE_IsParentProcess() && !strcmp(aTopic, LOCALES_CHANGED_TOPIC)) {
+    NS_ASSERTION(!strcmp(aTopic, LOCALES_CHANGED_TOPIC), "invalid topic");
+    FontListPrefChanged(nullptr);
+
+    if (XRE_IsParentProcess()) {
         gfxPlatform::ForceGlobalReflow();
     }
     return NS_OK;
 }
 
 MOZ_DEFINE_MALLOC_SIZE_OF(FontListMallocSizeOf)
 
 NS_IMPL_ISUPPORTS(gfxPlatformFontList::MemoryReporter, nsIMemoryReporter)
@@ -210,17 +216,18 @@ gfxPlatformFontList::gfxPlatformFontList
 
     LoadBadUnderlineList();
 
     // pref changes notification setup
     NS_ASSERTION(!gFontListPrefObserver,
                  "There has been font list pref observer already");
     gFontListPrefObserver = new gfxFontListPrefObserver();
     NS_ADDREF(gFontListPrefObserver);
-    Preferences::AddStrongObservers(gFontListPrefObserver, kObservedPrefs);
+
+    Preferences::RegisterPrefixCallbacks(FontListPrefChanged, kObservedPrefs);
 
     nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
     if (obs) {
         obs->AddObserver(gFontListPrefObserver, LOCALES_CHANGED_TOPIC, false);
     }
 
     // Only the parent process listens for whitelist changes; it will then
     // notify its children to rebuild their font lists.
@@ -232,17 +239,18 @@ gfxPlatformFontList::gfxPlatformFontList
     RegisterStrongMemoryReporter(new MemoryReporter());
 }
 
 gfxPlatformFontList::~gfxPlatformFontList()
 {
     mSharedCmaps.Clear();
     ClearLangGroupPrefFonts();
     NS_ASSERTION(gFontListPrefObserver, "There is no font list pref observer");
-    Preferences::RemoveObservers(gFontListPrefObserver, kObservedPrefs);
+
+    Preferences::UnregisterPrefixCallbacks(FontListPrefChanged, kObservedPrefs);
 
     nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
     if (obs) {
         obs->RemoveObserver(gFontListPrefObserver, LOCALES_CHANGED_TOPIC);
     }
 
     if (XRE_IsParentProcess()) {
         Preferences::UnregisterCallback(FontWhitelistPrefChanged,
--- a/gfx/thebes/gfxPrefs.cpp
+++ b/gfx/thebes/gfxPrefs.cpp
@@ -268,19 +268,19 @@ void gfxPrefs::PrefSet(const char* aPref
 
 void gfxPrefs::PrefSet(const char* aPref, std::string aValue)
 {
   MOZ_ASSERT(IsPrefsServiceAvailable());
   Preferences::SetCString(aPref, aValue.c_str());
 }
 
 static void
-OnGfxPrefChanged(const char* aPrefname, void* aClosure)
+OnGfxPrefChanged(const char* aPrefname, gfxPrefs::Pref* aPref)
 {
-  reinterpret_cast<gfxPrefs::Pref*>(aClosure)->OnChange();
+  aPref->OnChange();
 }
 
 void gfxPrefs::WatchChanges(const char* aPrefname, Pref* aPref)
 {
   MOZ_ASSERT(IsPrefsServiceAvailable());
   nsCString name;
   name.AssignLiteral(aPrefname, strlen(aPrefname));
   Preferences::RegisterCallback(OnGfxPrefChanged, name, aPref);
--- a/js/public/Principals.h
+++ b/js/public/Principals.h
@@ -10,17 +10,18 @@
 #define js_Principals_h
 
 #include "mozilla/Atomics.h"
 
 #include <stdint.h>
 
 #include "jspubtd.h"
 
-#include "js/StructuredClone.h"
+struct JSStructuredCloneReader;
+struct JSStructuredCloneWriter;
 
 namespace js {
     struct JS_PUBLIC_API(PerformanceGroup);
 } // namespace js
 
 struct JSPrincipals {
     /* Don't call "destroy"; use reference counting macros below. */
     mozilla::Atomic<int32_t> refcount;
--- a/js/rust/etc/wrapper.hpp
+++ b/js/rust/etc/wrapper.hpp
@@ -10,14 +10,15 @@
 #endif
 
 typedef uint32_t HashNumber;
 
 #include "jsfriendapi.h"
 #include "js/Conversions.h"
 #include "js/Initialization.h"
 #include "js/MemoryMetrics.h"
+#include "js/StructuredClone.h"
 
 // Replacements for types that are too difficult for rust-bindgen.
 
 /// <div rustbindgen replaces="JS::detail::MaybeWrapped" />
 template <typename T>
 using replaces_MaybeWrapped = T;
--- a/js/rust/src/jsglue.cpp
+++ b/js/rust/src/jsglue.cpp
@@ -13,16 +13,17 @@
 #endif
 
 #include "jsapi.h"
 #include "jsfriendapi.h"
 #include "js/Proxy.h"
 #include "js/Class.h"
 #include "js/MemoryMetrics.h"
 #include "js/Principals.h"
+#include "js/StructuredClone.h"
 #include "js/Wrapper.h"
 #include "assert.h"
 
 struct ProxyTraps {
     bool (*enter)(JSContext *cx, JS::HandleObject proxy, JS::HandleId id,
                   js::BaseProxyHandler::Action action, bool *bp);
 
     bool (*getOwnPropertyDescriptor)(JSContext *cx, JS::HandleObject proxy,
--- a/js/src/builtin/AtomicsObject.h
+++ b/js/src/builtin/AtomicsObject.h
@@ -13,16 +13,17 @@
 #include "threading/ConditionVariable.h"
 #include "vm/JSObject.h"
 #include "vm/MutexIDs.h"
 #include "vm/NativeObject.h"
 
 namespace js {
 
 class GlobalObject;
+class SharedArrayRawBuffer;
 
 class AtomicsObject : public NativeObject
 {
   public:
     static const Class class_;
     static JSObject* initClass(JSContext* cx, Handle<GlobalObject*> global);
     static MOZ_MUST_USE bool toString(JSContext* cx, unsigned int argc, Value* vp);
 };
--- a/js/src/builtin/SIMD.h
+++ b/js/src/builtin/SIMD.h
@@ -5,953 +5,30 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef builtin_SIMD_h
 #define builtin_SIMD_h
 
 #include "jsapi.h"
 #include "NamespaceImports.h"
 
-#include "builtin/TypedObjectConstants.h"
+#include "builtin/SIMDConstants.h"
 #include "jit/IonTypes.h"
 #include "js/Conversions.h"
 
 /*
  * JS SIMD functions.
  * Spec matching polyfill:
  * https://github.com/tc39/ecmascript_simd/blob/master/src/ecmascript_simd.js
  */
 
-// Bool8x16.
-#define BOOL8X16_UNARY_FUNCTION_LIST(V)                                               \
-  V(not, (UnaryFunc<Bool8x16, LogicalNot, Bool8x16>), 1)                              \
-  V(check, (UnaryFunc<Bool8x16, Identity, Bool8x16>), 1)                              \
-  V(splat, (FuncSplat<Bool8x16>), 1)                                                  \
-  V(allTrue, (AllTrue<Bool8x16>), 1)                                                  \
-  V(anyTrue, (AnyTrue<Bool8x16>), 1)
-
-#define BOOL8X16_BINARY_FUNCTION_LIST(V)                                              \
-  V(extractLane, (ExtractLane<Bool8x16>), 2)                                          \
-  V(and, (BinaryFunc<Bool8x16, And, Bool8x16>), 2)                                    \
-  V(or, (BinaryFunc<Bool8x16, Or, Bool8x16>), 2)                                      \
-  V(xor, (BinaryFunc<Bool8x16, Xor, Bool8x16>), 2)                                    \
-
-#define BOOL8X16_TERNARY_FUNCTION_LIST(V)                                             \
-  V(replaceLane, (ReplaceLane<Bool8x16>), 3)
-
-#define BOOL8X16_FUNCTION_LIST(V)                                                     \
-  BOOL8X16_UNARY_FUNCTION_LIST(V)                                                     \
-  BOOL8X16_BINARY_FUNCTION_LIST(V)                                                    \
-  BOOL8X16_TERNARY_FUNCTION_LIST(V)
-
-// Bool 16x8.
-#define BOOL16X8_UNARY_FUNCTION_LIST(V)                                               \
-  V(not, (UnaryFunc<Bool16x8, LogicalNot, Bool16x8>), 1)                              \
-  V(check, (UnaryFunc<Bool16x8, Identity, Bool16x8>), 1)                              \
-  V(splat, (FuncSplat<Bool16x8>), 1)                                                  \
-  V(allTrue, (AllTrue<Bool16x8>), 1)                                                  \
-  V(anyTrue, (AnyTrue<Bool16x8>), 1)
-
-#define BOOL16X8_BINARY_FUNCTION_LIST(V)                                              \
-  V(extractLane, (ExtractLane<Bool16x8>), 2)                                          \
-  V(and, (BinaryFunc<Bool16x8, And, Bool16x8>), 2)                                    \
-  V(or, (BinaryFunc<Bool16x8, Or, Bool16x8>), 2)                                      \
-  V(xor, (BinaryFunc<Bool16x8, Xor, Bool16x8>), 2)                                    \
-
-#define BOOL16X8_TERNARY_FUNCTION_LIST(V)                                             \
-  V(replaceLane, (ReplaceLane<Bool16x8>), 3)
-
-#define BOOL16X8_FUNCTION_LIST(V)                                                     \
-  BOOL16X8_UNARY_FUNCTION_LIST(V)                                                     \
-  BOOL16X8_BINARY_FUNCTION_LIST(V)                                                    \
-  BOOL16X8_TERNARY_FUNCTION_LIST(V)
-
-// Bool32x4.
-#define BOOL32X4_UNARY_FUNCTION_LIST(V)                                               \
-  V(not, (UnaryFunc<Bool32x4, LogicalNot, Bool32x4>), 1)                              \
-  V(check, (UnaryFunc<Bool32x4, Identity, Bool32x4>), 1)                              \
-  V(splat, (FuncSplat<Bool32x4>), 1)                                                  \
-  V(allTrue, (AllTrue<Bool32x4>), 1)                                                  \
-  V(anyTrue, (AnyTrue<Bool32x4>), 1)
-
-#define BOOL32X4_BINARY_FUNCTION_LIST(V)                                              \
-  V(extractLane, (ExtractLane<Bool32x4>), 2)                                          \
-  V(and, (BinaryFunc<Bool32x4, And, Bool32x4>), 2)                                    \
-  V(or, (BinaryFunc<Bool32x4, Or, Bool32x4>), 2)                                      \
-  V(xor, (BinaryFunc<Bool32x4, Xor, Bool32x4>), 2)                                    \
-
-#define BOOL32X4_TERNARY_FUNCTION_LIST(V)                                             \
-  V(replaceLane, (ReplaceLane<Bool32x4>), 3)
-
-#define BOOL32X4_FUNCTION_LIST(V)                                                     \
-  BOOL32X4_UNARY_FUNCTION_LIST(V)                                                     \
-  BOOL32X4_BINARY_FUNCTION_LIST(V)                                                    \
-  BOOL32X4_TERNARY_FUNCTION_LIST(V)
-
-// Bool64x2.
-#define BOOL64X2_UNARY_FUNCTION_LIST(V)                                               \
-  V(not, (UnaryFunc<Bool64x2, LogicalNot, Bool64x2>), 1)                              \
-  V(check, (UnaryFunc<Bool64x2, Identity, Bool64x2>), 1)                              \
-  V(splat, (FuncSplat<Bool64x2>), 1)                                                  \
-  V(allTrue, (AllTrue<Bool64x2>), 1)                                                  \
-  V(anyTrue, (AnyTrue<Bool64x2>), 1)
-
-#define BOOL64X2_BINARY_FUNCTION_LIST(V)                                              \
-  V(extractLane, (ExtractLane<Bool64x2>), 2)                                          \
-  V(and, (BinaryFunc<Bool64x2, And, Bool64x2>), 2)                                    \
-  V(or, (BinaryFunc<Bool64x2, Or, Bool64x2>), 2)                                      \
-  V(xor, (BinaryFunc<Bool64x2, Xor, Bool64x2>), 2)                                    \
-
-#define BOOL64X2_TERNARY_FUNCTION_LIST(V)                                             \
-  V(replaceLane, (ReplaceLane<Bool64x2>), 3)
-
-#define BOOL64X2_FUNCTION_LIST(V)                                                     \
-  BOOL64X2_UNARY_FUNCTION_LIST(V)                                                     \
-  BOOL64X2_BINARY_FUNCTION_LIST(V)                                                    \
-  BOOL64X2_TERNARY_FUNCTION_LIST(V)
-
-// Float32x4.
-#define FLOAT32X4_UNARY_FUNCTION_LIST(V)                                              \
-  V(abs, (UnaryFunc<Float32x4, Abs, Float32x4>), 1)                                   \
-  V(check, (UnaryFunc<Float32x4, Identity, Float32x4>), 1)                            \
-  V(fromFloat64x2Bits, (FuncConvertBits<Float64x2, Float32x4>), 1)                    \
-  V(fromInt8x16Bits,   (FuncConvertBits<Int8x16,   Float32x4>), 1)                    \
-  V(fromInt16x8Bits,   (FuncConvertBits<Int16x8,   Float32x4>), 1)                    \
-  V(fromInt32x4,       (FuncConvert<Int32x4,       Float32x4>), 1)                    \
-  V(fromInt32x4Bits,   (FuncConvertBits<Int32x4,   Float32x4>), 1)                    \
-  V(fromUint8x16Bits,  (FuncConvertBits<Uint8x16,  Float32x4>), 1)                    \
-  V(fromUint16x8Bits,  (FuncConvertBits<Uint16x8,  Float32x4>), 1)                    \
-  V(fromUint32x4,      (FuncConvert<Uint32x4,      Float32x4>), 1)                    \
-  V(fromUint32x4Bits,  (FuncConvertBits<Uint32x4,  Float32x4>), 1)                    \
-  V(neg, (UnaryFunc<Float32x4, Neg, Float32x4>), 1)                                   \
-  V(reciprocalApproximation, (UnaryFunc<Float32x4, RecApprox, Float32x4>), 1)         \
-  V(reciprocalSqrtApproximation, (UnaryFunc<Float32x4, RecSqrtApprox, Float32x4>), 1) \
-  V(splat, (FuncSplat<Float32x4>), 1)                                                 \
-  V(sqrt, (UnaryFunc<Float32x4, Sqrt, Float32x4>), 1)
-
-#define FLOAT32X4_BINARY_FUNCTION_LIST(V)                                             \
-  V(add, (BinaryFunc<Float32x4, Add, Float32x4>), 2)                                  \
-  V(div, (BinaryFunc<Float32x4, Div, Float32x4>), 2)                                  \
-  V(equal, (CompareFunc<Float32x4, Equal, Bool32x4>), 2)                              \
-  V(extractLane, (ExtractLane<Float32x4>), 2)                                         \
-  V(greaterThan, (CompareFunc<Float32x4, GreaterThan, Bool32x4>), 2)                  \
-  V(greaterThanOrEqual, (CompareFunc<Float32x4, GreaterThanOrEqual, Bool32x4>), 2)    \
-  V(lessThan, (CompareFunc<Float32x4, LessThan, Bool32x4>), 2)                        \
-  V(lessThanOrEqual, (CompareFunc<Float32x4, LessThanOrEqual, Bool32x4>), 2)          \
-  V(load,  (Load<Float32x4, 4>), 2)                                                   \
-  V(load3, (Load<Float32x4, 3>), 2)                                                   \
-  V(load2, (Load<Float32x4, 2>), 2)                                                   \
-  V(load1, (Load<Float32x4, 1>), 2)                                                   \
-  V(max, (BinaryFunc<Float32x4, Maximum, Float32x4>), 2)                              \
-  V(maxNum, (BinaryFunc<Float32x4, MaxNum, Float32x4>), 2)                            \
-  V(min, (BinaryFunc<Float32x4, Minimum, Float32x4>), 2)                              \
-  V(minNum, (BinaryFunc<Float32x4, MinNum, Float32x4>), 2)                            \
-  V(mul, (BinaryFunc<Float32x4, Mul, Float32x4>), 2)                                  \
-  V(notEqual, (CompareFunc<Float32x4, NotEqual, Bool32x4>), 2)                        \
-  V(sub, (BinaryFunc<Float32x4, Sub, Float32x4>), 2)
-
-#define FLOAT32X4_TERNARY_FUNCTION_LIST(V)                                            \
-  V(replaceLane, (ReplaceLane<Float32x4>), 3)                                         \
-  V(select, (Select<Float32x4, Bool32x4>), 3)                                         \
-  V(store,  (Store<Float32x4, 4>), 3)                                                 \
-  V(store3, (Store<Float32x4, 3>), 3)                                                 \
-  V(store2, (Store<Float32x4, 2>), 3)                                                 \
-  V(store1, (Store<Float32x4, 1>), 3)
-
-#define FLOAT32X4_SHUFFLE_FUNCTION_LIST(V)                                            \
-  V(swizzle, Swizzle<Float32x4>, 5)                                                   \
-  V(shuffle, Shuffle<Float32x4>, 6)
-
-#define FLOAT32X4_FUNCTION_LIST(V)                                                    \
-  FLOAT32X4_UNARY_FUNCTION_LIST(V)                                                    \
-  FLOAT32X4_BINARY_FUNCTION_LIST(V)                                                   \
-  FLOAT32X4_TERNARY_FUNCTION_LIST(V)                                                  \
-  FLOAT32X4_SHUFFLE_FUNCTION_LIST(V)
-
-// Float64x2.
-#define FLOAT64X2_UNARY_FUNCTION_LIST(V)                                              \
-  V(abs, (UnaryFunc<Float64x2, Abs, Float64x2>), 1)                                   \
-  V(check, (UnaryFunc<Float64x2, Identity, Float64x2>), 1)                            \
-  V(fromFloat32x4Bits, (FuncConvertBits<Float32x4, Float64x2>), 1)                    \
-  V(fromInt8x16Bits,   (FuncConvertBits<Int8x16,   Float64x2>), 1)                    \
-  V(fromInt16x8Bits,   (FuncConvertBits<Int16x8,   Float64x2>), 1)                    \
-  V(fromInt32x4Bits,   (FuncConvertBits<Int32x4,   Float64x2>), 1)                    \
-  V(fromUint8x16Bits,  (FuncConvertBits<Uint8x16,  Float64x2>), 1)                    \
-  V(fromUint16x8Bits,  (FuncConvertBits<Uint16x8,  Float64x2>), 1)                    \
-  V(fromUint32x4Bits,  (FuncConvertBits<Uint32x4,  Float64x2>), 1)                    \
-  V(neg, (UnaryFunc<Float64x2, Neg, Float64x2>), 1)                                   \
-  V(reciprocalApproximation, (UnaryFunc<Float64x2, RecApprox, Float64x2>), 1)         \
-  V(reciprocalSqrtApproximation, (UnaryFunc<Float64x2, RecSqrtApprox, Float64x2>), 1) \
-  V(splat, (FuncSplat<Float64x2>), 1)                                                 \
-  V(sqrt, (UnaryFunc<Float64x2, Sqrt, Float64x2>), 1)
-
-#define FLOAT64X2_BINARY_FUNCTION_LIST(V)                                             \
-  V(add, (BinaryFunc<Float64x2, Add, Float64x2>), 2)                                  \
-  V(div, (BinaryFunc<Float64x2, Div, Float64x2>), 2)                                  \
-  V(equal, (CompareFunc<Float64x2, Equal, Bool64x2>), 2)                              \
-  V(extractLane, (ExtractLane<Float64x2>), 2)                                         \
-  V(greaterThan, (CompareFunc<Float64x2, GreaterThan, Bool64x2>), 2)                  \
-  V(greaterThanOrEqual, (CompareFunc<Float64x2, GreaterThanOrEqual, Bool64x2>), 2)    \
-  V(lessThan, (CompareFunc<Float64x2, LessThan, Bool64x2>), 2)                        \
-  V(lessThanOrEqual, (CompareFunc<Float64x2, LessThanOrEqual, Bool64x2>), 2)          \
-  V(load,  (Load<Float64x2, 2>), 2)                                                   \
-  V(load1, (Load<Float64x2, 1>), 2)                                                   \
-  V(max, (BinaryFunc<Float64x2, Maximum, Float64x2>), 2)                              \
-  V(maxNum, (BinaryFunc<Float64x2, MaxNum, Float64x2>), 2)                            \
-  V(min, (BinaryFunc<Float64x2, Minimum, Float64x2>), 2)                              \
-  V(minNum, (BinaryFunc<Float64x2, MinNum, Float64x2>), 2)                            \
-  V(mul, (BinaryFunc<Float64x2, Mul, Float64x2>), 2)                                  \
-  V(notEqual, (CompareFunc<Float64x2, NotEqual, Bool64x2>), 2)                        \
-  V(sub, (BinaryFunc<Float64x2, Sub, Float64x2>), 2)
-
-#define FLOAT64X2_TERNARY_FUNCTION_LIST(V)                                            \
-  V(replaceLane, (ReplaceLane<Float64x2>), 3)                                         \
-  V(select, (Select<Float64x2, Bool64x2>), 3)                                         \
-  V(store,  (Store<Float64x2, 2>), 3)                                                 \
-  V(store1, (Store<Float64x2, 1>), 3)
-
-#define FLOAT64X2_SHUFFLE_FUNCTION_LIST(V)                                            \
-  V(swizzle, Swizzle<Float64x2>, 3)                                                   \
-  V(shuffle, Shuffle<Float64x2>, 4)
-
-#define FLOAT64X2_FUNCTION_LIST(V)                                                    \
-  FLOAT64X2_UNARY_FUNCTION_LIST(V)                                                    \
-  FLOAT64X2_BINARY_FUNCTION_LIST(V)                                                   \
-  FLOAT64X2_TERNARY_FUNCTION_LIST(V)                                                  \
-  FLOAT64X2_SHUFFLE_FUNCTION_LIST(V)
-
-// Int8x16.
-#define INT8X16_UNARY_FUNCTION_LIST(V)                                                \
-  V(check, (UnaryFunc<Int8x16, Identity, Int8x16>), 1)                                \
-  V(fromFloat32x4Bits, (FuncConvertBits<Float32x4, Int8x16>), 1)                      \
-  V(fromFloat64x2Bits, (FuncConvertBits<Float64x2, Int8x16>), 1)                      \
-  V(fromInt16x8Bits,   (FuncConvertBits<Int16x8,   Int8x16>), 1)                      \
-  V(fromInt32x4Bits,   (FuncConvertBits<Int32x4,   Int8x16>), 1)                      \
-  V(fromUint8x16Bits,  (FuncConvertBits<Uint8x16,  Int8x16>), 1)                      \
-  V(fromUint16x8Bits,  (FuncConvertBits<Uint16x8,  Int8x16>), 1)                      \
-  V(fromUint32x4Bits,  (FuncConvertBits<Uint32x4,  Int8x16>), 1)                      \
-  V(neg, (UnaryFunc<Int8x16, Neg, Int8x16>), 1)                                       \
-  V(not, (UnaryFunc<Int8x16, Not, Int8x16>), 1)                                       \
-  V(splat, (FuncSplat<Int8x16>), 1)
-
-#define INT8X16_BINARY_FUNCTION_LIST(V)                                               \
-  V(add, (BinaryFunc<Int8x16, Add, Int8x16>), 2)                                      \
-  V(addSaturate, (BinaryFunc<Int8x16, AddSaturate, Int8x16>), 2)                      \
-  V(and, (BinaryFunc<Int8x16, And, Int8x16>), 2)                                      \
-  V(equal, (CompareFunc<Int8x16, Equal, Bool8x16>), 2)                                \
-  V(extractLane, (ExtractLane<Int8x16>), 2)                                           \
-  V(greaterThan, (CompareFunc<Int8x16, GreaterThan, Bool8x16>), 2)                    \
-  V(greaterThanOrEqual, (CompareFunc<Int8x16, GreaterThanOrEqual, Bool8x16>), 2)      \
-  V(lessThan, (CompareFunc<Int8x16, LessThan, Bool8x16>), 2)                          \
-  V(lessThanOrEqual, (CompareFunc<Int8x16, LessThanOrEqual, Bool8x16>), 2)            \
-  V(load, (Load<Int8x16, 16>), 2)                                                     \
-  V(mul, (BinaryFunc<Int8x16, Mul, Int8x16>), 2)                                      \
-  V(notEqual, (CompareFunc<Int8x16, NotEqual, Bool8x16>), 2)                          \
-  V(or, (BinaryFunc<Int8x16, Or, Int8x16>), 2)                                        \
-  V(sub, (BinaryFunc<Int8x16, Sub, Int8x16>), 2)                                      \
-  V(subSaturate, (BinaryFunc<Int8x16, SubSaturate, Int8x16>), 2)                      \
-  V(shiftLeftByScalar, (BinaryScalar<Int8x16, ShiftLeft>), 2)                         \
-  V(shiftRightByScalar, (BinaryScalar<Int8x16, ShiftRightArithmetic>), 2)             \
-  V(xor, (BinaryFunc<Int8x16, Xor, Int8x16>), 2)
-
-#define INT8X16_TERNARY_FUNCTION_LIST(V)                                              \
-  V(replaceLane, (ReplaceLane<Int8x16>), 3)                                           \
-  V(select, (Select<Int8x16, Bool8x16>), 3)                                           \
-  V(store, (Store<Int8x16, 16>), 3)
-
-#define INT8X16_SHUFFLE_FUNCTION_LIST(V)                                              \
-  V(swizzle, Swizzle<Int8x16>, 17)                                                    \
-  V(shuffle, Shuffle<Int8x16>, 18)
-
-#define INT8X16_FUNCTION_LIST(V)                                                      \
-  INT8X16_UNARY_FUNCTION_LIST(V)                                                      \
-  INT8X16_BINARY_FUNCTION_LIST(V)                                                     \
-  INT8X16_TERNARY_FUNCTION_LIST(V)                                                    \
-  INT8X16_SHUFFLE_FUNCTION_LIST(V)
-
-// Uint8x16.
-#define UINT8X16_UNARY_FUNCTION_LIST(V)                                               \
-  V(check, (UnaryFunc<Uint8x16, Identity, Uint8x16>), 1)                              \
-  V(fromFloat32x4Bits, (FuncConvertBits<Float32x4, Uint8x16>), 1)                     \
-  V(fromFloat64x2Bits, (FuncConvertBits<Float64x2, Uint8x16>), 1)                     \
-  V(fromInt8x16Bits,   (FuncConvertBits<Int8x16,   Uint8x16>), 1)                     \
-  V(fromInt16x8Bits,   (FuncConvertBits<Int16x8,   Uint8x16>), 1)                     \
-  V(fromInt32x4Bits,   (FuncConvertBits<Int32x4,   Uint8x16>), 1)                     \
-  V(fromUint16x8Bits,  (FuncConvertBits<Uint16x8,  Uint8x16>), 1)                     \
-  V(fromUint32x4Bits,  (FuncConvertBits<Uint32x4,  Uint8x16>), 1)                     \
-  V(neg, (UnaryFunc<Uint8x16, Neg, Uint8x16>), 1)                                     \
-  V(not, (UnaryFunc<Uint8x16, Not, Uint8x16>), 1)                                     \
-  V(splat, (FuncSplat<Uint8x16>), 1)
-
-#define UINT8X16_BINARY_FUNCTION_LIST(V)                                              \
-  V(add, (BinaryFunc<Uint8x16, Add, Uint8x16>), 2)                                    \
-  V(addSaturate, (BinaryFunc<Uint8x16, AddSaturate, Uint8x16>), 2)                    \
-  V(and, (BinaryFunc<Uint8x16, And, Uint8x16>), 2)                                    \
-  V(equal, (CompareFunc<Uint8x16, Equal, Bool8x16>), 2)                               \
-  V(extractLane, (ExtractLane<Uint8x16>), 2)                                          \
-  V(greaterThan, (CompareFunc<Uint8x16, GreaterThan, Bool8x16>), 2)                   \
-  V(greaterThanOrEqual, (CompareFunc<Uint8x16, GreaterThanOrEqual, Bool8x16>), 2)     \
-  V(lessThan, (CompareFunc<Uint8x16, LessThan, Bool8x16>), 2)                         \
-  V(lessThanOrEqual, (CompareFunc<Uint8x16, LessThanOrEqual, Bool8x16>), 2)           \
-  V(load, (Load<Uint8x16, 16>), 2)                                                    \
-  V(mul, (BinaryFunc<Uint8x16, Mul, Uint8x16>), 2)                                    \
-  V(notEqual, (CompareFunc<Uint8x16, NotEqual, Bool8x16>), 2)                         \
-  V(or, (BinaryFunc<Uint8x16, Or, Uint8x16>), 2)                                      \
-  V(sub, (BinaryFunc<Uint8x16, Sub, Uint8x16>), 2)                                    \
-  V(subSaturate, (BinaryFunc<Uint8x16, SubSaturate, Uint8x16>), 2)                    \
-  V(shiftLeftByScalar, (BinaryScalar<Uint8x16, ShiftLeft>), 2)                        \
-  V(shiftRightByScalar, (BinaryScalar<Uint8x16, ShiftRightLogical>), 2)               \
-  V(xor, (BinaryFunc<Uint8x16, Xor, Uint8x16>), 2)
-
-#define UINT8X16_TERNARY_FUNCTION_LIST(V)                                             \
-  V(replaceLane, (ReplaceLane<Uint8x16>), 3)                                          \
-  V(select, (Select<Uint8x16, Bool8x16>), 3)                                          \
-  V(store, (Store<Uint8x16, 16>), 3)
-
-#define UINT8X16_SHUFFLE_FUNCTION_LIST(V)                                             \
-  V(swizzle, Swizzle<Uint8x16>, 17)                                                   \
-  V(shuffle, Shuffle<Uint8x16>, 18)
-
-#define UINT8X16_FUNCTION_LIST(V)                                                     \
-  UINT8X16_UNARY_FUNCTION_LIST(V)                                                     \
-  UINT8X16_BINARY_FUNCTION_LIST(V)                                                    \
-  UINT8X16_TERNARY_FUNCTION_LIST(V)                                                   \
-  UINT8X16_SHUFFLE_FUNCTION_LIST(V)
-
-// Int16x8.
-#define INT16X8_UNARY_FUNCTION_LIST(V)                                                \
-  V(check, (UnaryFunc<Int16x8, Identity, Int16x8>), 1)                                \
-  V(fromFloat32x4Bits, (FuncConvertBits<Float32x4, Int16x8>), 1)                      \
-  V(fromFloat64x2Bits, (FuncConvertBits<Float64x2, Int16x8>), 1)                      \
-  V(fromInt8x16Bits,   (FuncConvertBits<Int8x16,   Int16x8>), 1)                      \
-  V(fromInt32x4Bits,   (FuncConvertBits<Int32x4,   Int16x8>), 1)                      \
-  V(fromUint8x16Bits,  (FuncConvertBits<Uint8x16,  Int16x8>), 1)                      \
-  V(fromUint16x8Bits,  (FuncConvertBits<Uint16x8,  Int16x8>), 1)                      \
-  V(fromUint32x4Bits,  (FuncConvertBits<Uint32x4,  Int16x8>), 1)                      \
-  V(neg, (UnaryFunc<Int16x8, Neg, Int16x8>), 1)                                       \
-  V(not, (UnaryFunc<Int16x8, Not, Int16x8>), 1)                                       \
-  V(splat, (FuncSplat<Int16x8>), 1)
-
-#define INT16X8_BINARY_FUNCTION_LIST(V)                                               \
-  V(add, (BinaryFunc<Int16x8, Add, Int16x8>), 2)                                      \
-  V(addSaturate, (BinaryFunc<Int16x8, AddSaturate, Int16x8>), 2)                      \
-  V(and, (BinaryFunc<Int16x8, And, Int16x8>), 2)                                      \
-  V(equal, (CompareFunc<Int16x8, Equal, Bool16x8>), 2)                                \
-  V(extractLane, (ExtractLane<Int16x8>), 2)                                           \
-  V(greaterThan, (CompareFunc<Int16x8, GreaterThan, Bool16x8>), 2)                    \
-  V(greaterThanOrEqual, (CompareFunc<Int16x8, GreaterThanOrEqual, Bool16x8>), 2)      \
-  V(lessThan, (CompareFunc<Int16x8, LessThan, Bool16x8>), 2)                          \
-  V(lessThanOrEqual, (CompareFunc<Int16x8, LessThanOrEqual, Bool16x8>), 2)            \
-  V(load, (Load<Int16x8, 8>), 2)                                                      \
-  V(mul, (BinaryFunc<Int16x8, Mul, Int16x8>), 2)                                      \
-  V(notEqual, (CompareFunc<Int16x8, NotEqual, Bool16x8>), 2)                          \
-  V(or, (BinaryFunc<Int16x8, Or, Int16x8>), 2)                                        \
-  V(sub, (BinaryFunc<Int16x8, Sub, Int16x8>), 2)                                      \
-  V(subSaturate, (BinaryFunc<Int16x8, SubSaturate, Int16x8>), 2)                      \
-  V(shiftLeftByScalar, (BinaryScalar<Int16x8, ShiftLeft>), 2)                         \
-  V(shiftRightByScalar, (BinaryScalar<Int16x8, ShiftRightArithmetic>), 2)             \
-  V(xor, (BinaryFunc<Int16x8, Xor, Int16x8>), 2)
-
-#define INT16X8_TERNARY_FUNCTION_LIST(V)                                              \
-  V(replaceLane, (ReplaceLane<Int16x8>), 3)                                           \
-  V(select, (Select<Int16x8, Bool16x8>), 3)                                           \
-  V(store, (Store<Int16x8, 8>), 3)
-
-#define INT16X8_SHUFFLE_FUNCTION_LIST(V)                                              \
-  V(swizzle, Swizzle<Int16x8>, 9)                                                     \
-  V(shuffle, Shuffle<Int16x8>, 10)
-
-#define INT16X8_FUNCTION_LIST(V)                                                      \
-  INT16X8_UNARY_FUNCTION_LIST(V)                                                      \
-  INT16X8_BINARY_FUNCTION_LIST(V)                                                     \
-  INT16X8_TERNARY_FUNCTION_LIST(V)                                                    \
-  INT16X8_SHUFFLE_FUNCTION_LIST(V)
-
-// Uint16x8.
-#define UINT16X8_UNARY_FUNCTION_LIST(V)                                               \
-  V(check, (UnaryFunc<Uint16x8, Identity, Uint16x8>), 1)                              \
-  V(fromFloat32x4Bits, (FuncConvertBits<Float32x4, Uint16x8>), 1)                     \
-  V(fromFloat64x2Bits, (FuncConvertBits<Float64x2, Uint16x8>), 1)                     \
-  V(fromInt8x16Bits,   (FuncConvertBits<Int8x16,   Uint16x8>), 1)                     \
-  V(fromInt16x8Bits,   (FuncConvertBits<Int16x8,   Uint16x8>), 1)                     \
-  V(fromInt32x4Bits,   (FuncConvertBits<Int32x4,   Uint16x8>), 1)                     \
-  V(fromUint8x16Bits,  (FuncConvertBits<Uint8x16,  Uint16x8>), 1)                     \
-  V(fromUint32x4Bits,  (FuncConvertBits<Uint32x4,  Uint16x8>), 1)                     \
-  V(neg, (UnaryFunc<Uint16x8, Neg, Uint16x8>), 1)                                     \
-  V(not, (UnaryFunc<Uint16x8, Not, Uint16x8>), 1)                                     \
-  V(splat, (FuncSplat<Uint16x8>), 1)
-
-#define UINT16X8_BINARY_FUNCTION_LIST(V)                                              \
-  V(add, (BinaryFunc<Uint16x8, Add, Uint16x8>), 2)                                    \
-  V(addSaturate, (BinaryFunc<Uint16x8, AddSaturate, Uint16x8>), 2)                    \
-  V(and, (BinaryFunc<Uint16x8, And, Uint16x8>), 2)                                    \
-  V(equal, (CompareFunc<Uint16x8, Equal, Bool16x8>), 2)                               \
-  V(extractLane, (ExtractLane<Uint16x8>), 2)                                          \
-  V(greaterThan, (CompareFunc<Uint16x8, GreaterThan, Bool16x8>), 2)                   \
-  V(greaterThanOrEqual, (CompareFunc<Uint16x8, GreaterThanOrEqual, Bool16x8>), 2)     \
-  V(lessThan, (CompareFunc<Uint16x8, LessThan, Bool16x8>), 2)                         \
-  V(lessThanOrEqual, (CompareFunc<Uint16x8, LessThanOrEqual, Bool16x8>), 2)           \
-  V(load, (Load<Uint16x8, 8>), 2)                                                     \
-  V(mul, (BinaryFunc<Uint16x8, Mul, Uint16x8>), 2)                                    \
-  V(notEqual, (CompareFunc<Uint16x8, NotEqual, Bool16x8>), 2)                         \
-  V(or, (BinaryFunc<Uint16x8, Or, Uint16x8>), 2)                                      \
-  V(sub, (BinaryFunc<Uint16x8, Sub, Uint16x8>), 2)                                    \
-  V(subSaturate, (BinaryFunc<Uint16x8, SubSaturate, Uint16x8>), 2)                    \
-  V(shiftLeftByScalar, (BinaryScalar<Uint16x8, ShiftLeft>), 2)                        \
-  V(shiftRightByScalar, (BinaryScalar<Uint16x8, ShiftRightLogical>), 2)               \
-  V(xor, (BinaryFunc<Uint16x8, Xor, Uint16x8>), 2)
-
-#define UINT16X8_TERNARY_FUNCTION_LIST(V)                                             \
-  V(replaceLane, (ReplaceLane<Uint16x8>), 3)                                          \
-  V(select, (Select<Uint16x8, Bool16x8>), 3)                                          \
-  V(store, (Store<Uint16x8, 8>), 3)
-
-#define UINT16X8_SHUFFLE_FUNCTION_LIST(V)                                             \
-  V(swizzle, Swizzle<Uint16x8>, 9)                                                    \
-  V(shuffle, Shuffle<Uint16x8>, 10)
-
-#define UINT16X8_FUNCTION_LIST(V)                                                     \
-  UINT16X8_UNARY_FUNCTION_LIST(V)                                                     \
-  UINT16X8_BINARY_FUNCTION_LIST(V)                                                    \
-  UINT16X8_TERNARY_FUNCTION_LIST(V)                                                   \
-  UINT16X8_SHUFFLE_FUNCTION_LIST(V)
-
-// Int32x4.
-#define INT32X4_UNARY_FUNCTION_LIST(V)                                                \
-  V(check, (UnaryFunc<Int32x4, Identity, Int32x4>), 1)                                \
-  V(fromFloat32x4,     (FuncConvert<Float32x4,     Int32x4>), 1)                      \
-  V(fromFloat32x4Bits, (FuncConvertBits<Float32x4, Int32x4>), 1)                      \
-  V(fromFloat64x2Bits, (FuncConvertBits<Float64x2, Int32x4>), 1)                      \
-  V(fromInt8x16Bits,   (FuncConvertBits<Int8x16,   Int32x4>), 1)                      \
-  V(fromInt16x8Bits,   (FuncConvertBits<Int16x8,   Int32x4>), 1)                      \
-  V(fromUint8x16Bits,  (FuncConvertBits<Uint8x16,  Int32x4>), 1)                      \
-  V(fromUint16x8Bits,  (FuncConvertBits<Uint16x8,  Int32x4>), 1)                      \
-  V(fromUint32x4Bits,  (FuncConvertBits<Uint32x4,  Int32x4>), 1)                      \
-  V(neg, (UnaryFunc<Int32x4, Neg, Int32x4>), 1)                                       \
-  V(not, (UnaryFunc<Int32x4, Not, Int32x4>), 1)                                       \
-  V(splat, (FuncSplat<Int32x4>), 0)
-
-#define INT32X4_BINARY_FUNCTION_LIST(V)                                               \
-  V(add, (BinaryFunc<Int32x4, Add, Int32x4>), 2)                                      \
-  V(and, (BinaryFunc<Int32x4, And, Int32x4>), 2)                                      \
-  V(equal, (CompareFunc<Int32x4, Equal, Bool32x4>), 2)                                \
-  V(extractLane, (ExtractLane<Int32x4>), 2)                                           \
-  V(greaterThan, (CompareFunc<Int32x4, GreaterThan, Bool32x4>), 2)                    \
-  V(greaterThanOrEqual, (CompareFunc<Int32x4, GreaterThanOrEqual, Bool32x4>), 2)      \
-  V(lessThan, (CompareFunc<Int32x4, LessThan, Bool32x4>), 2)                          \
-  V(lessThanOrEqual, (CompareFunc<Int32x4, LessThanOrEqual, Bool32x4>), 2)            \
-  V(load,  (Load<Int32x4, 4>), 2)                                                     \
-  V(load3, (Load<Int32x4, 3>), 2)                                                     \
-  V(load2, (Load<Int32x4, 2>), 2)                                                     \
-  V(load1, (Load<Int32x4, 1>), 2)                                                     \
-  V(mul, (BinaryFunc<Int32x4, Mul, Int32x4>), 2)                                      \
-  V(notEqual, (CompareFunc<Int32x4, NotEqual, Bool32x4>), 2)                          \
-  V(or, (BinaryFunc<Int32x4, Or, Int32x4>), 2)                                        \
-  V(sub, (BinaryFunc<Int32x4, Sub, Int32x4>), 2)                                      \
-  V(shiftLeftByScalar, (BinaryScalar<Int32x4, ShiftLeft>), 2)                         \
-  V(shiftRightByScalar, (BinaryScalar<Int32x4, ShiftRightArithmetic>), 2)             \
-  V(xor, (BinaryFunc<Int32x4, Xor, Int32x4>), 2)
-
-#define INT32X4_TERNARY_FUNCTION_LIST(V)                                              \
-  V(replaceLane, (ReplaceLane<Int32x4>), 3)                                           \
-  V(select, (Select<Int32x4, Bool32x4>), 3)                                           \
-  V(store,  (Store<Int32x4, 4>), 3)                                                   \
-  V(store3, (Store<Int32x4, 3>), 3)                                                   \
-  V(store2, (Store<Int32x4, 2>), 3)                                                   \
-  V(store1, (Store<Int32x4, 1>), 3)
-
-#define INT32X4_SHUFFLE_FUNCTION_LIST(V)                                              \
-  V(swizzle, Swizzle<Int32x4>, 5)                                                     \
-  V(shuffle, Shuffle<Int32x4>, 6)
-
-#define INT32X4_FUNCTION_LIST(V)                                                      \
-  INT32X4_UNARY_FUNCTION_LIST(V)                                                      \
-  INT32X4_BINARY_FUNCTION_LIST(V)                                                     \
-  INT32X4_TERNARY_FUNCTION_LIST(V)                                                    \
-  INT32X4_SHUFFLE_FUNCTION_LIST(V)
-
-// Uint32x4.
-#define UINT32X4_UNARY_FUNCTION_LIST(V)                                               \
-  V(check, (UnaryFunc<Uint32x4, Identity, Uint32x4>), 1)                              \
-  V(fromFloat32x4,     (FuncConvert<Float32x4,     Uint32x4>), 1)                     \
-  V(fromFloat32x4Bits, (FuncConvertBits<Float32x4, Uint32x4>), 1)                     \
-  V(fromFloat64x2Bits, (FuncConvertBits<Float64x2, Uint32x4>), 1)                     \
-  V(fromInt8x16Bits,   (FuncConvertBits<Int8x16,   Uint32x4>), 1)                     \
-  V(fromInt16x8Bits,   (FuncConvertBits<Int16x8,   Uint32x4>), 1)                     \
-  V(fromInt32x4Bits,   (FuncConvertBits<Int32x4,   Uint32x4>), 1)                     \
-  V(fromUint8x16Bits,  (FuncConvertBits<Uint8x16,  Uint32x4>), 1)                     \
-  V(fromUint16x8Bits,  (FuncConvertBits<Uint16x8,  Uint32x4>), 1)                     \
-  V(neg, (UnaryFunc<Uint32x4, Neg, Uint32x4>), 1)                                     \
-  V(not, (UnaryFunc<Uint32x4, Not, Uint32x4>), 1)                                     \
-  V(splat, (FuncSplat<Uint32x4>), 0)
-
-#define UINT32X4_BINARY_FUNCTION_LIST(V)                                              \
-  V(add, (BinaryFunc<Uint32x4, Add, Uint32x4>), 2)                                    \
-  V(and, (BinaryFunc<Uint32x4, And, Uint32x4>), 2)                                    \
-  V(equal, (CompareFunc<Uint32x4, Equal, Bool32x4>), 2)                               \
-  V(extractLane, (ExtractLane<Uint32x4>), 2)                                          \
-  V(greaterThan, (CompareFunc<Uint32x4, GreaterThan, Bool32x4>), 2)                   \
-  V(greaterThanOrEqual, (CompareFunc<Uint32x4, GreaterThanOrEqual, Bool32x4>), 2)     \
-  V(lessThan, (CompareFunc<Uint32x4, LessThan, Bool32x4>), 2)                         \
-  V(lessThanOrEqual, (CompareFunc<Uint32x4, LessThanOrEqual, Bool32x4>), 2)           \
-  V(load,  (Load<Uint32x4, 4>), 2)                                                    \
-  V(load3, (Load<Uint32x4, 3>), 2)                                                    \
-  V(load2, (Load<Uint32x4, 2>), 2)                                                    \
-  V(load1, (Load<Uint32x4, 1>), 2)                                                    \
-  V(mul, (BinaryFunc<Uint32x4, Mul, Uint32x4>), 2)                                    \
-  V(notEqual, (CompareFunc<Uint32x4, NotEqual, Bool32x4>), 2)                         \
-  V(or, (BinaryFunc<Uint32x4, Or, Uint32x4>), 2)                                      \
-  V(sub, (BinaryFunc<Uint32x4, Sub, Uint32x4>), 2)                                    \
-  V(shiftLeftByScalar, (BinaryScalar<Uint32x4, ShiftLeft>), 2)                        \
-  V(shiftRightByScalar, (BinaryScalar<Uint32x4, ShiftRightLogical>), 2)               \
-  V(xor, (BinaryFunc<Uint32x4, Xor, Uint32x4>), 2)
-
-#define UINT32X4_TERNARY_FUNCTION_LIST(V)                                             \
-  V(replaceLane, (ReplaceLane<Uint32x4>), 3)                                          \
-  V(select, (Select<Uint32x4, Bool32x4>), 3)                                          \
-  V(store,  (Store<Uint32x4, 4>), 3)                                                  \
-  V(store3, (Store<Uint32x4, 3>), 3)                                                  \
-  V(store2, (Store<Uint32x4, 2>), 3)                                                  \
-  V(store1, (Store<Uint32x4, 1>), 3)
-
-#define UINT32X4_SHUFFLE_FUNCTION_LIST(V)                                             \
-  V(swizzle, Swizzle<Uint32x4>, 5)                                                    \
-  V(shuffle, Shuffle<Uint32x4>, 6)
-
-#define UINT32X4_FUNCTION_LIST(V)                                                     \
-  UINT32X4_UNARY_FUNCTION_LIST(V)                                                     \
-  UINT32X4_BINARY_FUNCTION_LIST(V)                                                    \
-  UINT32X4_TERNARY_FUNCTION_LIST(V)                                                   \
-  UINT32X4_SHUFFLE_FUNCTION_LIST(V)
-
-/*
- * The FOREACH macros below partition all of the SIMD operations into disjoint
- * sets.
- */
-
-// Operations available on all SIMD types. Mixed arity.
-#define FOREACH_COMMON_SIMD_OP(_)     \
-    _(extractLane)                    \
-    _(replaceLane)                    \
-    _(check)                          \
-    _(splat)
-
-// Lanewise operations available on numeric SIMD types.
-// Include lane-wise select here since it is not arithmetic and defined on
-// numeric types too.
-#define FOREACH_LANE_SIMD_OP(_)       \
-    _(select)                         \
-    _(swizzle)                        \
-    _(shuffle)
-
-// Memory operations available on numeric SIMD types.
-#define FOREACH_MEMORY_SIMD_OP(_)     \
-    _(load)                           \
-    _(store)
-
-// Memory operations available on numeric X4 SIMD types.
-#define FOREACH_MEMORY_X4_SIMD_OP(_)  \
-    _(load1)                          \
-    _(load2)                          \
-    _(load3)                          \
-    _(store1)                         \
-    _(store2)                         \
-    _(store3)
-
-// Unary operations on Bool vectors.
-#define FOREACH_BOOL_SIMD_UNOP(_)     \
-    _(allTrue)                        \
-    _(anyTrue)
-
-// Unary bitwise SIMD operators defined on all integer and boolean SIMD types.
-#define FOREACH_BITWISE_SIMD_UNOP(_)  \
-    _(not)
-
-// Binary bitwise SIMD operators defined on all integer and boolean SIMD types.
-#define FOREACH_BITWISE_SIMD_BINOP(_) \
-    _(and)                            \
-    _(or)                             \
-    _(xor)
-
-// Bitwise shifts defined on integer SIMD types.
-#define FOREACH_SHIFT_SIMD_OP(_)      \
-    _(shiftLeftByScalar)              \
-    _(shiftRightByScalar)
-
-// Unary arithmetic operators defined on numeric SIMD types.
-#define FOREACH_NUMERIC_SIMD_UNOP(_)  \
-    _(neg)
-
-// Binary arithmetic operators defined on numeric SIMD types.
-#define FOREACH_NUMERIC_SIMD_BINOP(_) \
-    _(add)                            \
-    _(sub)                            \
-    _(mul)
-
-// Unary arithmetic operators defined on floating point SIMD types.
-#define FOREACH_FLOAT_SIMD_UNOP(_)    \
-    _(abs)                            \
-    _(sqrt)                           \
-    _(reciprocalApproximation)        \
-    _(reciprocalSqrtApproximation)
-
-// Binary arithmetic operators defined on floating point SIMD types.
-#define FOREACH_FLOAT_SIMD_BINOP(_)   \
-    _(div)                            \
-    _(max)                            \
-    _(min)                            \
-    _(maxNum)                         \
-    _(minNum)
-
-// Binary operations on small integer (< 32 bits) vectors.
-#define FOREACH_SMINT_SIMD_BINOP(_)   \
-    _(addSaturate)                    \
-    _(subSaturate)
-
-// Comparison operators defined on numeric SIMD types.
-#define FOREACH_COMP_SIMD_OP(_)       \
-    _(lessThan)                       \
-    _(lessThanOrEqual)                \
-    _(equal)                          \
-    _(notEqual)                       \
-    _(greaterThan)                    \
-    _(greaterThanOrEqual)
-
-/*
- * All SIMD operations, excluding casts.
- */
-#define FORALL_SIMD_NONCAST_OP(_)     \
-    FOREACH_COMMON_SIMD_OP(_)         \
-    FOREACH_LANE_SIMD_OP(_)           \
-    FOREACH_MEMORY_SIMD_OP(_)         \
-    FOREACH_MEMORY_X4_SIMD_OP(_)      \
-    FOREACH_BOOL_SIMD_UNOP(_)         \
-    FOREACH_BITWISE_SIMD_UNOP(_)      \
-    FOREACH_BITWISE_SIMD_BINOP(_)     \
-    FOREACH_SHIFT_SIMD_OP(_)          \
-    FOREACH_NUMERIC_SIMD_UNOP(_)      \
-    FOREACH_NUMERIC_SIMD_BINOP(_)     \
-    FOREACH_FLOAT_SIMD_UNOP(_)        \
-    FOREACH_FLOAT_SIMD_BINOP(_)       \
-    FOREACH_SMINT_SIMD_BINOP(_)       \
-    FOREACH_COMP_SIMD_OP(_)
-
-/*
- * All operations on integer SIMD types, excluding casts and
- * FOREACH_MEMORY_X4_OP.
- */
-#define FORALL_INT_SIMD_OP(_)         \
-    FOREACH_COMMON_SIMD_OP(_)         \
-    FOREACH_LANE_SIMD_OP(_)           \
-    FOREACH_MEMORY_SIMD_OP(_)         \
-    FOREACH_BITWISE_SIMD_UNOP(_)      \
-    FOREACH_BITWISE_SIMD_BINOP(_)     \
-    FOREACH_SHIFT_SIMD_OP(_)          \
-    FOREACH_NUMERIC_SIMD_UNOP(_)      \
-    FOREACH_NUMERIC_SIMD_BINOP(_)     \
-    FOREACH_COMP_SIMD_OP(_)
-
-/*
- * All operations on floating point SIMD types, excluding casts and
- * FOREACH_MEMORY_X4_OP.
- */
-#define FORALL_FLOAT_SIMD_OP(_)       \
-    FOREACH_COMMON_SIMD_OP(_)         \
-    FOREACH_LANE_SIMD_OP(_)           \
-    FOREACH_MEMORY_SIMD_OP(_)         \
-    FOREACH_NUMERIC_SIMD_UNOP(_)      \
-    FOREACH_NUMERIC_SIMD_BINOP(_)     \
-    FOREACH_FLOAT_SIMD_UNOP(_)        \
-    FOREACH_FLOAT_SIMD_BINOP(_)       \
-    FOREACH_COMP_SIMD_OP(_)
-
-/*
- * All operations on Bool SIMD types.
- *
- * These types don't have casts, so no need to specialize.
- */
-#define FORALL_BOOL_SIMD_OP(_)        \
-    FOREACH_COMMON_SIMD_OP(_)         \
-    FOREACH_BOOL_SIMD_UNOP(_)         \
-    FOREACH_BITWISE_SIMD_UNOP(_)      \
-    FOREACH_BITWISE_SIMD_BINOP(_)
-
-/*
- * The sets of cast operations are listed per type below.
- *
- * These sets are not disjoint.
- */
-
-#define FOREACH_INT8X16_SIMD_CAST(_)  \
-    _(fromFloat32x4Bits)              \
-    _(fromFloat64x2Bits)              \
-    _(fromInt16x8Bits)                \
-    _(fromInt32x4Bits)
-
-#define FOREACH_INT16X8_SIMD_CAST(_)  \
-    _(fromFloat32x4Bits)              \
-    _(fromFloat64x2Bits)              \
-    _(fromInt8x16Bits)                \
-    _(fromInt32x4Bits)
-
-#define FOREACH_INT32X4_SIMD_CAST(_)  \
-    _(fromFloat32x4)                  \
-    _(fromFloat32x4Bits)              \
-    _(fromFloat64x2Bits)              \
-    _(fromInt8x16Bits)                \
-    _(fromInt16x8Bits)
-
-#define FOREACH_FLOAT32X4_SIMD_CAST(_)\
-    _(fromFloat64x2Bits)              \
-    _(fromInt8x16Bits)                \
-    _(fromInt16x8Bits)                \
-    _(fromInt32x4)                    \
-    _(fromInt32x4Bits)
-
-#define FOREACH_FLOAT64X2_SIMD_CAST(_)\
-    _(fromFloat32x4Bits)              \
-    _(fromInt8x16Bits)                \
-    _(fromInt16x8Bits)                \
-    _(fromInt32x4Bits)
-
-// All operations on Int32x4.
-#define FORALL_INT32X4_SIMD_OP(_)     \
-    FORALL_INT_SIMD_OP(_)             \
-    FOREACH_MEMORY_X4_SIMD_OP(_)      \
-    FOREACH_INT32X4_SIMD_CAST(_)
-
-// All operations on Float32X4
-#define FORALL_FLOAT32X4_SIMD_OP(_)   \
-    FORALL_FLOAT_SIMD_OP(_)           \
-    FOREACH_MEMORY_X4_SIMD_OP(_)      \
-    FOREACH_FLOAT32X4_SIMD_CAST(_)
-
-/*
- * All SIMD operations assuming only 32x4 types exist.
- * This is used in the current asm.js impl.
- */
-#define FORALL_SIMD_ASMJS_OP(_)       \
-    FORALL_SIMD_NONCAST_OP(_)         \
-    _(fromFloat32x4)                  \
-    _(fromFloat32x4Bits)              \
-    _(fromInt8x16Bits)                \
-    _(fromInt16x8Bits)                \
-    _(fromInt32x4)                    \
-    _(fromInt32x4Bits)                \
-    _(fromUint8x16Bits)               \
-    _(fromUint16x8Bits)               \
-    _(fromUint32x4)                   \
-    _(fromUint32x4Bits)
-
-// All operations on Int8x16 or Uint8x16 in the asm.js world.
-// Note: this does not include conversions and casts to/from Uint8x16 because
-// this list is shared between Int8x16 and Uint8x16.
-#define FORALL_INT8X16_ASMJS_OP(_)    \
-    FORALL_INT_SIMD_OP(_)             \
-    FOREACH_SMINT_SIMD_BINOP(_)       \
-    _(fromInt16x8Bits)                \
-    _(fromInt32x4Bits)                \
-    _(fromFloat32x4Bits)
-
-// All operations on Int16x8 or Uint16x8 in the asm.js world.
-// Note: this does not include conversions and casts to/from Uint16x8 because
-// this list is shared between Int16x8 and Uint16x8.
-#define FORALL_INT16X8_ASMJS_OP(_)    \
-    FORALL_INT_SIMD_OP(_)             \
-    FOREACH_SMINT_SIMD_BINOP(_)       \
-    _(fromInt8x16Bits)                \
-    _(fromInt32x4Bits)                \
-    _(fromFloat32x4Bits)
-
-// All operations on Int32x4 or Uint32x4 in the asm.js world.
-// Note: this does not include conversions and casts to/from Uint32x4 because
-// this list is shared between Int32x4 and Uint32x4.
-#define FORALL_INT32X4_ASMJS_OP(_)    \
-    FORALL_INT_SIMD_OP(_)             \
-    FOREACH_MEMORY_X4_SIMD_OP(_)      \
-    _(fromInt8x16Bits)                \
-    _(fromInt16x8Bits)                \
-    _(fromFloat32x4)                  \
-    _(fromFloat32x4Bits)
-
-// All operations on Float32X4 in the asm.js world.
-#define FORALL_FLOAT32X4_ASMJS_OP(_)  \
-    FORALL_FLOAT_SIMD_OP(_)           \
-    FOREACH_MEMORY_X4_SIMD_OP(_)      \
-    _(fromInt8x16Bits)                \
-    _(fromInt16x8Bits)                \
-    _(fromInt32x4Bits)                \
-    _(fromInt32x4)                    \
-    _(fromUint32x4)
-
 namespace js {
 
 class GlobalObject;
 
-// Complete set of SIMD types.
-// It must be kept in sync with the enumeration of values in
-// TypedObjectConstants.h; in particular we need to ensure that Count is
-// appropriately set with respect to the number of actual types.
-enum class SimdType {
-    Int8x16   = JS_SIMDTYPEREPR_INT8X16,
-    Int16x8   = JS_SIMDTYPEREPR_INT16X8,
-    Int32x4   = JS_SIMDTYPEREPR_INT32X4,
-    Uint8x16  = JS_SIMDTYPEREPR_UINT8X16,
-    Uint16x8  = JS_SIMDTYPEREPR_UINT16X8,
-    Uint32x4  = JS_SIMDTYPEREPR_UINT32X4,
-    Float32x4 = JS_SIMDTYPEREPR_FLOAT32X4,
-    Float64x2 = JS_SIMDTYPEREPR_FLOAT64X2,
-    Bool8x16  = JS_SIMDTYPEREPR_BOOL8X16,
-    Bool16x8  = JS_SIMDTYPEREPR_BOOL16X8,
-    Bool32x4  = JS_SIMDTYPEREPR_BOOL32X4,
-    Bool64x2  = JS_SIMDTYPEREPR_BOOL64X2,
-    Count
-};
-
-// The integer SIMD types have a lot of operations that do the exact same thing
-// for signed and unsigned integer types. Sometimes it is simpler to treat
-// signed and unsigned integer SIMD types as the same type, using a SimdSign to
-// distinguish the few cases where there is a difference.
-enum class SimdSign {
-    // Signedness is not applicable to this type. (i.e., Float or Bool).
-    NotApplicable,
-    // Treat as an unsigned integer with a range 0 .. 2^N-1.
-    Unsigned,
-    // Treat as a signed integer in two's complement encoding.
-    Signed,
-};
-
-// Get the signedness of a SIMD type.
-inline SimdSign
-GetSimdSign(SimdType t)
-{
-    switch(t) {
-      case SimdType::Int8x16:
-      case SimdType::Int16x8:
-      case SimdType::Int32x4:
-        return SimdSign::Signed;
-
-      case SimdType::Uint8x16:
-      case SimdType::Uint16x8:
-      case SimdType::Uint32x4:
-        return SimdSign::Unsigned;
-
-      default:
-        return SimdSign::NotApplicable;
-    }
-}
-
-inline bool
-IsSignedIntSimdType(SimdType type)
-{
-    return GetSimdSign(type) == SimdSign::Signed;
-}
-
-// Get the boolean SIMD type with the same shape as t.
-//
-// This is the result type of a comparison operation, and it can also be used to
-// identify the geometry of a SIMD type.
-inline SimdType
-GetBooleanSimdType(SimdType t)
-{
-    switch(t) {
-      case SimdType::Int8x16:
-      case SimdType::Uint8x16:
-      case SimdType::Bool8x16:
-        return SimdType::Bool8x16;
-
-      case SimdType::Int16x8:
-      case SimdType::Uint16x8:
-      case SimdType::Bool16x8:
-        return SimdType::Bool16x8;
-
-      case SimdType::Int32x4:
-      case SimdType::Uint32x4:
-      case SimdType::Float32x4:
-      case SimdType::Bool32x4:
-        return SimdType::Bool32x4;
-
-      case SimdType::Float64x2:
-      case SimdType::Bool64x2:
-        return SimdType::Bool64x2;
-
-      case SimdType::Count:
-        break;
-    }
-    MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Bad SIMD type");
-}
-
-// Get the number of lanes in a SIMD type.
-inline unsigned
-GetSimdLanes(SimdType t)
-{
-    switch(t) {
-      case SimdType::Int8x16:
-      case SimdType::Uint8x16:
-      case SimdType::Bool8x16:
-        return 16;
-
-      case SimdType::Int16x8:
-      case SimdType::Uint16x8:
-      case SimdType::Bool16x8:
-        return 8;
-
-      case SimdType::Int32x4:
-      case SimdType::Uint32x4:
-      case SimdType::Float32x4:
-      case SimdType::Bool32x4:
-        return 4;
-
-      case SimdType::Float64x2:
-      case SimdType::Bool64x2:
-        return 2;
-
-      case SimdType::Count:
-        break;
-    }
-    MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Bad SIMD type");
-}
-
-// Complete set of SIMD operations.
-//
-// No SIMD types implement all of these operations.
-//
-// C++ defines keywords and/or/xor/not, so prepend Fn_ to all named functions to
-// avoid clashes.
-//
-// Note: because of a gcc < v4.8's compiler bug, uint8_t can't be used as the
-// storage class here. See bug 1243810. See also
-// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=64037 .
-enum class SimdOperation {
-    // The constructor call. No Fn_ prefix here.
-    Constructor,
-
-    // All the operations, except for casts.
-#define DEFOP(x) Fn_##x,
-    FORALL_SIMD_NONCAST_OP(DEFOP)
-#undef DEFOP
-
-    // Int <-> Float conversions.
-    Fn_fromInt32x4,
-    Fn_fromUint32x4,
-    Fn_fromFloat32x4,
-
-    // Bitcasts. One for each type with a memory representation.
-    Fn_fromInt8x16Bits,
-    Fn_fromInt16x8Bits,
-    Fn_fromInt32x4Bits,
-    Fn_fromUint8x16Bits,
-    Fn_fromUint16x8Bits,
-    Fn_fromUint32x4Bits,
-    Fn_fromFloat32x4Bits,
-    Fn_fromFloat64x2Bits,
-
-    Last = Fn_fromFloat64x2Bits
-};
-
 // These classes implement the concept containing the following constraints:
 // - requires typename Elem: this is the scalar lane type, stored in each lane
 // of the SIMD vector.
 // - requires static const unsigned lanes: this is the number of lanes (length)
 // of the SIMD vector.
 // - requires static const SimdType type: this is the SimdType enum value
 // corresponding to the SIMD type.
 // - requires static bool Cast(JSContext*, JS::HandleValue, Elem*): casts a
new file mode 100644
--- /dev/null
+++ b/js/src/builtin/SIMDConstants.h
@@ -0,0 +1,941 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef builtin_SIMDConstants_h
+#define builtin_SIMDConstants_h
+
+#include "mozilla/Assertions.h"
+
+#include "builtin/TypedObjectConstants.h"
+
+// Bool8x16.
+#define BOOL8X16_UNARY_FUNCTION_LIST(V)                                               \
+  V(not, (UnaryFunc<Bool8x16, LogicalNot, Bool8x16>), 1)                              \
+  V(check, (UnaryFunc<Bool8x16, Identity, Bool8x16>), 1)                              \
+  V(splat, (FuncSplat<Bool8x16>), 1)                                                  \
+  V(allTrue, (AllTrue<Bool8x16>), 1)                                                  \
+  V(anyTrue, (AnyTrue<Bool8x16>), 1)
+
+#define BOOL8X16_BINARY_FUNCTION_LIST(V)                                              \
+  V(extractLane, (ExtractLane<Bool8x16>), 2)                                          \
+  V(and, (BinaryFunc<Bool8x16, And, Bool8x16>), 2)                                    \
+  V(or, (BinaryFunc<Bool8x16, Or, Bool8x16>), 2)                                      \
+  V(xor, (BinaryFunc<Bool8x16, Xor, Bool8x16>), 2)                                    \
+
+#define BOOL8X16_TERNARY_FUNCTION_LIST(V)                                             \
+  V(replaceLane, (ReplaceLane<Bool8x16>), 3)
+
+#define BOOL8X16_FUNCTION_LIST(V)                                                     \
+  BOOL8X16_UNARY_FUNCTION_LIST(V)                                                     \
+  BOOL8X16_BINARY_FUNCTION_LIST(V)                                                    \
+  BOOL8X16_TERNARY_FUNCTION_LIST(V)
+
+// Bool 16x8.
+#define BOOL16X8_UNARY_FUNCTION_LIST(V)                                               \
+  V(not, (UnaryFunc<Bool16x8, LogicalNot, Bool16x8>), 1)                              \
+  V(check, (UnaryFunc<Bool16x8, Identity, Bool16x8>), 1)                              \
+  V(splat, (FuncSplat<Bool16x8>), 1)                                                  \
+  V(allTrue, (AllTrue<Bool16x8>), 1)                                                  \
+  V(anyTrue, (AnyTrue<Bool16x8>), 1)
+
+#define BOOL16X8_BINARY_FUNCTION_LIST(V)                                              \
+  V(extractLane, (ExtractLane<Bool16x8>), 2)                                          \
+  V(and, (BinaryFunc<Bool16x8, And, Bool16x8>), 2)                                    \
+  V(or, (BinaryFunc<Bool16x8, Or, Bool16x8>), 2)                                      \
+  V(xor, (BinaryFunc<Bool16x8, Xor, Bool16x8>), 2)                                    \
+
+#define BOOL16X8_TERNARY_FUNCTION_LIST(V)                                             \
+  V(replaceLane, (ReplaceLane<Bool16x8>), 3)
+
+#define BOOL16X8_FUNCTION_LIST(V)                                                     \
+  BOOL16X8_UNARY_FUNCTION_LIST(V)                                                     \
+  BOOL16X8_BINARY_FUNCTION_LIST(V)                                                    \
+  BOOL16X8_TERNARY_FUNCTION_LIST(V)
+
+// Bool32x4.
+#define BOOL32X4_UNARY_FUNCTION_LIST(V)                                               \
+  V(not, (UnaryFunc<Bool32x4, LogicalNot, Bool32x4>), 1)                              \
+  V(check, (UnaryFunc<Bool32x4, Identity, Bool32x4>), 1)                              \
+  V(splat, (FuncSplat<Bool32x4>), 1)                                                  \
+  V(allTrue, (AllTrue<Bool32x4>), 1)                                                  \
+  V(anyTrue, (AnyTrue<Bool32x4>), 1)
+
+#define BOOL32X4_BINARY_FUNCTION_LIST(V)                                              \
+  V(extractLane, (ExtractLane<Bool32x4>), 2)                                          \
+  V(and, (BinaryFunc<Bool32x4, And, Bool32x4>), 2)                                    \
+  V(or, (BinaryFunc<Bool32x4, Or, Bool32x4>), 2)                                      \
+  V(xor, (BinaryFunc<Bool32x4, Xor, Bool32x4>), 2)                                    \
+
+#define BOOL32X4_TERNARY_FUNCTION_LIST(V)                                             \
+  V(replaceLane, (ReplaceLane<Bool32x4>), 3)
+
+#define BOOL32X4_FUNCTION_LIST(V)                                                     \
+  BOOL32X4_UNARY_FUNCTION_LIST(V)                                                     \
+  BOOL32X4_BINARY_FUNCTION_LIST(V)                                                    \
+  BOOL32X4_TERNARY_FUNCTION_LIST(V)
+
+// Bool64x2.
+#define BOOL64X2_UNARY_FUNCTION_LIST(V)                                               \
+  V(not, (UnaryFunc<Bool64x2, LogicalNot, Bool64x2>), 1)                              \
+  V(check, (UnaryFunc<Bool64x2, Identity, Bool64x2>), 1)                              \
+  V(splat, (FuncSplat<Bool64x2>), 1)                                                  \
+  V(allTrue, (AllTrue<Bool64x2>), 1)                                                  \
+  V(anyTrue, (AnyTrue<Bool64x2>), 1)
+
+#define BOOL64X2_BINARY_FUNCTION_LIST(V)                                              \
+  V(extractLane, (ExtractLane<Bool64x2>), 2)                                          \
+  V(and, (BinaryFunc<Bool64x2, And, Bool64x2>), 2)                                    \
+  V(or, (BinaryFunc<Bool64x2, Or, Bool64x2>), 2)                                      \
+  V(xor, (BinaryFunc<Bool64x2, Xor, Bool64x2>), 2)                                    \
+
+#define BOOL64X2_TERNARY_FUNCTION_LIST(V)                                             \
+  V(replaceLane, (ReplaceLane<Bool64x2>), 3)
+
+#define BOOL64X2_FUNCTION_LIST(V)                                                     \
+  BOOL64X2_UNARY_FUNCTION_LIST(V)                                                     \
+  BOOL64X2_BINARY_FUNCTION_LIST(V)                                                    \
+  BOOL64X2_TERNARY_FUNCTION_LIST(V)
+
+// Float32x4.
+#define FLOAT32X4_UNARY_FUNCTION_LIST(V)                                              \
+  V(abs, (UnaryFunc<Float32x4, Abs, Float32x4>), 1)                                   \
+  V(check, (UnaryFunc<Float32x4, Identity, Float32x4>), 1)                            \
+  V(fromFloat64x2Bits, (FuncConvertBits<Float64x2, Float32x4>), 1)                    \
+  V(fromInt8x16Bits,   (FuncConvertBits<Int8x16,   Float32x4>), 1)                    \
+  V(fromInt16x8Bits,   (FuncConvertBits<Int16x8,   Float32x4>), 1)                    \
+  V(fromInt32x4,       (FuncConvert<Int32x4,       Float32x4>), 1)                    \
+  V(fromInt32x4Bits,   (FuncConvertBits<Int32x4,   Float32x4>), 1)                    \
+  V(fromUint8x16Bits,  (FuncConvertBits<Uint8x16,  Float32x4>), 1)                    \
+  V(fromUint16x8Bits,  (FuncConvertBits<Uint16x8,  Float32x4>), 1)                    \
+  V(fromUint32x4,      (FuncConvert<Uint32x4,      Float32x4>), 1)                    \
+  V(fromUint32x4Bits,  (FuncConvertBits<Uint32x4,  Float32x4>), 1)                    \
+  V(neg, (UnaryFunc<Float32x4, Neg, Float32x4>), 1)                                   \
+  V(reciprocalApproximation, (UnaryFunc<Float32x4, RecApprox, Float32x4>), 1)         \
+  V(reciprocalSqrtApproximation, (UnaryFunc<Float32x4, RecSqrtApprox, Float32x4>), 1) \
+  V(splat, (FuncSplat<Float32x4>), 1)                                                 \
+  V(sqrt, (UnaryFunc<Float32x4, Sqrt, Float32x4>), 1)
+
+#define FLOAT32X4_BINARY_FUNCTION_LIST(V)                                             \
+  V(add, (BinaryFunc<Float32x4, Add, Float32x4>), 2)                                  \
+  V(div, (BinaryFunc<Float32x4, Div, Float32x4>), 2)                                  \
+  V(equal, (CompareFunc<Float32x4, Equal, Bool32x4>), 2)                              \
+  V(extractLane, (ExtractLane<Float32x4>), 2)                                         \
+  V(greaterThan, (CompareFunc<Float32x4, GreaterThan, Bool32x4>), 2)                  \
+  V(greaterThanOrEqual, (CompareFunc<Float32x4, GreaterThanOrEqual, Bool32x4>), 2)    \
+  V(lessThan, (CompareFunc<Float32x4, LessThan, Bool32x4>), 2)                        \
+  V(lessThanOrEqual, (CompareFunc<Float32x4, LessThanOrEqual, Bool32x4>), 2)          \
+  V(load,  (Load<Float32x4, 4>), 2)                                                   \
+  V(load3, (Load<Float32x4, 3>), 2)                                                   \
+  V(load2, (Load<Float32x4, 2>), 2)                                                   \
+  V(load1, (Load<Float32x4, 1>), 2)                                                   \
+  V(max, (BinaryFunc<Float32x4, Maximum, Float32x4>), 2)                              \
+  V(maxNum, (BinaryFunc<Float32x4, MaxNum, Float32x4>), 2)                            \
+  V(min, (BinaryFunc<Float32x4, Minimum, Float32x4>), 2)                              \
+  V(minNum, (BinaryFunc<Float32x4, MinNum, Float32x4>), 2)                            \
+  V(mul, (BinaryFunc<Float32x4, Mul, Float32x4>), 2)                                  \
+  V(notEqual, (CompareFunc<Float32x4, NotEqual, Bool32x4>), 2)                        \
+  V(sub, (BinaryFunc<Float32x4, Sub, Float32x4>), 2)
+
+#define FLOAT32X4_TERNARY_FUNCTION_LIST(V)                                            \
+  V(replaceLane, (ReplaceLane<Float32x4>), 3)                                         \
+  V(select, (Select<Float32x4, Bool32x4>), 3)                                         \
+  V(store,  (Store<Float32x4, 4>), 3)                                                 \
+  V(store3, (Store<Float32x4, 3>), 3)                                                 \
+  V(store2, (Store<Float32x4, 2>), 3)                                                 \
+  V(store1, (Store<Float32x4, 1>), 3)
+
+#define FLOAT32X4_SHUFFLE_FUNCTION_LIST(V)                                            \
+  V(swizzle, Swizzle<Float32x4>, 5)                                                   \
+  V(shuffle, Shuffle<Float32x4>, 6)
+
+#define FLOAT32X4_FUNCTION_LIST(V)                                                    \
+  FLOAT32X4_UNARY_FUNCTION_LIST(V)                                                    \
+  FLOAT32X4_BINARY_FUNCTION_LIST(V)                                                   \
+  FLOAT32X4_TERNARY_FUNCTION_LIST(V)                                                  \
+  FLOAT32X4_SHUFFLE_FUNCTION_LIST(V)
+
+// Float64x2.
+#define FLOAT64X2_UNARY_FUNCTION_LIST(V)                                              \
+  V(abs, (UnaryFunc<Float64x2, Abs, Float64x2>), 1)                                   \
+  V(check, (UnaryFunc<Float64x2, Identity, Float64x2>), 1)                            \
+  V(fromFloat32x4Bits, (FuncConvertBits<Float32x4, Float64x2>), 1)                    \
+  V(fromInt8x16Bits,   (FuncConvertBits<Int8x16,   Float64x2>), 1)                    \
+  V(fromInt16x8Bits,   (FuncConvertBits<Int16x8,   Float64x2>), 1)                    \
+  V(fromInt32x4Bits,   (FuncConvertBits<Int32x4,   Float64x2>), 1)                    \
+  V(fromUint8x16Bits,  (FuncConvertBits<Uint8x16,  Float64x2>), 1)                    \
+  V(fromUint16x8Bits,  (FuncConvertBits<Uint16x8,  Float64x2>), 1)                    \
+  V(fromUint32x4Bits,  (FuncConvertBits<Uint32x4,  Float64x2>), 1)                    \
+  V(neg, (UnaryFunc<Float64x2, Neg, Float64x2>), 1)                                   \
+  V(reciprocalApproximation, (UnaryFunc<Float64x2, RecApprox, Float64x2>), 1)         \
+  V(reciprocalSqrtApproximation, (UnaryFunc<Float64x2, RecSqrtApprox, Float64x2>), 1) \
+  V(splat, (FuncSplat<Float64x2>), 1)                                                 \
+  V(sqrt, (UnaryFunc<Float64x2, Sqrt, Float64x2>), 1)
+
+#define FLOAT64X2_BINARY_FUNCTION_LIST(V)                                             \
+  V(add, (BinaryFunc<Float64x2, Add, Float64x2>), 2)                                  \
+  V(div, (BinaryFunc<Float64x2, Div, Float64x2>), 2)                                  \
+  V(equal, (CompareFunc<Float64x2, Equal, Bool64x2>), 2)                              \
+  V(extractLane, (ExtractLane<Float64x2>), 2)                                         \
+  V(greaterThan, (CompareFunc<Float64x2, GreaterThan, Bool64x2>), 2)                  \
+  V(greaterThanOrEqual, (CompareFunc<Float64x2, GreaterThanOrEqual, Bool64x2>), 2)    \
+  V(lessThan, (CompareFunc<Float64x2, LessThan, Bool64x2>), 2)                        \
+  V(lessThanOrEqual, (CompareFunc<Float64x2, LessThanOrEqual, Bool64x2>), 2)          \
+  V(load,  (Load<Float64x2, 2>), 2)                                                   \
+  V(load1, (Load<Float64x2, 1>), 2)                                                   \
+  V(max, (BinaryFunc<Float64x2, Maximum, Float64x2>), 2)                              \
+  V(maxNum, (BinaryFunc<Float64x2, MaxNum, Float64x2>), 2)                            \
+  V(min, (BinaryFunc<Float64x2, Minimum, Float64x2>), 2)                              \
+  V(minNum, (BinaryFunc<Float64x2, MinNum, Float64x2>), 2)                            \
+  V(mul, (BinaryFunc<Float64x2, Mul, Float64x2>), 2)                                  \
+  V(notEqual, (CompareFunc<Float64x2, NotEqual, Bool64x2>), 2)                        \
+  V(sub, (BinaryFunc<Float64x2, Sub, Float64x2>), 2)
+
+#define FLOAT64X2_TERNARY_FUNCTION_LIST(V)                                            \
+  V(replaceLane, (ReplaceLane<Float64x2>), 3)                                         \
+  V(select, (Select<Float64x2, Bool64x2>), 3)                                         \
+  V(store,  (Store<Float64x2, 2>), 3)                                                 \
+  V(store1, (Store<Float64x2, 1>), 3)
+
+#define FLOAT64X2_SHUFFLE_FUNCTION_LIST(V)                                            \
+  V(swizzle, Swizzle<Float64x2>, 3)                                                   \
+  V(shuffle, Shuffle<Float64x2>, 4)
+
+#define FLOAT64X2_FUNCTION_LIST(V)                                                    \
+  FLOAT64X2_UNARY_FUNCTION_LIST(V)                                                    \
+  FLOAT64X2_BINARY_FUNCTION_LIST(V)                                                   \
+  FLOAT64X2_TERNARY_FUNCTION_LIST(V)                                                  \
+  FLOAT64X2_SHUFFLE_FUNCTION_LIST(V)
+
+// Int8x16.
+#define INT8X16_UNARY_FUNCTION_LIST(V)                                                \
+  V(check, (UnaryFunc<Int8x16, Identity, Int8x16>), 1)                                \
+  V(fromFloat32x4Bits, (FuncConvertBits<Float32x4, Int8x16>), 1)                      \
+  V(fromFloat64x2Bits, (FuncConvertBits<Float64x2, Int8x16>), 1)                      \
+  V(fromInt16x8Bits,   (FuncConvertBits<Int16x8,   Int8x16>), 1)                      \
+  V(fromInt32x4Bits,   (FuncConvertBits<Int32x4,   Int8x16>), 1)                      \
+  V(fromUint8x16Bits,  (FuncConvertBits<Uint8x16,  Int8x16>), 1)                      \
+  V(fromUint16x8Bits,  (FuncConvertBits<Uint16x8,  Int8x16>), 1)                      \
+  V(fromUint32x4Bits,  (FuncConvertBits<Uint32x4,  Int8x16>), 1)                      \
+  V(neg, (UnaryFunc<Int8x16, Neg, Int8x16>), 1)                                       \
+  V(not, (UnaryFunc<Int8x16, Not, Int8x16>), 1)                                       \
+  V(splat, (FuncSplat<Int8x16>), 1)
+
+#define INT8X16_BINARY_FUNCTION_LIST(V)                                               \
+  V(add, (BinaryFunc<Int8x16, Add, Int8x16>), 2)                                      \
+  V(addSaturate, (BinaryFunc<Int8x16, AddSaturate, Int8x16>), 2)                      \
+  V(and, (BinaryFunc<Int8x16, And, Int8x16>), 2)                                      \
+  V(equal, (CompareFunc<Int8x16, Equal, Bool8x16>), 2)                                \
+  V(extractLane, (ExtractLane<Int8x16>), 2)                                           \
+  V(greaterThan, (CompareFunc<Int8x16, GreaterThan, Bool8x16>), 2)                    \
+  V(greaterThanOrEqual, (CompareFunc<Int8x16, GreaterThanOrEqual, Bool8x16>), 2)      \
+  V(lessThan, (CompareFunc<Int8x16, LessThan, Bool8x16>), 2)                          \
+  V(lessThanOrEqual, (CompareFunc<Int8x16, LessThanOrEqual, Bool8x16>), 2)            \
+  V(load, (Load<Int8x16, 16>), 2)                                                     \
+  V(mul, (BinaryFunc<Int8x16, Mul, Int8x16>), 2)                                      \
+  V(notEqual, (CompareFunc<Int8x16, NotEqual, Bool8x16>), 2)                          \
+  V(or, (BinaryFunc<Int8x16, Or, Int8x16>), 2)                                        \
+  V(sub, (BinaryFunc<Int8x16, Sub, Int8x16>), 2)                                      \
+  V(subSaturate, (BinaryFunc<Int8x16, SubSaturate, Int8x16>), 2)                      \
+  V(shiftLeftByScalar, (BinaryScalar<Int8x16, ShiftLeft>), 2)                         \
+  V(shiftRightByScalar, (BinaryScalar<Int8x16, ShiftRightArithmetic>), 2)             \
+  V(xor, (BinaryFunc<Int8x16, Xor, Int8x16>), 2)
+
+#define INT8X16_TERNARY_FUNCTION_LIST(V)                                              \
+  V(replaceLane, (ReplaceLane<Int8x16>), 3)                                           \
+  V(select, (Select<Int8x16, Bool8x16>), 3)                                           \
+  V(store, (Store<Int8x16, 16>), 3)
+
+#define INT8X16_SHUFFLE_FUNCTION_LIST(V)                                              \
+  V(swizzle, Swizzle<Int8x16>, 17)                                                    \
+  V(shuffle, Shuffle<Int8x16>, 18)
+
+#define INT8X16_FUNCTION_LIST(V)                                                      \
+  INT8X16_UNARY_FUNCTION_LIST(V)                                                      \
+  INT8X16_BINARY_FUNCTION_LIST(V)                                                     \
+  INT8X16_TERNARY_FUNCTION_LIST(V)                                                    \
+  INT8X16_SHUFFLE_FUNCTION_LIST(V)
+
+// Uint8x16.
+#define UINT8X16_UNARY_FUNCTION_LIST(V)                                               \
+  V(check, (UnaryFunc<Uint8x16, Identity, Uint8x16>), 1)                              \
+  V(fromFloat32x4Bits, (FuncConvertBits<Float32x4, Uint8x16>), 1)                     \
+  V(fromFloat64x2Bits, (FuncConvertBits<Float64x2, Uint8x16>), 1)                     \
+  V(fromInt8x16Bits,   (FuncConvertBits<Int8x16,   Uint8x16>), 1)                     \
+  V(fromInt16x8Bits,   (FuncConvertBits<Int16x8,   Uint8x16>), 1)                     \
+  V(fromInt32x4Bits,   (FuncConvertBits<Int32x4,   Uint8x16>), 1)                     \
+  V(fromUint16x8Bits,  (FuncConvertBits<Uint16x8,  Uint8x16>), 1)                     \
+  V(fromUint32x4Bits,  (FuncConvertBits<Uint32x4,  Uint8x16>), 1)                     \
+  V(neg, (UnaryFunc<Uint8x16, Neg, Uint8x16>), 1)                                     \
+  V(not, (UnaryFunc<Uint8x16, Not, Uint8x16>), 1)                                     \
+  V(splat, (FuncSplat<Uint8x16>), 1)
+
+#define UINT8X16_BINARY_FUNCTION_LIST(V)                                              \
+  V(add, (BinaryFunc<Uint8x16, Add, Uint8x16>), 2)                                    \
+  V(addSaturate, (BinaryFunc<Uint8x16, AddSaturate, Uint8x16>), 2)                    \
+  V(and, (BinaryFunc<Uint8x16, And, Uint8x16>), 2)                                    \
+  V(equal, (CompareFunc<Uint8x16, Equal, Bool8x16>), 2)                               \
+  V(extractLane, (ExtractLane<Uint8x16>), 2)                                          \
+  V(greaterThan, (CompareFunc<Uint8x16, GreaterThan, Bool8x16>), 2)                   \
+  V(greaterThanOrEqual, (CompareFunc<Uint8x16, GreaterThanOrEqual, Bool8x16>), 2)     \
+  V(lessThan, (CompareFunc<Uint8x16, LessThan, Bool8x16>), 2)                         \
+  V(lessThanOrEqual, (CompareFunc<Uint8x16, LessThanOrEqual, Bool8x16>), 2)           \
+  V(load, (Load<Uint8x16, 16>), 2)                                                    \
+  V(mul, (BinaryFunc<Uint8x16, Mul, Uint8x16>), 2)                                    \
+  V(notEqual, (CompareFunc<Uint8x16, NotEqual, Bool8x16>), 2)                         \
+  V(or, (BinaryFunc<Uint8x16, Or, Uint8x16>), 2)                                      \
+  V(sub, (BinaryFunc<Uint8x16, Sub, Uint8x16>), 2)                                    \
+  V(subSaturate, (BinaryFunc<Uint8x16, SubSaturate, Uint8x16>), 2)                    \
+  V(shiftLeftByScalar, (BinaryScalar<Uint8x16, ShiftLeft>), 2)                        \
+  V(shiftRightByScalar, (BinaryScalar<Uint8x16, ShiftRightLogical>), 2)               \
+  V(xor, (BinaryFunc<Uint8x16, Xor, Uint8x16>), 2)
+
+#define UINT8X16_TERNARY_FUNCTION_LIST(V)                                             \
+  V(replaceLane, (ReplaceLane<Uint8x16>), 3)                                          \
+  V(select, (Select<Uint8x16, Bool8x16>), 3)                                          \
+  V(store, (Store<Uint8x16, 16>), 3)
+
+#define UINT8X16_SHUFFLE_FUNCTION_LIST(V)                                             \
+  V(swizzle, Swizzle<Uint8x16>, 17)                                                   \
+  V(shuffle, Shuffle<Uint8x16>, 18)
+
+#define UINT8X16_FUNCTION_LIST(V)                                                     \
+  UINT8X16_UNARY_FUNCTION_LIST(V)                                                     \
+  UINT8X16_BINARY_FUNCTION_LIST(V)                                                    \
+  UINT8X16_TERNARY_FUNCTION_LIST(V)                                                   \
+  UINT8X16_SHUFFLE_FUNCTION_LIST(V)
+
+// Int16x8.
+#define INT16X8_UNARY_FUNCTION_LIST(V)                                                \
+  V(check, (UnaryFunc<Int16x8, Identity, Int16x8>), 1)                                \
+  V(fromFloat32x4Bits, (FuncConvertBits<Float32x4, Int16x8>), 1)                      \
+  V(fromFloat64x2Bits, (FuncConvertBits<Float64x2, Int16x8>), 1)                      \
+  V(fromInt8x16Bits,   (FuncConvertBits<Int8x16,   Int16x8>), 1)                      \
+  V(fromInt32x4Bits,   (FuncConvertBits<Int32x4,   Int16x8>), 1)                      \
+  V(fromUint8x16Bits,  (FuncConvertBits<Uint8x16,  Int16x8>), 1)                      \
+  V(fromUint16x8Bits,  (FuncConvertBits<Uint16x8,  Int16x8>), 1)                      \
+  V(fromUint32x4Bits,  (FuncConvertBits<Uint32x4,  Int16x8>), 1)                      \
+  V(neg, (UnaryFunc<Int16x8, Neg, Int16x8>), 1)                                       \
+  V(not, (UnaryFunc<Int16x8, Not, Int16x8>), 1)                                       \
+  V(splat, (FuncSplat<Int16x8>), 1)
+
+#define INT16X8_BINARY_FUNCTION_LIST(V)                                               \
+  V(add, (BinaryFunc<Int16x8, Add, Int16x8>), 2)                                      \
+  V(addSaturate, (BinaryFunc<Int16x8, AddSaturate, Int16x8>), 2)                      \
+  V(and, (BinaryFunc<Int16x8, And, Int16x8>), 2)                                      \
+  V(equal, (CompareFunc<Int16x8, Equal, Bool16x8>), 2)                                \
+  V(extractLane, (ExtractLane<Int16x8>), 2)                                           \
+  V(greaterThan, (CompareFunc<Int16x8, GreaterThan, Bool16x8>), 2)                    \
+  V(greaterThanOrEqual, (CompareFunc<Int16x8, GreaterThanOrEqual, Bool16x8>), 2)      \
+  V(lessThan, (CompareFunc<Int16x8, LessThan, Bool16x8>), 2)                          \
+  V(lessThanOrEqual, (CompareFunc<Int16x8, LessThanOrEqual, Bool16x8>), 2)            \
+  V(load, (Load<Int16x8, 8>), 2)                                                      \
+  V(mul, (BinaryFunc<Int16x8, Mul, Int16x8>), 2)                                      \
+  V(notEqual, (CompareFunc<Int16x8, NotEqual, Bool16x8>), 2)                          \
+  V(or, (BinaryFunc<Int16x8, Or, Int16x8>), 2)                                        \
+  V(sub, (BinaryFunc<Int16x8, Sub, Int16x8>), 2)                                      \
+  V(subSaturate, (BinaryFunc<Int16x8, SubSaturate, Int16x8>), 2)                      \
+  V(shiftLeftByScalar, (BinaryScalar<Int16x8, ShiftLeft>), 2)                         \
+  V(shiftRightByScalar, (BinaryScalar<Int16x8, ShiftRightArithmetic>), 2)             \
+  V(xor, (BinaryFunc<Int16x8, Xor, Int16x8>), 2)
+
+#define INT16X8_TERNARY_FUNCTION_LIST(V)                                              \
+  V(replaceLane, (ReplaceLane<Int16x8>), 3)                                           \
+  V(select, (Select<Int16x8, Bool16x8>), 3)                                           \
+  V(store, (Store<Int16x8, 8>), 3)
+
+#define INT16X8_SHUFFLE_FUNCTION_LIST(V)                                              \
+  V(swizzle, Swizzle<Int16x8>, 9)                                                     \
+  V(shuffle, Shuffle<Int16x8>, 10)
+
+#define INT16X8_FUNCTION_LIST(V)                                                      \
+  INT16X8_UNARY_FUNCTION_LIST(V)                                                      \
+  INT16X8_BINARY_FUNCTION_LIST(V)                                                     \
+  INT16X8_TERNARY_FUNCTION_LIST(V)                                                    \
+  INT16X8_SHUFFLE_FUNCTION_LIST(V)
+
+// Uint16x8.
+#define UINT16X8_UNARY_FUNCTION_LIST(V)                                               \
+  V(check, (UnaryFunc<Uint16x8, Identity, Uint16x8>), 1)                              \
+  V(fromFloat32x4Bits, (FuncConvertBits<Float32x4, Uint16x8>), 1)                     \
+  V(fromFloat64x2Bits, (FuncConvertBits<Float64x2, Uint16x8>), 1)                     \
+  V(fromInt8x16Bits,   (FuncConvertBits<Int8x16,   Uint16x8>), 1)                     \
+  V(fromInt16x8Bits,   (FuncConvertBits<Int16x8,   Uint16x8>), 1)                     \
+  V(fromInt32x4Bits,   (FuncConvertBits<Int32x4,   Uint16x8>), 1)                     \
+  V(fromUint8x16Bits,  (FuncConvertBits<Uint8x16,  Uint16x8>), 1)                     \
+  V(fromUint32x4Bits,  (FuncConvertBits<Uint32x4,  Uint16x8>), 1)                     \
+  V(neg, (UnaryFunc<Uint16x8, Neg, Uint16x8>), 1)                                     \
+  V(not, (UnaryFunc<Uint16x8, Not, Uint16x8>), 1)                                     \
+  V(splat, (FuncSplat<Uint16x8>), 1)
+
+#define UINT16X8_BINARY_FUNCTION_LIST(V)                                              \
+  V(add, (BinaryFunc<Uint16x8, Add, Uint16x8>), 2)                                    \
+  V(addSaturate, (BinaryFunc<Uint16x8, AddSaturate, Uint16x8>), 2)                    \
+  V(and, (BinaryFunc<Uint16x8, And, Uint16x8>), 2)                                    \
+  V(equal, (CompareFunc<Uint16x8, Equal, Bool16x8>), 2)                               \
+  V(extractLane, (ExtractLane<Uint16x8>), 2)                                          \
+  V(greaterThan, (CompareFunc<Uint16x8, GreaterThan, Bool16x8>), 2)                   \
+  V(greaterThanOrEqual, (CompareFunc<Uint16x8, GreaterThanOrEqual, Bool16x8>), 2)     \
+  V(lessThan, (CompareFunc<Uint16x8, LessThan, Bool16x8>), 2)                         \
+  V(lessThanOrEqual, (CompareFunc<Uint16x8, LessThanOrEqual, Bool16x8>), 2)           \
+  V(load, (Load<Uint16x8, 8>), 2)                                                     \
+  V(mul, (BinaryFunc<Uint16x8, Mul, Uint16x8>), 2)                                    \
+  V(notEqual, (CompareFunc<Uint16x8, NotEqual, Bool16x8>), 2)                         \
+  V(or, (BinaryFunc<Uint16x8, Or, Uint16x8>), 2)                                      \
+  V(sub, (BinaryFunc<Uint16x8, Sub, Uint16x8>), 2)                                    \
+  V(subSaturate, (BinaryFunc<Uint16x8, SubSaturate, Uint16x8>), 2)                    \
+  V(shiftLeftByScalar, (BinaryScalar<Uint16x8, ShiftLeft>), 2)                        \
+  V(shiftRightByScalar, (BinaryScalar<Uint16x8, ShiftRightLogical>), 2)               \
+  V(xor, (BinaryFunc<Uint16x8, Xor, Uint16x8>), 2)
+
+#define UINT16X8_TERNARY_FUNCTION_LIST(V)                                             \
+  V(replaceLane, (ReplaceLane<Uint16x8>), 3)                                          \
+  V(select, (Select<Uint16x8, Bool16x8>), 3)                                          \
+  V(store, (Store<Uint16x8, 8>), 3)
+
+#define UINT16X8_SHUFFLE_FUNCTION_LIST(V)                                             \
+  V(swizzle, Swizzle<Uint16x8>, 9)                                                    \
+  V(shuffle, Shuffle<Uint16x8>, 10)
+
+#define UINT16X8_FUNCTION_LIST(V)                                                     \
+  UINT16X8_UNARY_FUNCTION_LIST(V)                                                     \
+  UINT16X8_BINARY_FUNCTION_LIST(V)                                                    \
+  UINT16X8_TERNARY_FUNCTION_LIST(V)                                                   \
+  UINT16X8_SHUFFLE_FUNCTION_LIST(V)
+
+// Int32x4.
+#define INT32X4_UNARY_FUNCTION_LIST(V)                                                \
+  V(check, (UnaryFunc<Int32x4, Identity, Int32x4>), 1)                                \
+  V(fromFloat32x4,     (FuncConvert<Float32x4,     Int32x4>), 1)                      \
+  V(fromFloat32x4Bits, (FuncConvertBits<Float32x4, Int32x4>), 1)                      \
+  V(fromFloat64x2Bits, (FuncConvertBits<Float64x2, Int32x4>), 1)                      \
+  V(fromInt8x16Bits,   (FuncConvertBits<Int8x16,   Int32x4>), 1)                      \
+  V(fromInt16x8Bits,   (FuncConvertBits<Int16x8,   Int32x4>), 1)                      \
+  V(fromUint8x16Bits,  (FuncConvertBits<Uint8x16,  Int32x4>), 1)                      \
+  V(fromUint16x8Bits,  (FuncConvertBits<Uint16x8,  Int32x4>), 1)                      \
+  V(fromUint32x4Bits,  (FuncConvertBits<Uint32x4,  Int32x4>), 1)                      \
+  V(neg, (UnaryFunc<Int32x4, Neg, Int32x4>), 1)                                       \
+  V(not, (UnaryFunc<Int32x4, Not, Int32x4>), 1)                                       \
+  V(splat, (FuncSplat<Int32x4>), 0)
+
+#define INT32X4_BINARY_FUNCTION_LIST(V)                                               \
+  V(add, (BinaryFunc<Int32x4, Add, Int32x4>), 2)                                      \
+  V(and, (BinaryFunc<Int32x4, And, Int32x4>), 2)                                      \
+  V(equal, (CompareFunc<Int32x4, Equal, Bool32x4>), 2)                                \
+  V(extractLane, (ExtractLane<Int32x4>), 2)                                           \
+  V(greaterThan, (CompareFunc<Int32x4, GreaterThan, Bool32x4>), 2)                    \
+  V(greaterThanOrEqual, (CompareFunc<Int32x4, GreaterThanOrEqual, Bool32x4>), 2)      \
+  V(lessThan, (CompareFunc<Int32x4, LessThan, Bool32x4>), 2)                          \
+  V(lessThanOrEqual, (CompareFunc<Int32x4, LessThanOrEqual, Bool32x4>), 2)            \
+  V(load,  (Load<Int32x4, 4>), 2)                                                     \
+  V(load3, (Load<Int32x4, 3>), 2)                                                     \
+  V(load2, (Load<Int32x4, 2>), 2)                                                     \
+  V(load1, (Load<Int32x4, 1>), 2)                                                     \
+  V(mul, (BinaryFunc<Int32x4, Mul, Int32x4>), 2)                                      \
+  V(notEqual, (CompareFunc<Int32x4, NotEqual, Bool32x4>), 2)                          \
+  V(or, (BinaryFunc<Int32x4, Or, Int32x4>), 2)                                        \
+  V(sub, (BinaryFunc<Int32x4, Sub, Int32x4>), 2)                                      \
+  V(shiftLeftByScalar, (BinaryScalar<Int32x4, ShiftLeft>), 2)                         \
+  V(shiftRightByScalar, (BinaryScalar<Int32x4, ShiftRightArithmetic>), 2)             \
+  V(xor, (BinaryFunc<Int32x4, Xor, Int32x4>), 2)
+
+#define INT32X4_TERNARY_FUNCTION_LIST(V)                                              \
+  V(replaceLane, (ReplaceLane<Int32x4>), 3)                                           \
+  V(select, (Select<Int32x4, Bool32x4>), 3)                                           \
+  V(store,  (Store<Int32x4, 4>), 3)                                                   \
+  V(store3, (Store<Int32x4, 3>), 3)                                                   \
+  V(store2, (Store<Int32x4, 2>), 3)                                                   \
+  V(store1, (Store<Int32x4, 1>), 3)
+
+#define INT32X4_SHUFFLE_FUNCTION_LIST(V)                                              \
+  V(swizzle, Swizzle<Int32x4>, 5)                                                     \
+  V(shuffle, Shuffle<Int32x4>, 6)
+
+#define INT32X4_FUNCTION_LIST(V)                                                      \
+  INT32X4_UNARY_FUNCTION_LIST(V)                                                      \
+  INT32X4_BINARY_FUNCTION_LIST(V)                                                     \
+  INT32X4_TERNARY_FUNCTION_LIST(V)                                                    \
+  INT32X4_SHUFFLE_FUNCTION_LIST(V)
+
+// Uint32x4.
+#define UINT32X4_UNARY_FUNCTION_LIST(V)                                               \
+  V(check, (UnaryFunc<Uint32x4, Identity, Uint32x4>), 1)                              \
+  V(fromFloat32x4,     (FuncConvert<Float32x4,     Uint32x4>), 1)                     \
+  V(fromFloat32x4Bits, (FuncConvertBits<Float32x4, Uint32x4>), 1)                     \
+  V(fromFloat64x2Bits, (FuncConvertBits<Float64x2, Uint32x4>), 1)                     \
+  V(fromInt8x16Bits,   (FuncConvertBits<Int8x16,   Uint32x4>), 1)                     \
+  V(fromInt16x8Bits,   (FuncConvertBits<Int16x8,   Uint32x4>), 1)                     \
+  V(fromInt32x4Bits,   (FuncConvertBits<Int32x4,   Uint32x4>), 1)                     \
+  V(fromUint8x16Bits,  (FuncConvertBits<Uint8x16,  Uint32x4>), 1)                     \
+  V(fromUint16x8Bits,  (FuncConvertBits<Uint16x8,  Uint32x4>), 1)                     \
+  V(neg, (UnaryFunc<Uint32x4, Neg, Uint32x4>), 1)                                     \
+  V(not, (UnaryFunc<Uint32x4, Not, Uint32x4>), 1)                                     \
+  V(splat, (FuncSplat<Uint32x4>), 0)
+
+#define UINT32X4_BINARY_FUNCTION_LIST(V)                                              \
+  V(add, (BinaryFunc<Uint32x4, Add, Uint32x4>), 2)                                    \
+  V(and, (BinaryFunc<Uint32x4, And, Uint32x4>), 2)                                    \
+  V(equal, (CompareFunc<Uint32x4, Equal, Bool32x4>), 2)                               \
+  V(extractLane, (ExtractLane<Uint32x4>), 2)                                          \
+  V(greaterThan, (CompareFunc<Uint32x4, GreaterThan, Bool32x4>), 2)                   \
+  V(greaterThanOrEqual, (CompareFunc<Uint32x4, GreaterThanOrEqual, Bool32x4>), 2)     \
+  V(lessThan, (CompareFunc<Uint32x4, LessThan, Bool32x4>), 2)                         \
+  V(lessThanOrEqual, (CompareFunc<Uint32x4, LessThanOrEqual, Bool32x4>), 2)           \
+  V(load,  (Load<Uint32x4, 4>), 2)                                                    \
+  V(load3, (Load<Uint32x4, 3>), 2)                                                    \
+  V(load2, (Load<Uint32x4, 2>), 2)                                                    \
+  V(load1, (Load<Uint32x4, 1>), 2)                                                    \
+  V(mul, (BinaryFunc<Uint32x4, Mul, Uint32x4>), 2)                                    \
+  V(notEqual, (CompareFunc<Uint32x4, NotEqual, Bool32x4>), 2)                         \
+  V(or, (BinaryFunc<Uint32x4, Or, Uint32x4>), 2)                                      \
+  V(sub, (BinaryFunc<Uint32x4, Sub, Uint32x4>), 2)                                    \
+  V(shiftLeftByScalar, (BinaryScalar<Uint32x4, ShiftLeft>), 2)                        \
+  V(shiftRightByScalar, (BinaryScalar<Uint32x4, ShiftRightLogical>), 2)               \
+  V(xor, (BinaryFunc<Uint32x4, Xor, Uint32x4>), 2)
+
+#define UINT32X4_TERNARY_FUNCTION_LIST(V)                                             \
+  V(replaceLane, (ReplaceLane<Uint32x4>), 3)                                          \
+  V(select, (Select<Uint32x4, Bool32x4>), 3)                                          \
+  V(store,  (Store<Uint32x4, 4>), 3)                                                  \
+  V(store3, (Store<Uint32x4, 3>), 3)                                                  \
+  V(store2, (Store<Uint32x4, 2>), 3)                                                  \
+  V(store1, (Store<Uint32x4, 1>), 3)
+
+#define UINT32X4_SHUFFLE_FUNCTION_LIST(V)                                             \
+  V(swizzle, Swizzle<Uint32x4>, 5)                                                    \
+  V(shuffle, Shuffle<Uint32x4>, 6)
+
+#define UINT32X4_FUNCTION_LIST(V)                                                     \
+  UINT32X4_UNARY_FUNCTION_LIST(V)                                                     \
+  UINT32X4_BINARY_FUNCTION_LIST(V)                                                    \
+  UINT32X4_TERNARY_FUNCTION_LIST(V)                                                   \
+  UINT32X4_SHUFFLE_FUNCTION_LIST(V)
+
+/*
+ * The FOREACH macros below partition all of the SIMD operations into disjoint
+ * sets.
+ */
+
+// Operations available on all SIMD types. Mixed arity.
+#define FOREACH_COMMON_SIMD_OP(_)     \
+    _(extractLane)                    \
+    _(replaceLane)                    \
+    _(check)                          \
+    _(splat)
+
+// Lanewise operations available on numeric SIMD types.
+// Include lane-wise select here since it is not arithmetic and defined on
+// numeric types too.
+#define FOREACH_LANE_SIMD_OP(_)       \
+    _(select)                         \
+    _(swizzle)                        \
+    _(shuffle)
+
+// Memory operations available on numeric SIMD types.
+#define FOREACH_MEMORY_SIMD_OP(_)     \
+    _(load)                           \
+    _(store)
+
+// Memory operations available on numeric X4 SIMD types.
+#define FOREACH_MEMORY_X4_SIMD_OP(_)  \
+    _(load1)                          \
+    _(load2)                          \
+    _(load3)                          \
+    _(store1)                         \
+    _(store2)                         \
+    _(store3)
+
+// Unary operations on Bool vectors.
+#define FOREACH_BOOL_SIMD_UNOP(_)     \
+    _(allTrue)                        \
+    _(anyTrue)
+
+// Unary bitwise SIMD operators defined on all integer and boolean SIMD types.
+#define FOREACH_BITWISE_SIMD_UNOP(_)  \
+    _(not)
+
+// Binary bitwise SIMD operators defined on all integer and boolean SIMD types.
+#define FOREACH_BITWISE_SIMD_BINOP(_) \
+    _(and)                            \
+    _(or)                             \
+    _(xor)
+
+// Bitwise shifts defined on integer SIMD types.
+#define FOREACH_SHIFT_SIMD_OP(_)      \
+    _(shiftLeftByScalar)              \
+    _(shiftRightByScalar)
+
+// Unary arithmetic operators defined on numeric SIMD types.
+#define FOREACH_NUMERIC_SIMD_UNOP(_)  \
+    _(neg)
+
+// Binary arithmetic operators defined on numeric SIMD types.
+#define FOREACH_NUMERIC_SIMD_BINOP(_) \
+    _(add)                            \
+    _(sub)                            \
+    _(mul)
+
+// Unary arithmetic operators defined on floating point SIMD types.
+#define FOREACH_FLOAT_SIMD_UNOP(_)    \
+    _(abs)                            \
+    _(sqrt)                           \
+    _(reciprocalApproximation)        \
+    _(reciprocalSqrtApproximation)
+
+// Binary arithmetic operators defined on floating point SIMD types.
+#define FOREACH_FLOAT_SIMD_BINOP(_)   \
+    _(div)                            \
+    _(max)                            \
+    _(min)                            \
+    _(maxNum)                         \
+    _(minNum)
+
+// Binary operations on small integer (< 32 bits) vectors.
+#define FOREACH_SMINT_SIMD_BINOP(_)   \
+    _(addSaturate)                    \
+    _(subSaturate)
+
+// Comparison operators defined on numeric SIMD types.
+#define FOREACH_COMP_SIMD_OP(_)       \
+    _(lessThan)                       \
+    _(lessThanOrEqual)                \
+    _(equal)                          \
+    _(notEqual)                       \
+    _(greaterThan)                    \
+    _(greaterThanOrEqual)
+
+/*
+ * All SIMD operations, excluding casts.
+ */
+#define FORALL_SIMD_NONCAST_OP(_)     \
+    FOREACH_COMMON_SIMD_OP(_)         \
+    FOREACH_LANE_SIMD_OP(_)           \
+    FOREACH_MEMORY_SIMD_OP(_)         \
+    FOREACH_MEMORY_X4_SIMD_OP(_)      \
+    FOREACH_BOOL_SIMD_UNOP(_)         \
+    FOREACH_BITWISE_SIMD_UNOP(_)      \
+    FOREACH_BITWISE_SIMD_BINOP(_)     \
+    FOREACH_SHIFT_SIMD_OP(_)          \
+    FOREACH_NUMERIC_SIMD_UNOP(_)      \
+    FOREACH_NUMERIC_SIMD_BINOP(_)     \
+    FOREACH_FLOAT_SIMD_UNOP(_)        \
+    FOREACH_FLOAT_SIMD_BINOP(_)       \
+    FOREACH_SMINT_SIMD_BINOP(_)       \
+    FOREACH_COMP_SIMD_OP(_)
+
+/*
+ * All operations on integer SIMD types, excluding casts and
+ * FOREACH_MEMORY_X4_OP.
+ */
+#define FORALL_INT_SIMD_OP(_)         \
+    FOREACH_COMMON_SIMD_OP(_)         \
+    FOREACH_LANE_SIMD_OP(_)           \
+    FOREACH_MEMORY_SIMD_OP(_)         \
+    FOREACH_BITWISE_SIMD_UNOP(_)      \
+    FOREACH_BITWISE_SIMD_BINOP(_)     \
+    FOREACH_SHIFT_SIMD_OP(_)          \
+    FOREACH_NUMERIC_SIMD_UNOP(_)      \
+    FOREACH_NUMERIC_SIMD_BINOP(_)     \
+    FOREACH_COMP_SIMD_OP(_)
+
+/*
+ * All operations on floating point SIMD types, excluding casts and
+ * FOREACH_MEMORY_X4_OP.
+ */
+#define FORALL_FLOAT_SIMD_OP(_)       \
+    FOREACH_COMMON_SIMD_OP(_)         \
+    FOREACH_LANE_SIMD_OP(_)           \
+    FOREACH_MEMORY_SIMD_OP(_)         \
+    FOREACH_NUMERIC_SIMD_UNOP(_)      \
+    FOREACH_NUMERIC_SIMD_BINOP(_)     \
+    FOREACH_FLOAT_SIMD_UNOP(_)        \
+    FOREACH_FLOAT_SIMD_BINOP(_)       \
+    FOREACH_COMP_SIMD_OP(_)
+
+/*
+ * All operations on Bool SIMD types.
+ *
+ * These types don't have casts, so no need to specialize.
+ */
+#define FORALL_BOOL_SIMD_OP(_)        \
+    FOREACH_COMMON_SIMD_OP(_)         \
+    FOREACH_BOOL_SIMD_UNOP(_)         \
+    FOREACH_BITWISE_SIMD_UNOP(_)      \
+    FOREACH_BITWISE_SIMD_BINOP(_)
+
+/*
+ * The sets of cast operations are listed per type below.
+ *
+ * These sets are not disjoint.
+ */
+
+#define FOREACH_INT8X16_SIMD_CAST(_)  \
+    _(fromFloat32x4Bits)              \
+    _(fromFloat64x2Bits)              \
+    _(fromInt16x8Bits)                \
+    _(fromInt32x4Bits)
+
+#define FOREACH_INT16X8_SIMD_CAST(_)  \
+    _(fromFloat32x4Bits)              \
+    _(fromFloat64x2Bits)              \
+    _(fromInt8x16Bits)                \
+    _(fromInt32x4Bits)
+
+#define FOREACH_INT32X4_SIMD_CAST(_)  \
+    _(fromFloat32x4)                  \
+    _(fromFloat32x4Bits)              \
+    _(fromFloat64x2Bits)              \
+    _(fromInt8x16Bits)                \
+    _(fromInt16x8Bits)
+
+#define FOREACH_FLOAT32X4_SIMD_CAST(_)\
+    _(fromFloat64x2Bits)              \
+    _(fromInt8x16Bits)                \
+    _(fromInt16x8Bits)                \
+    _(fromInt32x4)                    \
+    _(fromInt32x4Bits)
+
+#define FOREACH_FLOAT64X2_SIMD_CAST(_)\
+    _(fromFloat32x4Bits)              \
+    _(fromInt8x16Bits)                \
+    _(fromInt16x8Bits)                \
+    _(fromInt32x4Bits)
+
+// All operations on Int32x4.
+#define FORALL_INT32X4_SIMD_OP(_)     \
+    FORALL_INT_SIMD_OP(_)             \
+    FOREACH_MEMORY_X4_SIMD_OP(_)      \
+    FOREACH_INT32X4_SIMD_CAST(_)
+
+// All operations on Float32X4
+#define FORALL_FLOAT32X4_SIMD_OP(_)   \
+    FORALL_FLOAT_SIMD_OP(_)           \
+    FOREACH_MEMORY_X4_SIMD_OP(_)      \
+    FOREACH_FLOAT32X4_SIMD_CAST(_)
+
+/*
+ * All SIMD operations assuming only 32x4 types exist.
+ * This is used in the current asm.js impl.
+ */
+#define FORALL_SIMD_ASMJS_OP(_)       \
+    FORALL_SIMD_NONCAST_OP(_)         \
+    _(fromFloat32x4)                  \
+    _(fromFloat32x4Bits)              \
+    _(fromInt8x16Bits)                \
+    _(fromInt16x8Bits)                \
+    _(fromInt32x4)                    \
+    _(fromInt32x4Bits)                \
+    _(fromUint8x16Bits)               \
+    _(fromUint16x8Bits)               \
+    _(fromUint32x4)                   \
+    _(fromUint32x4Bits)
+
+// All operations on Int8x16 or Uint8x16 in the asm.js world.
+// Note: this does not include conversions and casts to/from Uint8x16 because
+// this list is shared between Int8x16 and Uint8x16.
+#define FORALL_INT8X16_ASMJS_OP(_)    \
+    FORALL_INT_SIMD_OP(_)             \
+    FOREACH_SMINT_SIMD_BINOP(_)       \
+    _(fromInt16x8Bits)                \
+    _(fromInt32x4Bits)                \
+    _(fromFloat32x4Bits)
+
+// All operations on Int16x8 or Uint16x8 in the asm.js world.
+// Note: this does not include conversions and casts to/from Uint16x8 because
+// this list is shared between Int16x8 and Uint16x8.
+#define FORALL_INT16X8_ASMJS_OP(_)    \
+    FORALL_INT_SIMD_OP(_)             \
+    FOREACH_SMINT_SIMD_BINOP(_)       \
+    _(fromInt8x16Bits)                \
+    _(fromInt32x4Bits)                \
+    _(fromFloat32x4Bits)
+
+// All operations on Int32x4 or Uint32x4 in the asm.js world.
+// Note: this does not include conversions and casts to/from Uint32x4 because
+// this list is shared between Int32x4 and Uint32x4.
+#define FORALL_INT32X4_ASMJS_OP(_)    \
+    FORALL_INT_SIMD_OP(_)             \
+    FOREACH_MEMORY_X4_SIMD_OP(_)      \
+    _(fromInt8x16Bits)                \
+    _(fromInt16x8Bits)                \
+    _(fromFloat32x4)                  \
+    _(fromFloat32x4Bits)
+
+// All operations on Float32X4 in the asm.js world.
+#define FORALL_FLOAT32X4_ASMJS_OP(_)  \
+    FORALL_FLOAT_SIMD_OP(_)           \
+    FOREACH_MEMORY_X4_SIMD_OP(_)      \
+    _(fromInt8x16Bits)                \
+    _(fromInt16x8Bits)                \
+    _(fromInt32x4Bits)                \
+    _(fromInt32x4)                    \
+    _(fromUint32x4)
+
+namespace js {
+
+// Complete set of SIMD types.
+// It must be kept in sync with the enumeration of values in
+// TypedObjectConstants.h; in particular we need to ensure that Count is
+// appropriately set with respect to the number of actual types.
+enum class SimdType {
+    Int8x16   = JS_SIMDTYPEREPR_INT8X16,
+    Int16x8   = JS_SIMDTYPEREPR_INT16X8,
+    Int32x4   = JS_SIMDTYPEREPR_INT32X4,
+    Uint8x16  = JS_SIMDTYPEREPR_UINT8X16,
+    Uint16x8  = JS_SIMDTYPEREPR_UINT16X8,
+    Uint32x4  = JS_SIMDTYPEREPR_UINT32X4,
+    Float32x4 = JS_SIMDTYPEREPR_FLOAT32X4,
+    Float64x2 = JS_SIMDTYPEREPR_FLOAT64X2,
+    Bool8x16  = JS_SIMDTYPEREPR_BOOL8X16,
+    Bool16x8  = JS_SIMDTYPEREPR_BOOL16X8,
+    Bool32x4  = JS_SIMDTYPEREPR_BOOL32X4,
+    Bool64x2  = JS_SIMDTYPEREPR_BOOL64X2,
+    Count
+};
+
+// The integer SIMD types have a lot of operations that do the exact same thing
+// for signed and unsigned integer types. Sometimes it is simpler to treat
+// signed and unsigned integer SIMD types as the same type, using a SimdSign to
+// distinguish the few cases where there is a difference.
+enum class SimdSign {
+    // Signedness is not applicable to this type. (i.e., Float or Bool).
+    NotApplicable,
+    // Treat as an unsigned integer with a range 0 .. 2^N-1.
+    Unsigned,
+    // Treat as a signed integer in two's complement encoding.
+    Signed,
+};
+
+// Get the signedness of a SIMD type.
+inline SimdSign
+GetSimdSign(SimdType t)
+{
+    switch(t) {
+      case SimdType::Int8x16:
+      case SimdType::Int16x8:
+      case SimdType::Int32x4:
+        return SimdSign::Signed;
+
+      case SimdType::Uint8x16:
+      case SimdType::Uint16x8:
+      case SimdType::Uint32x4:
+        return SimdSign::Unsigned;
+
+      default:
+        return SimdSign::NotApplicable;
+    }
+}
+
+inline bool
+IsSignedIntSimdType(SimdType type)
+{
+    return GetSimdSign(type) == SimdSign::Signed;
+}
+
+// Get the boolean SIMD type with the same shape as t.
+//
+// This is the result type of a comparison operation, and it can also be used to
+// identify the geometry of a SIMD type.
+inline SimdType
+GetBooleanSimdType(SimdType t)
+{
+    switch(t) {
+      case SimdType::Int8x16:
+      case SimdType::Uint8x16:
+      case SimdType::Bool8x16:
+        return SimdType::Bool8x16;
+
+      case SimdType::Int16x8:
+      case SimdType::Uint16x8:
+      case SimdType::Bool16x8:
+        return SimdType::Bool16x8;
+
+      case SimdType::Int32x4:
+      case SimdType::Uint32x4:
+      case SimdType::Float32x4:
+      case SimdType::Bool32x4:
+        return SimdType::Bool32x4;
+
+      case SimdType::Float64x2:
+      case SimdType::Bool64x2:
+        return SimdType::Bool64x2;
+
+      case SimdType::Count:
+        break;
+    }
+    MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Bad SIMD type");
+}
+
+// Get the number of lanes in a SIMD type.
+inline unsigned
+GetSimdLanes(SimdType t)
+{
+    switch(t) {
+      case SimdType::Int8x16:
+      case SimdType::Uint8x16:
+      case SimdType::Bool8x16:
+        return 16;
+
+      case SimdType::Int16x8:
+      case SimdType::Uint16x8:
+      case SimdType::Bool16x8:
+        return 8;
+
+      case SimdType::Int32x4:
+      case SimdType::Uint32x4:
+      case SimdType::Float32x4:
+      case SimdType::Bool32x4:
+        return 4;
+
+      case SimdType::Float64x2:
+      case SimdType::Bool64x2:
+        return 2;
+
+      case SimdType::Count:
+        break;
+    }
+    MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Bad SIMD type");
+}
+
+// Complete set of SIMD operations.
+//
+// No SIMD types implement all of these operations.
+//
+// C++ defines keywords and/or/xor/not, so prepend Fn_ to all named functions to
+// avoid clashes.
+//
+// Note: because of a gcc < v4.8's compiler bug, uint8_t can't be used as the
+// storage class here. See bug 1243810. See also
+// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=64037 .
+enum class SimdOperation {
+    // The constructor call. No Fn_ prefix here.
+    Constructor,
+
+    // All the operations, except for casts.
+#define DEFOP(x) Fn_##x,
+    FORALL_SIMD_NONCAST_OP(DEFOP)
+#undef DEFOP
+
+    // Int <-> Float conversions.
+    Fn_fromInt32x4,
+    Fn_fromUint32x4,
+    Fn_fromFloat32x4,
+
+    // Bitcasts. One for each type with a memory representation.
+    Fn_fromInt8x16Bits,
+    Fn_fromInt16x8Bits,
+    Fn_fromInt32x4Bits,
+    Fn_fromUint8x16Bits,
+    Fn_fromUint16x8Bits,
+    Fn_fromUint32x4Bits,
+    Fn_fromFloat32x4Bits,
+    Fn_fromFloat64x2Bits,
+
+    Last = Fn_fromFloat64x2Bits
+};
+
+} // namespace js
+
+#endif /* builtin_SIMDConstants_h */
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -33,16 +33,17 @@
 #include "frontend/TokenStream.h"
 #include "irregexp/RegExpAST.h"
 #include "irregexp/RegExpEngine.h"
 #include "irregexp/RegExpParser.h"
 #endif
 #include "gc/Heap.h"
 #include "jit/BaselineJIT.h"
 #include "jit/InlinableNatives.h"
+#include "jit/JitRealm.h"
 #include "js/Debug.h"
 #include "js/HashTable.h"
 #include "js/StructuredClone.h"
 #include "js/UbiNode.h"
 #include "js/UbiNodeBreadthFirst.h"
 #include "js/UbiNodeShortestPaths.h"
 #include "js/UniquePtr.h"
 #include "js/Vector.h"
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -6,17 +6,17 @@
 
 #include "builtin/TypedObject-inl.h"
 
 #include "mozilla/Casting.h"
 #include "mozilla/CheckedInt.h"
 
 #include "jsutil.h"
 
-#include "builtin/SIMD.h"
+#include "builtin/SIMDConstants.h"
 #include "gc/Marking.h"
 #include "js/Vector.h"
 #include "util/StringBuffer.h"
 #include "vm/GlobalObject.h"
 #include "vm/JSFunction.h"
 #include "vm/Realm.h"
 #include "vm/SelfHosting.h"
 #include "vm/StringType.h"
--- a/js/src/fuzz-tests/testStructuredCloneReader.cpp
+++ b/js/src/fuzz-tests/testStructuredCloneReader.cpp
@@ -6,16 +6,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 
 #include "mozilla/ScopeExit.h"
 
 #include "jsapi.h"
 
 #include "fuzz-tests/tests.h"
+#include "js/StructuredClone.h"
 #include "vm/Interpreter.h"
 
 #include "vm/JSContext-inl.h"
 
 using namespace js;
 
 // These are defined and pre-initialized by the harness (in tests.cpp).
 extern JS::PersistentRootedObject gGlobal;
--- a/js/src/gc/DeletePolicy.h
+++ b/js/src/gc/DeletePolicy.h
@@ -3,18 +3,21 @@
  * 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 gc_DeletePolicy_h
 #define gc_DeletePolicy_h
 
 #include "js/TracingAPI.h"
+
 #ifdef ENABLE_BIGINT
-#include "vm/BigIntType.h"
+namespace JS {
+class BigInt;
+} // namespace JS
 #endif
 
 namespace js {
 namespace gc {
 
 struct ClearEdgesTracer : public JS::CallbackTracer
 {
     ClearEdgesTracer();
--- a/js/src/gc/GC.cpp
+++ b/js/src/gc/GC.cpp
@@ -218,16 +218,17 @@
 #include "gc/GCInternals.h"
 #include "gc/GCTrace.h"
 #include "gc/Memory.h"
 #include "gc/Policy.h"
 #include "gc/WeakMap.h"
 #include "jit/BaselineJIT.h"
 #include "jit/IonCode.h"
 #include "jit/JitcodeMap.h"
+#include "jit/JitRealm.h"
 #include "js/SliceBudget.h"
 #include "proxy/DeadObjectProxy.h"
 #include "util/Windows.h"
 #ifdef ENABLE_BIGINT
 #include "vm/BigIntType.h"
 #endif
 #include "vm/Debugger.h"
 #include "vm/GeckoProfiler.h"
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -14,16 +14,17 @@
 
 #include "jsutil.h"
 
 #include "gc/FreeOp.h"
 #include "gc/GCInternals.h"
 #include "gc/Memory.h"
 #include "gc/PublicIterators.h"
 #include "jit/JitFrames.h"
+#include "jit/JitRealm.h"
 #include "vm/ArrayObject.h"
 #include "vm/Debugger.h"
 #if defined(DEBUG)
 #include "vm/EnvironmentObject.h"
 #endif
 #include "vm/JSONPrinter.h"
 #include "vm/Realm.h"
 #include "vm/Time.h"
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/regress/debug-osr.js
@@ -0,0 +1,15 @@
+g = newGlobal();
+g.parent = this;
+g.eval("(" + function() {
+    Debugger(parent).onExceptionUnwind = function(frame) {
+        frame.older
+    }
+} + ")()");
+
+let o = {};
+
+let func = wasmEvalText(`
+  (module (import $imp "" "inc") (func) (func $start (call $imp)) (start $start) (export "" $start))
+`, { "": { inc: function() { o = o.set; } } }).exports[""];
+
+assertErrorMessage(func, TypeError, /is undefined/);
--- a/js/src/jit/BaselineBailouts.cpp
+++ b/js/src/jit/BaselineBailouts.cpp
@@ -1836,16 +1836,24 @@ jit::FinishBailoutToBaseline(BaselineBai
     topFrame->setOverridePc(bailoutInfo->resumePC);
 
     jsbytecode* faultPC = bailoutInfo->faultPC;
     jsbytecode* tryPC = bailoutInfo->tryPC;
     uint32_t numFrames = bailoutInfo->numFrames;
     MOZ_ASSERT(numFrames > 0);
     BailoutKind bailoutKind = bailoutInfo->bailoutKind;
     bool checkGlobalDeclarationConflicts = bailoutInfo->checkGlobalDeclarationConflicts;
+    uint8_t* incomingStack = bailoutInfo->incomingStack;
+
+    // We have to get rid of the rematerialized frame, whether it is
+    // restored or unwound.
+    auto guardRemoveRematerializedFramesFromDebugger = mozilla::MakeScopeExit([&] {
+        JitActivation* act = cx->activation()->asJit();
+        act->removeRematerializedFramesFromDebugger(cx, incomingStack);
+    });
 
     // Free the bailout buffer.
     js_free(bailoutInfo);
     bailoutInfo = nullptr;
 
     if (topFrame->environmentChain()) {
         // Ensure the frame has a call object if it needs one. If the env chain
         // is nullptr, we will enter baseline code at the prologue so no need to do
@@ -1909,16 +1917,17 @@ jit::FinishBailoutToBaseline(BaselineBai
             }
 
             if (frameno == 0)
                 innerScript = frame->script();
 
             if (frameno == numFrames - 1) {
                 outerScript = frame->script();
                 outerFp = iter.fp();
+                MOZ_ASSERT(outerFp == incomingStack);
             }
 
             frameno++;
         }
 
         ++iter;
     }
 
@@ -1927,36 +1936,40 @@ jit::FinishBailoutToBaseline(BaselineBai
     MOZ_ASSERT(outerFp);
 
     // If we rematerialized Ion frames due to debug mode toggling, copy their
     // values into the baseline frame. We need to do this even when debug mode
     // is off, as we should respect the mutations made while debug mode was
     // on.
     JitActivation* act = cx->activation()->asJit();
     if (act->hasRematerializedFrame(outerFp)) {
-        JSJitFrameIter iter(cx->activation()->asJit());
+        JSJitFrameIter iter(act);
         size_t inlineDepth = numFrames;
         bool ok = true;
         while (inlineDepth > 0) {
             if (iter.isBaselineJS()) {
                 // We must attempt to copy all rematerialized frames over,
                 // even if earlier ones failed, to invoke the proper frame
                 // cleanup in the Debugger.
-                ok = CopyFromRematerializedFrame(cx, act, outerFp, --inlineDepth,
-                                                 iter.baselineFrame());
+                if (!CopyFromRematerializedFrame(cx, act, outerFp, --inlineDepth,
+                                                 iter.baselineFrame()))
+                {
+                    ok = false;
+                }
             }
             ++iter;
         }
 
+        if (!ok)
+            return false;
+
         // After copying from all the rematerialized frames, remove them from
         // the table to keep the table up to date.
+        guardRemoveRematerializedFramesFromDebugger.release();
         act->removeRematerializedFrame(outerFp);
-
-        if (!ok)
-            return false;
     }
 
     // If we are catching an exception, we need to unwind scopes.
     // See |SettleOnTryNote|
     if (cx->isExceptionPending() && faultPC) {
         EnvironmentIter ei(cx, topFrame, faultPC);
         UnwindEnvironment(cx, ei, tryPC);
     }
--- a/js/src/jit/BaselineDebugModeOSR.cpp
+++ b/js/src/jit/BaselineDebugModeOSR.cpp
@@ -1177,11 +1177,14 @@ JitRuntime::generateBaselineDebugModeOSR
     return code;
 }
 
 /* static */ void
 DebugModeOSRVolatileJitFrameIter::forwardLiveIterators(JSContext* cx,
                                                        uint8_t* oldAddr, uint8_t* newAddr)
 {
     DebugModeOSRVolatileJitFrameIter* iter;
-    for (iter = cx->liveVolatileJitFrameIter_; iter; iter = iter->prev)
+    for (iter = cx->liveVolatileJitFrameIter_; iter; iter = iter->prev) {
+        if (iter->isWasm())
+            continue;
         iter->asJSJit().exchangeReturnAddressIfMatch(oldAddr, newAddr);
+    }
 }
--- a/js/src/jit/BaselineDebugModeOSR.h
+++ b/js/src/jit/BaselineDebugModeOSR.h
@@ -77,17 +77,17 @@ class DebugModeOSRVolatileStub
     T& get() { MOZ_ASSERT(!invalid()); return stub_; }
     const T& get() const { MOZ_ASSERT(!invalid()); return stub_; }
 
     bool operator!=(const T& other) const { MOZ_ASSERT(!invalid()); return stub_ != other; }
     bool operator==(const T& other) const { MOZ_ASSERT(!invalid()); return stub_ == other; }
 };
 
 //
-// A JitFrameIter that updates internal JSJitFrameIter in case of
+// A frame iterator that updates internal JSJitFrameIter in case of
 // recompilation of an on-stack baseline script.
 //
 
 class DebugModeOSRVolatileJitFrameIter : public JitFrameIter
 {
     DebugModeOSRVolatileJitFrameIter** stack;
     DebugModeOSRVolatileJitFrameIter* prev;
 
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -9,17 +9,17 @@
 #include "mozilla/DebugOnly.h"
 #include "mozilla/TemplateLib.h"
 
 #include "jsfriendapi.h"
 #include "jslibmath.h"
 #include "jstypes.h"
 
 #include "builtin/Eval.h"
-#include "builtin/SIMD.h"
+#include "builtin/SIMDConstants.h"
 #include "gc/Policy.h"
 #include "jit/BaselineCacheIRCompiler.h"
 #include "jit/BaselineDebugModeOSR.h"
 #include "jit/BaselineJIT.h"
 #include "jit/InlinableNatives.h"
 #include "jit/JitSpewer.h"
 #include "jit/Linker.h"
 #include "jit/Lowering.h"
--- a/js/src/jit/BaselineJIT.h
+++ b/js/src/jit/BaselineJIT.h
@@ -7,17 +7,18 @@
 #ifndef jit_BaselineJIT_h
 #define jit_BaselineJIT_h
 
 #include "mozilla/MemoryReporting.h"
 
 #include "ds/LifoAlloc.h"
 #include "jit/Bailouts.h"
 #include "jit/IonCode.h"
-#include "jit/MacroAssembler.h"
+#include "jit/shared/Assembler-shared.h"
+#include "vm/EnvironmentObject.h"
 #include "vm/JSContext.h"
 #include "vm/Realm.h"
 #include "vm/TraceLogging.h"
 
 namespace js {
 namespace jit {
 
 class StackValue;
--- a/js/src/jit/CacheIRCompiler.cpp
+++ b/js/src/jit/CacheIRCompiler.cpp
@@ -1,16 +1,18 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "jit/CacheIRCompiler.h"
 
+#include "mozilla/ScopeExit.h"
+
 #include <utility>
 
 #include "jit/IonIC.h"
 #include "jit/SharedICHelpers.h"
 
 #include "builtin/Boolean-inl.h"
 
 #include "jit/MacroAssembler-inl.h"
--- a/js/src/jit/IonAnalysis.h
+++ b/js/src/jit/IonAnalysis.h
@@ -5,23 +5,26 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef jit_IonAnalysis_h
 #define jit_IonAnalysis_h
 
 // This file declares various analysis passes that operate on MIR.
 
 #include "jit/JitAllocPolicy.h"
-#include "jit/MIR.h"
 
 namespace js {
 namespace jit {
 
+class MBasicBlock;
+class MCompare;
+class MDefinition;
 class MIRGenerator;
 class MIRGraph;
+class MTest;
 
 MOZ_MUST_USE bool
 PruneUnusedBranches(MIRGenerator* mir, MIRGraph& graph);
 
 MOZ_MUST_USE bool
 FoldTests(MIRGraph& graph);
 
 MOZ_MUST_USE bool
@@ -94,18 +97,16 @@ void
 AssertExtendedGraphCoherency(MIRGraph& graph, bool underValueNumberer = false, bool force = false);
 
 MOZ_MUST_USE bool
 EliminateRedundantChecks(MIRGraph& graph);
 
 MOZ_MUST_USE bool
 AddKeepAliveInstructions(MIRGraph& graph);
 
-class MDefinition;
-
 // Simple linear sum of the form 'n' or 'x + n'.
 struct SimpleLinearSum
 {
     MDefinition* term;
     int32_t constant;
 
     SimpleLinearSum(MDefinition* term, int32_t constant)
         : term(term), constant(constant)
--- a/js/src/jit/IonTypes.h
+++ b/js/src/jit/IonTypes.h
@@ -929,12 +929,17 @@ enum class RoundingMode {
 // limit has a generous buffer before the real end of the native stack.
 static const uint32_t MAX_UNCHECKED_LEAF_FRAME_SIZE = 64;
 
 // Truncating conversion modifiers.
 typedef uint32_t TruncFlags;
 static const TruncFlags TRUNC_UNSIGNED   = TruncFlags(1) << 0;
 static const TruncFlags TRUNC_SATURATING = TruncFlags(1) << 1;
 
+enum BranchDirection {
+    FALSE_BRANCH,
+    TRUE_BRANCH
+};
+
 } // namespace jit
 } // namespace js
 
 #endif /* jit_IonTypes_h */
--- a/js/src/jit/JSJitFrameIter.cpp
+++ b/js/src/jit/JSJitFrameIter.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "jit/JSJitFrameIter-inl.h"
 
 #include "jit/BaselineDebugModeOSR.h"
 #include "jit/BaselineIC.h"
 #include "jit/JitcodeMap.h"
 #include "jit/JitFrames.h"
+#include "jit/Safepoints.h"
 
 using namespace js;
 using namespace js::jit;
 
 JSJitFrameIter::JSJitFrameIter(const JitActivation* activation)
   : current_(activation->jsExitFP()),
     type_(JitFrame_Exit),
     returnAddressToFp_(nullptr),
--- a/js/src/jit/Jit.cpp
+++ b/js/src/jit/Jit.cpp
@@ -4,16 +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 "jit/Jit.h"
 
 #include "jit/BaselineJIT.h"
 #include "jit/Ion.h"
 #include "jit/JitCommon.h"
+#include "jit/JitRealm.h"
 #include "vm/Interpreter.h"
 
 #include "vm/Stack-inl.h"
 
 using namespace js;
 using namespace js::jit;
 
 static EnterJitStatus
--- a/js/src/jit/JitFrames.cpp
+++ b/js/src/jit/JitFrames.cpp
@@ -719,17 +719,22 @@ HandleException(ResumeFromException* rfe
                 if (!frames.more()) {
                     TraceLogStopEvent(logger, TraceLogger_IonMonkey);
                     TraceLogStopEvent(logger, TraceLogger_Scripts);
                     break;
                 }
                 ++frames;
             }
 
+            // Remove left-over state which might have been needed for bailout.
             activation->removeIonFrameRecovery(frame.jsFrame());
+            activation->removeRematerializedFrame(frame.fp());
+
+            // If invalidated, decrement the number of frames remaining on the
+            // stack for the given IonScript.
             if (invalidated)
                 ionScript->decrementInvalidationCount(cx->runtime()->defaultFreeOp());
 
         } else if (frame.isBaselineJS()) {
             // Set a flag on the frame to signal to DebugModeOSR that we're
             // handling an exception. Also ensure the frame has an override
             // pc. We clear the frame's override pc when we leave this block,
             // this is fine because we're either:
--- a/js/src/jit/JitFrames.h
+++ b/js/src/jit/JitFrames.h
@@ -5,30 +5,31 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef jit_JitFrames_h
 #define jit_JitFrames_h
 
 #include <stdint.h>
 
 #include "jit/JSJitFrameIter.h"
-#include "jit/Safepoints.h"
 #include "vm/JSContext.h"
 #include "vm/JSFunction.h"
 
 namespace js {
 namespace jit {
 
 enum CalleeTokenTag
 {
     CalleeToken_Function = 0x0, // untagged
     CalleeToken_FunctionConstructing = 0x1,
     CalleeToken_Script = 0x2
 };
 
+struct SafepointSlotEntry;
+
 static const uintptr_t CalleeTokenMask = ~uintptr_t(0x3);
 
 static inline CalleeTokenTag
 GetCalleeTokenTag(CalleeToken token)
 {
     CalleeTokenTag tag = CalleeTokenTag(uintptr_t(token) & 0x3);
     MOZ_ASSERT(tag <= CalleeToken_Script);
     return tag;
--- a/js/src/jit/JitcodeMap.cpp
+++ b/js/src/jit/JitcodeMap.cpp
@@ -9,16 +9,17 @@
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/Sprintf.h"
 
 #include "gc/Marking.h"
 #include "gc/Statistics.h"
 #include "jit/BaselineJIT.h"
+#include "jit/JitRealm.h"
 #include "jit/JitSpewer.h"
 #include "js/Vector.h"
 #include "vm/GeckoProfiler.h"
 
 #include "vm/GeckoProfiler-inl.h"
 #include "vm/JSScript-inl.h"
 #include "vm/TypeInference-inl.h"
 
@@ -1195,18 +1196,18 @@ JitcodeRegionEntry::ReadDelta(CompactBuf
         pcDeltaU |= ~ENC4_PC_DELTA_MAX;
     *pcDelta = pcDeltaU;
 
     MOZ_ASSERT(*pcDelta != 0);
     MOZ_ASSERT_IF(*nativeDelta == 0, *pcDelta <= 0);
 }
 
 /* static */ uint32_t
-JitcodeRegionEntry::ExpectedRunLength(const CodeGeneratorShared::NativeToBytecode* entry,
-                                      const CodeGeneratorShared::NativeToBytecode* end)
+JitcodeRegionEntry::ExpectedRunLength(const NativeToBytecode* entry,
+                                      const NativeToBytecode* end)
 {
     MOZ_ASSERT(entry < end);
 
     // We always use the first entry, so runLength starts at 1
     uint32_t runLength = 1;
 
     uint32_t curNativeOffset = entry->nativeOffset.offset();
     uint32_t curBytecodeOffset = entry->tree->script()->pcToOffset(entry->pc);
@@ -1283,17 +1284,17 @@ struct JitcodeMapBufferWriteSpewer
     void spewAndAdvance(const char* name) {}
 #endif // JS_JITSPEW
 };
 
 // Write a run, starting at the given NativeToBytecode entry, into the given buffer writer.
 /* static */ bool
 JitcodeRegionEntry::WriteRun(CompactBufferWriter& writer,
                              JSScript** scriptList, uint32_t scriptListSize,
-                             uint32_t runLength, const CodeGeneratorShared::NativeToBytecode* entry)
+                             uint32_t runLength, const NativeToBytecode* entry)
 {
     MOZ_ASSERT(runLength > 0);
     MOZ_ASSERT(runLength <= MAX_RUN_LENGTH);
 
     // Calculate script depth.
     MOZ_ASSERT(entry->tree->depth() <= 0xff);
     uint8_t scriptDepth = entry->tree->depth();
     uint32_t regionNativeOffset = entry->nativeOffset.offset();
@@ -1533,18 +1534,18 @@ JitcodeIonTable::findRegionEntry(uint32_
         }
     }
     return idx;
 }
 
 /* static */ bool
 JitcodeIonTable::WriteIonTable(CompactBufferWriter& writer,
                                JSScript** scriptList, uint32_t scriptListSize,
-                               const CodeGeneratorShared::NativeToBytecode* start,
-                               const CodeGeneratorShared::NativeToBytecode* end,
+                               const NativeToBytecode* start,
+                               const NativeToBytecode* end,
                                uint32_t* tableOffsetOut, uint32_t* numRegionsOut)
 {
     MOZ_ASSERT(tableOffsetOut != nullptr);
     MOZ_ASSERT(numRegionsOut != nullptr);
     MOZ_ASSERT(writer.length() == 0);
     MOZ_ASSERT(scriptListSize > 0);
 
     JitSpew(JitSpew_Profiling, "Writing native to bytecode map for %s:%u (%zu entries)",
@@ -1554,17 +1555,17 @@ JitcodeIonTable::WriteIonTable(CompactBu
     JitSpew(JitSpew_Profiling, "  ScriptList of size %d", int(scriptListSize));
     for (uint32_t i = 0; i < scriptListSize; i++) {
         JitSpew(JitSpew_Profiling, "  Script %d - %s:%u",
                 int(i), scriptList[i]->filename(), scriptList[i]->lineno());
     }
 
     // Write out runs first.  Keep a vector tracking the positive offsets from payload
     // start to the run.
-    const CodeGeneratorShared::NativeToBytecode* curEntry = start;
+    const NativeToBytecode* curEntry = start;
     js::Vector<uint32_t, 32, SystemAllocPolicy> runOffsets;
 
     while (curEntry != end) {
         // Calculate the length of the next run.
         uint32_t runLength = JitcodeRegionEntry::ExpectedRunLength(curEntry, end);
         MOZ_ASSERT(runLength > 0);
         MOZ_ASSERT(runLength <= uintptr_t(end - curEntry));
         JitSpew(JitSpew_Profiling, "  Run at entry %d, length %d, buffer offset %d",
--- a/js/src/jit/JitcodeMap.h
+++ b/js/src/jit/JitcodeMap.h
@@ -6,17 +6,16 @@
 
 #ifndef jit_JitcodeMap_h
 #define jit_JitcodeMap_h
 
 #include "jit/CompactBuffer.h"
 #include "jit/CompileInfo.h"
 #include "jit/ExecutableAllocator.h"
 #include "jit/OptimizationTracking.h"
-#include "jit/shared/CodeGenerator-shared.h"
 
 namespace js {
 namespace jit {
 
 /*
  * The Ion jitcode map implements tables to allow mapping from addresses in ion jitcode
  * to the list of (JSScript*, jsbytecode*) pairs that are implicitly active in the frame at
  * that point in the native code.
@@ -32,16 +31,22 @@ namespace jit {
  */
 
 class JitcodeGlobalTable;
 class JitcodeIonTable;
 class JitcodeRegionEntry;
 
 class JitcodeGlobalEntry;
 
+struct NativeToBytecode {
+    CodeOffset nativeOffset;
+    InlineScriptTree* tree;
+    jsbytecode* pc;
+};
+
 class JitcodeSkiplistTower
 {
   public:
     static const unsigned MAX_HEIGHT = 32;
 
   private:
     uint8_t height_;
     bool isFree_;
@@ -1257,23 +1262,23 @@ class JitcodeRegionEntry
     static void ReadScriptPc(CompactBufferReader& reader, uint32_t* scriptIdx, uint32_t* pcOffset);
 
     static void WriteDelta(CompactBufferWriter& writer, uint32_t nativeDelta, int32_t pcDelta);
     static void ReadDelta(CompactBufferReader& reader, uint32_t* nativeDelta, int32_t* pcDelta);
 
     // Given a pointer into an array of NativeToBytecode (and a pointer to the end of the array),
     // compute the number of entries that would be consume by outputting a run starting
     // at this one.
-    static uint32_t ExpectedRunLength(const CodeGeneratorShared::NativeToBytecode* entry,
-                                      const CodeGeneratorShared::NativeToBytecode* end);
+    static uint32_t ExpectedRunLength(const NativeToBytecode* entry,
+                                      const NativeToBytecode* end);
 
     // Write a run, starting at the given NativeToBytecode entry, into the given buffer writer.
     static MOZ_MUST_USE bool WriteRun(CompactBufferWriter& writer, JSScript** scriptList,
                                       uint32_t scriptListSize, uint32_t runLength,
-                                      const CodeGeneratorShared::NativeToBytecode* entry);
+                                      const NativeToBytecode* entry);
 
     // Delta Run entry formats are encoded little-endian:
     //
     //  byte 0
     //  NNNN-BBB0
     //      Single byte format.  nativeDelta in [0, 15], pcDelta in [0, 7]
     //
     static const uint32_t ENC1_MASK = 0x1;
@@ -1519,18 +1524,18 @@ class JitcodeIonTable
 
     const uint8_t* payloadStart() const {
         // The beginning of the payload the beginning of the first region are the same.
         return payloadEnd() - regionOffset(0);
     }
 
     static MOZ_MUST_USE bool WriteIonTable(CompactBufferWriter& writer,
                                            JSScript** scriptList, uint32_t scriptListSize,
-                                           const CodeGeneratorShared::NativeToBytecode* start,
-                                           const CodeGeneratorShared::NativeToBytecode* end,
+                                           const NativeToBytecode* start,
+                                           const NativeToBytecode* end,
                                            uint32_t* tableOffsetOut, uint32_t* numRegionsOut);
 };
 
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_JitcodeMap_h */
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -10,17 +10,17 @@
 
 #include "builtin/AtomicsObject.h"
 #include "builtin/intl/Collator.h"
 #include "builtin/intl/DateTimeFormat.h"
 #include "builtin/intl/NumberFormat.h"
 #include "builtin/intl/PluralRules.h"
 #include "builtin/intl/RelativeTimeFormat.h"
 #include "builtin/MapObject.h"
-#include "builtin/SIMD.h"
+#include "builtin/SIMDConstants.h"
 #include "builtin/String.h"
 #include "builtin/TestingFunctions.h"
 #include "builtin/TypedObject.h"
 #include "jit/BaselineInspector.h"
 #include "jit/InlinableNatives.h"
 #include "jit/IonBuilder.h"
 #include "jit/Lowering.h"
 #include "jit/MIR.h"
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -12,16 +12,17 @@
 #include "mozilla/IntegerPrintfMacros.h"
 #include "mozilla/MathAlgorithms.h"
 
 #include <ctype.h>
 
 #include "jslibmath.h"
 
 #include "builtin/RegExp.h"
+#include "builtin/SIMD.h"
 #include "builtin/String.h"
 #include "jit/AtomicOperations.h"
 #include "jit/BaselineInspector.h"
 #include "jit/IonBuilder.h"
 #include "jit/JitSpewer.h"
 #include "jit/MIRGraph.h"
 #include "jit/RangeAnalysis.h"
 #include "js/Conversions.h"
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -12,17 +12,17 @@
 #ifndef jit_MIR_h
 #define jit_MIR_h
 
 #include "mozilla/Alignment.h"
 #include "mozilla/Array.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/MacroForEach.h"
 
-#include "builtin/SIMD.h"
+#include "builtin/SIMDConstants.h"
 #include "jit/AtomicOp.h"
 #include "jit/BaselineIC.h"
 #include "jit/FixedList.h"
 #include "jit/InlineList.h"
 #include "jit/JitAllocPolicy.h"
 #include "jit/MacroAssembler.h"
 #include "jit/MOpcodes.h"
 #include "jit/TypedObjectPrediction.h"
@@ -3149,21 +3149,16 @@ class MGoto
     MBasicBlock* target() {
         return getSuccessor(0);
     }
     AliasSet getAliasSet() const override {
         return AliasSet::None();
     }
 };
 
-enum BranchDirection {
-    FALSE_BRANCH,
-    TRUE_BRANCH
-};
-
 static inline BranchDirection
 NegateBranchDirection(BranchDirection dir)
 {
     return (dir == FALSE_BRANCH) ? TRUE_BRANCH : FALSE_BRANCH;
 }
 
 // Tests if the input instruction evaluates to true or false, and jumps to the
 // start of a corresponding basic block.
--- a/js/src/jit/OptimizationTracking.h
+++ b/js/src/jit/OptimizationTracking.h
@@ -8,24 +8,31 @@
 #define jit_OptimizationTracking_h
 
 #include "mozilla/Maybe.h"
 
 #include "jit/CompactBuffer.h"
 #include "jit/CompileInfo.h"
 #include "jit/JitAllocPolicy.h"
 #include "jit/JitSpewer.h"
+#include "jit/shared/Assembler-shared.h"
 #include "js/TrackedOptimizationInfo.h"
 #include "vm/TypeInference.h"
 
 namespace js {
 
 namespace jit {
 
-struct NativeToTrackedOptimizations;
+struct NativeToTrackedOptimizations
+{
+    // [startOffset, endOffset]
+    CodeOffset startOffset;
+    CodeOffset endOffset;
+    const TrackedOptimizations* optimizations;
+};
 
 class OptimizationAttempt
 {
     JS::TrackedStrategy strategy_;
     JS::TrackedOutcome outcome_;
 
   public:
     OptimizationAttempt(JS::TrackedStrategy strategy, JS::TrackedOutcome outcome)
--- a/js/src/jit/shared/CodeGenerator-shared.h
+++ b/js/src/jit/shared/CodeGenerator-shared.h
@@ -6,16 +6,17 @@
 
 #ifndef jit_shared_CodeGenerator_shared_h
 #define jit_shared_CodeGenerator_shared_h
 
 #include "mozilla/Alignment.h"
 #include "mozilla/Move.h"
 #include "mozilla/TypeTraits.h"
 
+#include "jit/JitcodeMap.h"
 #include "jit/JitFrames.h"
 #include "jit/LIR.h"
 #include "jit/MacroAssembler.h"
 #include "jit/MIRGenerator.h"
 #include "jit/MIRGraph.h"
 #include "jit/OptimizationTracking.h"
 #include "jit/Safepoints.h"
 #include "jit/Snapshots.h"
@@ -34,27 +35,16 @@ class OutOfLineCallVM;
 
 class OutOfLineTruncateSlow;
 
 struct ReciprocalMulConstants {
     int64_t multiplier;
     int32_t shiftAmount;
 };
 
-// This should be nested in CodeGeneratorShared, but it is used in
-// optimization tracking implementation and nested classes cannot be
-// forward-declared.
-struct NativeToTrackedOptimizations
-{
-    // [startOffset, endOffset]
-    CodeOffset startOffset;
-    CodeOffset endOffset;
-    const TrackedOptimizations* optimizations;
-};
-
 class CodeGeneratorShared : public LElementVisitor
 {
     js::Vector<OutOfLineCode*, 0, SystemAllocPolicy> outOfLineCode_;
 
     MacroAssembler& ensureMasm(MacroAssembler* masm);
     mozilla::Maybe<IonHeapMacroAssembler> maybeMasm_;
 
   public:
@@ -106,23 +96,16 @@ class CodeGeneratorShared : public LElem
         PatchableTLEvent(CodeOffset offset, const char* event)
             : offset(offset), event(event)
         {}
     };
     js::Vector<PatchableTLEvent, 0, SystemAllocPolicy> patchableTLEvents_;
     js::Vector<CodeOffset, 0, SystemAllocPolicy> patchableTLScripts_;
 #endif
 
-  public:
-    struct NativeToBytecode {
-        CodeOffset nativeOffset;
-        InlineScriptTree* tree;
-        jsbytecode* pc;
-    };
-
   protected:
     js::Vector<NativeToBytecode, 0, SystemAllocPolicy> nativeToBytecodeList_;
     uint8_t* nativeToBytecodeMap_;
     uint32_t nativeToBytecodeMapSize_;
     uint32_t nativeToBytecodeTableOffset_;
     uint32_t nativeToBytecodeNumRegions_;
 
     JSScript** nativeToBytecodeScriptList_;
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -38,30 +38,31 @@
 #include "builtin/JSON.h"
 #include "builtin/MapObject.h"
 #include "builtin/Promise.h"
 #include "builtin/RegExp.h"
 #include "builtin/Stream.h"
 #include "builtin/String.h"
 #include "builtin/Symbol.h"
 #ifdef ENABLE_SIMD
-# include "builtin/SIMD.h"
+# include "builtin/SIMDConstants.h"
 #endif
 #ifdef ENABLE_BINARYDATA
 # include "builtin/TypedObject.h"
 #endif
 #include "frontend/BytecodeCompiler.h"
 #include "frontend/FullParseHandler.h"  // for JS_BufferIsCompileableUnit
 #include "frontend/Parser.h" // for JS_BufferIsCompileableUnit
 #include "gc/FreeOp.h"
 #include "gc/Marking.h"
 #include "gc/Policy.h"
 #include "gc/PublicIterators.h"
 #include "gc/WeakMap.h"
 #include "jit/JitCommon.h"
+#include "jit/JitSpewer.h"
 #include "js/CharacterEncoding.h"
 #include "js/Conversions.h"
 #include "js/Date.h"
 #include "js/Initialization.h"
 #include "js/Proxy.h"
 #include "js/SliceBudget.h"
 #include "js/StructuredClone.h"
 #include "js/Utility.h"
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -70,24 +70,24 @@
 # include "frontend/BinSource.h"
 #endif // defined(JS_BUILD_BINAST)
 #include "frontend/Parser.h"
 #include "gc/PublicIterators.h"
 #include "jit/arm/Simulator-arm.h"
 #include "jit/InlinableNatives.h"
 #include "jit/Ion.h"
 #include "jit/JitcodeMap.h"
-#include "jit/OptimizationTracking.h"
+#include "jit/JitRealm.h"
+#include "jit/shared/CodeGenerator-shared.h"
 #include "js/Debug.h"
 #include "js/GCVector.h"
 #include "js/Initialization.h"
 #include "js/Printf.h"
 #include "js/StructuredClone.h"
 #include "js/SweepingAPI.h"
-#include "js/TrackedOptimizationInfo.h"
 #include "js/Wrapper.h"
 #include "perf/jsperf.h"
 #include "shell/jsoptparse.h"
 #include "shell/jsshell.h"
 #include "shell/OSObject.h"
 #include "threading/ConditionVariable.h"
 #include "threading/ExclusiveData.h"
 #include "threading/LockGuard.h"
--- a/js/src/vm/EnvironmentObject.h
+++ b/js/src/vm/EnvironmentObject.h
@@ -8,16 +8,17 @@
 #define vm_EnvironmentObject_h
 
 #include "builtin/ModuleObject.h"
 #include "frontend/NameAnalysisTypes.h"
 #include "gc/Barrier.h"
 #include "gc/WeakMap.h"
 #include "js/GCHashTable.h"
 #include "vm/ArgumentsObject.h"
+#include "vm/GlobalObject.h"
 #include "vm/JSContext.h"
 #include "vm/JSObject.h"
 #include "vm/ProxyObject.h"
 #include "vm/Scope.h"
 
 namespace js {
 
 class ModuleObject;
--- a/js/src/vm/ErrorReporting.cpp
+++ b/js/src/vm/ErrorReporting.cpp
@@ -8,16 +8,17 @@
 
 #include "mozilla/Move.h"
 
 #include <stdarg.h>
 
 #include "jsexn.h"
 #include "jsfriendapi.h"
 
+#include "vm/GlobalObject.h"
 #include "vm/JSContext.h"
 
 #include "vm/JSContext-inl.h"
 
 using JS::HandleObject;
 using JS::HandleValue;
 using JS::UniqueTwoByteChars;
 
--- a/js/src/vm/GeckoProfiler.cpp
+++ b/js/src/vm/GeckoProfiler.cpp
@@ -5,21 +5,23 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "vm/GeckoProfiler-inl.h"
 
 #include "mozilla/DebugOnly.h"
 
 #include "jsnum.h"
 
+#include "gc/GC.h"
 #include "gc/PublicIterators.h"
 #include "jit/BaselineFrame.h"
 #include "jit/BaselineJIT.h"
 #include "jit/JitcodeMap.h"
 #include "jit/JitFrames.h"
+#include "jit/JitRealm.h"
 #include "jit/JSJitFrameIter.h"
 #include "util/StringBuffer.h"
 #include "vm/JSScript.h"
 
 #include "gc/Marking-inl.h"
 
 using namespace js;
 
--- a/js/src/vm/JSScript.cpp
+++ b/js/src/vm/JSScript.cpp
@@ -30,16 +30,17 @@
 
 #include "frontend/BytecodeCompiler.h"
 #include "frontend/BytecodeEmitter.h"
 #include "frontend/SharedContext.h"
 #include "gc/FreeOp.h"
 #include "jit/BaselineJIT.h"
 #include "jit/Ion.h"
 #include "jit/IonCode.h"
+#include "jit/JitRealm.h"
 #include "js/MemoryMetrics.h"
 #include "js/Printf.h"
 #include "js/UniquePtr.h"
 #include "js/Utility.h"
 #include "js/Wrapper.h"
 #include "util/StringBuffer.h"
 #include "util/Text.h"
 #include "vm/ArgumentsObject.h"
--- a/js/src/vm/Realm-inl.h
+++ b/js/src/vm/Realm-inl.h
@@ -6,16 +6,17 @@
 
 #ifndef vm_Realm_inl_h
 #define vm_Realm_inl_h
 
 #include "vm/Realm.h"
 
 #include "gc/Barrier.h"
 #include "gc/Marking.h"
+#include "vm/GlobalObject.h"
 #include "vm/Iteration.h"
 
 #include "vm/JSContext-inl.h"
 
 inline void
 JS::Realm::initGlobal(js::GlobalObject& global)
 {
     MOZ_ASSERT(global.realm() == this);
--- a/js/src/vm/Realm.h
+++ b/js/src/vm/Realm.h
@@ -13,22 +13,22 @@
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/Tuple.h"
 #include "mozilla/Variant.h"
 #include "mozilla/XorShift128PlusRNG.h"
 
 #include <stddef.h>
 
+#include "builtin/Array.h"
 #include "gc/Barrier.h"
 #include "gc/Zone.h"
 #include "js/UniquePtr.h"
 #include "vm/ArrayBufferObject.h"
 #include "vm/Compartment.h"
-#include "vm/GlobalObject.h"
 #include "vm/ReceiverGuard.h"
 #include "vm/RegExpShared.h"
 #include "vm/SavedStacks.h"
 #include "vm/Time.h"
 #include "wasm/WasmRealm.h"
 
 namespace js {
 
--- a/js/src/vm/RegExpObject.cpp
+++ b/js/src/vm/RegExpObject.cpp
@@ -16,16 +16,17 @@
 
 #include "builtin/RegExp.h"
 #include "frontend/TokenStream.h"
 #include "gc/HashUtil.h"
 #ifdef DEBUG
 #include "irregexp/RegExpBytecode.h"
 #endif
 #include "irregexp/RegExpParser.h"
+#include "jit/VMFunctions.h"
 #include "util/StringBuffer.h"
 #include "vm/MatchPairs.h"
 #include "vm/RegExpStatics.h"
 #include "vm/StringType.h"
 #include "vm/TraceLogging.h"
 #ifdef DEBUG
 #include "util/Unicode.h"
 #endif
--- a/js/src/vm/Scope.cpp
+++ b/js/src/vm/Scope.cpp
@@ -1,16 +1,18 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "vm/Scope.h"
 
+#include "mozilla/ScopeExit.h"
+
 #include <memory>
 #include <new>
 
 #include "builtin/ModuleObject.h"
 #include "gc/Allocator.h"
 #include "gc/FreeOp.h"
 #include "util/StringBuffer.h"
 #include "vm/EnvironmentObject.h"
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -7,16 +7,17 @@
 #include "vm/Stack-inl.h"
 
 #include <utility>
 
 #include "gc/Marking.h"
 #include "jit/BaselineFrame.h"
 #include "jit/JitcodeMap.h"
 #include "jit/JitRealm.h"
+#include "jit/shared/CodeGenerator-shared.h"
 #include "vm/Debugger.h"
 #include "vm/JSContext.h"
 #include "vm/Opcodes.h"
 
 #include "jit/JSJitFrameIter-inl.h"
 #include "vm/Compartment-inl.h"
 #include "vm/EnvironmentObject-inl.h"
 #include "vm/Interpreter-inl.h"
@@ -1714,16 +1715,18 @@ jit::JitActivation::removeRematerialized
     // Ion bailout can fail due to overrecursion and OOM. In such cases we
     // cannot honor any further Debugger hooks on the frame, and need to
     // ensure that its Debugger.Frame entry is cleaned up.
     if (!cx->realm()->isDebuggee() || !rematerializedFrames_)
         return;
     if (RematerializedFrameTable::Ptr p = rematerializedFrames_->lookup(top)) {
         for (uint32_t i = 0; i < p->value().length(); i++)
             Debugger::handleUnrecoverableIonBailoutError(cx, p->value()[i]);
+        RematerializedFrame::FreeInVector(p->value());
+        rematerializedFrames_->remove(p);
     }
 }
 
 void
 jit::JitActivation::traceRematerializedFrames(JSTracer* trc)
 {
     if (!rematerializedFrames_)
         return;
--- a/js/src/vm/Stack.h
+++ b/js/src/vm/Stack.h
@@ -1910,19 +1910,18 @@ class InterpreterFrameIterator
 //
 // For ease of use, there is also OnlyJSJitFrameIter, which skips all the
 // non-JSJit frames.
 //
 // Note it is allowed to get a handle to the internal frame iterator via
 // asJSJit() and asWasm(), but the user has to be careful not to have those be
 // used after JitFrameIter leaves the scope or the operator++ is called.
 //
-// TODO(bug 1360211) In particular, this can handle the transition from wasm to
-// ion and from ion to wasm, since these will be interleaved in the same
-// JitActivation.
+// In particular, this can handle the transition from wasm to jit and from jit
+// to wasm, since these can be interleaved in the same JitActivation.
 class JitFrameIter
 {
   protected:
     jit::JitActivation* act_;
     mozilla::MaybeOneOf<jit::JSJitFrameIter, wasm::WasmFrameIter> iter_;
     bool mustUnwindActivation_;
 
     void settle();
--- a/js/src/wasm/WasmBinaryConstants.h
+++ b/js/src/wasm/WasmBinaryConstants.h
@@ -14,17 +14,17 @@
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #ifndef wasm_binary_h
 #define wasm_binary_h
 
-#include "builtin/SIMD.h"
+#include "builtin/SIMDConstants.h"
 
 namespace js {
 namespace wasm {
 
 static const uint32_t MagicNumber        = 0x6d736100; // "\0asm"
 static const uint32_t EncodingVersion    = 0x01;
 
 enum class SectionId
--- a/js/src/wasm/WasmInstance.cpp
+++ b/js/src/wasm/WasmInstance.cpp
@@ -13,20 +13,22 @@
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #include "wasm/WasmInstance.h"
 
+#include "builtin/SIMD.h"
 #include "jit/AtomicOperations.h"
 #include "jit/BaselineJIT.h"
 #include "jit/InlinableNatives.h"
 #include "jit/JitCommon.h"
+#include "jit/JitRealm.h"
 #include "wasm/WasmBuiltins.h"
 #include "wasm/WasmModule.h"
 
 #include "gc/StoreBuffer-inl.h"
 #include "vm/ArrayBufferObject-inl.h"
 #include "vm/JSObject-inl.h"
 
 using namespace js;
--- a/js/src/wasm/WasmJS.h
+++ b/js/src/wasm/WasmJS.h
@@ -24,16 +24,17 @@
 #include "wasm/WasmTypes.h"
 
 namespace js {
 
 class GlobalObject;
 class TypedArrayObject;
 class WasmFunctionScope;
 class WasmInstanceScope;
+class SharedArrayRawBuffer;
 
 namespace wasm {
 
 // Return whether WebAssembly can be compiled on this platform.
 // This must be checked and must be true to call any of the top-level wasm
 // eval/compile methods.
 
 bool
--- a/js/src/wasm/WasmProcess.cpp
+++ b/js/src/wasm/WasmProcess.cpp
@@ -14,16 +14,17 @@
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #include "wasm/WasmProcess.h"
 
 #include "mozilla/BinarySearch.h"
+#include "mozilla/ScopeExit.h"
 
 #include "vm/MutexIDs.h"
 #include "wasm/WasmBuiltins.h"
 #include "wasm/WasmCode.h"
 #include "wasm/WasmInstance.h"
 
 using namespace js;
 using namespace wasm;
--- a/js/xpconnect/src/XPCJSContext.cpp
+++ b/js/xpconnect/src/XPCJSContext.cpp
@@ -229,58 +229,56 @@ class Watchdog
     bool mShuttingDown;
     mozilla::Atomic<int32_t> mMinScriptRunTimeSeconds;
 };
 
 #define PREF_MAX_SCRIPT_RUN_TIME_CONTENT "dom.max_script_run_time"
 #define PREF_MAX_SCRIPT_RUN_TIME_CHROME "dom.max_chrome_script_run_time"
 #define PREF_MAX_SCRIPT_RUN_TIME_EXT_CONTENT "dom.max_ext_content_script_run_time"
 
-class WatchdogManager : public nsIObserver
+static const char* gCallbackPrefs[] = {
+    "dom.use_watchdog",
+    PREF_MAX_SCRIPT_RUN_TIME_CONTENT,
+    PREF_MAX_SCRIPT_RUN_TIME_CHROME,
+    PREF_MAX_SCRIPT_RUN_TIME_EXT_CONTENT,
+    nullptr,
+};
+
+class WatchdogManager
 {
   public:
-
-    NS_DECL_ISUPPORTS
     explicit WatchdogManager()
     {
         // All the timestamps start at zero.
         PodArrayZero(mTimestamps);
 
         // Register ourselves as an observer to get updates on the pref.
-        mozilla::Preferences::AddStrongObserver(this, "dom.use_watchdog");
-        mozilla::Preferences::AddStrongObserver(this, PREF_MAX_SCRIPT_RUN_TIME_CONTENT);
-        mozilla::Preferences::AddStrongObserver(this, PREF_MAX_SCRIPT_RUN_TIME_CHROME);
-        mozilla::Preferences::AddStrongObserver(this, PREF_MAX_SCRIPT_RUN_TIME_EXT_CONTENT);
+        Preferences::RegisterCallbacks(PrefsChanged, gCallbackPrefs, this);
     }
 
-  protected:
-
     virtual ~WatchdogManager()
     {
         // Shutting down the watchdog requires context-switching to the watchdog
         // thread, which isn't great to do in a destructor. So we require
         // consumers to shut it down manually before releasing it.
         MOZ_ASSERT(!mWatchdog);
     }
 
+  private:
+
+    static void PrefsChanged(const char* aPref, WatchdogManager* aSelf)
+    {
+        aSelf->RefreshWatchdog();
+    }
+
   public:
 
     void Shutdown()
     {
-        mozilla::Preferences::RemoveObserver(this, "dom.use_watchdog");
-        mozilla::Preferences::RemoveObserver(this, PREF_MAX_SCRIPT_RUN_TIME_CONTENT);
-        mozilla::Preferences::RemoveObserver(this, PREF_MAX_SCRIPT_RUN_TIME_CHROME);
-        mozilla::Preferences::RemoveObserver(this, PREF_MAX_SCRIPT_RUN_TIME_EXT_CONTENT);
-    }
-
-    NS_IMETHOD Observe(nsISupports* aSubject, const char* aTopic,
-                       const char16_t* aData) override
-    {
-        RefreshWatchdog();
-        return NS_OK;
+        Preferences::UnregisterCallbacks(PrefsChanged, gCallbackPrefs, this);
     }
 
     void
     RegisterContext(XPCJSContext* aContext)
     {
         MOZ_ASSERT(NS_IsMainThread());
         AutoLockWatchdog lock(mWatchdog);
 
@@ -449,18 +447,16 @@ class WatchdogManager : public nsIObserv
     LinkedList<XPCJSContext> mActiveContexts;
     LinkedList<XPCJSContext> mInactiveContexts;
     nsAutoPtr<Watchdog> mWatchdog;
 
     // We store ContextStateChange on the contexts themselves.
     PRTime mTimestamps[kWatchdogTimestampCategoryCount - 1];
 };
 
-NS_IMPL_ISUPPORTS(WatchdogManager, nsIObserver)
-
 AutoLockWatchdog::AutoLockWatchdog(Watchdog* aWatchdog MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
   : mWatchdog(aWatchdog)
 {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     if (mWatchdog) {
         PR_Lock(mWatchdog->GetLock());
     }
 }
@@ -728,19 +724,18 @@ bool xpc::ExtraWarningsForSystemJS() { r
 #endif
 
 static mozilla::Atomic<bool> sSharedMemoryEnabled(false);
 
 bool
 xpc::SharedMemoryEnabled() { return sSharedMemoryEnabled; }
 
 static void
-ReloadPrefsCallback(const char* pref, void* data)
+ReloadPrefsCallback(const char* pref, XPCJSContext* xpccx)
 {
-    XPCJSContext* xpccx = static_cast<XPCJSContext*>(data);
     JSContext* cx = xpccx->Context();
 
     bool useBaseline = Preferences::GetBool(JS_OPTIONS_DOT_STR "baselinejit");
     bool useIon = Preferences::GetBool(JS_OPTIONS_DOT_STR "ion");
     bool useAsmJS = Preferences::GetBool(JS_OPTIONS_DOT_STR "asmjs");
     bool useWasm = Preferences::GetBool(JS_OPTIONS_DOT_STR "wasm");
     bool useWasmIon = Preferences::GetBool(JS_OPTIONS_DOT_STR "wasm_ionjit");
     bool useWasmBaseline = Preferences::GetBool(JS_OPTIONS_DOT_STR "wasm_baselinejit");
@@ -1142,17 +1137,17 @@ XPCJSContext::Initialize(XPCJSContext* a
     return NS_OK;
 }
 
 // static
 uint32_t
 XPCJSContext::sInstanceCount;
 
 // static
-StaticRefPtr<WatchdogManager>
+StaticAutoPtr<WatchdogManager>
 XPCJSContext::sWatchdogInstance;
 
 // static
 WatchdogManager*
 XPCJSContext::GetWatchdogManager()
 {
     if (sWatchdogInstance) {
         return sWatchdogInstance;
--- a/js/xpconnect/src/XPCWrappedJSClass.cpp
+++ b/js/xpconnect/src/XPCWrappedJSClass.cpp
@@ -948,21 +948,21 @@ nsXPCWrappedJSClass::CallMethod(nsXPCWra
     if (!ccx.IsValid())
         return retval;
 
     JSContext* cx = ccx.GetJSContext();
 
     if (!cx || !IsReflectable(methodIndex))
         return NS_ERROR_FAILURE;
 
-    // [implicit_jscontext] and [optional_argc] have a different calling
-    // convention, which we don't support for JS-implemented components.
-    if (info->WantsOptArgc() || info->WantsContext()) {
-        const char* str = "IDL methods marked with [implicit_jscontext] "
-                          "or [optional_argc] may not be implemented in JS";
+    // [optional_argc] has a different calling convention, which we don't
+    // support for JS-implemented components.
+    if (info->WantsOptArgc()) {
+        const char* str = "IDL methods marked with [optional_argc] may not "
+                          "be implemented in JS";
         // Throw and warn for good measure.
         JS_ReportErrorASCII(cx, "%s", str);
         NS_WARNING(str);
         return CheckForException(ccx, aes, name, GetInterfaceName());
     }
 
     RootedValue fval(cx);
     RootedObject obj(cx, wrapper->GetJSObject());
--- a/js/xpconnect/src/XPCWrappedNative.cpp
+++ b/js/xpconnect/src/XPCWrappedNative.cpp
@@ -1437,48 +1437,38 @@ CallMethodHelper::QueryInterfaceFastPath
 
 bool
 CallMethodHelper::InitializeDispatchParams()
 {
     const uint8_t wantsOptArgc = mMethodInfo->WantsOptArgc() ? 1 : 0;
     const uint8_t wantsJSContext = mMethodInfo->WantsContext() ? 1 : 0;
     const uint8_t paramCount = mMethodInfo->GetParamCount();
     uint8_t requiredArgs = paramCount;
-    uint8_t hasRetval = 0;
 
     // XXX ASSUMES that retval is last arg. The xpidl compiler ensures this.
-    if (mMethodInfo->HasRetval()) {
-        hasRetval = 1;
+    if (mMethodInfo->HasRetval())
         requiredArgs--;
-    }
 
     if (mArgc < requiredArgs || wantsOptArgc) {
-        if (wantsOptArgc)
-            mOptArgcIndex = requiredArgs;
+        if (wantsOptArgc) {
+            // The implicit JSContext*, if we have one, comes first.
+            mOptArgcIndex = requiredArgs + wantsJSContext;
+        }
 
         // skip over any optional arguments
         while (requiredArgs && mMethodInfo->GetParam(requiredArgs-1).IsOptional())
             requiredArgs--;
 
         if (mArgc < requiredArgs) {
             Throw(NS_ERROR_XPC_NOT_ENOUGH_ARGS, mCallContext);
             return false;
         }
     }
 
-    if (wantsJSContext) {
-        if (wantsOptArgc)
-            // Need to bump mOptArgcIndex up one here.
-            mJSContextIndex = mOptArgcIndex++;
-        else if (mMethodInfo->IsSetter() || mMethodInfo->IsGetter())
-            // For attributes, we always put the JSContext* first.
-            mJSContextIndex = 0;
-        else
-            mJSContextIndex = paramCount - hasRetval;
-    }
+    mJSContextIndex = mMethodInfo->IndexOfJSContext();
 
     // iterate through the params to clear flags (for safe cleanup later)
     for (uint8_t i = 0; i < paramCount + wantsJSContext + wantsOptArgc; i++) {
         nsXPTCVariant* dp = mDispatchParams.AppendElement();
         dp->ClearFlags();
         dp->val.p = nullptr;
     }
 
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -431,17 +431,17 @@ private:
     XPCCallContext*          mCallContext;
     AutoMarkingPtr*          mAutoRoots;
     jsid                     mResolveName;
     XPCWrappedNative*        mResolvingWrapper;
     WatchdogManager*         mWatchdogManager;
 
     // Number of XPCJSContexts currently alive.
     static uint32_t         sInstanceCount;
-    static mozilla::StaticRefPtr<WatchdogManager> sWatchdogInstance;
+    static mozilla::StaticAutoPtr<WatchdogManager> sWatchdogInstance;
     static WatchdogManager* GetWatchdogManager();
 
     // If we spend too much time running JS code in an event handler, then we
     // want to show the slow script UI. The timeout T is controlled by prefs. We
     // invoke the interrupt callback once after T/2 seconds and set
     // mSlowScriptSecondHalf to true. After another T/2 seconds, we invoke the
     // interrupt callback again. Since mSlowScriptSecondHalf is now true, it
     // shows the slow script UI. The reason we invoke the callback twice is to
--- a/js/xpconnect/tests/components/js/xpctest_bug809674.js
+++ b/js/xpconnect/tests/components/js/xpctest_bug809674.js
@@ -7,13 +7,36 @@ function TestBug809674() {}
 TestBug809674.prototype = {
 
   /* Boilerplate */
   QueryInterface: ChromeUtils.generateQI([Ci["nsIXPCTestBug809674"]]),
   contractID: "@mozilla.org/js/xpc/test/js/Bug809674;1",
   classID: Components.ID("{2df46559-da21-49bf-b863-0d7b7bbcbc73}"),
 
   /* nsIXPCTestBug809674 */
-  jsvalProperty: {},
+  methodWithOptionalArgc() {},
+
+  addArgs(x, y) {
+    return x + y;
+  },
+  addSubMulArgs(x, y, subOut, mulOut) {
+    subOut.value = x - y;
+    mulOut.value = x * y;
+    return x + y;
+  },
+  addVals(x, y) {
+    return x + y;
+  },
+  addMany(x1, x2, x3, x4, x5, x6, x7, x8) {
+    return x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8;
+  },
+
+  methodNoArgs() {
+    return 7;
+  },
+  methodNoArgsNoRetVal() {},
+
+  valProperty: {value: 42},
+  uintProperty: 123,
 };
 
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([TestBug809674]);
--- a/js/xpconnect/tests/idl/xpctest_bug809674.idl
+++ b/js/xpconnect/tests/idl/xpctest_bug809674.idl
@@ -3,16 +3,45 @@
  * 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"
 /*
  * Test interface for https://bugzilla.mozilla.org/show_bug.cgi?id=809674 .
  *
- * This test makes sure that accessing an attribute marked with
- * [implicit_jscontext] will throw without crashing.
+ * This test makes sure that accessing JS-implemented attributes or methods
+ * marked with [implicit_jscontext] works as expected.
+ *
+ * It also makes sure [optional_argc] is not supported on JS-implemented
+ * methods.
  */
 
 [scriptable, uuid(2df46559-da21-49bf-b863-0d7b7bbcbc73)]
 interface nsIXPCTestBug809674 : nsISupports {
-	[implicit_jscontext] attribute jsval jsvalProperty;
+    // Various interesting [implicit_jscontext] cases.
+    [implicit_jscontext] unsigned long addArgs(in unsigned long x, in unsigned long y);
+    [implicit_jscontext] unsigned long addSubMulArgs(in unsigned long x, in unsigned long y,
+                                                     out unsigned long subOut,
+                                                     out unsigned long mulOut);
+    [implicit_jscontext] jsval addVals(in jsval x, in jsval y);
+
+    [implicit_jscontext] unsigned long methodNoArgs();
+    [implicit_jscontext] void methodNoArgsNoRetVal();
+
+    // When there are many arguments, the context is passed on the stack on
+    // most platforms.
+    [implicit_jscontext] unsigned long addMany(in unsigned long x1,
+                                               in unsigned long x2,
+                                               in unsigned long x3,
+                                               in unsigned long x4,
+                                               in unsigned long x5,
+                                               in unsigned long x6,
+                                               in unsigned long x7,
+                                               in unsigned long x8);
+
+    // Attributes can use [implicit_jscontext], too.
+    [implicit_jscontext] attribute jsval valProperty;
+    [implicit_jscontext] attribute unsigned long uintProperty;
+
+    // [optional_argc] is not supported.
+    [optional_argc] void methodWithOptionalArgc();
 };
--- a/js/xpconnect/tests/unit/test_bug809674.js
+++ b/js/xpconnect/tests/unit/test_bug809674.js
@@ -2,27 +2,49 @@
  * 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/. */
 
 function run_test() {
 
   // Load the component manifest.
   Components.manager.autoRegister(do_get_file('../components/js/xpctest.manifest'));
 
-  // Test for each component.
-  test_property_throws("@mozilla.org/js/xpc/test/js/Bug809674;1");
-}
+  // Instantiate the object.
+  var o = Cc["@mozilla.org/js/xpc/test/js/Bug809674;1"].createInstance(Ci["nsIXPCTestBug809674"]);
+
+  // Methods marked [implicit_jscontext].
+
+  Assert.equal(o.addArgs(12, 34), 46);
 
-function test_property_throws(contractid) {
+  var subRes = {}, mulRes = {};
+  Assert.equal(o.addSubMulArgs(9, 7, subRes, mulRes), 16);
+  Assert.equal(subRes.value, 2);
+  Assert.equal(mulRes.value, 63);
+
+  Assert.equal(o.addVals("foo", "x"), "foox");
+  Assert.equal(o.addVals("foo", 1.2), "foo1.2");
+  Assert.equal(o.addVals(1234, "foo"), "1234foo");
 
-  // Instantiate the object.
-  var o = Cc[contractid].createInstance(Ci["nsIXPCTestBug809674"]);
+  Assert.equal(o.addMany(1, 2, 4, 8, 16, 32, 64, 128), 255);
+
+  Assert.equal(o.methodNoArgs(), 7);
+  Assert.equal(o.methodNoArgsNoRetVal(), undefined);
+
+  // Attributes marked [implicit_jscontext].
 
-  // Test the initial values.
+  Assert.equal(o.valProperty.value, 42);
+  o.valProperty = o;
+  Assert.equal(o.valProperty, o);
+
+  Assert.equal(o.uintProperty, 123);
+  o.uintProperty++;
+  Assert.equal(o.uintProperty, 124);
+
+  // [optional_argc] is not supported.
   try {
-    o.jsvalProperty;
+    o.methodWithOptionalArgc();
     Assert.ok(false);
   } catch (e) {
     Assert.ok(true);
-    Assert.ok(/implicit_jscontext/.test(e))
+    Assert.ok(/optional_argc/.test(e))
   }
 
 }
--- a/layout/base/nsPresContext.cpp
+++ b/layout/base/nsPresContext.cpp
@@ -145,28 +145,16 @@ nsPresContext::IsDOMPaintEventPending()
     // invalidation doesn't invalidate anything further.
     NotifyInvalidation(drpc->mRefreshDriver->LastTransactionId().Next(), nsRect(0, 0, 0, 0));
     return true;
   }
   return false;
 }
 
 void
-nsPresContext::PrefChangedCallback(const char* aPrefName, void* instance_data)
-{
-  RefPtr<nsPresContext>  presContext =
-    static_cast<nsPresContext*>(instance_data);
-
-  NS_ASSERTION(presContext, "bad instance data");
-  if (presContext) {
-    presContext->PreferenceChanged(aPrefName);
-  }
-}
-
-void
 nsPresContext::ForceReflowForFontInfoUpdate()
 {
   // We can trigger reflow by pretending a font.* preference has changed;
   // this is the same mechanism as gfxPlatform::ForceGlobalReflow() uses
   // if new fonts are installed during the session, for example.
   PreferenceChanged("font.internaluseonly.changed");
 }
 
@@ -286,75 +274,57 @@ nsPresContext::nsPresContext(nsIDocument
     mTextPerf = new gfxTextPerfMetrics();
   }
 
   if (Preferences::GetBool(GFX_MISSING_FONTS_NOTIFY_PREF)) {
     mMissingFonts = new gfxMissingFontRecorder();
   }
 }
 
+static const char* gExactCallbackPrefs[] = {
+  "browser.underline_anchors",
+  "browser.anchor_color",
+  "browser.active_color",
+  "browser.visited_color",
+  "image.animation_mode",
+  "dom.send_after_paint_to_content",
+  "layout.css.dpi",
+  "layout.css.devPixelsPerPx",
+  "nglayout.debug.paint_flashing",
+  "nglayout.debug.paint_flashing_chrome",
+  kUseStandinsForNativeColors,
+  "intl.accept_languages",
+  nullptr,
+};
+
+static const char* gPrefixCallbackPrefs[] = {
+  "font.",
+  "browser.display.",
+  "bidi.",
+  "gfx.font_rendering.",
+  nullptr,
+};
+
 void
 nsPresContext::Destroy()
 {
   if (mEventManager) {
     // unclear if these are needed, but can't hurt
     mEventManager->NotifyDestroyPresContext(this);
     mEventManager->SetPresContext(nullptr);
     mEventManager = nullptr;
   }
 
   // Unregister preference callbacks
-  Preferences::UnregisterPrefixCallback(nsPresContext::PrefChangedCallback,
-                                        "font.",
-                                        this);
-  Preferences::UnregisterPrefixCallback(nsPresContext::PrefChangedCallback,
-                                        "browser.display.",
-                                        this);
-  Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback,
-                                  "browser.underline_anchors",
-                                  this);
-  Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback,
-                                  "browser.anchor_color",
-                                  this);
-  Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback,
-                                  "browser.active_color",
-                                  this);
-  Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback,
-                                  "browser.visited_color",
-                                  this);
-  Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback,
-                                  "image.animation_mode",
-                                  this);
-  Preferences::UnregisterPrefixCallback(nsPresContext::PrefChangedCallback,
-                                        "bidi.",
-                                        this);
-  Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback,
-                                  "dom.send_after_paint_to_content",
-                                  this);
-  Preferences::UnregisterPrefixCallback(nsPresContext::PrefChangedCallback,
-                                        "gfx.font_rendering.",
-                                        this);
-  Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback,
-                                  "layout.css.dpi",
-                                  this);
-  Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback,
-                                  "layout.css.devPixelsPerPx",
-                                  this);
-  Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback,
-                                  "nglayout.debug.paint_flashing",
-                                  this);
-  Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback,
-                                  "nglayout.debug.paint_flashing_chrome",
-                                  this);
-  Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback,
-                                  kUseStandinsForNativeColors,
-                                  this);
-  Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback,
-                                  "intl.accept_languages",
-                                  this);
+  Preferences::UnregisterPrefixCallbacks(
+    PREF_CHANGE_METHOD(nsPresContext::PreferenceChanged),
+    gPrefixCallbackPrefs, this);
+  Preferences::UnregisterCallbacks(
+    PREF_CHANGE_METHOD(nsPresContext::PreferenceChanged),
+    gExactCallbackPrefs, this);
 
   mRefreshDriver = nullptr;
 }
 
 nsPresContext::~nsPresContext()
 {
   MOZ_ASSERT(!mShell, "Presshell forgot to clear our mShell pointer");
   DetachShell();
@@ -878,64 +848,22 @@ nsPresContext::Init(nsDeviceContext* aDe
     }
 
     if (!mRefreshDriver) {
       mRefreshDriver = new nsRefreshDriver(this);
     }
   }
 
   // Register callbacks so we're notified when the preferences change
-  Preferences::RegisterPrefixCallback(nsPresContext::PrefChangedCallback,
-                                      "font.",
-                                      this);
-  Preferences::RegisterPrefixCallback(nsPresContext::PrefChangedCallback,
-                                      "browser.display.",
-                                      this);
-  Preferences::RegisterCallback(nsPresContext::PrefChangedCallback,
-                                "browser.underline_anchors",
-                                this);
-  Preferences::RegisterCallback(nsPresContext::PrefChangedCallback,
-                                "browser.anchor_color",
-                                this);
-  Preferences::RegisterCallback(nsPresContext::PrefChangedCallback,
-                                "browser.active_color",
-                                this);
-  Preferences::RegisterCallback(nsPresContext::PrefChangedCallback,
-                                "browser.visited_color",
-                                this);
-  Preferences::RegisterCallback(nsPresContext::PrefChangedCallback,
-                                "image.animation_mode",
-                                this);
-  Preferences::RegisterPrefixCallback(nsPresContext::PrefChangedCallback,
-                                      "bidi.",
-                                      this);
-  Preferences::RegisterCallback(nsPresContext::PrefChangedCallback,
-                                "dom.send_after_paint_to_content",
-                                this);
-  Preferences::RegisterPrefixCallback(nsPresContext::PrefChangedCallback,
-                                      "gfx.font_rendering.",
-                                      this);
-  Preferences::RegisterCallback(nsPresContext::PrefChangedCallback,
-                                "layout.css.dpi",
-                                this);
-  Preferences::RegisterCallback(nsPresContext::PrefChangedCallback,
-                                "layout.css.devPixelsPerPx",
-                                this);
-  Preferences::RegisterCallback(nsPresContext::PrefChangedCallback,
-                                "nglayout.debug.paint_flashing",
-                                this);
-  Preferences::RegisterCallback(nsPresContext::PrefChangedCallback,
-                                "nglayout.debug.paint_flashing_chrome",
-                                this);
-  Preferences::RegisterCallback(nsPresContext::PrefChangedCallback,
-                                kUseStandinsForNativeColors,
-                                this);
-  Preferences::RegisterCallback(nsPresContext::PrefChangedCallback,
-                                "intl.accept_languages",
-                                this);
+  Preferences::RegisterPrefixCallbacks(
+    PREF_CHANGE_METHOD(nsPresContext::PreferenceChanged),
+    gPrefixCallbackPrefs, this);
+  Preferences::RegisterCallbacks(
+    PREF_CHANGE_METHOD(nsPresContext::PreferenceChanged),
+    gExactCallbackPrefs, this);
 
   nsresult rv = mEventManager->Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
   mEventManager->SetPresContext(this);
 
 #ifdef DEBUG
   mInitialized = true;
--- a/layout/base/nsPresContext.h
+++ b/layout/base/nsPresContext.h
@@ -1193,17 +1193,16 @@ protected:
   UIResolutionChangedSubdocumentCallback(nsIDocument* aDocument, void* aData);
 
   void SetImgAnimations(nsIContent *aParent, uint16_t aMode);
   void SetSMILAnimations(nsIDocument *aDoc, uint16_t aNewMode,
                                      uint16_t aOldMode);
   void GetDocumentColorPreferences();
 
   void PreferenceChanged(const char* aPrefName);
-  static void PrefChangedCallback(const char*, void*);
 
   void UpdateAfterPreferencesChanged();
   void DispatchPrefChangedRunnableIfNeeded();
 
   void GetUserPreferences();
 
   /**
    * Fetch the user's font preferences for the given aLanguage's
--- a/layout/generic/BRFrame.cpp
+++ b/layout/generic/BRFrame.cpp
@@ -153,17 +153,17 @@ BRFrame::Reflow(nsPresContext* aPresCont
       // XXX This also fixes bug 10036!
       // Warning: nsTextControlFrame::CalculateSizeStandard depends on
       // the following line, see bug 228752.
       // The code below in AddInlinePrefISize also adds 1 appunit to width
       finalSize.ISize(wm) = 1;
     }
 
     // Return our reflow status
-    StyleClear breakType = aReflowInput.mStyleDisplay->PhysicalBreakType(wm);
+    StyleClear breakType = aReflowInput.mStyleDisplay->mBreakType;
     if (StyleClear::None == breakType) {
       breakType = StyleClear::Line;
     }
 
     aStatus.SetInlineLineBreakAfter(breakType);
     ll->SetLineEndsInBR(true);
   }
 
--- a/layout/generic/BlockReflowInput.cpp
+++ b/layout/generic/BlockReflowInput.cpp
@@ -737,17 +737,17 @@ BlockReflowInput::FlowAndPlaceFloat(nsIF
   // ``above'' another float that preceded it in the flow.
   mBCoord = std::max(FloatManager()->GetLowestFloatTop(), mBCoord);
 
   // See if the float should clear any preceding floats...
   // XXX We need to mark this float somehow so that it gets reflowed
   // when floats are inserted before it.
   if (StyleClear::None != floatDisplay->mBreakType) {
     // XXXldb Does this handle vertical margins correctly?
-    mBCoord = ClearFloats(mBCoord, floatDisplay->PhysicalBreakType(wm));
+    mBCoord = ClearFloats(mBCoord, floatDisplay->mBreakType);
   }
   // Get the band of available space with respect to margin box.
   nsFlowAreaRect floatAvailableSpace =
     GetFloatAvailableSpaceForPlacingFloat(mBCoord);
   LogicalRect adjustedAvailableSpace =
     mBlock->AdjustFloatAvailableSpace(*this, floatAvailableSpace.mRect, aFloat);
 
   NS_ASSERTION(aFloat->GetParent() == mBlock,
@@ -780,17 +780,17 @@ BlockReflowInput::FlowAndPlaceFloat(nsIF
                  "letter frames and orthogonal floats with auto block-size "
                  "shouldn't break, and if they do now, then they're breaking "
                  "at the wrong point");
   }
 
   // Find a place to place the float. The CSS2 spec doesn't want
   // floats overlapping each other or sticking out of the containing
   // block if possible (CSS2 spec section 9.5.1, see the rule list).
-  StyleFloat floatStyle = floatDisplay->PhysicalFloats(wm);
+  StyleFloat floatStyle = floatDisplay->mFloat;
   MOZ_ASSERT(StyleFloat::Left == floatStyle || StyleFloat::Right == floatStyle,
              "Invalid float type!");
 
   // Can the float fit here?
   bool keepFloatOnSameLine = false;
 
   // Are we required to place at least part of the float because we're
   // at the top of the page (to avoid an infinite loop of pushing and
@@ -1040,18 +1040,17 @@ BlockReflowInput::FlowAndPlaceFloat(nsIF
 void
 BlockReflowInput::PushFloatPastBreak(nsIFrame *aFloat)
 {
   // This ensures that we:
   //  * don't try to place later but smaller floats (which CSS says
   //    must have their tops below the top of this float)
   //  * don't waste much time trying to reflow this float again until
   //    after the break
-  StyleFloat floatStyle =
-    aFloat->StyleDisplay()->PhysicalFloats(mReflowInput.GetWritingMode());
+  StyleFloat floatStyle = aFloat->StyleDisplay()->mFloat;
   if (floatStyle == StyleFloat::Left) {
     FloatManager()->SetPushedLeftFloatPastBreak();
   } else {
     MOZ_ASSERT(floatStyle == StyleFloat::Right, "Unexpected float value!");
     FloatManager()->SetPushedRightFloatPastBreak();
   }
 
   // Put the float on the pushed floats list, even though it
--- a/layout/generic/WritingModes.h
+++ b/layout/generic/WritingModes.h
@@ -2182,42 +2182,16 @@ nsStylePosition::MinBSizeDependsOnContai
 }
 inline bool
 nsStylePosition::MaxBSizeDependsOnContainer(mozilla::WritingMode aWM) const
 {
   return aWM.IsVertical() ? MaxWidthDependsOnContainer()
                           : MaxHeightDependsOnContainer();
 }
 
-inline mozilla::StyleFloat
-nsStyleDisplay::PhysicalFloats(mozilla::WritingMode aWM) const
-{
-  using StyleFloat = mozilla::StyleFloat;
-  if (mFloat == StyleFloat::InlineStart) {
-    return aWM.IsBidiLTR() ? StyleFloat::Left : StyleFloat::Right;
-  }
-  if (mFloat == StyleFloat::InlineEnd) {
-    return aWM.IsBidiLTR() ? StyleFloat::Right : StyleFloat::Left;
-  }
-  return mFloat;
-}
-
-inline mozilla::StyleClear
-nsStyleDisplay::PhysicalBreakType(mozilla::WritingMode aWM) const
-{
-  using StyleClear = mozilla::StyleClear;
-  if (mBreakType == StyleClear::InlineStart) {
-    return aWM.IsBidiLTR() ? StyleClear::Left : StyleClear::Right;
-  }
-  if (mBreakType == StyleClear::InlineEnd) {
-    return aWM.IsBidiLTR() ? StyleClear::Right : StyleClear::Left;
-  }
-  return mBreakType;
-}
-
 inline bool
 nsStyleMargin::HasBlockAxisAuto(mozilla::WritingMode aWM) const
 {
   return mMargin.HasBlockAxisAuto(aWM);
 }
 inline bool
 nsStyleMargin::HasInlineAxisAuto(mozilla::WritingMode aWM) const
 {
--- a/layout/generic/nsBlockFrame.cpp
+++ b/layout/generic/nsBlockFrame.cpp
@@ -837,18 +837,17 @@ nsBlockFrame::GetPrefISize(gfxContext *a
       }
       AutoNoisyIndenter lineindent(gNoisyIntrinsic);
 #endif
       if (line->IsBlock()) {
         StyleClear breakType;
         if (!data.mLineIsEmpty || BlockCanIntersectFloats(line->mFirstChild)) {
           breakType = StyleClear::Both;
         } else {
-          breakType = line->mFirstChild->
-            StyleDisplay()->PhysicalBreakType(data.mLineContainerWM);
+          breakType = line->mFirstChild->StyleDisplay()->mBreakType;
         }
         data.ForceBreak(breakType);
         data.mCurrentLine = nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
                         line->mFirstChild, nsLayoutUtils::PREF_ISIZE);
         data.ForceBreak();
       } else {
         if (!curFrame->GetPrevContinuation() &&
             line == curFrame->LinesBegin()) {
@@ -3221,18 +3220,17 @@ nsBlockFrame::ReflowBlockFrame(BlockRefl
   if (!frame) {
     NS_ASSERTION(false, "program error - unexpected empty line");
     return;
   }
 
   // Prepare the block reflow engine
   nsBlockReflowContext brc(aState.mPresContext, aState.mReflowInput);
 
-  StyleClear breakType = frame->StyleDisplay()->
-    PhysicalBreakType(aState.mReflowInput.GetWritingMode());
+  StyleClear breakType = frame->StyleDisplay()->mBreakType;
   if (StyleClear::None != aState.mFloatBreakType) {
     breakType = nsLayoutUtils::CombineBreakType(breakType,
                                                 aState.mFloatBreakType);
     aState.mFloatBreakType = StyleClear::None;
   }
 
   // Clear past floats before the block if the clear style is not none
   aLine->SetBreakTypeBefore(breakType);
@@ -4341,18 +4339,17 @@ nsBlockFrame::SplitFloat(BlockReflowInpu
   } else {
     nextInFlow = aState.mPresContext->PresShell()->FrameConstructor()->
       CreateContinuingFrame(aState.mPresContext, aFloat, this);
   }
   if (aFloatStatus.IsOverflowIncomplete()) {
     nextInFlow->AddStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
   }
 
-  StyleFloat floatStyle =
-    aFloat->StyleDisplay()->PhysicalFloats(aState.mReflowInput.GetWritingMode());
+  StyleFloat floatStyle = aFloat->StyleDisplay()->mFloat;
   if (floatStyle == StyleFloat::Left) {
     aState.FloatManager()->SetSplitLeftFloatAcrossBreak();
   } else {
     MOZ_ASSERT(floatStyle == StyleFloat::Right, "Unexpected float side!");
     aState.FloatManager()->SetSplitRightFloatAcrossBreak();
   }
 
   aState.AppendPushedFloatChain(nextInFlow);
--- a/layout/generic/nsFloatManager.cpp
+++ b/layout/generic/nsFloatManager.cpp
@@ -201,17 +201,17 @@ nsFloatManager::GetFlowArea(WritingMode 
     // WidthWithinHeight call is at least as narrow on both sides as a
     // BandFromPoint call beginning at its blockStart.
     else if (blockStart < floatBEnd &&
              (floatBStart < blockEnd ||
               (floatBStart == blockEnd && blockStart == blockEnd))) {
       // This float is in our band.
 
       // Shrink our band's width if needed.
-      StyleFloat floatStyle = fi.mFrame->StyleDisplay()->PhysicalFloats(aWM);
+      StyleFloat floatStyle = fi.mFrame->StyleDisplay()->mFloat;
 
       // When aBandInfoType is BandFromPoint, we're only intended to
       // consider a point along the y axis rather than a band.
       const nscoord bandBlockEnd =
         aBandInfoType == BandInfoType::BandFromPoint ? blockStart : blockEnd;
       if (floatStyle == StyleFloat::Left) {
         // A left float
         nscoord lineRightEdge =
@@ -278,17 +278,17 @@ nsFloatManager::AddFloat(nsIFrame* aFloa
   if (HasAnyFloats()) {
     FloatInfo &tail = mFloats[mFloats.Length() - 1];
     info.mLeftBEnd = tail.mLeftBEnd;
     info.mRightBEnd = tail.mRightBEnd;
   } else {
     info.mLeftBEnd = nscoord_MIN;
     info.mRightBEnd = nscoord_MIN;
   }
-  StyleFloat floatStyle = aFloatFrame->StyleDisplay()->PhysicalFloats(aWM);
+  StyleFloat floatStyle = aFloatFrame->StyleDisplay()->mFloat;
   MOZ_ASSERT(floatStyle == StyleFloat::Left || floatStyle == StyleFloat::Right,
              "Unexpected float style!");
   nscoord& sideBEnd =
     floatStyle == StyleFloat::Left ? info.mLeftBEnd : info.mRightBEnd;
   nscoord thisBEnd = info.BEnd();
   if (thisBEnd > sideBEnd)
     sideBEnd = thisBEnd;
 
@@ -311,17 +311,17 @@ nsFloatManager::CalculateRegionFor(Writi
   region.Inflate(aWM, aMargin);
 
   // Don't store rectangles with negative margin-box width or height in
   // the float manager; it can't deal with them.
   if (region.ISize(aWM) < 0) {
     // Preserve the right margin-edge for left floats and the left
     // margin-edge for right floats
     const nsStyleDisplay* display = aFloat->StyleDisplay();
-    StyleFloat floatStyle = display->PhysicalFloats(aWM);
+    StyleFloat floatStyle = display->mFloat;
     if ((StyleFloat::Left == floatStyle) == aWM.IsBidiLTR()) {
       region.IStart(aWM) = region.IEnd(aWM);
     }
     region.ISize(aWM) = 0;
   }
   if (region.BSize(aWM) < 0) {
     region.BSize(aWM) = 0;
   }
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -5352,39 +5352,38 @@ nsIFrame::InlinePrefISizeData::ForceBrea
   if (mFloats.Length() != 0 && aBreakType != StyleClear::None) {
             // preferred widths accumulated for floats that have already
             // been cleared past
     nscoord floats_done = 0,
             // preferred widths accumulated for floats that have not yet
             // been cleared past
             floats_cur_left = 0,
             floats_cur_right = 0;
-    const WritingMode wm = mLineContainerWM;
 
     for (uint32_t i = 0, i_end = mFloats.Length(); i != i_end; ++i) {
       const FloatInfo& floatInfo = mFloats[i];
       const nsStyleDisplay* floatDisp = floatInfo.Frame()->StyleDisplay();
-      StyleClear breakType = floatDisp->PhysicalBreakType(wm);
+      StyleClear breakType = floatDisp->mBreakType;
       if (breakType == StyleClear::Left ||
           breakType == StyleClear::Right ||
           breakType == StyleClear::Both) {
         nscoord floats_cur = NSCoordSaturatingAdd(floats_cur_left,
                                                   floats_cur_right);
         if (floats_cur > floats_done) {
           floats_done = floats_cur;
         }
         if (breakType != StyleClear::Right) {
           floats_cur_left = 0;
         }
         if (breakType != StyleClear::Left) {
           floats_cur_right = 0;
         }
       }
 
-      StyleFloat floatStyle = floatDisp->PhysicalFloats(wm);
+      StyleFloat floatStyle = floatDisp->mFloat;
       nscoord& floats_cur =
         floatStyle == StyleFloat::Left ? floats_cur_left : floats_cur_right;
       nscoord floatWidth = floatInfo.Width();
       // Negative-width floats don't change the available space so they
       // shouldn't change our intrinsic line width either.
       floats_cur =
         NSCoordSaturatingAdd(floats_cur, std::max(0, floatWidth));
     }
@@ -5409,27 +5408,27 @@ nsIFrame::InlinePrefISizeData::ForceBrea
                  aBreakType == StyleClear::Right,
                  "Other values should have been handled in other branches");
       StyleFloat clearFloatType =
         aBreakType == StyleClear::Left ? StyleFloat::Left : StyleFloat::Right;
       // Iterate the array in reverse so that we can stop when there are
       // no longer any floats we need to keep. See below.
       for (FloatInfo& floatInfo : Reversed(mFloats)) {
         const nsStyleDisplay* floatDisp = floatInfo.Frame()->StyleDisplay();
-        if (floatDisp->PhysicalFloats(wm) != clearFloatType) {
+        if (floatDisp->mFloat != clearFloatType) {
           newFloats.AppendElement(floatInfo);
         } else {
           // This is a float on the side that this break directly clears
           // which means we're not keeping it in mFloats. However, if
           // this float clears floats on the opposite side (via a value
           // of either 'both' or one of 'left'/'right'), any remaining
           // (earlier) floats on that side would be indirectly cleared
           // as well. Thus, we should break out of this loop and stop
           // considering earlier floats to be kept in mFloats.
-          StyleClear floatBreakType = floatDisp->PhysicalBreakType(wm);
+          StyleClear floatBreakType = floatDisp->mBreakType;
           if (floatBreakType != aBreakType &&
               floatBreakType != StyleClear::None) {
             break;
           }
         }
       }
       newFloats.Reverse();
       mFloats = std::move(newFloats);
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -2185,32 +2185,29 @@ public:
       , mSkipWhitespace(true)
     {}
 
     // The line. This may be null if the inlines are not associated with
     // a block or if we just don't know the line.
     const nsLineList_iterator* mLine;
 
     // The line container. Private, to ensure we always use SetLineContainer
-    // to update it (so that we have a chance to store the mLineContainerWM).
+    // to update it.
     //
     // Note that nsContainerFrame::DoInlineIntrinsicISize will clear the
     // |mLine| and |mLineContainer| fields when following a next-in-flow link,
     // so we must not assume these can always be dereferenced.
   private:
     nsIFrame* mLineContainer;
 
     // Setter and getter for the lineContainer field:
   public:
     void SetLineContainer(nsIFrame* aLineContainer)
     {
       mLineContainer = aLineContainer;
-      if (mLineContainer) {
-        mLineContainerWM = mLineContainer->GetWritingMode();
-      }
     }
     nsIFrame* LineContainer() const { return mLineContainer; }
 
     // The maximum intrinsic width for all previous lines.
     nscoord mPrevLines;
 
     // The maximum intrinsic width for the current line.  At a line
     // break (mandatory for preferred width; allowed for minimum width),
@@ -2221,20 +2218,16 @@ public:
     // |mCurrentLine|; it is zero if there is no such whitespace.
     nscoord mTrailingWhitespace;
 
     // True if initial collapsable whitespace should be skipped.  This
     // should be true at the beginning of a block, after hard breaks
     // and when the last text ended with whitespace.
     bool mSkipWhitespace;
 
-    // Writing mode of the line container (stored here so that we don't
-    // lose track of it if the mLineContainer field is reset).
-    mozilla::WritingMode mLineContainerWM;
-
     // Floats encountered in the lines.
     class FloatInfo {
     public:
       FloatInfo(const nsIFrame* aFrame, nscoord aWidth)
         : mFrame(aFrame), mWidth(aWidth)
       { }
       const nsIFrame* Frame() const { return mFrame; }
       nscoord         Width() const { return mWidth; }
--- a/layout/generic/nsLineBox.cpp
+++ b/layout/generic/nsLineBox.cpp
@@ -205,18 +205,16 @@ ListFloats(FILE* out, const char* aPrefi
 
 /* static */ const char*
 nsLineBox::BreakTypeToString(StyleClear aBreakType)
 {
   switch (aBreakType) {
     case StyleClear::None: return "nobr";
     case StyleClear::Left: return "leftbr";
     case StyleClear::Right: return "rightbr";
-    case StyleClear::InlineStart: return "inlinestartbr";
-    case StyleClear::InlineEnd: return "inlineendbr";
     case StyleClear::Both: return "leftbr+rightbr";
     case StyleClear::Line: return "linebr";
     case StyleClear::Max: return "leftbr+rightbr+linebr";
   }
   return "unknown";
 }
 
 char*
--- a/layout/inspector/InspectorUtils.cpp
+++ b/layout/inspector/InspectorUtils.cpp
@@ -227,17 +227,25 @@ InspectorUtils::GetCSSStyleRules(GlobalO
       rule = map->Lookup(rawRule);
       if (rule) {
         break;
       }
     }
     if (rule) {
       aResult.AppendElement(rule);
     } else {
-      MOZ_ASSERT_UNREACHABLE("We should be able to map a raw rule to a rule");
+#ifdef DEBUG
+      nsAutoCString str;
+      fprintf(stderr, "%s\n", str.get());
+      Servo_StyleRule_Debug(rawRule, &str);
+      MOZ_CRASH_UNSAFE_PRINTF(
+        "We should be able to map a raw rule to a rule: %s\n",
+        str.get()
+      );
+#endif
     }
   }
 }
 
 /* static */ uint32_t
 InspectorUtils::GetRuleLine(GlobalObject& aGlobal, css::Rule& aRule)
 {
   return aRule.GetLineNumber();
--- a/layout/style/ServoCSSPropList.mako.py
+++ b/layout/style/ServoCSSPropList.mako.py
@@ -69,20 +69,22 @@ def method(prop):
     return prop.camel_case
 
 # Colors, integers and lengths are easy as well.
 #
 # TODO(emilio): This will go away once the rest of the longhands have been
 # moved or perhaps using a blacklist for the ones with non-layout-dependence
 # but other non-trivial dependence like scrollbar colors.
 SERIALIZED_PREDEFINED_TYPES = [
+    "Clear",
     "Color",
     "Content",
     "CounterIncrement",
     "CounterReset",
+    "Float",
     "FontFamily",
     "FontFeatureSettings",
     "FontLanguageOverride",
     "FontSize",
     "FontSizeAdjust",
     "FontStretch",
     "FontStyle",
     "FontSynthesis",
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -7,16 +7,17 @@
 /* DOM object returned from element.getComputedStyle() */
 
 #include "nsComputedDOMStyle.h"
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/FontPropertyTypes.h"
 #include "mozilla/Preferences.h"
+#include "mozilla/StaticPtr.h"
 
 #include "nsError.h"
 #include "nsIFrame.h"
 #include "nsIFrameInlines.h"
 #include "mozilla/ComputedStyle.h"
 #include "nsIScrollableFrame.h"
 #include "nsContentUtils.h"
 #include "nsIContent.h"
@@ -5464,19 +5465,19 @@ nsComputedDOMStyle::DoGetAnimationIterat
 
 already_AddRefed<CSSValue>
 nsComputedDOMStyle::DummyGetter()
 {
   MOZ_CRASH("DummyGetter is not supposed to be invoked");
 }
 
 static void
-MarkComputedStyleMapDirty(const char* aPref, void* aData)
-{
-  static_cast<ComputedStyleMap*>(aData)->MarkDirty();
+MarkComputedStyleMapDirty(const char* aPref, ComputedStyleMap* aData)
+{
+  aData->MarkDirty();
 }
 
 void
 nsComputedDOMStyle::ParentChainChanged(nsIContent* aContent)
 {
   NS_ASSERTION(mElement == aContent, "didn't we register mElement?");
   NS_ASSERTION(mResolvedComputedStyle,
                "should have only registered an observer when "
@@ -5487,34 +5488,55 @@ nsComputedDOMStyle::ParentChainChanged(n
 
 /* static */ ComputedStyleMap*
 nsComputedDOMStyle::GetComputedStyleMap()
 {
   static ComputedStyleMap map{};
   return &map;
 }
 
+static StaticAutoPtr<nsTArray<const char*>> gCallbackPrefs;
+
 /* static */ void
 nsComputedDOMStyle::RegisterPrefChangeCallbacks()
 {
   // Note that this will register callbacks for all properties with prefs, not
   // just those that are implemented on computed style objects, as it's not
   // easy to grab specific property data from ServoCSSPropList.h based on the
   // entries iterated in nsComputedDOMStylePropertyList.h.
-  ComputedStyleMap* data = GetComputedStyleMap();
+
+  AutoTArray<const char*, 64> prefs;
   for (const auto* p = nsCSSProps::kPropertyPrefTable;
        p->mPropID != eCSSProperty_UNKNOWN; p++) {
-    nsCString name;
-    name.AssignLiteral(p->mPref, strlen(p->mPref));
-    Preferences::RegisterCallback(MarkComputedStyleMapDirty, name, data);
-  }
+    // Many properties are controlled by the same preference, so de-duplicate
+    // them before adding observers.
+    //
+    // Note: This is done by pointer comparison, which works because the mPref
+    // members are string literals from the same same translation unit, and are
+    // therefore de-duplicated by the compiler. On the off chance that we wind
+    // up with some duplicates with different pointers, though, it's not a bit
+    // deal.
+    if (!prefs.ContainsSorted(p->mPref)) {
+      prefs.InsertElementSorted(p->mPref);
+    }
+  }
+  prefs.AppendElement(nullptr);
+
+  MOZ_ASSERT(!gCallbackPrefs);
+  gCallbackPrefs = new nsTArray<const char*>(std::move(prefs));
+
+  Preferences::RegisterCallbacks(MarkComputedStyleMapDirty,
+                                 gCallbackPrefs->Elements(),
+                                 GetComputedStyleMap());
 }
 
 /* static */ void
 nsComputedDOMStyle::UnregisterPrefChangeCallbacks()
 {
-  ComputedStyleMap* data = GetComputedStyleMap();
-  for (const auto* p = nsCSSProps::kPropertyPrefTable;
-       p->mPropID != eCSSProperty_UNKNOWN; p++) {
-    Preferences::UnregisterCallback(MarkComputedStyleMapDirty,
-                                    nsDependentCString(p->mPref), data);
-  }
-}
+  if (!gCallbackPrefs) {
+    return;
+  }
+
+  Preferences::UnregisterCallbacks(MarkComputedStyleMapDirty,
+                                   gCallbackPrefs->Elements(),
+                                   GetComputedStyleMap());
+  gCallbackPrefs = nullptr;
+}
--- a/layout/style/nsStyleConsts.h
+++ b/layout/style/nsStyleConsts.h
@@ -72,21 +72,22 @@ enum class StyleBoxShadowType : uint8_t 
   Inset,
 };
 
 // clear
 enum class StyleClear : uint8_t {
   None = 0,
   Left,
   Right,
-  InlineStart,
-  InlineEnd,
   Both,
   // StyleClear::Line can be added to one of the other values in layout
   // so it needs to use a bit value that none of the other values can have.
+  //
+  // FIXME(emilio): Doesn't look like we do that anymore, so probably can be
+  // made a single value instead, and Max removed.
   Line = 8,
   Max = 13  // Max = (Both | Line)
 };
 
 // Counters and generated content.
 enum class StyleContentType : uint8_t {
   String = 1,
   Image = 10,
@@ -139,18 +140,16 @@ enum class StyleFillRule : uint8_t {
 };
 
 // float
 // https://developer.mozilla.org/en-US/docs/Web/CSS/float
 enum class StyleFloat : uint8_t {
   None,
   Left,
   Right,
-  InlineStart,
-  InlineEnd
 };
 
 // float-edge
 enum class StyleFloatEdge : uint8_t {
   ContentBox,
   MarginBox,
 };
 
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -2450,22 +2450,16 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
 
 private:
   // Helpers for above functions, which do some but not all of the tests
   // for them (since transform must be tested separately for each).
   inline bool HasAbsPosContainingBlockStyleInternal() const;
   inline bool HasFixedPosContainingBlockStyleInternal(
     mozilla::ComputedStyle&) const;
   void GenerateCombinedTransform();
-public:
-  // Return the 'float' and 'clear' properties, with inline-{start,end} values
-  // resolved to {left,right} according to the given writing mode. These are
-  // defined in WritingModes.h.
-  inline mozilla::StyleFloat PhysicalFloats(mozilla::WritingMode aWM) const;
-  inline mozilla::StyleClear PhysicalBreakType(mozilla::WritingMode aWM) const;
 };
 
 struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleTable
 {
   explicit nsStyleTable(const nsPresContext* aContext);
   nsStyleTable(const nsStyleTable& aOther);
   ~nsStyleTable();
   void FinishStyle(nsPresContext*, const nsStyleTable*) {}
--- a/modules/libpref/Preferences.cpp
+++ b/modules/libpref/Preferences.cpp
@@ -1180,63 +1180,105 @@ Pref::FromWrapper(PrefWrapper& aWrapper)
 
 class CallbackNode
 {
 public:
   CallbackNode(const nsACString& aDomain,
                PrefChangedFunc aFunc,
                void* aData,
                Preferences::MatchKind aMatchKind)
-    : mDomain(aDomain)
+    : mDomain(AsVariant(nsCString(aDomain)))
+    , mFunc(aFunc)
+    , mData(aData)
+    , mNextAndMatchKind(aMatchKind)
+  {
+  }
+
+  CallbackNode(const char** aDomains,
+               PrefChangedFunc aFunc,
+               void* aData,
+               Preferences::MatchKind aMatchKind)
+    : mDomain(AsVariant(aDomains))
     , mFunc(aFunc)
     , mData(aData)
     , mNextAndMatchKind(aMatchKind)
   {
   }
 
   // mDomain is a UniquePtr<>, so any uses of Domain() should only be temporary
   // borrows.
-  const nsCString& Domain() const { return mDomain; }
+  const Variant<nsCString, const char**>& Domain() const { return mDomain; }
 
   PrefChangedFunc Func() const { return mFunc; }
   void ClearFunc() { mFunc = nullptr; }
 
   void* Data() const { return mData; }
 
   Preferences::MatchKind MatchKind() const
   {
     return static_cast<Preferences::MatchKind>(mNextAndMatchKind &
                                                kMatchKindMask);
   }
 
+  bool DomainIs(const nsACString& aDomain) const
+  {
+    return mDomain.is<nsCString>() && mDomain.as<nsCString>() == aDomain;
+  }
+
+  bool DomainIs(const char** aPrefs) const
+  {
+    return mDomain == AsVariant(aPrefs);
+  }
+
+  bool Matches(const nsACString& aPrefName) const
+  {
+    auto match = [&](const nsACString& aStr) {
+      return MatchKind() == Preferences::ExactMatch
+               ? aPrefName == aStr
+               : StringBeginsWith(aPrefName, aStr);
+    };
+
+    if (mDomain.is<nsCString>()) {
+      return match(mDomain.as<nsCString>());
+    }
+    for (const char** ptr = mDomain.as<const char**>(); *ptr; ptr++) {
+      if (match(nsDependentCString(*ptr))) {
+        return true;
+      }
+    }
+    return false;
+  }
+
   CallbackNode* Next() const
   {
     return reinterpret_cast<CallbackNode*>(mNextAndMatchKind & kNextMask);
   }
 
   void SetNext(CallbackNode* aNext)
   {
     uintptr_t matchKind = mNextAndMatchKind & kMatchKindMask;
     mNextAndMatchKind = reinterpret_cast<uintptr_t>(aNext);
     MOZ_ASSERT((mNextAndMatchKind & kMatchKindMask) == 0);
     mNextAndMatchKind |= matchKind;
   }
 
   void AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf, PrefsSizes& aSizes)
   {
     aSizes.mCallbacksObjects += aMallocSizeOf(this);
-    aSizes.mCallbacksDomains +=
-      mDomain.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
+    if (mDomain.is<nsCString>()) {
+      aSizes.mCallbacksDomains +=
+        mDomain.as<nsCString>().SizeOfExcludingThisIfUnshared(aMallocSizeOf);
+    }
   }
 
 private:
   static const uintptr_t kMatchKindMask = uintptr_t(0x1);
   static const uintptr_t kNextMask = ~kMatchKindMask;
 
-  nsCString mDomain;
+  Variant<nsCString, const char**> mDomain;
 
   // If someone attempts to remove the node from the callback list while
   // NotifyCallbacks() is running, |func| is set to nullptr. Such nodes will
   // be removed at the end of NotifyCallbacks().
   PrefChangedFunc mFunc;
   void* mData;
 
   // Conceptually this is two fields:
@@ -1777,20 +1819,17 @@ NotifyCallbacks(const char* aPrefName, c
   // out the |func| pointer. We release them at the end of this function
   // if we haven't reentered.
   gCallbacksInProgress = true;
 
   nsDependentCString prefName(aPrefName);
 
   for (CallbackNode* node = gFirstCallback; node; node = node->Next()) {
     if (node->Func()) {
-      bool matches = node->MatchKind() == Preferences::ExactMatch
-                       ? node->Domain() == prefName
-                       : StringBeginsWith(prefName, node->Domain());
-      if (matches) {
+      if (node->Matches(prefName)) {
         (node->Func())(aPrefName, node->Data());
       }
     }
   }
 
   gCallbacksInProgress = reentered;
 
   if (gShouldCleanupDeadNodes && !gCallbacksInProgress) {
@@ -5325,22 +5364,23 @@ Preferences::RemoveObservers(nsIObserver
 
   for (uint32_t i = 0; aPrefs[i]; i++) {
     nsresult rv = RemoveObserver(aObserver, nsDependentCString(aPrefs[i]));
     NS_ENSURE_SUCCESS(rv, rv);
   }
   return NS_OK;
 }
 
+template<typename T>
 /* static */ nsresult
-Preferences::RegisterCallback(PrefChangedFunc aCallback,
-                              const nsACString& aPrefNode,
-                              void* aData,
-                              MatchKind aMatchKind,
-                              bool aIsPriority)
+Preferences::RegisterCallbackImpl(PrefChangedFunc aCallback,
+                                  T& aPrefNode,
+                                  void* aData,
+                                  MatchKind aMatchKind,
+                                  bool aIsPriority)
 {
   NS_ENSURE_ARG(aCallback);
 
   NS_ENSURE_TRUE(InitStaticMembers(), NS_ERROR_NOT_AVAILABLE);
 
   auto node = new CallbackNode(aPrefNode, aCallback, aData, aMatchKind);
 
   if (aIsPriority) {
@@ -5360,49 +5400,87 @@ Preferences::RegisterCallback(PrefChange
       gFirstCallback = node;
     }
   }
 
   return NS_OK;
 }
 
 /* static */ nsresult
+Preferences::RegisterCallback(PrefChangedFunc aCallback,
+                              const nsACString& aPrefNode,
+                              void* aData,
+                              MatchKind aMatchKind,
+                              bool aIsPriority)
+{
+  return RegisterCallbackImpl(
+    aCallback, aPrefNode, aData, aMatchKind, aIsPriority);
+}
+
+/* static */ nsresult
+Preferences::RegisterCallbacks(PrefChangedFunc aCallback,
+                               const char** aPrefs,
+                               void* aData,
+                               MatchKind aMatchKind)
+{
+  return RegisterCallbackImpl(aCallback, aPrefs, aData, aMatchKind);
+}
+
+/* static */ nsresult
 Preferences::RegisterCallbackAndCall(PrefChangedFunc aCallback,
                                      const nsACString& aPref,
                                      void* aClosure,
                                      MatchKind aMatchKind)
 {
   MOZ_ASSERT(aCallback);
   nsresult rv = RegisterCallback(aCallback, aPref, aClosure, aMatchKind);
   if (NS_SUCCEEDED(rv)) {
     (*aCallback)(PromiseFlatCString(aPref).get(), aClosure);
   }
   return rv;
 }
 
 /* static */ nsresult
-Preferences::UnregisterCallback(PrefChangedFunc aCallback,
-                                const nsACString& aPrefNode,
-                                void* aData,
-                                MatchKind aMatchKind)
+Preferences::RegisterCallbacksAndCall(PrefChangedFunc aCallback,
+                                      const char** aPrefs,
+                                      void* aClosure)
+{
+  MOZ_ASSERT(aCallback);
+
+  nsresult rv =
+    RegisterCallbacks(aCallback, aPrefs, aClosure, MatchKind::ExactMatch);
+  if (NS_SUCCEEDED(rv)) {
+    for (const char** ptr = aPrefs; *ptr; ptr++) {
+      (*aCallback)(*ptr, aClosure);
+    }
+  }
+  return rv;
+}
+
+template<typename T>
+/* static */ nsresult
+Preferences::UnregisterCallbackImpl(PrefChangedFunc aCallback,
+                                    T& aPrefNode,
+                                    void* aData,
+                                    MatchKind aMatchKind)
 {
   MOZ_ASSERT(aCallback);
   if (sShutdown) {
     MOZ_ASSERT(!sPreferences);
     return NS_OK; // Observers have been released automatically.
   }
   NS_ENSURE_TRUE(sPreferences, NS_ERROR_NOT_AVAILABLE);
 
   nsresult rv = NS_ERROR_FAILURE;
   CallbackNode* node = gFirstCallback;
   CallbackNode* prev_node = nullptr;
 
   while (node) {
     if (node->Func() == aCallback && node->Data() == aData &&
-        node->MatchKind() == aMatchKind && node->Domain() == aPrefNode) {
+        node->MatchKind() == aMatchKind && node->DomainIs(aPrefNode)) {
       if (gCallbacksInProgress) {
         // Postpone the node removal until after callbacks enumeration is
         // finished.
         node->ClearFunc();
         gShouldCleanupDeadNodes = true;
         prev_node = node;
         node = node->Next();
       } else {
@@ -5412,16 +5490,35 @@ Preferences::UnregisterCallback(PrefChan
     } else {
       prev_node = node;
       node = node->Next();
     }
   }
   return rv;
 }
 
+/* static */ nsresult
+Preferences::UnregisterCallback(PrefChangedFunc aCallback,
+                                const nsACString& aPrefNode,
+                                void* aData,
+                                MatchKind aMatchKind)
+{
+  return UnregisterCallbackImpl<const nsACString&>(
+    aCallback, aPrefNode, aData, aMatchKind);
+}
+
+/* static */ nsresult
+Preferences::UnregisterCallbacks(PrefChangedFunc aCallback,
+                                 const char** aPrefs,
+                                 void* aData,
+                                 MatchKind aMatchKind)
+{
+  return UnregisterCallbackImpl(aCallback, aPrefs, aData, aMatchKind);
+}
+
 static void
 CacheDataAppendElement(CacheData* aData)
 {
   if (!gCacheData) {
     MOZ_CRASH_UNSAFE_PRINTF("!gCacheData: %s", gCacheDataDesc);
   }
   gCacheData->AppendElement(aData);
 }
--- a/modules/libpref/Preferences.h
+++ b/modules/libpref/Preferences.h
@@ -29,16 +29,99 @@ class nsIFile;
 // The callback function will get passed the pref name which triggered the call
 // and the void* data which was passed to the registered callback function.
 typedef void (*PrefChangedFunc)(const char* aPref, void* aData);
 
 class nsPrefBranch;
 
 namespace mozilla {
 
+// A typesafe version of PrefChangeFunc, with its data argument type deduced
+// from the type of the argument passed to RegisterCallback.
+//
+// Note: We specify this as a dependent type TypedPrefChangeFunc<T>::SelfType so
+// that it does not participate in argument type deduction. This allows us to
+// use its implicit conversion constructor, and also allows our Register and
+// Unregister methods to accept non-capturing lambdas (which will not match
+// void(*)(const char*, T*) when used in type deduction) as callback functions.
+template<typename T>
+struct TypedPrefChangeFunc
+{
+  using Type = TypedPrefChangeFunc<T>;
+  using CallbackType = void (*)(const char*, T*);
+
+  MOZ_IMPLICIT TypedPrefChangeFunc(CallbackType aCallback)
+    : mCallback(aCallback)
+  {
+  }
+
+  template<typename F>
+  MOZ_IMPLICIT TypedPrefChangeFunc(F&& aLambda)
+    : mCallback(aLambda)
+  {
+  }
+
+  operator PrefChangedFunc() const
+  {
+    return reinterpret_cast<PrefChangedFunc>(mCallback);
+  }
+
+  CallbackType mCallback;
+};
+
+// Similar to PrefChangedFunc, but for use with instance methods.
+//
+// Any instance method with this signature may be passed to the
+// PREF_CHANGE_METHOD macro, which will wrap it into a typesafe preference
+// callback function, which accepts a preference name as its first argument, and
+// an instance of the appropriate class as the second.
+//
+// When called, the wrapper will forward the call to the wrapped method on the
+// given instance, with the notified preference as its only argument.
+typedef void(PrefChangedMethod)(const char* aPref);
+
+namespace detail {
+// Helper to extract the instance type from any instance method. For an instance
+// method `Method = U T::*`, InstanceType<Method>::Type returns T.
+template<typename T>
+struct InstanceType;
+
+template<typename T, typename U>
+struct InstanceType<U T::*>
+{
+  using Type = T;
+};
+
+// A wrapper for a PrefChangeMethod instance method which forwards calls to the
+// wrapped method on the given instance.
+template<typename T, PrefChangedMethod T::*Method>
+void
+PrefChangeMethod(const char* aPref, T* aInst)
+{
+  ((*aInst).*Method)(aPref);
+}
+} // namespace detail
+
+// Creates a wrapper around an instance method, with the signature of
+// PrefChangedMethod, from an arbitrary class, so that it can be used as a
+// preference callback. The closure data passed to RegisterCallback must be an
+// instance of this class.
+//
+// Note: This is implemented as a macro rather than a pure template function
+// because, prior to C++17, value template arguments must have their types
+// fully-specified. Once all of our supported compilers have C++17 support, we
+// can give PrefChangeMethod a single <auto Method> argument, and use
+// PrefChangeMethod<&meth> directly.
+#define PREF_CHANGE_METHOD(meth)                                               \
+  (&::mozilla::detail::PrefChangeMethod<                                       \
+    ::mozilla::detail::InstanceType<decltype(&meth)>::Type,                    \
+    &meth>)
+
+class PreferenceServiceReporter;
+
 namespace dom {
 class Pref;
 class PrefValue;
 } // namespace dom
 
 namespace ipc {
 class FileDescriptor;
 } // namespace ipc
@@ -300,115 +383,178 @@ public:
   // Note: All preference strings *must* be statically-allocated string
   // literals.
   static nsresult AddStrongObservers(nsIObserver* aObserver,
                                      const char** aPrefs);
   static nsresult AddWeakObservers(nsIObserver* aObserver, const char** aPrefs);
   static nsresult RemoveObservers(nsIObserver* aObserver, const char** aPrefs);
 
   // Registers/Unregisters the callback function for the aPref.
-  static nsresult RegisterCallback(PrefChangedFunc aCallback,
-                                   const nsACString& aPref,
-                                   void* aClosure = nullptr)
+  template<typename T = void>
+  static nsresult RegisterCallback(
+    typename TypedPrefChangeFunc<T>::Type aCallback,
+    const nsACString& aPref,
+    T* aClosure = nullptr)
   {
     return RegisterCallback(aCallback, aPref, aClosure, ExactMatch);
   }
 
-  static nsresult UnregisterCallback(PrefChangedFunc aCallback,
-                                     const nsACString& aPref,
-                                     void* aClosure = nullptr)
+  template<typename T = void>
+  static nsresult UnregisterCallback(
+    typename TypedPrefChangeFunc<T>::Type aCallback,
+    const nsACString& aPref,
+    T* aClosure = nullptr)
   {
     return UnregisterCallback(aCallback, aPref, aClosure, ExactMatch);
   }
 
   // Like RegisterCallback, but also calls the callback immediately for
   // initialization.
-  static nsresult RegisterCallbackAndCall(PrefChangedFunc aCallback,
-                                          const nsACString& aPref,
-                                          void* aClosure = nullptr)
+  template<typename T = void>
+  static nsresult RegisterCallbackAndCall(
+    typename TypedPrefChangeFunc<T>::Type aCallback,
+    const nsACString& aPref,
+    T* aClosure = nullptr)
   {
     return RegisterCallbackAndCall(aCallback, aPref, aClosure, ExactMatch);
   }
 
   // Like RegisterCallback, but registers a callback for a prefix of multiple
   // pref names, not a single pref name.
-  static nsresult RegisterPrefixCallback(PrefChangedFunc aCallback,
-                                         const nsACString& aPref,
-                                         void* aClosure = nullptr)
+  template<typename T = void>
+  static nsresult RegisterPrefixCallback(
+    typename TypedPrefChangeFunc<T>::Type aCallback,
+    const nsACString& aPref,
+    T* aClosure = nullptr)
   {
     return RegisterCallback(aCallback, aPref, aClosure, PrefixMatch);
   }
 
   // Like RegisterPrefixCallback, but also calls the callback immediately for
   // initialization.
-  static nsresult RegisterPrefixCallbackAndCall(PrefChangedFunc aCallback,
-                                                const nsACString& aPref,
-                                                void* aClosure = nullptr)
+  template<typename T = void>
+  static nsresult RegisterPrefixCallbackAndCall(
+    typename TypedPrefChangeFunc<T>::Type aCallback,
+    const nsACString& aPref,
+    T* aClosure = nullptr)
   {
     return RegisterCallbackAndCall(aCallback, aPref, aClosure, PrefixMatch);
   }
 
   // Unregister a callback registered with RegisterPrefixCallback or
   // RegisterPrefixCallbackAndCall.
-  static nsresult UnregisterPrefixCallback(PrefChangedFunc aCallback,
-                                           const nsACString& aPref,
-                                           void* aClosure = nullptr)
+  template<typename T = void>
+  static nsresult UnregisterPrefixCallback(
+    typename TypedPrefChangeFunc<T>::Type aCallback,
+    const nsACString& aPref,
+    T* aClosure = nullptr)
   {
     return UnregisterCallback(aCallback, aPref, aClosure, PrefixMatch);
   }
 
-  template<int N>
-  static nsresult RegisterCallback(PrefChangedFunc aCallback,
-                                   const char (&aPref)[N],
-                                   void* aClosure = nullptr)
+  // Variants of the above which register a single callback to handle multiple
+  // preferences.
+  //
+  // The array of preference names must be null terminated. It may be
+  // dynamically allocated, but the caller is responsible for keeping it alive
+  // until the callback is unregistered.
+  //
+  // Also note that the exact same aPrefs pointer must be passed to the
+  // Unregister call as was passed to the Register call.
+  template<typename T = void>
+  static nsresult RegisterCallbacks(
+    typename TypedPrefChangeFunc<T>::Type aCallback,
+    const char** aPrefs,
+    T* aClosure = nullptr)
+  {
+    return RegisterCallbacks(aCallback, aPrefs, aClosure, ExactMatch);
+  }
+  static nsresult RegisterCallbacksAndCall(PrefChangedFunc aCallback,
+                                           const char** aPrefs,
+                                           void* aClosure = nullptr);
+  template<typename T = void>
+  static nsresult UnregisterCallbacks(
+    typename TypedPrefChangeFunc<T>::Type aCallback,
+    const char** aPrefs,
+    T* aClosure = nullptr)
+  {
+    return UnregisterCallbacks(aCallback, aPrefs, aClosure, ExactMatch);
+  }
+  template<typename T = void>
+  static nsresult RegisterPrefixCallbacks(
+    typename TypedPrefChangeFunc<T>::Type aCallback,
+    const char** aPrefs,
+    T* aClosure = nullptr)
+  {
+    return RegisterCallbacks(aCallback, aPrefs, aClosure, PrefixMatch);
+  }
+  template<typename T = void>
+  static nsresult UnregisterPrefixCallbacks(
+    typename TypedPrefChangeFunc<T>::Type aCallback,
+    const char** aPrefs,
+    T* aClosure = nullptr)
+  {
+    return UnregisterCallbacks(aCallback, aPrefs, aClosure, PrefixMatch);
+  }
+
+  template<int N, typename T = void>
+  static nsresult RegisterCallback(
+    typename TypedPrefChangeFunc<T>::Type aCallback,
+    const char (&aPref)[N],
+    T* aClosure = nullptr)
   {
     return RegisterCallback(
       aCallback, nsLiteralCString(aPref), aClosure, ExactMatch);
   }
 
-  template<int N>
-  static nsresult UnregisterCallback(PrefChangedFunc aCallback,
-                                     const char (&aPref)[N],
-                                     void* aClosure = nullptr)
+  template<int N, typename T = void>
+  static nsresult UnregisterCallback(
+    typename TypedPrefChangeFunc<T>::Type aCallback,
+    const char (&aPref)[N],
+    T* aClosure = nullptr)
   {
     return UnregisterCallback(
       aCallback, nsLiteralCString(aPref), aClosure, ExactMatch);
   }
 
-  template<int N>
-  static nsresult RegisterCallbackAndCall(PrefChangedFunc aCallback,
-                                          const char (&aPref)[N],
-                                          void* aClosure = nullptr)
+  template<int N, typename T = void>
+  static nsresult RegisterCallbackAndCall(
+    typename TypedPrefChangeFunc<T>::Type aCallback,
+    const char (&aPref)[N],
+    T* aClosure = nullptr)
   {
     return RegisterCallbackAndCall(
       aCallback, nsLiteralCString(aPref), aClosure, ExactMatch);
   }
 
-  template<int N>
-  static nsresult RegisterPrefixCallback(PrefChangedFunc aCallback,
-                                         const char (&aPref)[N],
-                                         void* aClosure = nullptr)
+  template<int N, typename T = void>
+  static nsresult RegisterPrefixCallback(
+    typename TypedPrefChangeFunc<T>::Type aCallback,
+    const char (&aPref)[N],
+    T* aClosure = nullptr)
   {
     return RegisterCallback(
       aCallback, nsLiteralCString(aPref), aClosure, PrefixMatch);
   }
 
-  template<int N>
-  static nsresult RegisterPrefixCallbackAndCall(PrefChangedFunc aCallback,
-                                                const char (&aPref)[N],
-                                                void* aClosure = nullptr)
+  template<int N, typename T = void>
+  static nsresult RegisterPrefixCallbackAndCall(
+    typename TypedPrefChangeFunc<T>::Type aCallback,
+    const char (&aPref)[N],
+    T* aClosure = nullptr)
   {
     return RegisterCallbackAndCall(
       aCallback, nsLiteralCString(aPref), aClosure, PrefixMatch);
   }
 
-  template<int N>
-  static nsresult UnregisterPrefixCallback(PrefChangedFunc aCallback,
-                                           const char (&aPref)[N],
-                                           void* aClosure = nullptr)
+  template<int N, typename T = void>
+  static nsresult UnregisterPrefixCallback(
+    typename TypedPrefChangeFunc<T>::Type aCallback,
+    const char (&aPref)[N],
+    T* aClosure = nullptr)
   {
     return UnregisterCallback(
       aCallback, nsLiteralCString(aPref), aClosure, PrefixMatch);
   }
 
   // Adds the aVariable to cache table. |aVariable| must be a pointer for a
   // static variable. The value will be modified when the pref value is changed
   // but note that even if you modified it, the value isn't assigned to the
@@ -590,16 +736,37 @@ private:
                                      const nsACString& aPref,
                                      void* aClosure,
                                      MatchKind aMatchKind);
   static nsresult RegisterCallbackAndCall(PrefChangedFunc aCallback,
                                           const nsACString& aPref,
                                           void* aClosure,
                                           MatchKind aMatchKind);
 
+  static nsresult RegisterCallbacks(PrefChangedFunc aCallback,
+                                    const char** aPrefs,
+                                    void* aClosure,
+                                    MatchKind aMatchKind);
+  static nsresult UnregisterCallbacks(PrefChangedFunc aCallback,
+                                      const char** aPrefs,
+                                      void* aClosure,
+                                      MatchKind aMatchKind);
+
+  template<typename T>
+  static nsresult RegisterCallbackImpl(PrefChangedFunc aCallback,
+                                       T& aPref,
+                                       void* aClosure,
+                                       MatchKind aMatchKind,
+                                       bool aIsPriority = false);
+  template<typename T>
+  static nsresult UnregisterCallbackImpl(PrefChangedFunc aCallback,
+                                         T& aPref,
+                                         void* aClosure,
+                                         MatchKind aMatchKind);
+
   static nsresult RegisterCallback(PrefChangedFunc aCallback,
                                    const char* aPref,
                                    void* aClosure,
                                    MatchKind aMatchKind,
                                    bool aIsPriority = false)
   {
     return RegisterCallback(
       aCallback, nsDependentCString(aPref), aClosure, aMatchKind, aIsPriority);
--- a/modules/libpref/init/StaticPrefList.h
+++ b/modules/libpref/init/StaticPrefList.h
@@ -181,17 +181,17 @@ VARCACHE_PREF(
    dom_payments_response_timeout,
   uint32_t, 5000
 )
 
 //---------------------------------------------------------------------------
 // Clear-Site-Data prefs
 //---------------------------------------------------------------------------
 
-#ifdef NIGHTLY
+#ifdef NIGHTLY_BUILD
 # define PREF_VALUE true
 #else
 # define PREF_VALUE false
 #endif
 VARCACHE_PREF(
   "dom.clearSiteData.enabled",
    dom_clearSiteData_enabled,
   bool, PREF_VALUE
@@ -1173,16 +1173,22 @@ VARCACHE_PREF(
 //---------------------------------------------------------------------------
 
 VARCACHE_PREF(
   "privacy.restrict3rdpartystorage.enabled",
    privacy_restrict3rdpartystorage_enabled,
   RelaxedAtomicBool, false
 )
 
+VARCACHE_PREF(
+  "privacy.restrict3rdpartystorage.ui.enabled",
+   privacy_restrict3rdpartystorage_ui_enabled,
+  RelaxedAtomicBool, false
+)
+
 // Anti-tracking permission expiration
 VARCACHE_PREF(
   "privacy.restrict3rdpartystorage.expiration",
    privacy_restrict3rdpartystorage_expiration,
   uint32_t, 2592000000 // 30 days
 )
 
 //---------------------------------------------------------------------------
--- a/modules/libpref/test/gtest/CallbackAndVarCacheOrder.cpp
+++ b/modules/libpref/test/gtest/CallbackAndVarCacheOrder.cpp
@@ -16,22 +16,21 @@ struct Closure
 {
   U* mLocation;
   T mExpected;
   bool mCalled;
 };
 
 template<typename T, typename U>
 void
-VarChanged(const char* aPrefName, void* aData)
+VarChanged(const char* aPrefName, Closure<T, U>* aClosure)
 {
-  auto closure = static_cast<Closure<T, U>*>(aData);
-  ASSERT_EQ(*closure->mLocation, closure->mExpected);
-  ASSERT_FALSE(closure->mCalled);
-  closure->mCalled = true;
+  ASSERT_EQ(*aClosure->mLocation, aClosure->mExpected);
+  ASSERT_FALSE(aClosure->mCalled);
+  aClosure->mCalled = true;
 }
 
 void
 SetFunc(const nsCString& aPrefName, bool aValue)
 {
   nsresult rv = Preferences::SetBool(aPrefName.get(), aValue);
   ASSERT_TRUE(NS_SUCCEEDED(rv));
 }
--- a/netwerk/base/nsChannelClassifier.cpp
+++ b/netwerk/base/nsChannelClassifier.cpp
@@ -89,17 +89,17 @@ public:
   void SetTrackingBlackList(const nsACString& aList) { mTrackingBlacklist = aList; }
   nsCString GetTrackingBlackList() { return mTrackingBlacklist; }
 
 private:
   friend class StaticAutoPtr<CachedPrefs>;
   CachedPrefs();
   ~CachedPrefs();
 
-  static void OnPrefsChange(const char* aPrefName, void* );
+  static void OnPrefsChange(const char* aPrefName, CachedPrefs*);
 
   // Whether channels should be annotated as being on the tracking protection
   // list.
   static bool sAnnotateChannelEnabled;
   // Whether the priority of the channels annotated as being on the tracking
   // protection list should be lowered.
   static bool sLowerNetworkPriority;
   static bool sAllowListExample;
@@ -114,34 +114,32 @@ private:
 bool CachedPrefs::sAllowListExample = false;
 bool CachedPrefs::sLowerNetworkPriority = false;
 bool CachedPrefs::sAnnotateChannelEnabled = false;
 
 StaticAutoPtr<CachedPrefs> CachedPrefs::sInstance;
 
 // static
 void
-CachedPrefs::OnPrefsChange(const char* aPref, void* aClosure)
+CachedPrefs::OnPrefsChange(const char* aPref, CachedPrefs* aPrefs)
 {
-  CachedPrefs* prefs = static_cast<CachedPrefs*> (aClosure);
-
   if (!strcmp(aPref, URLCLASSIFIER_SKIP_HOSTNAMES)) {
     nsCString skipHostnames;
     Preferences::GetCString(URLCLASSIFIER_SKIP_HOSTNAMES, skipHostnames);
     ToLowerCase(skipHostnames);
-    prefs->SetSkipHostnames(skipHostnames);
+    aPrefs->SetSkipHostnames(skipHostnames);
   } else if (!strcmp(aPref, URLCLASSIFIER_TRACKING_WHITELIST)) {
     nsCString trackingWhitelist;
     Preferences::GetCString(URLCLASSIFIER_TRACKING_WHITELIST,
                             trackingWhitelist);
-    prefs->SetTrackingWhiteList(trackingWhitelist);
+    aPrefs->SetTrackingWhiteList(trackingWhitelist);
   } else if (!strcmp(aPref, URLCLASSIFIER_TRACKING_TABLE)) {
     nsCString trackingBlacklist;
     Preferences::GetCString(URLCLASSIFIER_TRACKING_TABLE, trackingBlacklist);
-    prefs->SetTrackingBlackList(trackingBlacklist);
+    aPrefs->SetTrackingBlackList(trackingBlacklist);
   }
 }
 
 void
 CachedPrefs::Init()
 {
   Preferences::AddBoolVarCache(&sAnnotateChannelEnabled,
                                "privacy.trackingprotection.annotate_channels");
@@ -789,43 +787,16 @@ nsChannelClassifier::HasBeenClassified(n
         return false;
     }
 
     nsCString tag;
     cacheEntry->GetMetaDataElement("necko:classified", getter_Copies(tag));
     return tag.EqualsLiteral("1");
 }
 
-//static
-bool
-nsChannelClassifier::SameLoadingURI(nsIDocument *aDoc, nsIChannel *aChannel)
-{
-  nsCOMPtr<nsIURI> docURI = aDoc->GetDocumentURI();
-  nsCOMPtr<nsILoadInfo> channelLoadInfo = aChannel->GetLoadInfo();
-  if (!channelLoadInfo || !docURI) {
-    return false;
-  }
-
-  nsCOMPtr<nsIPrincipal> channelLoadingPrincipal = channelLoadInfo->LoadingPrincipal();
-  if (!channelLoadingPrincipal) {
-    // TYPE_DOCUMENT loads will not have a channelLoadingPrincipal. But top level
-    // loads should not be blocked by Tracking Protection, so we will return
-    // false
-    return false;
-  }
-  nsCOMPtr<nsIURI> channelLoadingURI;
-  channelLoadingPrincipal->GetURI(getter_AddRefs(channelLoadingURI));
-  if (!channelLoadingURI) {
-    return false;
-  }
-  bool equals = false;
-  nsresult rv = docURI->EqualsExceptRef(channelLoadingURI, &equals);
-  return NS_SUCCEEDED(rv) && equals;
-}
-
 // static
 nsresult
 nsChannelClassifier::SetBlockedContent(nsIChannel *channel,
                                        nsresult aErrorCode,
                                        const nsACString& aList,
                                        const nsACString& aProvider,
                                        const nsACString& aFullHash)
 {
@@ -858,43 +829,23 @@ nsChannelClassifier::SetBlockedContent(n
   auto* pwin = nsPIDOMWindowOuter::From(win);
   nsCOMPtr<nsIDocShell> docShell = pwin->GetDocShell();
   if (!docShell) {
     return NS_OK;
   }
   nsCOMPtr<nsIDocument> doc = docShell->GetDocument();
   NS_ENSURE_TRUE(doc, NS_OK);
 
-  // This event might come after the user has navigated to another page.
-  // To prevent showing the TrackingProtection UI on the wrong page, we need to
-  // check that the loading URI for the channel is the same as the URI currently
-  // loaded in the document.
-  if (!SameLoadingURI(doc, channel)) {
-    return NS_OK;
+  unsigned state;
+  if (aErrorCode == NS_ERROR_TRACKING_URI) {
+    state = nsIWebProgressListener::STATE_BLOCKED_TRACKING_CONTENT;
+  } else {
+    state = nsIWebProgressListener::STATE_BLOCKED_UNSAFE_CONTENT;
   }
-
-  // Notify nsIWebProgressListeners of this security event.
-  // Can be used to change the UI state.
-  nsCOMPtr<nsISecurityEventSink> eventSink = do_QueryInterface(docShell, &rv);
-  NS_ENSURE_SUCCESS(rv, NS_OK);
-  uint32_t state = 0;
-  nsCOMPtr<nsISecureBrowserUI> securityUI;
-  docShell->GetSecurityUI(getter_AddRefs(securityUI));
-  if (!securityUI) {
-    return NS_OK;
-  }
-  securityUI->GetState(&state);
-  if (aErrorCode == NS_ERROR_TRACKING_URI) {
-    doc->SetHasTrackingContentBlocked(true);
-    state |= nsIWebProgressListener::STATE_BLOCKED_TRACKING_CONTENT;
-  } else {
-    state |= nsIWebProgressListener::STATE_BLOCKED_UNSAFE_CONTENT;
-  }
-
-  eventSink->OnSecurityChange(channel, state);
+  pwin->NotifyContentBlockingState(state, channel);
 
   // Log a warning to the web console.
   nsCOMPtr<nsIURI> uri;
   channel->GetURI(getter_AddRefs(uri));
   NS_ConvertUTF8toUTF16 spec(uri->GetSpecOrDefault());
   const char16_t* params[] = { spec.get() };
   const char* message = (aErrorCode == NS_ERROR_TRACKING_URI) ?
     "TrackingUriBlocked" : "UnsafeUriBlocked";
--- a/netwerk/base/nsChannelClassifier.h
+++ b/netwerk/base/nsChannelClassifier.h
@@ -73,18 +73,16 @@ private:
     void MarkEntryClassified(nsresult status);
     bool HasBeenClassified(nsIChannel *aChannel);
     // Helper function so that we ensure we call ContinueBeginConnect once
     // Start is called. Returns NS_OK if and only if we will get a callback
     // from the classifier service.
     nsresult StartInternal();
     // Helper function to check a URI against the hostname whitelist
     bool IsHostnameWhitelisted(nsIURI *aUri, const nsACString &aWhitelisted);
-    // Checks that the channel was loaded by the URI currently loaded in aDoc
-    static bool SameLoadingURI(nsIDocument *aDoc, nsIChannel *aChannel);
     // Note this function will be also used to decide whether or not to enable
     // channel annotation. When |aAnnotationsOnly| is true, this function
     // is called by ShouldEnableTrackingAnnotation(). Otherwise, this is called
     // by ShouldEnableTrackingProtection().
     nsresult ShouldEnableTrackingProtectionInternal(nsIChannel *aChannel,
                                                     bool aAnnotationsOnly,
                                                     bool *result);
 
--- a/netwerk/base/nsIOService.cpp
+++ b/netwerk/base/nsIOService.cpp
@@ -196,16 +196,26 @@ nsIOService::nsIOService()
     , mNetWon(0)
     , mLastOfflineStateChange(PR_IntervalNow())
     , mLastConnectivityChange(PR_IntervalNow())
     , mLastNetworkLinkChange(PR_IntervalNow())
     , mNetTearingDownStarted(0)
 {
 }
 
+static const char* gCallbackPrefs[] = {
+    PORT_PREF_PREFIX,
+    MANAGE_OFFLINE_STATUS_PREF,
+    NECKO_BUFFER_CACHE_COUNT_PREF,
+    NECKO_BUFFER_CACHE_SIZE_PREF,
+    NETWORK_NOTIFY_CHANGED_PREF,
+    NETWORK_CAPTIVE_PORTAL_PREF,
+    nullptr,
+};
+
 nsresult
 nsIOService::Init()
 {
     // XXX hack until xpidl supports error info directly (bug 13423)
     nsCOMPtr<nsIErrorService> errorService = do_GetService(NS_ERRORSERVICE_CONTRACTID);
     if (errorService) {
         errorService->RegisterErrorStringBundle(NS_ERROR_MODULE_NETWORK, NECKO_MSGS_URL);
     }
@@ -214,27 +224,20 @@ nsIOService::Init()
 
     InitializeCaptivePortalService();
 
     // setup our bad port list stuff
     for(int i=0; gBadPortList[i]; i++)
         mRestrictedPortList.AppendElement(gBadPortList[i]);
 
     // Further modifications to the port list come from prefs
-    nsCOMPtr<nsIPrefBranch> prefBranch;
-    GetPrefBranch(getter_AddRefs(prefBranch));
-    if (prefBranch) {
-        prefBranch->AddObserver(PORT_PREF_PREFIX, this, true);
-        prefBranch->AddObserver(MANAGE_OFFLINE_STATUS_PREF, this, true);
-        prefBranch->AddObserver(NECKO_BUFFER_CACHE_COUNT_PREF, this, true);
-        prefBranch->AddObserver(NECKO_BUFFER_CACHE_SIZE_PREF, this, true);
-        prefBranch->AddObserver(NETWORK_NOTIFY_CHANGED_PREF, this, true);
-        prefBranch->AddObserver(NETWORK_CAPTIVE_PORTAL_PREF, this, true);
-        PrefsChanged(prefBranch);
-    }
+    Preferences::RegisterPrefixCallbacks(
+        PREF_CHANGE_METHOD(nsIOService::PrefsChanged),
+        gCallbackPrefs, this);
+    PrefsChanged();
 
     // Register for profile change notifications
     nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
     if (observerService) {
         observerService->AddObserver(this, kProfileChangeNetTeardownTopic, true);
         observerService->AddObserver(this, kProfileChangeNetRestoreTopic, true);
         observerService->AddObserver(this, kProfileDoChange, true);
         observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, true);
@@ -1212,89 +1215,87 @@ nsIOService::AllowPort(int32_t inPort, c
 
     *_retval = true;
     return NS_OK;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 
 void
-nsIOService::PrefsChanged(nsIPrefBranch *prefs, const char *pref)
+nsIOService::PrefsChanged(const char *pref)
 {
-    if (!prefs) return;
-
     // Look for extra ports to block
     if (!pref || strcmp(pref, PORT_PREF("banned")) == 0)
-        ParsePortList(prefs, PORT_PREF("banned"), false);
+        ParsePortList(PORT_PREF("banned"), false);
 
     // ...as well as previous blocks to remove.
     if (!pref || strcmp(pref, PORT_PREF("banned.override")) == 0)
-        ParsePortList(prefs, PORT_PREF("banned.override"), true);
+        ParsePortList(PORT_PREF("banned.override"), true);
 
     if (!pref || strcmp(pref, MANAGE_OFFLINE_STATUS_PREF) == 0) {
         bool manage;
         if (mNetworkLinkServiceInitialized &&
-            NS_SUCCEEDED(prefs->GetBoolPref(MANAGE_OFFLINE_STATUS_PREF,
-                                            &manage))) {
+            NS_SUCCEEDED(Preferences::GetBool(MANAGE_OFFLINE_STATUS_PREF,
+                                              &manage))) {
             LOG(("nsIOService::PrefsChanged ManageOfflineStatus manage=%d\n", manage));
             SetManageOfflineStatus(manage);
         }
     }
 
     if (!pref || strcmp(pref, NECKO_BUFFER_CACHE_COUNT_PREF) == 0) {
         int32_t count;
-        if (NS_SUCCEEDED(prefs->GetIntPref(NECKO_BUFFER_CACHE_COUNT_PREF,
-                                           &count)))
+        if (NS_SUCCEEDED(Preferences::GetInt(NECKO_BUFFER_CACHE_COUNT_PREF,
+                                             &count)))
             /* check for bogus values and default if we find such a value */
             if (count > 0)
                 gDefaultSegmentCount = count;
     }
 
     if (!pref || strcmp(pref, NECKO_BUFFER_CACHE_SIZE_PREF) == 0) {
         int32_t size;
-        if (NS_SUCCEEDED(prefs->GetIntPref(NECKO_BUFFER_CACHE_SIZE_PREF,
-                                           &size)))
+        if (NS_SUCCEEDED(Preferences::GetInt(NECKO_BUFFER_CACHE_SIZE_PREF,
+                                             &size)))
             /* check for bogus values and default if we find such a value
              * the upper limit here is arbitrary. having a 1mb segment size
              * is pretty crazy.  if you remove this, consider adding some
              * integer rollover test.
              */
             if (size > 0 && size < 1024*1024)
                 gDefaultSegmentSize = size;
         NS_WARNING_ASSERTION(!(size & (size - 1)),
                              "network segment size is not a power of 2!");
     }
 
     if (!pref || strcmp(pref, NETWORK_NOTIFY_CHANGED_PREF) == 0) {
         bool allow;
-        nsresult rv = prefs->GetBoolPref(NETWORK_NOTIFY_CHANGED_PREF, &allow);
+        nsresult rv = Preferences::GetBool(NETWORK_NOTIFY_CHANGED_PREF, &allow);
         if (NS_SUCCEEDED(rv)) {
             mNetworkNotifyChanged = allow;
         }
     }
 
     if (!pref || strcmp(pref, NETWORK_CAPTIVE_PORTAL_PREF) == 0) {
-        nsresult rv = prefs->GetBoolPref(NETWORK_CAPTIVE_PORTAL_PREF, &gCaptivePortalEnabled);
+        nsresult rv = Preferences::GetBool(NETWORK_CAPTIVE_PORTAL_PREF, &gCaptivePortalEnabled);
         if (NS_SUCCEEDED(rv) && mCaptivePortalService) {
             if (gCaptivePortalEnabled) {
                 static_cast<CaptivePortalService*>(mCaptivePortalService.get())->Start();
             } else {
                 static_cast<CaptivePortalService*>(mCaptivePortalService.get())->Stop();
             }
         }
     }
 }
 
 void
-nsIOService::ParsePortList(nsIPrefBranch *prefBranch, const char *pref, bool remove)
+nsIOService::ParsePortList(const char *pref, bool remove)
 {
     nsAutoCString portList;
 
     // Get a pref string and chop it up into a list of ports.
-    prefBranch->GetCharPref(pref, portList);
+    Preferences::GetCString(pref, portList);
     if (!portList.IsVoid()) {
         nsTArray<nsCString> portListArray;
         ParseString(portList, ',', portListArray);
         uint32_t index;
         for (index=0; index < portListArray.Length(); index++) {
             portListArray[index].StripWhitespace();
             int32_t portBegin, portEnd;
 
@@ -1319,23 +1320,16 @@ nsIOService::ParsePortList(nsIPrefBranch
                        mRestrictedPortList.AppendElement(port);
                }
             }
 
         }
     }
 }
 
-void
-nsIOService::GetPrefBranch(nsIPrefBranch **result)
-{
-    *result = nullptr;
-    CallGetService(NS_PREFSERVICE_CONTRACTID, result);
-}
-
 class nsWakeupNotifier : public Runnable
 {
 public:
   explicit nsWakeupNotifier(nsIIOServiceInternal* ioService)
     : Runnable("net::nsWakeupNotifier")
     , mIOService(ioService)
   {
   }
@@ -1376,21 +1370,17 @@ nsIOService::SetHttpHandlerAlreadyShutin
 }
 
 // nsIObserver interface
 NS_IMETHODIMP
 nsIOService::Observe(nsISupports *subject,
                      const char *topic,
                      const char16_t *data)
 {
-    if (!strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
-        nsCOMPtr<nsIPrefBranch> prefBranch = do_QueryInterface(subject);
-        if (prefBranch)
-            PrefsChanged(prefBranch, NS_ConvertUTF16toUTF8(data).get());
-    } else if (!strcmp(topic, kProfileChangeNetTeardownTopic)) {
+    if (!strcmp(topic, kProfileChangeNetTeardownTopic)) {
         if (!mHttpHandlerAlreadyShutingDown) {
           mNetTearingDownStarted = PR_IntervalNow();
         }
         mHttpHandlerAlreadyShutingDown = false;
         if (!mOffline) {
             mOfflineForProfileChange = true;
             SetOffline(true);
         }
@@ -1403,19 +1393,17 @@ nsIOService::Observe(nsISupports *subjec
         if (data && NS_LITERAL_STRING("startup").Equals(data)) {
             // Lazy initialization of network link service (see bug 620472)
             InitializeNetworkLinkService();
             // Set up the initilization flag regardless the actuall result.
             // If we fail here, we will fail always on.
             mNetworkLinkServiceInitialized = true;
 
             // And now reflect the preference setting
-            nsCOMPtr<nsIPrefBranch> prefBranch;
-            GetPrefBranch(getter_AddRefs(prefBranch));
-            PrefsChanged(prefBranch, MANAGE_OFFLINE_STATUS_PREF);
+            PrefsChanged(MANAGE_OFFLINE_STATUS_PREF);
 
             // Bug 870460 - Read cookie database at an early-as-possible time
             // off main thread. Hence, we have more chance to finish db query
             // before something calls into the cookie service.
             nsCOMPtr<nsISupports> cookieServ = do_GetService(NS_COOKIESERVICE_CONTRACTID);
         }
     } else if (!strcmp(topic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
         // Remember we passed XPCOM shutdown notification to prevent any
--- a/netwerk/base/nsIOService.h
+++ b/netwerk/base/nsIOService.h
@@ -128,19 +128,18 @@ private:
                                                   uint32_t end=0);
     nsresult CacheProtocolHandler(const char *scheme,
                                               nsIProtocolHandler* hdlr);
 
     nsresult InitializeCaptivePortalService();
     nsresult RecheckCaptivePortalIfLocalRedirect(nsIChannel* newChan);
 
     // Prefs wrangling
-    void PrefsChanged(nsIPrefBranch *prefs, const char *pref = nullptr);
-    void GetPrefBranch(nsIPrefBranch **);
-    void ParsePortList(nsIPrefBranch *prefBranch, const char *pref, bool remove);
+    void PrefsChanged(const char *pref = nullptr);
+    void ParsePortList(const char *pref, bool remove);
 
     nsresult InitializeSocketTransportService();
     nsresult InitializeNetworkLinkService();
     nsresult InitializeProtocolProxyService();
 
     // consolidated helper function
     void LookupProxyInfo(nsIURI *aURI, nsIURI *aProxyURI, uint32_t aProxyFlags,
                          nsCString *aScheme, nsIProxyInfo **outPI);
--- a/netwerk/base/nsSocketTransportService2.cpp
+++ b/netwerk/base/nsSocketTransportService2.cpp
@@ -582,16 +582,35 @@ NS_IMPL_ISUPPORTS(nsSocketTransportServi
                   nsISocketTransportService,
                   nsIRoutedSocketTransportService,
                   nsIEventTarget,
                   nsIThreadObserver,
                   nsIRunnable,
                   nsPISocketTransportService,
                   nsIObserver)
 
+static const char* gCallbackPrefs[] = {