Merge inbound to mozilla-central r=merge a=merge
authorCiure Andrei <aciure@mozilla.com>
Thu, 11 Jan 2018 11:54:56 +0200
changeset 453010 e61c4485494ec4823da22217f665d73858c57e35
parent 452926 1ce9b4193d624d5d4ed4bceecfbeb1514f818848 (current diff)
parent 453009 e8663f41a424979b28d588bb850ffbb3fe4247cc (diff)
child 453011 2cb19d6666dbc1ebb4774f4c33e32c9768624332
child 453031 7d6913474e2a0aba2f6da873ed7892c348f09d86
child 453087 edd0e29a1ab40f2e5120eadeded1ab8f90c71120
push id1648
push usermtabara@mozilla.com
push dateThu, 01 Mar 2018 12:45:47 +0000
treeherdermozilla-release@cbb9688c2eeb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge, merge
milestone59.0a1
first release with
nightly linux32
e61c4485494e / 59.0a1 / 20180111100722 / files
nightly linux64
e61c4485494e / 59.0a1 / 20180111100722 / files
nightly mac
e61c4485494e / 59.0a1 / 20180111100722 / files
nightly win32
e61c4485494e / 59.0a1 / 20180111100722 / files
nightly win64
e61c4485494e / 59.0a1 / 20180111100722 / 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 r=merge a=merge
docshell/base/nsDocShell.cpp
dom/base/nsIDocument.h
dom/performance/PerformanceTiming.cpp
dom/performance/PerformanceTiming.h
dom/security/test/hsts/browser.ini
dom/security/test/hsts/browser_hsts-priming_allow_active.js
dom/security/test/hsts/browser_hsts-priming_allow_display.js
dom/security/test/hsts/browser_hsts-priming_block_active.js
dom/security/test/hsts/browser_hsts-priming_block_active_css.js
dom/security/test/hsts/browser_hsts-priming_block_active_with_redir_same.js
dom/security/test/hsts/browser_hsts-priming_block_display.js
dom/security/test/hsts/browser_hsts-priming_cache-timeout.js
dom/security/test/hsts/browser_hsts-priming_hsts_after_mixed.js
dom/security/test/hsts/browser_hsts-priming_include-subdomains.js
dom/security/test/hsts/browser_hsts-priming_no-duplicates.js
dom/security/test/hsts/browser_hsts-priming_no-ip-address.js
dom/security/test/hsts/browser_hsts-priming_no-non-standard-ports.js
dom/security/test/hsts/browser_hsts-priming_timeout.js
dom/security/test/hsts/file_1x1.png
dom/security/test/hsts/file_priming-top.html
dom/security/test/hsts/file_priming.js
dom/security/test/hsts/file_stylesheet.css
dom/security/test/hsts/file_testserver.sjs
dom/security/test/hsts/head.js
dom/tests/mochitest/general/test_interfaces.js
gfx/thebes/gfxPlatform.cpp
gfx/thebes/gfxPrefs.h
layout/inspector/inCSSValueSearch.cpp
layout/inspector/inCSSValueSearch.h
layout/inspector/inDOMUtils.cpp
layout/inspector/inDOMUtils.h
layout/inspector/inICSSValueSearch.idl
layout/inspector/inIDOMUtils.idl
layout/inspector/inISearchObserver.idl
layout/inspector/inISearchProcess.idl
layout/inspector/nsFontFace.cpp
layout/inspector/nsFontFace.h
layout/inspector/nsFontFaceList.cpp
layout/inspector/nsFontFaceList.h
layout/inspector/nsIDOMFontFace.idl
layout/inspector/nsIDOMFontFaceList.idl
layout/inspector/tests/test_bug806192.html
media/webrtc/trunk/build/linux/chrome_linux.croc
media/webrtc/trunk/build/linux/dump_app_syms
media/webrtc/trunk/build/linux/pkg-config-wrapper
media/webrtc/trunk/build/linux/python_arch.sh
media/webrtc/trunk/build/linux/rewrite_dirs.py
media/webrtc/trunk/build/linux/system.gyp
media/webrtc/trunk/webrtc/api/api.gyp
media/webrtc/trunk/webrtc/api/api_java.gyp
media/webrtc/trunk/webrtc/audio/webrtc_audio.gypi
media/webrtc/trunk/webrtc/base/base.gyp
media/webrtc/trunk/webrtc/base/base_tests.gyp
media/webrtc/trunk/webrtc/build/arm_neon.gypi
media/webrtc/trunk/webrtc/build/chromium_common.gypi
media/webrtc/trunk/webrtc/build/common.gypi
media/webrtc/trunk/webrtc/build/filename_rules.gypi
media/webrtc/trunk/webrtc/build/ios/merge_ios_libs.gyp
media/webrtc/trunk/webrtc/build/ios/objc_app.gypi
media/webrtc/trunk/webrtc/build/isolate.gypi
media/webrtc/trunk/webrtc/build/merge_libs.gyp
media/webrtc/trunk/webrtc/build/merge_libs_voice.gyp
media/webrtc/trunk/webrtc/build/merge_voice_libs.gyp
media/webrtc/trunk/webrtc/build/objc_common.gypi
media/webrtc/trunk/webrtc/build/protoc.gypi
media/webrtc/trunk/webrtc/call/webrtc_call.gypi
media/webrtc/trunk/webrtc/common.gyp
media/webrtc/trunk/webrtc/common_audio/common_audio.gyp
media/webrtc/trunk/webrtc/common_video/common_video.gyp
media/webrtc/trunk/webrtc/media/media.gyp
media/webrtc/trunk/webrtc/modules/audio_coding/audio_coding.gypi
media/webrtc/trunk/webrtc/modules/audio_coding/audio_coding_tests.gypi
media/webrtc/trunk/webrtc/modules/audio_coding/audio_network_adaptor/audio_network_adaptor.gypi
media/webrtc/trunk/webrtc/modules/audio_coding/codecs/cng/cng.gypi
media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g711/g711.gypi
media/webrtc/trunk/webrtc/modules/audio_coding/codecs/g722/g722.gypi
media/webrtc/trunk/webrtc/modules/audio_coding/codecs/ilbc/ilbc.gypi
media/webrtc/trunk/webrtc/modules/audio_coding/codecs/interfaces.gypi
media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/isac.gypi
media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/isac_common.gypi
media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/isac_test.gypi
media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/isacfix.gypi
media/webrtc/trunk/webrtc/modules/audio_coding/codecs/isac/isacfix_test.gypi
media/webrtc/trunk/webrtc/modules/audio_coding/codecs/opus/opus.gypi
media/webrtc/trunk/webrtc/modules/audio_coding/codecs/pcm16b/pcm16b.gypi
media/webrtc/trunk/webrtc/modules/audio_coding/codecs/red/red.gypi
media/webrtc/trunk/webrtc/modules/audio_coding/neteq/neteq.gypi
media/webrtc/trunk/webrtc/modules/audio_coding/neteq/neteq_tests.gypi
media/webrtc/trunk/webrtc/modules/audio_conference_mixer/audio_conference_mixer.gypi
media/webrtc/trunk/webrtc/modules/audio_device/audio_device.gypi
media/webrtc/trunk/webrtc/modules/audio_mixer/audio_mixer.gypi
media/webrtc/trunk/webrtc/modules/audio_processing/audio_processing.gypi
media/webrtc/trunk/webrtc/modules/audio_processing/audio_processing_tests.gypi
media/webrtc/trunk/webrtc/modules/bitrate_controller/bitrate_controller.gypi
media/webrtc/trunk/webrtc/modules/congestion_controller/congestion_controller.gypi
media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capture.gypi
media/webrtc/trunk/webrtc/modules/media_file/media_file.gypi
media/webrtc/trunk/webrtc/modules/modules.gyp
media/webrtc/trunk/webrtc/modules/pacing/pacing.gypi
media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/remote_bitrate_estimator.gypi
media/webrtc/trunk/webrtc/modules/rtp_rtcp/rtp_rtcp.gypi
media/webrtc/trunk/webrtc/modules/rtp_rtcp/test/testFec/test_fec.gypi
media/webrtc/trunk/webrtc/modules/utility/utility.gypi
media/webrtc/trunk/webrtc/modules/video_capture/video_capture.gypi
media/webrtc/trunk/webrtc/modules/video_coding/codecs/h264/h264.gypi
media/webrtc/trunk/webrtc/modules/video_coding/codecs/i420/i420.gypi
media/webrtc/trunk/webrtc/modules/video_coding/codecs/test/video_codecs_test_framework.gypi
media/webrtc/trunk/webrtc/modules/video_coding/codecs/tools/video_codecs_tools.gypi
media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp8/vp8.gyp
media/webrtc/trunk/webrtc/modules/video_coding/codecs/vp9/vp9.gyp
media/webrtc/trunk/webrtc/modules/video_coding/utility/video_coding_utility.gyp
media/webrtc/trunk/webrtc/modules/video_coding/video_coding.gypi
media/webrtc/trunk/webrtc/modules/video_coding/video_coding_test.gypi
media/webrtc/trunk/webrtc/modules/video_processing/video_processing.gypi
media/webrtc/trunk/webrtc/p2p/p2p.gyp
media/webrtc/trunk/webrtc/pc/pc.gyp
media/webrtc/trunk/webrtc/sdk/sdk.gyp
media/webrtc/trunk/webrtc/sdk/sdk.gypi
media/webrtc/trunk/webrtc/stats/stats.gyp
media/webrtc/trunk/webrtc/supplement.gypi
media/webrtc/trunk/webrtc/system_wrappers/cpu_features_chromium.gyp
media/webrtc/trunk/webrtc/system_wrappers/cpu_features_webrtc.gyp
media/webrtc/trunk/webrtc/system_wrappers/system_wrappers.gyp
media/webrtc/trunk/webrtc/test/test.gyp
media/webrtc/trunk/webrtc/tools/internal_tools.gyp
media/webrtc/trunk/webrtc/tools/tools.gyp
media/webrtc/trunk/webrtc/video/webrtc_video.gypi
media/webrtc/trunk/webrtc/voice_engine/voice_engine.gyp
media/webrtc/trunk/webrtc/webrtc.gyp
media/webrtc/trunk/webrtc/webrtc_examples.gyp
media/webrtc/trunk/webrtc/webrtc_tests.gypi
mobile/android/chrome/content/browser.js
modules/libpref/init/all.js
netwerk/base/LoadInfo.cpp
netwerk/base/LoadInfo.h
netwerk/base/nsILoadInfo.idl
netwerk/protocol/http/HSTSPrimerListener.cpp
netwerk/protocol/http/HSTSPrimerListener.h
netwerk/protocol/http/nsHttpChannel.cpp
netwerk/protocol/http/nsIHstsPrimingCallback.idl
security/manager/ssl/nsNSSShutDown.cpp
security/manager/ssl/security-prefs.js
--- a/browser/components/extensions/ext-browserAction.js
+++ b/browser/components/extensions/ext-browserAction.js
@@ -14,31 +14,29 @@ XPCOMUtils.defineLazyModuleGetter(this, 
                                   "resource://gre/modules/Timer.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "setTimeout",
                                   "resource://gre/modules/Timer.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "TelemetryStopwatch",
                                   "resource://gre/modules/TelemetryStopwatch.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "ViewPopup",
                                   "resource:///modules/ExtensionPopups.jsm");
 
-XPCOMUtils.defineLazyServiceGetter(this, "DOMUtils",
-                                   "@mozilla.org/inspector/dom-utils;1",
-                                   "inIDOMUtils");
-
 var {
   DefaultWeakMap,
 } = ExtensionUtils;
 
 Cu.import("resource://gre/modules/ExtensionParent.jsm");
 
 var {
   IconDetails,
   StartupCache,
 } = ExtensionParent;
 
+Cu.importGlobalProperties(["InspectorUtils"]);
+
 const POPUP_PRELOAD_TIMEOUT_MS = 200;
 const POPUP_OPEN_MS_HISTOGRAM = "WEBEXT_BROWSERACTION_POPUP_OPEN_MS";
 const POPUP_RESULT_HISTOGRAM = "WEBEXT_BROWSERACTION_POPUP_PRELOAD_RESULT_COUNT";
 
 var XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 
 const isAncestorOrSelf = (target, node) => {
   for (; node; node = node.parentNode) {
@@ -668,17 +666,17 @@ this.browserAction = class extends Exten
           let popup = browserAction.getProperty(tab, "popup");
           return Promise.resolve(popup);
         },
 
         setBadgeBackgroundColor: function(details) {
           let tab = getTab(details.tabId);
           let color = details.color;
           if (!Array.isArray(color)) {
-            let col = DOMUtils.colorToRGBA(color);
+            let col = InspectorUtils.colorToRGBA(color);
             color = col && [col.r, col.g, col.b, Math.round(col.a * 255)];
           }
           browserAction.setProperty(tab, "badgeBackgroundColor", color);
         },
 
         getBadgeBackgroundColor: function(details, callback) {
           let tab = getTab(details.tabId);
 
--- a/browser/components/places/tests/browser/browser.ini
+++ b/browser/components/places/tests/browser/browser.ini
@@ -21,16 +21,17 @@ skip-if = (os == 'win' && ccov) # Bug 14
 support-files =
   pageopeningwindow.html
 [browser_bookmarkProperties_addFolderDefaultButton.js]
 skip-if = (os == 'win' && ccov) # Bug 1423667
 [browser_bookmarkProperties_addKeywordForThisSearch.js]
 skip-if = (os == 'win' && ccov) # Bug 1423667
 [browser_bookmarkProperties_addLivemark.js]
 skip-if = (os == 'win' && ccov) # Bug 1423667
+[browser_bookmarkProperties_addTags.js]
 [browser_bookmarkProperties_bookmarkAllTabs.js]
 skip-if = (os == 'win' && ccov) # Bug 1423667
 [browser_bookmarkProperties_cancel.js]
 skip-if = (os == 'win' && ccov) # Bug 1423667
 [browser_bookmarkProperties_editTagContainer.js]
 [browser_bookmarkProperties_readOnlyRoot.js]
 skip-if = (os == 'win' && ccov) # Bug 1423667
 [browser_bookmarksProperties.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/places/tests/browser/browser_bookmarkProperties_addTags.js
@@ -0,0 +1,74 @@
+/**
+ *  Test that tags can be added to bookmarks using the star-shaped button.
+ */
+"use strict";
+
+let bookmarkPanel = document.getElementById("editBookmarkPanel");
+let doneButton = document.getElementById("editBookmarkPanelDoneButton");
+let bookmarkStar = BookmarkingUI.star;
+let bookmarkPanelTitle = document.getElementById("editBookmarkPanelTitle");
+let TEST_URL = "about:buildconfig";
+
+async function clickBookmarkStar() {
+  let shownPromise = promisePopupShown(bookmarkPanel);
+  bookmarkStar.click();
+  await shownPromise;
+}
+
+async function hideBookmarksPanel(callback) {
+  let hiddenPromise = promisePopupHidden(bookmarkPanel);
+  callback();
+  await hiddenPromise;
+}
+
+add_task(async function test_add_tags() {
+  let tab = await BrowserTestUtils.openNewForegroundTab({
+    gBrowser,
+    opening: TEST_URL,
+    waitForStateStop: true
+  });
+
+  // The bookmarks panel is expected to auto-close after this step.
+  await hideBookmarksPanel(async () => {
+    // Click the bookmark star to bookmark the page.
+    await clickBookmarkStar();
+    Assert.equal(bookmarkPanelTitle.value, gNavigatorBundle.getString("editBookmarkPanel.pageBookmarkedTitle"), "Bookmark title is correct");
+    Assert.equal(bookmarkStar.getAttribute("starred"), "true", "Page is starred");
+  });
+
+  // Click the bookmark star again to add tags.
+  await clickBookmarkStar();
+  Assert.equal(bookmarkPanelTitle.value, gNavigatorBundle.getString("editBookmarkPanel.editBookmarkTitle"), "Bookmark title is correct");
+  let promiseNotification = PlacesTestUtils.waitForNotification("onItemAdded", (id, parentId, index, type, itemUrl) => {
+    if (itemUrl !== null) {
+      return itemUrl.equals(Services.io.newURI(TEST_URL));
+    }
+    return true;
+  });
+  await fillBookmarkTextField("editBMPanel_tagsField", "tag1", window);
+  await promiseNotification;
+  let bookmarks = [];
+  await PlacesUtils.bookmarks.fetch({ url: TEST_URL }, bm => bookmarks.push(bm));
+  Assert.equal(PlacesUtils.tagging.getTagsForURI(Services.io.newURI(TEST_URL)).length, 1, "Found the right number of tags");
+  Assert.deepEqual(PlacesUtils.tagging.getTagsForURI(Services.io.newURI(TEST_URL)), ["tag1"]);
+  await hideBookmarksPanel(() => doneButton.click());
+
+  // Click the bookmark star again, add more tags.
+  await clickBookmarkStar();
+  promiseNotification = PlacesTestUtils.waitForNotification("onItemChanged", (id, property) => property == "tags");
+  await fillBookmarkTextField("editBMPanel_tagsField", "tag1, tag2, tag3", window);
+  await promiseNotification;
+  await hideBookmarksPanel(() => doneButton.click());
+
+  bookmarks = [];
+  await PlacesUtils.bookmarks.fetch({ url: TEST_URL }, bm => bookmarks.push(bm));
+  Assert.equal(bookmarks.length, 1, "Only one bookmark should exist");
+  Assert.equal(PlacesUtils.tagging.getTagsForURI(Services.io.newURI(TEST_URL)).length, 3, "Found the right number of tags");
+  Assert.deepEqual(PlacesUtils.tagging.getTagsForURI(Services.io.newURI(TEST_URL)), ["tag1", "tag2", "tag3"]);
+
+  // Cleanup.
+  registerCleanupFunction(async function() {
+    await PlacesUtils.bookmarks.eraseEverything();
+    await BrowserTestUtils.removeTab(tab);
+  });
+});
--- a/browser/components/places/tests/browser/head.js
+++ b/browser/components/places/tests/browser/head.js
@@ -440,8 +440,34 @@ function promisePlacesInitComplete() {
 
   let placesInitCompleteObserved = TestUtils.topicObserved("places-browser-init-complete");
 
   gBrowserGlue.observe(null, "browser-glue-test",
     "places-browser-init-complete");
 
   return placesInitCompleteObserved;
 }
+
+// Function copied from browser/base/content/test/general/head.js.
+function promisePopupShown(popup) {
+  return new Promise(resolve => {
+    if (popup.state == "open") {
+      resolve();
+    } else {
+      let onPopupShown = event => {
+        popup.removeEventListener("popupshown", onPopupShown);
+        resolve();
+      };
+      popup.addEventListener("popupshown", onPopupShown);
+    }
+  });
+}
+
+// Function copied from browser/base/content/test/general/head.js.
+function promisePopupHidden(popup) {
+  return new Promise(resolve => {
+    let onPopupHidden = event => {
+      popup.removeEventListener("popuphidden", onPopupHidden);
+      resolve();
+    };
+    popup.addEventListener("popuphidden", onPopupHidden);
+  });
+}
--- a/browser/components/sessionstore/test/browser_354894_perwindowpb.js
+++ b/browser/components/sessionstore/test/browser_354894_perwindowpb.js
@@ -136,17 +136,17 @@ let setupTest = async function(options, 
 
   for (let o in observing) {
     Services.obs.addObserver(observer, o);
   }
 
   let private = options.private || false;
   let newWin = await promiseNewWindowLoaded({ private });
 
-  injectTestTabs(newWin);
+  await injectTestTabs(newWin);
 
   await testFunction(newWin, observing);
 
   let count = getBrowserWindowsCount();
   is(count.open, 0, "Got right number of open windows");
   is(count.winstates, 1, "Got right number of stored window states");
 
   for (let o in observing) {
@@ -158,19 +158,19 @@ let setupTest = async function(options, 
 
 /**
  * Loads a TEST_URLS into a browser window.
  *
  * @param win (Window)
  *        The browser window to load the tabs in
  */
 function injectTestTabs(win) {
-  TEST_URLS.forEach(function(url) {
-    win.gBrowser.addTab(url);
-  });
+  let promises = TEST_URLS.map(url => win.gBrowser.addTab(url))
+                          .map(tab => BrowserTestUtils.browserLoaded(tab.linkedBrowser));
+  return Promise.all(promises);
 }
 
 /**
  * Attempts to close a window via BrowserTryToCloseWindow so that
  * we get the browser-lastwindow-close-requested and
  * browser-lastwindow-close-granted observer notifications.
  *
  * @param win (Window)
--- a/browser/modules/PluginContent.jsm
+++ b/browser/modules/PluginContent.jsm
@@ -10,16 +10,18 @@ var Cu = Components.utils;
 
 this.EXPORTED_SYMBOLS = [ "PluginContent" ];
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/Timer.jsm");
 Cu.import("resource://gre/modules/BrowserUtils.jsm");
 
+Cu.importGlobalProperties(["InspectorUtils"]);
+
 XPCOMUtils.defineLazyGetter(this, "gNavigatorBundle", function() {
   const url = "chrome://browser/locale/browser.properties";
   return Services.strings.createBundle(url);
 });
 
 XPCOMUtils.defineLazyModuleGetter(this, "AppConstants",
   "resource://gre/modules/AppConstants.jsm");
 
@@ -514,20 +516,18 @@ PluginContent.prototype = {
     }
 
     let plugin = event.target;
 
     if (eventType == "PluginPlaceholderReplaced") {
       plugin.removeAttribute("href");
       let overlay = this.getPluginUI(plugin, "main");
       this.setVisibility(plugin, overlay, OVERLAY_DISPLAY.FULL);
-      let inIDOMUtils = Cc["@mozilla.org/inspector/dom-utils;1"]
-                          .getService(Ci.inIDOMUtils);
-      // Add psuedo class so our styling will take effect
-      inIDOMUtils.addPseudoClassLock(plugin, "-moz-handler-clicktoplay");
+      // Add pseudo class so our styling will take effect
+      InspectorUtils.addPseudoClassLock(plugin, "-moz-handler-clicktoplay");
       overlay.addEventListener("click", this, true);
       return;
     }
 
     if (!(plugin instanceof Ci.nsIObjectLoadingContent))
       return;
 
     if (eventType == "PluginBindingAttached") {
--- a/browser/themes/osx/preferences/preferences.css
+++ b/browser/themes/osx/preferences/preferences.css
@@ -9,17 +9,16 @@
 
 .windowDialog {
   padding: 12px;
   font: -moz-dialog;
 }
 
 .prefwindow {
   padding: 0;
-  font: -moz-dialog !important;
 }
 
 .prefwindow[type="child"] {
   padding-top: 18px;
   padding-bottom: 15px;
   padding-inline-start: 18px;
   padding-inline-end: 20px;
 }
--- a/browser/tools/mozscreenshots/mozscreenshots/extension/configurations/Tabs.jsm
+++ b/browser/tools/mozscreenshots/mozscreenshots/extension/configurations/Tabs.jsm
@@ -12,16 +12,18 @@ const CUST_TAB = "chrome://browser/skin/
 const PREFS_TAB = "chrome://browser/skin/settings.svg";
 const DEFAULT_FAVICON_TAB = `data:text/html,<meta%20charset="utf-8"><title>No%20favicon</title>`;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/Timer.jsm");
 Cu.import("resource://testing-common/TestUtils.jsm");
 Cu.import("resource://testing-common/BrowserTestUtils.jsm");
 
+Cu.importGlobalProperties(["InspectorUtils"]);
+
 this.Tabs = {
   init(libDir) {},
 
   configurations: {
     fiveTabs: {
       selectors: ["#tabbrowser-tabs"],
       async applyConfig() {
         fiveTabsHelper();
@@ -184,20 +186,19 @@ function closeAllButOneTab(url = "about:
   gBrowser.selectedBrowser.loadURI(url);
   if (gBrowser.selectedTab.pinned)
     gBrowser.unpinTab(gBrowser.selectedTab);
   let newTabButton = browserWindow.document.getAnonymousElementByAttribute(browserWindow.gBrowser.tabContainer, "class", "tabs-newtab-button toolbarbutton-1");
   hoverTab(newTabButton, false);
 }
 
 function hoverTab(tab, hover = true) {
-  const inIDOMUtils = Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
   if (hover) {
-    inIDOMUtils.addPseudoClassLock(tab, ":hover");
+    InspectorUtils.addPseudoClassLock(tab, ":hover");
   } else {
-    inIDOMUtils.clearPseudoClassLocks(tab);
+    InspectorUtils.clearPseudoClassLocks(tab);
   }
   // XXX TODO: this isn't necessarily testing what we ship
   if (tab.nextElementSibling)
     tab.nextElementSibling.setAttribute("afterhovered", hover || null);
   if (tab.previousElementSibling)
     tab.previousElementSibling.setAttribute("beforehovered", hover || null);
 }
new file mode 100644
--- /dev/null
+++ b/build/gn.mozbuild
@@ -0,0 +1,36 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+gn_vars = {}
+
+if CONFIG['MOZ_DEBUG']:
+   gn_vars['is_debug'] = True
+else:
+   gn_vars['is_debug'] = False
+
+os = CONFIG['OS_TARGET']
+
+flavors = {
+    'WINNT': 'win',
+    'Android': 'android',
+    'Linux': 'linux',
+    'Darwin': 'mac' if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa' else 'ios',
+    'SunOS': 'solaris',
+    'GNU/kFreeBSD': 'freebsd',
+    'DragonFly': 'dragonfly',
+    'FreeBSD': 'freebsd',
+    'NetBSD': 'netbsd',
+    'OpenBSD': 'openbsd',
+}
+gn_vars['target_os'] = flavors.get(os)
+
+arches = {
+    'x86_64': 'x64',
+    'aarch64': 'arm64',
+}
+
+gn_vars['host_cpu'] = arches.get(CONFIG['HOST_CPU_ARCH'], CONFIG['HOST_CPU_ARCH'])
+gn_vars['target_cpu'] = arches.get(CONFIG['CPU_ARCH'], CONFIG['CPU_ARCH'])
--- a/build/moz.configure/toolchain.configure
+++ b/build/moz.configure/toolchain.configure
@@ -513,16 +513,22 @@ def check_compiler(compiler, language, t
         # add it directly.
         flags.append('-fms-compatibility-version=19.11.25547')
 
     # Check compiler target
     # --------------------------------------------------------------------
     if not info.cpu or info.cpu != target.cpu:
         if info.type == 'clang':
             append_flag('--target=%s' % target.toolchain)
+        elif info.type == 'clang-cl':
+            # Ideally this would share the 'clang' branch above, but on Windows
+            # the --target needs additional data like ms-compatibility-version.
+            if (info.cpu, target.cpu) == ('x86_64', 'x86'):
+                # -m32 does not use -Xclang, so add it directly.
+                flags.append('-m32')
         elif info.type == 'gcc':
             same_arch_different_bits = (
                 ('x86', 'x86_64'),
                 ('ppc', 'ppc64'),
                 ('sparc', 'sparc64'),
             )
             if (target.cpu, info.cpu) in same_arch_different_bits:
                 append_flag('-m32')
--- a/devtools/client/shared/test/browser_filter-editor-01.js
+++ b/devtools/client/shared/test/browser_filter-editor-01.js
@@ -1,26 +1,25 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 // Tests that the Filter Editor Widget parses filter values correctly (setCssValue)
 
 const {CSSFilterEditorWidget} = require("devtools/client/shared/widgets/FilterWidget");
-const DOMUtils =
-      Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
+const InspectorUtils = require("InspectorUtils");
 
 const TEST_URI = CHROME_URL_ROOT + "doc_filter-editor-01.html";
 const {getClientCssProperties} = require("devtools/shared/fronts/css-properties");
 
 // Verify that the given string consists of a valid CSS URL token.
 // Return true on success, false on error.
 function verifyURL(string) {
-  let lexer = DOMUtils.getCSSLexer(string);
+  let lexer = InspectorUtils.getCSSLexer(string);
 
   let token = lexer.nextToken();
   if (!token || token.tokenType !== "url") {
     return false;
   }
 
   return lexer.nextToken() === null;
 }
--- a/devtools/client/shared/test/test-actor.js
+++ b/devtools/client/shared/test/test-actor.js
@@ -13,19 +13,19 @@ const {
   getRect, getElementFromPoint, getAdjustedQuads, getWindowDimensions
 } = require("devtools/shared/layout/utils");
 const defer = require("devtools/shared/defer");
 const {Task} = require("devtools/shared/task");
 const {
   isContentStylesheet,
   getCSSStyleRules
 } = require("devtools/shared/inspector/css-logic");
-const DOMUtils = Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
 const loader = Cc["@mozilla.org/moz/jssubscript-loader;1"]
                  .getService(Ci.mozIJSSubScriptLoader);
+const InspectorUtils = require("InspectorUtils");
 
 // Set up a dummy environment so that EventUtils works. We need to be careful to
 // pass a window object into each EventUtils method we call rather than having
 // it rely on the |window| global.
 let EventUtils = {};
 EventUtils.window = {};
 EventUtils.parent = {};
 /* eslint-disable camelcase */
@@ -541,17 +541,17 @@ var TestActor = exports.TestActor = prot
   /**
    * Check that an element currently has a pseudo-class lock.
    * @param {String} selector The node selector to get the pseudo-class from
    * @param {String} pseudo The pseudoclass to check for
    * @return {Boolean}
    */
   hasPseudoClassLock: function (selector, pseudo) {
     let node = this._querySelector(selector);
-    return DOMUtils.hasPseudoClassLock(node, pseudo);
+    return InspectorUtils.hasPseudoClassLock(node, pseudo);
   },
 
   loadAndWaitForCustomEvent: function (url) {
     return new Promise(resolve => {
       // Wait for DOMWindowCreated first, as listening on the current outerwindow
       // doesn't allow receiving test-page-processing-done.
       this.tabActor.chromeEventHandler.addEventListener("DOMWindowCreated", () => {
         this.content.addEventListener(
@@ -779,18 +779,18 @@ var TestActor = exports.TestActor = prot
    * - {Boolean} isContentSheet.
    */
   getStyleSheetsInfoForNode: function (selector) {
     let node = this._querySelector(selector);
     let domRules = getCSSStyleRules(node);
 
     let sheets = [];
 
-    for (let i = 0, n = domRules.Count(); i < n; i++) {
-      let sheet = domRules.GetElementAt(i).parentStyleSheet;
+    for (let i = 0, n = domRules.length; i < n; i++) {
+      let sheet = domRules[i].parentStyleSheet;
       sheets.push({
         href: sheet.href,
         isContentSheet: isContentStylesheet(sheet)
       });
     }
 
     return sheets;
   },
--- a/devtools/client/shared/test/unit/test_cssColor-01.js
+++ b/devtools/client/shared/test/unit/test_cssColor-01.js
@@ -1,25 +1,20 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Test classifyColor.
 
 "use strict";
 
 var Cu = Components.utils;
-var Ci = Components.interfaces;
-var Cc = Components.classes;
 
-var {require, loader} = Cu.import("resource://devtools/shared/Loader.jsm", {});
+var {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
 const {colorUtils} = require("devtools/shared/css/color");
-
-loader.lazyGetter(this, "DOMUtils", function () {
-  return Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
-});
+const InspectorUtils = require("InspectorUtils");
 
 const CLASSIFY_TESTS = [
   { input: "rgb(255,0,192)", output: "rgb" },
   { input: "RGB(255,0,192)", output: "rgb" },
   { input: "RGB(100%,0%,83%)", output: "rgb" },
   { input: "rgba(255,0,192, 0.25)", output: "rgb" },
   { input: "hsl(5, 5%, 5%)", output: "hsl" },
   { input: "hsla(5, 5%, 5%, 0.25)", output: "hsl" },
@@ -29,20 +24,20 @@ const CLASSIFY_TESTS = [
   { input: "#fe01cb", output: "hex" },
   { input: "#fe01cb80", output: "hex" },
   { input: "#FE01CB", output: "hex" },
   { input: "#FE01CB80", output: "hex" },
   { input: "blue", output: "name" },
   { input: "orange", output: "name" }
 ];
 
-function compareWithDomutils(input, isColor) {
+function compareWithInspectorUtils(input, isColor) {
   let ours = colorUtils.colorToRGBA(input);
-  let platform = DOMUtils.colorToRGBA(input);
-  deepEqual(ours, platform, "color " + input + " matches DOMUtils");
+  let platform = InspectorUtils.colorToRGBA(input);
+  deepEqual(ours, platform, "color " + input + " matches InspectorUtils");
   if (isColor) {
     ok(ours !== null, "'" + input + "' is a color");
   } else {
     ok(ours === null, "'" + input + "' is not a color");
   }
 }
 
 function run_test() {
@@ -50,22 +45,22 @@ function run_test() {
     let result = colorUtils.classifyColor(test.input);
     equal(result, test.output, "test classifyColor(" + test.input + ")");
 
     let obj = new colorUtils.CssColor("purple");
     obj.setAuthoredUnitFromColor(test.input);
     equal(obj.colorUnit, test.output,
           "test setAuthoredUnitFromColor(" + test.input + ")");
 
-    // Check that our implementation matches DOMUtils.
-    compareWithDomutils(test.input, true);
+    // Check that our implementation matches InspectorUtils.
+    compareWithInspectorUtils(test.input, true);
 
     // And check some obvious errors.
-    compareWithDomutils("mumble" + test.input, false);
-    compareWithDomutils(test.input + "trailingstuff", false);
+    compareWithInspectorUtils("mumble" + test.input, false);
+    compareWithInspectorUtils(test.input + "trailingstuff", false);
   }
 
   // Regression test for bug 1303826.
   let black = new colorUtils.CssColor("#000");
   black.colorUnit = "name";
   equal(black.toString(), "black", "test non-upper-case color cycling");
 
   let upper = new colorUtils.CssColor("BLACK");
--- a/devtools/client/shared/test/unit/test_cssColor-03.js
+++ b/devtools/client/shared/test/unit/test_cssColor-03.js
@@ -1,25 +1,20 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Test css-color-4 color function syntax and old-style syntax.
 
 "use strict";
 
 var Cu = Components.utils;
-var Ci = Components.interfaces;
-var Cc = Components.classes;
 
-var {require, loader} = Cu.import("resource://devtools/shared/Loader.jsm", {});
+var {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
 const {colorUtils} = require("devtools/shared/css/color");
-
-loader.lazyGetter(this, "DOMUtils", function () {
-  return Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
-});
+const InspectorUtils = require("InspectorUtils");
 
 const OLD_STYLE_TESTS = [
   "rgb(255,0,192)",
   "RGB(255,0,192)",
   "RGB(100%,0%,83%)",
   "rgba(255,0,192,0.25)",
   "hsl(120, 100%, 40%)",
   "hsla(120, 100%, 40%, 0.25)",
@@ -38,24 +33,25 @@ const CSS_COLOR_4_TESTS = [
   "hsl(50deg 25% 33% / 0.25)",
   "hsl(60 120% 60% / 0.25)",
   "hSlA(5turn 40% 4%)",
 ];
 
 function run_test() {
   for (let test of OLD_STYLE_TESTS) {
     let ours = colorUtils.colorToRGBA(test, false);
-    let platform = DOMUtils.colorToRGBA(test);
-    deepEqual(ours, platform, "color " + test + " matches DOMUtils");
+    let platform = InspectorUtils.colorToRGBA(test);
+    deepEqual(ours, platform, "color " + test + " matches InspectorUtils");
     ok(ours !== null, "'" + test + "' is a color");
   }
 
   for (let test of CSS_COLOR_4_TESTS) {
     let oursOld = colorUtils.colorToRGBA(test, false);
     let oursNew = colorUtils.colorToRGBA(test, true);
-    let platform = DOMUtils.colorToRGBA(test);
+    let platform = InspectorUtils.colorToRGBA(test);
     notEqual(oursOld, platform, "old style parser for color " + test +
-             " should not match DOMUtils");
+             " should not match InspectorUtils");
     ok(oursOld === null, "'" + test + "' is not a color with old parser");
-    deepEqual(oursNew, platform, `css-color-4 parser for color ${test} matches DOMUtils`);
+    deepEqual(oursNew, platform,
+              `css-color-4 parser for color ${test} matches InspectorUtils`);
     ok(oursNew !== null, "'" + test + "' is a color with css-color-4 parser");
   }
 }
--- a/devtools/client/shared/test/unit/test_cssColorDatabase.js
+++ b/devtools/client/shared/test/unit/test_cssColorDatabase.js
@@ -1,63 +1,60 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Test that css-color-db matches platform.
 
 "use strict";
 
 var Cu = Components.utils;
-var Ci = Components.interfaces;
-var Cc = Components.classes;
 
 var {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
 
-const DOMUtils = Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
-
 const {colorUtils} = require("devtools/shared/css/color");
 const {cssColors} = require("devtools/shared/css/color-db");
+const InspectorUtils = require("InspectorUtils");
 
 function isValid(colorName) {
   ok(colorUtils.isValidCSSColor(colorName),
      colorName + " is valid in database");
-  ok(DOMUtils.isValidCSSColor(colorName),
-     colorName + " is valid in DOMUtils");
+  ok(InspectorUtils.isValidCSSColor(colorName),
+     colorName + " is valid in InspectorUtils");
 }
 
 function checkOne(colorName, checkName) {
   let ours = colorUtils.colorToRGBA(colorName);
-  let fromDom = DOMUtils.colorToRGBA(colorName);
-  deepEqual(ours, fromDom, colorName + " agrees with DOMUtils");
+  let fromDom = InspectorUtils.colorToRGBA(colorName);
+  deepEqual(ours, fromDom, colorName + " agrees with InspectorUtils");
 
   isValid(colorName);
 
   if (checkName) {
     let {r, g, b} = ours;
 
     // The color we got might not map back to the same name; but our
-    // implementation should agree with DOMUtils about which name is
+    // implementation should agree with InspectorUtils about which name is
     // canonical.
     let ourName = colorUtils.rgbToColorName(r, g, b);
-    let domName = DOMUtils.rgbToColorName(r, g, b);
+    let domName = InspectorUtils.rgbToColorName(r, g, b);
 
     equal(ourName, domName,
-          colorName + " canonical name agrees with DOMUtils");
+          colorName + " canonical name agrees with InspectorUtils");
   }
 }
 
 function run_test() {
   for (let name in cssColors) {
     checkOne(name, true);
   }
   checkOne("transparent", false);
 
   // Now check that platform didn't add a new name when we weren't
   // looking.
-  let names = DOMUtils.getCSSValuesForProperty("background-color");
+  let names = InspectorUtils.getCSSValuesForProperty("background-color");
   for (let name of names) {
     if (name !== "hsl" && name !== "hsla" &&
         name !== "rgb" && name !== "rgba" &&
         name !== "inherit" && name !== "initial" && name !== "unset") {
       checkOne(name, true);
     }
   }
 }
--- a/devtools/client/shared/widgets/FilterWidget.js
+++ b/devtools/client/shared/widgets/FilterWidget.js
@@ -5,31 +5,26 @@
 "use strict";
 
 /**
   * This is a CSS Filter Editor widget used
   * for Rule View's filter swatches
   */
 
 const EventEmitter = require("devtools/shared/old-event-emitter");
-const { Cc, Ci } = require("chrome");
 const XHTML_NS = "http://www.w3.org/1999/xhtml";
 
 const { LocalizationHelper } = require("devtools/shared/l10n");
 const STRINGS_URI = "devtools/client/locales/filterwidget.properties";
 const L10N = new LocalizationHelper(STRINGS_URI);
 
 const {cssTokenizer} = require("devtools/shared/css/parsing-utils");
 
 const asyncStorage = require("devtools/shared/async-storage");
 
-loader.lazyGetter(this, "DOMUtils", () => {
-  return Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
-});
-
 const DEFAULT_FILTER_TYPE = "length";
 const UNIT_MAPPING = {
   percentage: "%",
   length: "px",
   angle: "deg",
   string: ""
 };
 
--- a/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_block_mixedcontent_securityerrors.js
+++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_block_mixedcontent_securityerrors.js
@@ -77,14 +77,12 @@ add_task(async function() {
   url = await simulateLinkClick(learnMoreLink);
   is(url, LEARN_MORE_URI, `Clicking the provided link opens ${url}`);
 });
 
 function pushPrefEnv() {
   const prefs = [
     ["security.mixed_content.block_active_content", true],
     ["security.mixed_content.block_display_content", true],
-    ["security.mixed_content.use_hsts", false],
-    ["security.mixed_content.send_hsts_priming", false],
   ];
 
   return Promise.all(prefs.map(([pref, value]) => pushPref(pref, value)));
 }
--- a/devtools/client/webconsole/test/browser_webconsole_block_mixedcontent_securityerrors.js
+++ b/devtools/client/webconsole/test/browser_webconsole_block_mixedcontent_securityerrors.js
@@ -56,18 +56,16 @@ add_task(function* () {
 });
 
 function pushPrefEnv() {
   let deferred = defer();
   let options = {
     "set": [
       ["security.mixed_content.block_active_content", true],
       ["security.mixed_content.block_display_content", true],
-      ["security.mixed_content.use_hsts", false],
-      ["security.mixed_content.send_hsts_priming", false],
     ]
   };
   SpecialPowers.pushPrefEnv(options, deferred.resolve);
   return deferred.promise;
 }
 
 function mixedContentOverrideTest2(hud, browser) {
   let deferred = defer();
--- a/devtools/client/webconsole/test/browser_webconsole_bug_632817.js
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_632817.js
@@ -90,20 +90,16 @@ function testXhrGet() {
 function testXhrWarn() {
   // Start the XMLHttpRequest() warn test.
   ContentTask.spawn(gBrowser.selectedBrowser, {}, function*() {
     content.wrappedJSObject.testXhrWarn();
   });
 
   let lastRequest = yield waitForFinishedRequest(XHR_WARN_REQUEST_PREDICATE);
   if (lastRequest.request.method == "HEAD") {
-    // in non-e10s, we get the HEAD request that priming sends, so make sure
-    // a priming request should be sent, and then get the actual request
-    is(Services.prefs.getBoolPref("security.mixed_content.send_hsts_priming"),
-        true, "Found HSTS Priming Request");
     lastRequest = yield waitForFinishedRequest(XHR_WARN_REQUEST_PREDICATE);
   }
 
   ok(lastRequest, "testXhrWarn() was logged");
   is(lastRequest.request.method, "GET", "Method is correct");
   ok(lastRequest.isXHR, "It's an XHR request");
   is(lastRequest.securityInfo, "insecure", "It's an insecure request");
 }
--- a/devtools/server/actors/css-properties.js
+++ b/devtools/server/actors/css-properties.js
@@ -1,78 +1,77 @@
 /* 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/. */
 
 "use strict";
 
-loader.lazyServiceGetter(this, "DOMUtils",
-  "@mozilla.org/inspector/dom-utils;1", "inIDOMUtils");
 loader.lazyRequireGetter(this, "CSS_TYPES",
   "devtools/shared/css/properties-db", true);
 
 const protocol = require("devtools/shared/protocol");
 const { ActorClassWithSpec, Actor } = protocol;
 const { cssPropertiesSpec } = require("devtools/shared/specs/css-properties");
 const { cssColors } = require("devtools/shared/css/color-db");
+const InspectorUtils = require("InspectorUtils");
 
 exports.CssPropertiesActor = ActorClassWithSpec(cssPropertiesSpec, {
   typeName: "cssProperties",
 
   initialize(conn) {
     Actor.prototype.initialize.call(this, conn);
   },
 
   destroy() {
     Actor.prototype.destroy.call(this);
   },
 
   getCSSDatabase() {
     const properties = generateCssProperties();
-    const pseudoElements = DOMUtils.getCSSPseudoElementNames();
+    const pseudoElements = InspectorUtils.getCSSPseudoElementNames();
     const supportedFeature = {
       // checking for css-color-4 color function support.
-      "css-color-4-color-function": DOMUtils.isValidCSSColor("rgb(1 1 1 / 100%)"),
+      "css-color-4-color-function": InspectorUtils.isValidCSSColor("rgb(1 1 1 / 100%)"),
     };
 
     return { properties, pseudoElements, supportedFeature };
   }
 });
 
 /**
  * Generate the CSS properties object. Every key is the property name, while
  * the values are objects that contain information about that property.
  *
  * @return {Object}
  */
 function generateCssProperties() {
   const properties = {};
-  const propertyNames = DOMUtils.getCSSPropertyNames(DOMUtils.INCLUDE_ALIASES);
+  const propertyNames = InspectorUtils.getCSSPropertyNames({ includeAliases: true });
   const colors = Object.keys(cssColors);
 
   propertyNames.forEach(name => {
     // Get the list of CSS types this property supports.
     let supports = [];
     for (let type in CSS_TYPES) {
-      if (safeCssPropertySupportsType(name, DOMUtils["TYPE_" + type])) {
+      if (safeCssPropertySupportsType(name, InspectorUtils["TYPE_" + type])) {
         supports.push(CSS_TYPES[type]);
       }
     }
 
     // Don't send colors over RDP, these will be re-attached by the front.
-    let values = DOMUtils.getCSSValuesForProperty(name);
+    let values = InspectorUtils.getCSSValuesForProperty(name);
     if (values.includes("aliceblue")) {
       values = values.filter(x => !colors.includes(x));
       values.unshift("COLOR");
     }
 
-    let subproperties = DOMUtils.getSubpropertiesForCSSProperty(name);
+    let subproperties = InspectorUtils.getSubpropertiesForCSSProperty(name);
 
     properties[name] = {
-      isInherited: DOMUtils.isInheritedProperty(name),
+      isInherited: InspectorUtils.isInheritedProperty(name),
       values,
       supports,
       subproperties,
     };
   });
 
   return properties;
 }
@@ -84,33 +83,33 @@ exports.generateCssProperties = generate
  * @param {string} name
  * @return {Boolean}
  */
 function isCssPropertyKnown(name) {
   try {
     // If the property name is unknown, the cssPropertyIsShorthand
     // will throw an exception.  But if it is known, no exception will
     // be thrown; so we just ignore the return value.
-    DOMUtils.cssPropertyIsShorthand(name);
+    InspectorUtils.cssPropertyIsShorthand(name);
     return true;
   } catch (e) {
     return false;
   }
 }
 
 exports.isCssPropertyKnown = isCssPropertyKnown;
 
 /**
- * A wrapper for DOMUtils.cssPropertySupportsType that ignores invalid
+ * A wrapper for InspectorUtils.cssPropertySupportsType that ignores invalid
  * properties.
  *
  * @param {String} name The property name.
  * @param {number} type The type tested for support.
  * @return {Boolean} Whether the property supports the type.
  *        If the property is unknown, false is returned.
  */
 function safeCssPropertySupportsType(name, type) {
   try {
-    return DOMUtils.cssPropertySupportsType(name, type);
+    return InspectorUtils.cssPropertySupportsType(name, type);
   } catch (e) {
     return false;
   }
 }
--- a/devtools/server/actors/csscoverage.js
+++ b/devtools/server/actors/csscoverage.js
@@ -1,25 +1,23 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-const { Cc, Ci } = require("chrome");
+const { Ci } = require("chrome");
 
+const InspectorUtils = require("InspectorUtils");
 const Services = require("Services");
 const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");
 
 const protocol = require("devtools/shared/protocol");
 const { cssUsageSpec } = require("devtools/shared/specs/csscoverage");
 
-loader.lazyGetter(this, "DOMUtils", () => {
-  return Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
-});
 loader.lazyRequireGetter(this, "stylesheets", "devtools/server/actors/stylesheets");
 loader.lazyRequireGetter(this, "prettifyCSS", "devtools/shared/inspector/css-logic", true);
 
 const CSSRule = Ci.nsIDOMCSSRule;
 
 const MAX_UNUSED_RULES = 10000;
 
 /**
@@ -536,18 +534,18 @@ function getImportedSheets(stylesheet) {
 }
 
 /**
  * Get a unique identifier for a rule. This is currently the string
  * <CSS-URL>|<START-LINE>|<START-COLUMN>
  * @see deconstructRuleId(ruleId)
  */
 function ruleToId(rule) {
-  let line = DOMUtils.getRelativeRuleLine(rule);
-  let column = DOMUtils.getRuleColumn(rule);
+  let line = InspectorUtils.getRelativeRuleLine(rule);
+  let column = InspectorUtils.getRuleColumn(rule);
   return sheetToUrl(rule.parentStyleSheet) + "|" + line + "|" + column;
 }
 
 /**
  * Convert a ruleId to an object with { url, line, column } properties
  * @see ruleToId(rule)
  */
 const deconstructRuleId = exports.deconstructRuleId = function (ruleId) {
--- a/devtools/server/actors/highlighters/box-model.js
+++ b/devtools/server/actors/highlighters/box-model.js
@@ -114,71 +114,77 @@ class BoxModelHighlighter extends AutoRe
     let { pageListenerTarget } = highlighterEnv;
     pageListenerTarget.addEventListener("pagehide", this.onPageHide);
   }
 
   _buildMarkup() {
     let doc = this.win.document;
 
     let highlighterContainer = doc.createElement("div");
+    highlighterContainer.setAttribute("role", "presentation");
     highlighterContainer.className = "highlighter-container box-model";
 
     // Build the root wrapper, used to adapt to the page zoom.
     let rootWrapper = createNode(this.win, {
       parent: highlighterContainer,
       attributes: {
         "id": "root",
-        "class": "root"
+        "class": "root",
+        "role": "presentation"
       },
       prefix: this.ID_CLASS_PREFIX
     });
 
     // Building the SVG element with its polygons and lines
 
     let svg = createSVGNode(this.win, {
       nodeType: "svg",
       parent: rootWrapper,
       attributes: {
         "id": "elements",
         "width": "100%",
         "height": "100%",
-        "hidden": "true"
+        "hidden": "true",
+        "role": "presentation"
       },
       prefix: this.ID_CLASS_PREFIX
     });
 
     let regions = createSVGNode(this.win, {
       nodeType: "g",
       parent: svg,
       attributes: {
-        "class": "regions"
+        "class": "regions",
+        "role": "presentation"
       },
       prefix: this.ID_CLASS_PREFIX
     });
 
     for (let region of BOX_MODEL_REGIONS) {
       createSVGNode(this.win, {
         nodeType: "path",
         parent: regions,
         attributes: {
           "class": region,
-          "id": region
+          "id": region,
+          "role": "presentation"
         },
         prefix: this.ID_CLASS_PREFIX
       });
     }
 
     for (let side of BOX_MODEL_SIDES) {
       createSVGNode(this.win, {
         nodeType: "line",
         parent: svg,
         attributes: {
           "class": "guide-" + side,
           "id": "guide-" + side,
-          "stroke-width": GUIDE_STROKE_WIDTH
+          "stroke-width": GUIDE_STROKE_WIDTH,
+          "role": "presentation"
         },
         prefix: this.ID_CLASS_PREFIX
       });
     }
 
     // Building the nodeinfo bar markup
 
     let infobarContainer = createNode(this.win, {
--- a/devtools/server/actors/highlighters/geometry-editor.js
+++ b/devtools/server/actors/highlighters/geometry-editor.js
@@ -126,18 +126,18 @@ function getOffsetParent(node) {
 function getDefinedGeometryProperties(node) {
   let props = new Map();
   if (!node) {
     return props;
   }
 
   // Get the list of css rules applying to the current node.
   let cssRules = getCSSStyleRules(node);
-  for (let i = 0; i < cssRules.Count(); i++) {
-    let rule = cssRules.GetElementAt(i);
+  for (let i = 0; i < cssRules.length; i++) {
+    let rule = cssRules[i];
     for (let name of GeoProp.allProps()) {
       let value = rule.style.getPropertyValue(name);
       if (value && value !== "auto") {
         // getCSSStyleRules returns rules ordered from least to most specific
         // so just override any previous properties we have set.
         props.set(name, {
           cssRule: rule
         });
--- a/devtools/server/actors/highlighters/shapes.js
+++ b/devtools/server/actors/highlighters/shapes.js
@@ -2512,18 +2512,18 @@ class ShapesHighlighter extends AutoRefr
  */
 function getDefinedShapeProperties(node, property) {
   let prop = "";
   if (!node) {
     return prop;
   }
 
   let cssRules = getCSSStyleRules(node);
-  for (let i = 0; i < cssRules.Count(); i++) {
-    let rule = cssRules.GetElementAt(i);
+  for (let i = 0; i < cssRules.length; i++) {
+    let rule = cssRules[i];
     let value = rule.style.getPropertyValue(property);
     if (value && value !== "auto") {
       prop = value;
     }
   }
 
   if (node.style) {
     let value = node.style.getPropertyValue(property);
--- a/devtools/server/actors/highlighters/utils/markup.js
+++ b/devtools/server/actors/highlighters/utils/markup.js
@@ -1,39 +1,38 @@
 /* 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/. */
 
 "use strict";
 
-const { Cc, Ci, Cu, Cr } = require("chrome");
+const { Ci, Cu, Cr } = require("chrome");
 const { getCurrentZoom, getWindowDimensions, getViewportDimensions,
   getRootBindingParent, loadSheet } = require("devtools/shared/layout/utils");
 const EventEmitter = require("devtools/shared/event-emitter");
+const InspectorUtils = require("InspectorUtils");
 
 const lazyContainer = {};
 
 loader.lazyRequireGetter(lazyContainer, "CssLogic",
   "devtools/server/css-logic", true);
 exports.getComputedStyle = (node) =>
   lazyContainer.CssLogic.getComputedStyle(node);
 
 exports.getBindingElementAndPseudo = (node) =>
   lazyContainer.CssLogic.getBindingElementAndPseudo(node);
 
-loader.lazyGetter(lazyContainer, "DOMUtils", () =>
-  Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils));
 exports.hasPseudoClassLock = (...args) =>
-  lazyContainer.DOMUtils.hasPseudoClassLock(...args);
+  InspectorUtils.hasPseudoClassLock(...args);
 
 exports.addPseudoClassLock = (...args) =>
-  lazyContainer.DOMUtils.addPseudoClassLock(...args);
+  InspectorUtils.addPseudoClassLock(...args);
 
 exports.removePseudoClassLock = (...args) =>
-  lazyContainer.DOMUtils.removePseudoClassLock(...args);
+  InspectorUtils.removePseudoClassLock(...args);
 
 const SVG_NS = "http://www.w3.org/2000/svg";
 const XHTML_NS = "http://www.w3.org/1999/xhtml";
 const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 const STYLESHEET_URI = "resource://devtools/server/actors/" +
                        "highlighters.css";
 
 const _tokens = Symbol("classList/tokens");
--- a/devtools/server/actors/inspector.js
+++ b/devtools/server/actors/inspector.js
@@ -53,16 +53,17 @@
 const {Cc, Ci, Cu} = require("chrome");
 const Services = require("Services");
 const protocol = require("devtools/shared/protocol");
 const {LongStringActor} = require("devtools/server/actors/string");
 const promise = require("promise");
 const defer = require("devtools/shared/defer");
 const {Task} = require("devtools/shared/task");
 const EventEmitter = require("devtools/shared/event-emitter");
+const InspectorUtils = require("InspectorUtils");
 
 const {walkerSpec, inspectorSpec} = require("devtools/shared/specs/inspector");
 const {nodeSpec, nodeListSpec} = require("devtools/shared/specs/node");
 
 loader.lazyRequireGetter(this, "DevToolsUtils", "devtools/shared/DevToolsUtils");
 loader.lazyRequireGetter(this, "AsyncUtils", "devtools/shared/async-utils");
 loader.lazyRequireGetter(this, "CssLogic", "devtools/server/css-logic", true);
 loader.lazyRequireGetter(this, "findCssSelector", "devtools/shared/inspector/css-logic", true);
@@ -422,17 +423,17 @@ var NodeActor = exports.NodeActor = prot
   },
 
   writePseudoClassLocks: function () {
     if (this.rawNode.nodeType !== Ci.nsIDOMNode.ELEMENT_NODE) {
       return undefined;
     }
     let ret = undefined;
     for (let pseudo of PSEUDO_CLASSES) {
-      if (DOMUtils.hasPseudoClassLock(this.rawNode, pseudo)) {
+      if (InspectorUtils.hasPseudoClassLock(this.rawNode, pseudo)) {
         ret = ret || [];
         ret.push(pseudo);
       }
     }
     return ret;
   },
 
   /**
@@ -1838,17 +1839,17 @@ var WalkerActor = protocol.ActorClassWit
   addPseudoClassLock: function (node, pseudo, options = {}) {
     if (isNodeDead(node)) {
       return;
     }
 
     // There can be only one node locked per pseudo, so dismiss all existing
     // ones
     for (let locked of this._activePseudoClassLocks) {
-      if (DOMUtils.hasPseudoClassLock(locked.rawNode, pseudo)) {
+      if (InspectorUtils.hasPseudoClassLock(locked.rawNode, pseudo)) {
         this._removePseudoClassLock(locked, pseudo);
       }
     }
 
     let enabled = options.enabled === undefined ||
                   options.enabled;
     this._addPseudoClassLock(node, pseudo, enabled);
 
@@ -1871,17 +1872,17 @@ var WalkerActor = protocol.ActorClassWit
       pseudoClassLocks: node.writePseudoClassLocks()
     });
   },
 
   _addPseudoClassLock: function (node, pseudo, enabled) {
     if (node.rawNode.nodeType !== Ci.nsIDOMNode.ELEMENT_NODE) {
       return false;
     }
-    DOMUtils.addPseudoClassLock(node.rawNode, pseudo, enabled);
+    InspectorUtils.addPseudoClassLock(node.rawNode, pseudo, enabled);
     this._activePseudoClassLocks.add(node);
     this._queuePseudoClassMutation(node);
     return true;
   },
 
   hideNode: function (node) {
     if (isNodeDead(node)) {
       return;
@@ -1919,17 +1920,17 @@ var WalkerActor = protocol.ActorClassWit
     }
 
     this._removePseudoClassLock(node, pseudo);
 
     // Remove pseudo class for children as we don't want to allow
     // turning it on for some childs without setting it on some parents
     for (let locked of this._activePseudoClassLocks) {
       if (node.rawNode.contains(locked.rawNode) &&
-          DOMUtils.hasPseudoClassLock(locked.rawNode, pseudo)) {
+          InspectorUtils.hasPseudoClassLock(locked.rawNode, pseudo)) {
         this._removePseudoClassLock(locked, pseudo);
       }
     }
 
     if (!options.parents) {
       return;
     }
 
@@ -1940,17 +1941,17 @@ var WalkerActor = protocol.ActorClassWit
       this._removePseudoClassLock(curNode, pseudo);
     }
   },
 
   _removePseudoClassLock: function (node, pseudo) {
     if (node.rawNode.nodeType != Ci.nsIDOMNode.ELEMENT_NODE) {
       return false;
     }
-    DOMUtils.removePseudoClassLock(node.rawNode, pseudo);
+    InspectorUtils.removePseudoClassLock(node.rawNode, pseudo);
     if (!node.writePseudoClassLocks()) {
       this._activePseudoClassLocks.delete(node);
     }
 
     this._queuePseudoClassMutation(node);
     return true;
   },
 
@@ -1959,22 +1960,22 @@ var WalkerActor = protocol.ActorClassWit
    * @param {NodeActor} node Optional node to clear pseudo-classes on
    */
   clearPseudoClassLocks: function (node) {
     if (node && isNodeDead(node)) {
       return;
     }
 
     if (node) {
-      DOMUtils.clearPseudoClassLocks(node.rawNode);
+      InspectorUtils.clearPseudoClassLocks(node.rawNode);
       this._activePseudoClassLocks.delete(node);
       this._queuePseudoClassMutation(node);
     } else {
       for (let locked of this._activePseudoClassLocks) {
-        DOMUtils.clearPseudoClassLocks(locked.rawNode);
+        InspectorUtils.clearPseudoClassLocks(locked.rawNode);
         this._activePseudoClassLocks.delete(locked);
         this._queuePseudoClassMutation(locked);
       }
     }
   },
 
   /**
    * Get a node's innerHTML property.
@@ -3396,12 +3397,8 @@ var imageToImageData = Task.async(functi
     data: imageData,
     size: {
       naturalWidth: imgWidth,
       naturalHeight: imgHeight,
       resized: resizeRatio !== 1
     }
   };
 });
-
-loader.lazyGetter(this, "DOMUtils", function () {
-  return Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
-});
--- a/devtools/server/actors/object.js
+++ b/devtools/server/actors/object.js
@@ -1765,17 +1765,16 @@ DebuggerServer.ObjectActorPreviewers.Obj
   function ArrayLike({obj, hooks}, grip, rawObj) {
     if (isWorker || !rawObj ||
         obj.class != "DOMStringList" &&
         obj.class != "DOMTokenList" &&
         !(rawObj instanceof Ci.nsIDOMMozNamedAttrMap ||
           rawObj instanceof Ci.nsIDOMCSSRuleList ||
           rawObj instanceof Ci.nsIDOMCSSValueList ||
           rawObj instanceof Ci.nsIDOMFileList ||
-          rawObj instanceof Ci.nsIDOMFontFaceList ||
           rawObj instanceof Ci.nsIDOMMediaList ||
           rawObj instanceof Ci.nsIDOMNodeList ||
           rawObj instanceof Ci.nsIDOMStyleSheetList)) {
       return false;
     }
 
     if (typeof rawObj.length != "number") {
       return false;
--- a/devtools/server/actors/styles.js
+++ b/devtools/server/actors/styles.js
@@ -4,16 +4,17 @@
 
 "use strict";
 
 const {Ci} = require("chrome");
 const promise = require("promise");
 const protocol = require("devtools/shared/protocol");
 const {LongStringActor} = require("devtools/server/actors/string");
 const {Task} = require("devtools/shared/task");
+const InspectorUtils = require("InspectorUtils");
 
 // This will also add the "stylesheet" actor type for protocol.js to recognize
 
 const {pageStyleSpec, styleRuleSpec, ELEMENT_STYLE} = require("devtools/shared/specs/styles");
 
 loader.lazyRequireGetter(this, "CssLogic", "devtools/server/css-logic", true);
 loader.lazyRequireGetter(this, "SharedCssLogic", "devtools/shared/inspector/css-logic");
 loader.lazyRequireGetter(this, "getDefinedGeometryProperties",
@@ -22,20 +23,18 @@ loader.lazyRequireGetter(this, "isCssPro
   "devtools/server/actors/css-properties", true);
 loader.lazyRequireGetter(this, "parseNamedDeclarations",
   "devtools/shared/css/parsing-utils", true);
 loader.lazyRequireGetter(this, "UPDATE_PRESERVING_RULES",
   "devtools/server/actors/stylesheets", true);
 loader.lazyRequireGetter(this, "UPDATE_GENERAL",
   "devtools/server/actors/stylesheets", true);
 
-loader.lazyServiceGetter(this, "DOMUtils", "@mozilla.org/inspector/dom-utils;1", "inIDOMUtils");
-
 loader.lazyGetter(this, "PSEUDO_ELEMENTS", () => {
-  return DOMUtils.getCSSPseudoElementNames();
+  return InspectorUtils.getCSSPseudoElementNames();
 });
 
 const XHTML_NS = "http://www.w3.org/1999/xhtml";
 const FONT_PREVIEW_TEXT = "Abc";
 const FONT_PREVIEW_FONT_SIZE = 40;
 const FONT_PREVIEW_FILLSTYLE = "black";
 const NORMAL_FONT_WEIGHT = 400;
 const BOLD_FONT_WEIGHT = 700;
@@ -58,17 +57,17 @@ var PageStyleActor = protocol.ActorClass
   initialize: function (inspector) {
     protocol.Actor.prototype.initialize.call(this, null);
     this.inspector = inspector;
     if (!this.inspector.walker) {
       throw Error("The inspector's WalkerActor must be created before " +
                    "creating a PageStyleActor.");
     }
     this.walker = inspector.walker;
-    this.cssLogic = new CssLogic(DOMUtils.isInheritedProperty);
+    this.cssLogic = new CssLogic(InspectorUtils.isInheritedProperty);
 
     // Stores the association of DOM objects -> actors
     this.refMap = new Map();
 
     // Maps document elements to style elements, used to add new rules.
     this.styleElements = new WeakMap();
 
     this.onFrameUnload = this.onFrameUnload.bind(this);
@@ -266,21 +265,21 @@ var PageStyleActor = protocol.ActorClass
    */
   getUsedFontFaces: function (node, options) {
     // node.rawNode is defined for NodeActor objects
     let actualNode = node.rawNode || node;
     let contentDocument = actualNode.ownerDocument;
     // We don't get fonts for a node, but for a range
     let rng = contentDocument.createRange();
     rng.selectNodeContents(actualNode);
-    let fonts = DOMUtils.getUsedFontFaces(rng);
+    let fonts = InspectorUtils.getUsedFontFaces(rng);
     let fontsArray = [];
 
     for (let i = 0; i < fonts.length; i++) {
-      let font = fonts.item(i);
+      let font = fonts[i];
       let fontFace = {
         name: font.name,
         CSSFamilyName: font.CSSFamilyName,
         srcIndex: font.srcIndex,
         URI: font.URI,
         format: font.format,
         localName: font.localName,
         metadata: font.metadata
@@ -461,17 +460,17 @@ var PageStyleActor = protocol.ActorClass
       // See the comment in |form| to understand this.
       yield rule.getAuthoredCssText();
     }
     return result;
   }),
 
   _hasInheritedProps: function (style) {
     return Array.prototype.some.call(style, prop => {
-      return DOMUtils.isInheritedProperty(prop);
+      return InspectorUtils.isInheritedProperty(prop);
     });
   },
 
   isPositionEditable: Task.async(function* (node) {
     if (!node || node.rawNode.nodeType !== node.rawNode.ELEMENT_NODE) {
       return false;
     }
 
@@ -563,39 +562,39 @@ var PageStyleActor = protocol.ActorClass
    * @param DOMNode node
    * @param string pseudo
    * @param DOMNode inherited
    * @param object options
    *
    * @returns Array
    */
   _getElementRules: function (node, pseudo, inherited, options) {
-    let domRules = DOMUtils.getCSSStyleRules(node, pseudo);
+    let domRules = InspectorUtils.getCSSStyleRules(node, pseudo);
     if (!domRules) {
       return [];
     }
 
     let rules = [];
 
     // getCSSStyleRules returns ordered from least-specific to
     // most-specific.
-    for (let i = domRules.Count() - 1; i >= 0; i--) {
-      let domRule = domRules.GetElementAt(i);
+    for (let i = domRules.length - 1; i >= 0; i--) {
+      let domRule = domRules[i];
 
       let isSystem = !SharedCssLogic.isContentStylesheet(domRule.parentStyleSheet);
 
       if (isSystem && options.filter != SharedCssLogic.FILTER.UA) {
         continue;
       }
 
       if (inherited) {
         // Don't include inherited rules if none of its properties
         // are inheritable.
         let hasInherited = [...domRule.style].some(
-          prop => DOMUtils.isInheritedProperty(prop)
+          prop => InspectorUtils.isInheritedProperty(prop)
         );
         if (!hasInherited) {
           continue;
         }
       }
 
       let ruleActor = this._styleRef(domRule);
       rules.push({
@@ -673,18 +672,18 @@ var PageStyleActor = protocol.ActorClass
         let domRule = entry.rule.rawRule;
         let selectors = CssLogic.getSelectors(domRule);
         let element = entry.inherited ? entry.inherited.rawNode : node.rawNode;
 
         let {bindingElement, pseudo} =
             CssLogic.getBindingElementAndPseudo(element);
         entry.matchedSelectors = [];
         for (let i = 0; i < selectors.length; i++) {
-          if (DOMUtils.selectorMatchesElement(bindingElement, domRule, i,
-                                              pseudo)) {
+          if (InspectorUtils.selectorMatchesElement(bindingElement, domRule, i,
+                                                    pseudo)) {
             entry.matchedSelectors.push(selectors[i]);
           }
         }
       }
     }
 
     // Add all the keyframes rule associated with the element
     let computedStyle = this.cssLogic.computedStyle;
@@ -949,18 +948,18 @@ var StyleRuleActor = protocol.ActorClass
     this._onStyleApplied = this._onStyleApplied.bind(this);
 
     if (item instanceof (Ci.nsIDOMCSSRule)) {
       this.type = item.type;
       this.rawRule = item;
       if ((this.type === Ci.nsIDOMCSSRule.STYLE_RULE ||
            this.type === Ci.nsIDOMCSSRule.KEYFRAME_RULE) &&
           this.rawRule.parentStyleSheet) {
-        this.line = DOMUtils.getRelativeRuleLine(this.rawRule);
-        this.column = DOMUtils.getRuleColumn(this.rawRule);
+        this.line = InspectorUtils.getRelativeRuleLine(this.rawRule);
+        this.column = InspectorUtils.getRuleColumn(this.rawRule);
         this._parentSheet = this.rawRule.parentStyleSheet;
         this._computeRuleIndex();
         this.sheetActor = this.pageStyle._sheetRef(this._parentSheet);
         this.sheetActor.on("style-applied", this._onStyleApplied);
       }
     } else {
       // Fake a rule
       this.type = ELEMENT_STYLE;
@@ -1211,18 +1210,18 @@ var StyleRuleActor = protocol.ActorClass
       // The sheet was updated by this actor, in a way that preserves
       // the rules.  Now, recompute our new rule from the style sheet,
       // so that we aren't left with a reference to a dangling rule.
       let oldRule = this.rawRule;
       this.rawRule = this._getRuleFromIndex(this._parentSheet);
       // Also tell the page style so that future calls to _styleRef
       // return the same StyleRuleActor.
       this.pageStyle.updateStyleRef(oldRule, this.rawRule, this);
-      let line = DOMUtils.getRelativeRuleLine(this.rawRule);
-      let column = DOMUtils.getRuleColumn(this.rawRule);
+      let line = InspectorUtils.getRelativeRuleLine(this.rawRule);
+      let column = InspectorUtils.getRuleColumn(this.rawRule);
       if (line !== this.line || column !== this.column) {
         this._notifyLocationChanged(line, column);
       }
       this.line = line;
       this.column = column;
     }
   },
 
@@ -1578,17 +1577,17 @@ exports.getFontPreviewData = getFontPrev
  */
 function getRuleText(initialText, line, column) {
   if (typeof line === "undefined" || typeof column === "undefined") {
     throw new Error("Location information is missing");
   }
 
   let {offset: textOffset, text} =
       getTextAtLineColumn(initialText, line, column);
-  let lexer = DOMUtils.getCSSLexer(text);
+  let lexer = InspectorUtils.getCSSLexer(text);
 
   // Search forward for the opening brace.
   while (true) {
     let token = lexer.nextToken();
     if (!token) {
       throw new Error("couldn't find start of the rule");
     }
     if (token.tokenType === "symbol" && token.text === "{") {
@@ -1652,17 +1651,17 @@ exports.getRuleText = getRuleText;
  */
 function getSelectorOffsets(initialText, line, column) {
   if (typeof line === "undefined" || typeof column === "undefined") {
     throw new Error("Location information is missing");
   }
 
   let {offset: textOffset, text} =
       getTextAtLineColumn(initialText, line, column);
-  let lexer = DOMUtils.getCSSLexer(text);
+  let lexer = InspectorUtils.getCSSLexer(text);
 
   // Search forward for the opening brace.
   let endOffset;
   while (true) {
     let token = lexer.nextToken();
     if (!token) {
       break;
     }
--- a/devtools/server/actors/stylesheets.js
+++ b/devtools/server/actors/stylesheets.js
@@ -11,26 +11,25 @@ const defer = require("devtools/shared/d
 const {Task} = require("devtools/shared/task");
 const protocol = require("devtools/shared/protocol");
 const {LongStringActor} = require("devtools/server/actors/string");
 const {fetch} = require("devtools/shared/DevToolsUtils");
 const {mediaRuleSpec, styleSheetSpec,
        styleSheetsSpec} = require("devtools/shared/specs/stylesheets");
 const {
   addPseudoClassLock, removePseudoClassLock } = require("devtools/server/actors/highlighters/utils/markup");
+const InspectorUtils = require("InspectorUtils");
 
 loader.lazyRequireGetter(this, "CssLogic", "devtools/shared/inspector/css-logic");
 loader.lazyRequireGetter(this, "addPseudoClassLock",
   "devtools/server/actors/highlighters/utils/markup", true);
 loader.lazyRequireGetter(this, "removePseudoClassLock",
   "devtools/server/actors/highlighters/utils/markup", true);
 loader.lazyRequireGetter(this, "loadSheet", "devtools/shared/layout/utils", true);
 
-loader.lazyServiceGetter(this, "DOMUtils", "@mozilla.org/inspector/dom-utils;1", "inIDOMUtils");
-
 var TRANSITION_PSEUDO_CLASS = ":-moz-styleeditor-transitioning";
 var TRANSITION_DURATION_MS = 500;
 var TRANSITION_BUFFER_MS = 1000;
 var TRANSITION_RULE_SELECTOR =
 `:root${TRANSITION_PSEUDO_CLASS}, :root${TRANSITION_PSEUDO_CLASS} *`;
 
 var TRANSITION_SHEET = "data:text/css;charset=utf-8," + encodeURIComponent(`
   ${TRANSITION_RULE_SELECTOR} {
@@ -78,18 +77,18 @@ var MediaRuleActor = protocol.ActorClass
     protocol.Actor.prototype.initialize.call(this, null);
 
     this.rawRule = mediaRule;
     this.parentActor = parentActor;
     this.conn = this.parentActor.conn;
 
     this._matchesChange = this._matchesChange.bind(this);
 
-    this.line = DOMUtils.getRuleLine(mediaRule);
-    this.column = DOMUtils.getRuleColumn(mediaRule);
+    this.line = InspectorUtils.getRuleLine(mediaRule);
+    this.column = InspectorUtils.getRuleColumn(mediaRule);
 
     try {
       this.mql = this.window.matchMedia(mediaRule.media.mediaText);
     } catch (e) {
       // Ignored
     }
 
     if (this.mql) {
@@ -248,17 +247,17 @@ var StyleSheetActor = protocol.ActorClas
       rules = this.rawSheet.cssRules;
     } catch (e) {
       // sheet isn't loaded yet
       return true;
     }
 
     for (let i = 0; i < rules.length; i++) {
       let rule = rules[i];
-      if (DOMUtils.getRelativeRuleLine(rule) === 0) {
+      if (InspectorUtils.getRelativeRuleLine(rule) === 0) {
         return false;
       }
     }
 
     return true;
   },
 
   /**
@@ -589,17 +588,17 @@ var StyleSheetActor = protocol.ActorClas
    * Update the style sheet in place with new text.
    *
    * @param  {object} request
    *         'text' - new text
    *         'transition' - whether to do CSS transition for change.
    *         'kind' - either UPDATE_PRESERVING_RULES or UPDATE_GENERAL
    */
   update: function (text, transition, kind = UPDATE_GENERAL) {
-    DOMUtils.parseStyleSheet(this.rawSheet, text);
+    InspectorUtils.parseStyleSheet(this.rawSheet, text);
 
     modifiedStyleSheets.set(this.rawSheet, text);
 
     this.text = text;
 
     this._notifyPropertyChanged("ruleCount");
 
     if (transition) {
@@ -800,17 +799,18 @@ var StyleSheetsActor = protocol.ActorCla
   _addStyleSheets: function (win) {
     return Task.spawn(function* () {
       let doc = win.document;
       // We have to set this flag in order to get the
       // StyleSheetApplicableStateChanged events.  See Document.webidl.
       doc.styleSheetChangeEventsEnabled = true;
 
       let isChrome = Services.scriptSecurityManager.isSystemPrincipal(doc.nodePrincipal);
-      let styleSheets = isChrome ? DOMUtils.getAllStyleSheets(doc) : doc.styleSheets;
+      let styleSheets =
+        isChrome ? InspectorUtils.getAllStyleSheets(doc) : doc.styleSheets;
       let actors = [];
       for (let i = 0; i < styleSheets.length; i++) {
         let sheet = styleSheets[i];
         if (!this._shouldListSheet(sheet)) {
           continue;
         }
 
         let actor = this.parentActor.createStyleSheetActor(sheet);
--- a/devtools/server/css-logic.js
+++ b/devtools/server/css-logic.js
@@ -24,28 +24,28 @@
  * - CssPropertyInfo contains style information for a single property for the
  *   highlighted element.
  * - CssSelectorInfo is a wrapper around CssSelector, which adds sorting with
  *   reference to the selected element.
  */
 
 "use strict";
 
-const { Cc, Ci, Cu } = require("chrome");
-const DevToolsUtils = require("devtools/shared/DevToolsUtils");
+const { Cu } = require("chrome");
 const nodeConstants = require("devtools/shared/dom-node-constants");
 const {
   getBindingElementAndPseudo,
   getCSSStyleRules,
   l10n,
   isContentStylesheet,
   shortSource,
   FILTER,
   STATUS
 } = require("devtools/shared/inspector/css-logic");
+const InspectorUtils = require("InspectorUtils");
 
 /**
  * @param {function} isInherited A function that determines if the CSS property
  *                   is inherited.
  */
 function CssLogic(isInherited) {
   // The cache of examined CSS properties.
   this._isInherited = isInherited;
@@ -469,17 +469,17 @@ CssLogic.prototype = {
    *        The index of the selector within the DOMRule.
    * @return {boolean}
    *         true if the given selector matches the highlighted element or any
    *         of its parents, otherwise false is returned.
    */
   selectorMatchesElement: function (domRule, idx) {
     let element = this.viewedElement;
     do {
-      if (domUtils.selectorMatchesElement(element, domRule, idx)) {
+      if (InspectorUtils.selectorMatchesElement(element, domRule, idx)) {
         return true;
       }
     } while ((element = element.parentNode) &&
              element.nodeType === nodeConstants.ELEMENT_NODE);
 
     return false;
   },
 
@@ -547,19 +547,19 @@ CssLogic.prototype = {
       try {
         domRules = getCSSStyleRules(element);
       } catch (ex) {
         console.log("CL__buildMatchedRules error: " + ex);
         continue;
       }
 
       // getCSSStyleRules can return null with a shadow DOM element.
-      let numDomRules = domRules ? domRules.Count() : 0;
+      let numDomRules = domRules ? domRules.length : 0;
       for (let i = 0; i < numDomRules; i++) {
-        let domRule = domRules.GetElementAt(i);
+        let domRule = domRules[i];
         if (domRule.type !== CSSRule.STYLE_RULE) {
           continue;
         }
 
         let sheet = this.getSheet(domRule.parentStyleSheet, -1);
         if (sheet._passId !== this._passId) {
           sheet.index = sheetIndex++;
           sheet._passId = this._passId;
@@ -635,19 +635,19 @@ CssLogic.getShortName = function (elemen
  * @param {DOMRule} domRule
  *        The DOMRule to parse.
  * @return {Array}
  *         An array of string selectors.
  */
 CssLogic.getSelectors = function (domRule) {
   let selectors = [];
 
-  let len = domUtils.getSelectorCount(domRule);
+  let len = InspectorUtils.getSelectorCount(domRule);
   for (let i = 0; i < len; i++) {
-    let text = domUtils.getSelectorText(domRule, i);
+    let text = InspectorUtils.getSelectorText(domRule, i);
     selectors.push(text);
   }
   return selectors;
 };
 
 /**
  * Given a node, check to see if it is a ::before or ::after element.
  * If so, return the node that is accessible from within the document
@@ -899,18 +899,18 @@ function CssRule(cssSheet, domRule, elem
   let parentRule = domRule.parentRule;
   if (parentRule && parentRule.type == CSSRule.MEDIA_RULE) {
     this.mediaText = parentRule.media.mediaText;
   }
 
   if (this._cssSheet) {
     // parse domRule.selectorText on call to this.selectors
     this._selectors = null;
-    this.line = domUtils.getRuleLine(this.domRule);
-    this.column = domUtils.getRuleColumn(this.domRule);
+    this.line = InspectorUtils.getRuleLine(this.domRule);
+    this.column = InspectorUtils.getRuleColumn(this.domRule);
     this.source = this._cssSheet.shortSource + ":" + this.line;
     if (this.mediaText) {
       this.source += " @media " + this.mediaText;
     }
     this.href = this._cssSheet.href;
     this.contentRule = this._cssSheet.contentSheet;
   } else if (element) {
     this._selectors = [ new CssSelector(this, "@element.style", 0) ];
@@ -1124,18 +1124,18 @@ CssSelector.prototype = {
       // directly. @see http://www.w3.org/TR/CSS2/cascade.html#specificity
       return 0x40000000;
     }
 
     if (this._specificity) {
       return this._specificity;
     }
 
-    this._specificity = domUtils.getSpecificity(this.cssRule.domRule,
-                                                this.selectorIndex);
+    this._specificity = InspectorUtils.getSpecificity(this.cssRule.domRule,
+                                                      this.selectorIndex);
 
     return this._specificity;
   },
 
   toString: function () {
     return this.text;
   },
 };
@@ -1478,12 +1478,8 @@ CssSelectorInfo.prototype = {
 
     return 0;
   },
 
   toString: function () {
     return this.selector + " -> " + this.value;
   },
 };
-
-DevToolsUtils.defineLazyGetter(this, "domUtils", function () {
-  return Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
-});
--- a/devtools/server/tests/mochitest/test_css-logic-media-queries.html
+++ b/devtools/server/tests/mochitest/test_css-logic-media-queries.html
@@ -23,28 +23,26 @@ Test that css-logic handles media-querie
   </style>
 </head>
 <body>
   <div></div>
   <script type="application/javascript">
   "use strict";
 
   window.onload = function () {
-    const { classes: Cc, utils: Cu, interfaces: Ci } = Components;
-    const DOMUtils = Cc["@mozilla.org/inspector/dom-utils;1"]
-      .getService(Ci.inIDOMUtils);
+    const { utils: Cu } = Components;
 
     const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
     const Services = require("Services");
     const {CssLogic} = require("devtools/server/css-logic");
 
     SimpleTest.waitForExplicitFinish();
 
     let div = document.querySelector("div");
-    let cssLogic = new CssLogic(DOMUtils.isInheritedProperty);
+    let cssLogic = new CssLogic(InspectorUtils.isInheritedProperty);
     cssLogic.highlight(div);
     cssLogic.processMatchedSelectors();
 
     let _strings = Services.strings
       .createBundle("chrome://devtools-shared/locale/styleinspector.properties");
 
     let inline = _strings.GetStringFromName("rule.sourceInline");
 
--- a/devtools/server/tests/mochitest/test_css-logic-specificity.html
+++ b/devtools/server/tests/mochitest/test_css-logic-specificity.html
@@ -8,22 +8,21 @@ Test that css-logic calculates CSS speci
   <title>Test css-logic specificity</title>
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
 </head>
 <body style="background:blue;">
   <script type="application/javascript">
   "use strict";
 
   window.onload = function () {
-    const {utils: Cu, classes: Cc, interfaces: Ci} = Components;
+    const {utils: Cu} = Components;
 
     const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
     const {CssLogic, CssSelector} = require("devtools/server/css-logic");
-    const DOMUtils = Cc["@mozilla.org/inspector/dom-utils;1"]
-                       .getService(Ci.inIDOMUtils);
+    const InspectorUtils = SpecialPowers.InspectorUtils;
 
     const TEST_DATA = [
       {text: "*", expected: 0},
       {text: "LI", expected: 1},
       {text: "UL LI", expected: 2},
       {text: "UL OL + LI", expected: 3},
       {text: "H1 + [REL=\"up\"]", expected: 1025},
       {text: "UL OL LI.red", expected: 1027},
@@ -48,32 +47,32 @@ Test that css-logic calculates CSS speci
 
     function getExpectedSpecificity(selectorText) {
       return TEST_DATA.filter(i => i.text === selectorText)[0].expected;
     }
 
     SimpleTest.waitForExplicitFinish();
 
     createDocument();
-    let cssLogic = new CssLogic(DOMUtils.isInheritedProperty);
+    let cssLogic = new CssLogic(InspectorUtils.isInheritedProperty);
 
     cssLogic.highlight(document.body);
     let cssSheet = cssLogic.sheets[0];
     let cssRule = cssSheet.domSheet.cssRules[0];
     let selectors = CssLogic.getSelectors(cssRule);
 
     info("Iterating over the test selectors");
     for (let i = 0; i < selectors.length; i++) {
       let selectorText = selectors[i];
       info("Testing selector " + selectorText);
 
       let selector = new CssSelector(cssRule, selectorText, i);
       let expected = getExpectedSpecificity(selectorText);
-      let specificity = DOMUtils.getSpecificity(selector.cssRule,
-                                                selector.selectorIndex);
+      let specificity = InspectorUtils.getSpecificity(selector.cssRule,
+                                                      selector.selectorIndex);
       is(specificity, expected,
         'Selector "' + selectorText + '" has a specificity of ' + expected);
     }
 
     info("Testing specificity of element.style");
     let colorProp = cssLogic.getPropertyInfo("background");
     is(colorProp.matchedSelectors[0].specificity, 0x40000000,
        "Element styles have specificity of 0x40000000 (1073741824).");
--- a/devtools/server/tests/mochitest/test_inspector-pseudoclass-lock.html
+++ b/devtools/server/tests/mochitest/test_inspector-pseudoclass-lock.html
@@ -8,19 +8,16 @@ https://bugzilla.mozilla.org/show_bug.cg
   <title>Test for Bug </title>
 
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
   <script type="application/javascript" src="inspector-helpers.js"></script>
   <script type="application/javascript">
 "use strict";
 
-const DOMUtils = Components.classes["@mozilla.org/inspector/dom-utils;1"]
-                           .getService(Components.interfaces.inIDOMUtils);
-
 const KNOWN_PSEUDOCLASSES = [":hover", ":active", ":focus"];
 
 window.onload = function () {
   SimpleTest.waitForExplicitFinish();
   runNextTest();
 };
 
 let gInspectee = null;
@@ -61,26 +58,27 @@ function checkChange(change, expectation
 
   is(target.pseudoClassLocks.length, expectation.pseudos.length,
      "Expect " + expectation.pseudos.length + " pseudoclass locks.");
   for (let i = 0; i < expectation.pseudos.length; i++) {
     let pseudo = expectation.pseudos[i];
     let enabled = expectation.enabled === undefined ? true : expectation.enabled[i];
     ok(target.hasPseudoClassLock(pseudo), "Expect lock: " + pseudo);
     let rawNode = target.rawNode();
-    ok(DOMUtils.hasPseudoClassLock(rawNode, pseudo), "Expect lock in dom: " + pseudo);
+    ok(InspectorUtils.hasPseudoClassLock(rawNode, pseudo),
+       "Expect lock in dom: " + pseudo);
 
     is(rawNode.matches(pseudo), enabled,
        `Target should match pseudoclass, '${pseudo}', if enabled (with .matches())`);
   }
 
   for (let pseudo of KNOWN_PSEUDOCLASSES) {
     if (!expectation.pseudos.some(expected => pseudo === expected)) {
       ok(!target.hasPseudoClassLock(pseudo), "Don't expect lock: " + pseudo);
-      ok(!DOMUtils.hasPseudoClassLock(target.rawNode(), pseudo),
+      ok(!InspectorUtils.hasPseudoClassLock(target.rawNode(), pseudo),
          "Don't expect lock in dom: " + pseudo);
     }
   }
 }
 
 function checkMutations(mutations, expectations) {
   is(mutations.length, expectations.length, "Should get the right number of mutations.");
   for (let i = 0; i < mutations.length; i++) {
@@ -167,17 +165,17 @@ addTest(function testPseudoClassLock() {
         nodeName: "DIV",
         pseudos: [":hover", ":active"],
         enabled: [false, true]
       });
     }).then(() => {
       // Now shut down the walker and make sure that clears up the remaining lock.
       return gWalker.release();
     }).then(() => {
-      ok(!DOMUtils.hasPseudoClassLock(contentNode, ":active"),
+      ok(!InspectorUtils.hasPseudoClassLock(contentNode, ":active"),
          "Pseudoclass should have been removed during destruction.");
       teardown();
     }).then(runNextTest));
   });
 });
 
   </script>
 </head>
--- a/devtools/shared/builtin-modules.js
+++ b/devtools/shared/builtin-modules.js
@@ -18,19 +18,19 @@ const promise = Cu.import("resource://gr
 const jsmScope = Cu.import("resource://gre/modules/Services.jsm", {});
 const { Services } = jsmScope;
 // Steal various globals only available in JSM scope (and not Sandbox one)
 const { PromiseDebugging, ChromeUtils, HeapSnapshot,
         atob, btoa, TextEncoder, TextDecoder } = Cu.getGlobalForObject(jsmScope);
 
 // Create a single Sandbox to access global properties needed in this module.
 // Sandbox are memory expensive, so we should create as little as possible.
-const { CSS, FileReader, indexedDB, URL } =
+const { CSS, FileReader, indexedDB, InspectorUtils, URL } =
     Cu.Sandbox(CC("@mozilla.org/systemprincipal;1", "nsIPrincipal")(), {
-      wantGlobalProperties: ["CSS", "FileReader", "indexedDB", "URL"]
+      wantGlobalProperties: ["CSS", "FileReader", "indexedDB", "InspectorUtils", "URL"]
     });
 
 /**
  * Defines a getter on a specified object that will be created upon first use.
  *
  * @param object
  *        The object to define the lazy getter on.
  * @param name
@@ -174,16 +174,17 @@ exports.modules = {
   promise,
   // Expose "chrome" Promise, which aren't related to any document
   // and so are never frozen, even if the browser loader module which
   // pull it is destroyed. See bug 1402779.
   Promise,
   PromiseDebugging,
   ChromeUtils,
   HeapSnapshot,
+  InspectorUtils,
   FileReader,
 };
 
 defineLazyGetter(exports.modules, "Debugger", () => {
   // addDebuggerToGlobal only allows adding the Debugger object to a global. The
   // this object is not guaranteed to be a global (in particular on B2G, due to
   // compartment sharing), so add the Debugger object to a sandbox instead.
   let sandbox = Cu.Sandbox(CC("@mozilla.org/systemprincipal;1", "nsIPrincipal")());
--- a/devtools/shared/css/generated/generate-properties-db.js
+++ b/devtools/shared/css/generated/generate-properties-db.js
@@ -1,21 +1,26 @@
 /* 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/. */
+
 "use strict";
 
+/* globals InspectorUtils */
+
 /*
  * This is an xpcshell script that runs to generate a static list of CSS properties
  * as known by the platform. It is run from ./mach_commands.py by running
  * `mach devtools-css-db`.
  */
 var {require} = Components.utils.import("resource://devtools/shared/Loader.jsm", {});
 var {generateCssProperties} = require("devtools/server/actors/css-properties");
 
+Components.utils.importGlobalProperties(["InspectorUtils"]);
+
 // xpcshell can output extra information, so place some delimiter text between
 // the output of the css properties database.
 dump("DEVTOOLS_CSS_DB_DELIMITER");
 
 // Output JSON
 dump(JSON.stringify({
   cssProperties: cssProperties(),
   pseudoElements: pseudoElements()
@@ -43,13 +48,10 @@ function cssProperties() {
   }
   return properties;
 }
 
 /**
  * The list of all CSS Pseudo Elements.
  */
 function pseudoElements() {
-  const {classes: Cc, interfaces: Ci} = Components;
-  const domUtils = Cc["@mozilla.org/inspector/dom-utils;1"]
-                             .getService(Ci.inIDOMUtils);
-  return domUtils.getCSSPseudoElementNames();
+  return InspectorUtils.getCSSPseudoElementNames();
 }
--- a/devtools/shared/css/generated/mach_commands.py
+++ b/devtools/shared/css/generated/mach_commands.py
@@ -1,16 +1,16 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 """
-This script implements the `mach devtools-css-db` command. It runs an xpcshell script
-that uses inIDOMUtils to query the CSS properties used by the browser. This information
-is used to generate the properties-db.js file.
+This script implements the `mach devtools-css-db` command. It runs an xpcshell
+script that uses InspectorUtils to query the CSS properties used by the browser.
+This information is used to generate the properties-db.js file.
 """
 
 import json
 import os
 import sys
 import string
 import subprocess
 from mozbuild import shellutil
--- a/devtools/shared/inspector/css-logic.js
+++ b/devtools/shared/inspector/css-logic.js
@@ -3,17 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { getRootBindingParent } = require("devtools/shared/layout/utils");
 const { getTabPrefs } = require("devtools/shared/indentation");
-const { Ci, Cc } = require("chrome");
+const InspectorUtils = require("InspectorUtils");
 
 const MAX_DATA_URL_LENGTH = 40;
 
 /*
  * About the objects defined in this file:
  * - CssLogic contains style information about a view context. It provides
  *   access to 2 sets of objects: Css[Sheet|Rule|Selector] provide access to
  *   information that does not change when the selected element changes while
@@ -33,19 +33,19 @@ const MAX_DATA_URL_LENGTH = 40;
  * - CssPropertyInfo contains style information for a single property for the
  *   highlighted element.
  * - CssSelectorInfo is a wrapper around CssSelector, which adds sorting with
  *   reference to the selected element.
  */
 
 /**
  * Provide access to the style information in a page.
- * CssLogic uses the standard DOM API, and the Gecko inIDOMUtils API to access
- * styling information in the page, and present this to the user in a way that
- * helps them understand:
+ * CssLogic uses the standard DOM API, and the Gecko InspectorUtils API to
+ * access styling information in the page, and present this to the user in a way
+ * that helps them understand:
  * - why their expectations may not have been fulfilled
  * - how browsers process CSS
  * @constructor
  */
 
 const Services = require("Services");
 
 loader.lazyImporter(this, "findCssSelector", "resource://gre/modules/css-selector.js");
@@ -506,14 +506,12 @@ function getBindingElementAndPseudo(node
 exports.getBindingElementAndPseudo = getBindingElementAndPseudo;
 
 /**
  * Returns css style rules for a given a node.
  * This function can handle ::before or ::after pseudo element as well as
  * normal element.
  */
 function getCSSStyleRules(node) {
-  const DOMUtils =
-    Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
   let { bindingElement, pseudo } = getBindingElementAndPseudo(node);
-  return DOMUtils.getCSSStyleRules(bindingElement, pseudo);
+  return InspectorUtils.getCSSStyleRules(bindingElement, pseudo);
 }
 exports.getCSSStyleRules = getCSSStyleRules;
--- a/devtools/shared/tests/unit/test_css-properties-db.js
+++ b/devtools/shared/tests/unit/test_css-properties-db.js
@@ -14,33 +14,33 @@
  * Because of these difficulties, the database only needs to be up to date with Nightly.
  * It is a fallback that is only used if the remote debugging protocol doesn't support
  * providing a CSS database, so it's ok if the provided properties don't exactly match
  * the inspected target in this particular case.
  */
 
 "use strict";
 
-const DOMUtils = Components.classes["@mozilla.org/inspector/dom-utils;1"]
-                           .getService(Components.interfaces.inIDOMUtils);
-
 const {PSEUDO_ELEMENTS, CSS_PROPERTIES, PREFERENCES} = require("devtools/shared/css/generated/properties-db");
 const {generateCssProperties} = require("devtools/server/actors/css-properties");
 const {Preferences} = require("resource://gre/modules/Preferences.jsm");
 
+Components.utils.importGlobalProperties(["InspectorUtils"]);
+
 function run_test() {
   const propertiesErrorMessage = "If this assertion fails, then the client side CSS " +
                                  "properties list in devtools is out of sync with the " +
                                  "CSS properties on the platform. To fix this " +
                                  "assertion run `mach devtools-css-db` to re-generate " +
                                  "the client side properties.";
 
   // Check that the platform and client match for pseudo elements.
-  deepEqual(PSEUDO_ELEMENTS, DOMUtils.getCSSPseudoElementNames(), `The pseudo elements ` +
-            `match on the client and platform. ${propertiesErrorMessage}`);
+  deepEqual(PSEUDO_ELEMENTS, InspectorUtils.getCSSPseudoElementNames(),
+            "The pseudo elements match on the client and platform. " +
+            propertiesErrorMessage);
 
   /**
    * Check that the platform and client match for the details on their CSS properties.
    * Enumerate each property to aid in debugging. Sometimes these properties don't
    * completely agree due to differences in preferences. Check the currently set
    * preference for that property to see if it's enabled.
    */
   const platformProperties = generateCssProperties();
--- a/devtools/shared/tests/unit/test_csslexer.js
+++ b/devtools/shared/tests/unit/test_csslexer.js
@@ -5,24 +5,23 @@
 
 // This file is a copy of layout/style/test/test_csslexer.js, modified
 // to use both our pure-JS lexer and the DOMUtils lexer for
 // cross-checking.
 
 "use strict";
 
 const jsLexer = require("devtools/shared/css/lexer");
-const domutils = Components.classes["@mozilla.org/inspector/dom-utils;1"]
-                           .getService(Components.interfaces.inIDOMUtils);
+const InspectorUtils = require("InspectorUtils");
 
 // An object that acts like a CSSLexer but verifies that the DOM lexer
 // and the JS lexer do the same thing.
 function DoubleLexer(input) {
   info("DoubleLexer input: " + input);
-  this.domLexer = domutils.getCSSLexer(input);
+  this.domLexer = InspectorUtils.getCSSLexer(input);
   this.jsLexer = jsLexer.getCSSLexer(input);
 }
 
 DoubleLexer.prototype = {
   checkState: function () {
     equal(this.domLexer.lineNumber, this.jsLexer.lineNumber,
           "check line number");
     equal(this.domLexer.columnNumber, this.jsLexer.columnNumber,
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -9554,37 +9554,16 @@ nsDocShell::InternalLoad(nsIURI* aURI,
 
     if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
       if (NS_SUCCEEDED(rv) && shouldLoad == nsIContentPolicy::REJECT_TYPE) {
         return NS_ERROR_CONTENT_BLOCKED_SHOW_ALT;
       }
 
       return NS_ERROR_CONTENT_BLOCKED;
     }
-
-    // If HSTS priming was set by nsMixedContentBlocker::ShouldLoad, and we
-    // would block due to mixed content, go ahead and block here. If we try to
-    // proceed with priming, we will error out later on.
-    nsCOMPtr<nsIDocShell> docShell = NS_CP_GetDocShellFromContext(requestingContext);
-    // When loading toplevel windows, requestingContext can be null.  We don't
-    // really care about HSTS in that situation, though; loads in toplevel
-    // windows should all be browser UI.
-    if (docShell) {
-      nsIDocument* document = docShell->GetDocument();
-      NS_ENSURE_TRUE(document, NS_OK);
-
-      HSTSPrimingState state = document->GetHSTSPrimingStateForLocation(aURI);
-      if (state == HSTSPrimingState::eHSTS_PRIMING_BLOCK) {
-        // HSTS Priming currently disabled for InternalLoad, so we need to clear
-        // the location that was added by nsMixedContentBlocker::ShouldLoad
-        // Bug 1269815 will address images loaded via InternalLoad
-        document->ClearHSTSPrimingLocation(aURI);
-        return NS_ERROR_CONTENT_BLOCKED;
-      }
-    }
   }
 
   nsCOMPtr<nsIPrincipal> principalToInherit = aPrincipalToInherit;
   //
   // Get a principal from the current document if necessary.  Note that we only
   // do this for URIs that inherit a security context and local file URIs;
   // in particular we do NOT do this for about:blank.  This way, random
   // about:blank loads that have no principal (which basically means they were
--- a/dom/base/nsGlobalWindowInner.cpp
+++ b/dom/base/nsGlobalWindowInner.cpp
@@ -1745,18 +1745,42 @@ nsGlobalWindowInner::InnerSetNewDocument
 
 nsresult
 nsGlobalWindowInner::EnsureClientSource()
 {
   MOZ_DIAGNOSTIC_ASSERT(mDoc);
 
   bool newClientSource = false;
 
+  // Get the load info for the document if we performed a load.  Be careful
+  // not to look at about:blank or about:srcdoc loads, though. They will have
+  // a channel and loadinfo, but their loadinfo will never be controlled.  This
+  // would in turn inadvertantly trigger the logic below to clear the inherited
+  // controller.
+  nsCOMPtr<nsILoadInfo> loadInfo;
   nsCOMPtr<nsIChannel> channel = mDoc->GetChannel();
-  nsCOMPtr<nsILoadInfo> loadInfo = channel ? channel->GetLoadInfo() : nullptr;
+  if (channel) {
+    nsCOMPtr<nsIURI> uri;
+    Unused << channel->GetURI(getter_AddRefs(uri));
+
+    bool ignoreLoadInfo = false;
+
+    // Note, this is mostly copied from NS_IsAboutBlank().  Its duplicated
+    // here so we can efficiently check about:srcdoc as well.
+    bool isAbout = false;
+    if (NS_SUCCEEDED(uri->SchemeIs("about", &isAbout)) && isAbout) {
+      nsCString spec = uri->GetSpecOrDefault();
+      ignoreLoadInfo = spec.EqualsLiteral("about:blank") ||
+                       spec.EqualsLiteral("about:srcdoc");
+    }
+
+    if (!ignoreLoadInfo) {
+      loadInfo = channel->GetLoadInfo();
+    }
+  }
 
   // Take the initial client source from the docshell immediately.  Even if we
   // don't end up using it here we should consume it.
   UniquePtr<ClientSource> initialClientSource;
   nsIDocShell* docshell = GetDocShell();
   if (docshell) {
     initialClientSource = docshell->TakeInitialClientSource();
   }
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -14,22 +14,22 @@
 #include "nsGkAtoms.h"                   // for static class members
 #include "nsIDocumentObserver.h"         // for typedef (nsUpdateType)
 #include "nsILoadGroup.h"                // for member (in nsCOMPtr)
 #include "nsINode.h"                     // for base class
 #include "nsIParser.h"
 #include "nsIPresShell.h"
 #include "nsIScriptGlobalObject.h"       // for member (in nsCOMPtr)
 #include "nsIServiceManager.h"
+#include "nsIURI.h"                      // for use in inline functions
 #include "nsIUUIDGenerator.h"
 #include "nsPIDOMWindow.h"               // for use in inline functions
 #include "nsPropertyTable.h"             // for member
 #include "nsStringFwd.h"
-#include "nsDataHashtable.h"             // for member
-#include "nsURIHashKey.h"                // for member
+#include "nsTHashtable.h"                // for member
 #include "mozilla/net/ReferrerPolicy.h"  // for member
 #include "nsWeakReference.h"
 #include "mozilla/UseCounter.h"
 #include "mozilla/WeakPtr.h"
 #include "Units.h"
 #include "nsContentListDeclarations.h"
 #include "nsExpirationTracker.h"
 #include "nsClassHashtable.h"
@@ -189,23 +189,16 @@ enum class CallerType : uint32_t;
 // Enum for requesting a particular type of document when creating a doc
 enum DocumentFlavor {
   DocumentFlavorLegacyGuess, // compat with old code until made HTML5-compliant
   DocumentFlavorHTML, // HTMLDocument with HTMLness bit set to true
   DocumentFlavorSVG, // SVGDocument
   DocumentFlavorPlain, // Just a Document
 };
 
-// Enum for HSTS priming states
-enum class HSTSPrimingState {
-  eNO_HSTS_PRIMING = 0,    // don't do HSTS Priming
-  eHSTS_PRIMING_ALLOW = 1, // if HSTS priming fails, allow the load to proceed
-  eHSTS_PRIMING_BLOCK = 2  // if HSTS priming fails, block the load
-};
-
 // Document states
 
 // RTL locale: specific to the XUL localedir attribute
 #define NS_DOCUMENT_STATE_RTL_LOCALE              NS_DEFINE_EVENT_STATE_MACRO(0)
 // Window activation status
 #define NS_DOCUMENT_STATE_WINDOW_INACTIVE         NS_DEFINE_EVENT_STATE_MACRO(1)
 
 // Some function forward-declarations
@@ -420,44 +413,16 @@ public:
     return mUpgradeInsecureRequests;
   }
 
   void SetReferrer(const nsACString& aReferrer) {
     mReferrer = aReferrer;
   }
 
   /**
-   * Check to see if a subresource we want to load requires HSTS priming
-   * to be done.
-   */
-  HSTSPrimingState GetHSTSPrimingStateForLocation(nsIURI* aContentLocation) const
-  {
-    HSTSPrimingState state;
-    if (mHSTSPrimingURIList.Get(aContentLocation, &state)) {
-      return state;
-    }
-    return HSTSPrimingState::eNO_HSTS_PRIMING;
-  }
-
-  /**
-   * Add a subresource to the HSTS priming list. If this URI is
-   * not in the HSTS cache, it will trigger an HSTS priming request
-   * when we try to load it.
-   */
-  void AddHSTSPrimingLocation(nsIURI* aContentLocation, HSTSPrimingState aState)
-  {
-    mHSTSPrimingURIList.Put(aContentLocation, aState);
-  }
-
-  void ClearHSTSPrimingLocation(nsIURI* aContentLocation)
-  {
-    mHSTSPrimingURIList.Remove(aContentLocation);
-  }
-
-  /**
    * Set the principal responsible for this document.  Chances are,
    * you do not want to be using this.
    */
   virtual void SetPrincipal(nsIPrincipal *aPrincipal) = 0;
 
   /**
    * Get the list of ancestor principals for a document.  This is the same as
    * the ancestor list for the document's docshell the last time SetContainer()
@@ -3334,21 +3299,16 @@ protected:
   bool mReferrerPolicySet;
   ReferrerPolicyEnum mReferrerPolicy;
 
   bool mBlockAllMixedContent;
   bool mBlockAllMixedContentPreloads;
   bool mUpgradeInsecureRequests;
   bool mUpgradeInsecurePreloads;
 
-  // if nsMixedContentBlocker requires sending an HSTS priming request,
-  // temporarily store that in the document so that it can be propogated to the
-  // LoadInfo and eventually the HTTP Channel
-  nsDataHashtable<nsURIHashKey, HSTSPrimingState> mHSTSPrimingURIList;
-
   mozilla::WeakPtr<nsDocShell> mDocumentContainer;
 
   NotNull<const Encoding*> mCharacterSet;
   int32_t mCharacterSetSource;
 
   // This is just a weak pointer; the parent document owns its children.
   nsIDocument* mParentDocument;
 
--- a/dom/base/nsPlainTextSerializer.cpp
+++ b/dom/base/nsPlainTextSerializer.cpp
@@ -1248,17 +1248,17 @@ nsPlainTextSerializer::Output(nsString& 
   }
   mOutputString->Append(aString);
 }
 
 static bool
 IsSpaceStuffable(const char16_t *s)
 {
   if (s[0] == '>' || s[0] == ' ' || s[0] == kNBSP ||
-      nsCRT::strncmp(s, u"From ", 5) == 0)
+      NS_strncmp(s, u"From ", 5) == 0)
     return true;
   else
     return false;
 }
 
 /**
  * This function adds a piece of text to the current stored line. If we are
  * wrapping text and the stored line will become too long, a suitable
--- a/dom/base/nsRange.cpp
+++ b/dom/base/nsRange.cpp
@@ -20,30 +20,30 @@
 #include "nsIDOMText.h"
 #include "nsError.h"
 #include "nsIContentIterator.h"
 #include "nsIDOMNodeList.h"
 #include "nsGkAtoms.h"
 #include "nsContentUtils.h"
 #include "nsGenericDOMDataNode.h"
 #include "nsTextFrame.h"
-#include "nsFontFaceList.h"
 #include "mozilla/dom/DocumentFragment.h"
 #include "mozilla/dom/DocumentType.h"
 #include "mozilla/dom/RangeBinding.h"
 #include "mozilla/dom/DOMRect.h"
 #include "mozilla/dom/DOMStringList.h"
 #include "mozilla/dom/ShadowRoot.h"
 #include "mozilla/dom/Selection.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/Likely.h"
 #include "nsCSSFrameConstructor.h"
 #include "nsStyleStruct.h"
 #include "nsStyleStructInlines.h"
 #include "nsComputedDOMStyle.h"
+#include "mozilla/dom/InspectorFontFace.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 JSObject*
 nsRange::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return RangeBinding::Wrap(aCx, this, aGivenProto);
@@ -3462,35 +3462,37 @@ nsRange::GetClientRectsAndTexts(
   aResult.mRectList = new DOMRectList(static_cast<nsIDOMRange*>(this));
 
   nsLayoutUtils::RectListBuilder builder(aResult.mRectList);
 
   CollectClientRectsAndText(&builder, &aResult.mTextList, this,
     mStart.Container(), mStart.Offset(), mEnd.Container(), mEnd.Offset(), true, true);
 }
 
-NS_IMETHODIMP
-nsRange::GetUsedFontFaces(nsIDOMFontFaceList** aResult)
+nsresult
+nsRange::GetUsedFontFaces(nsTArray<nsAutoPtr<InspectorFontFace>>& aResult)
 {
-  *aResult = nullptr;
-
   NS_ENSURE_TRUE(mStart.Container(), NS_ERROR_UNEXPECTED);
 
   nsCOMPtr<nsINode> startContainer = do_QueryInterface(mStart.Container());
   nsCOMPtr<nsINode> endContainer = do_QueryInterface(mEnd.Container());
 
   // Flush out layout so our frames are up to date.
   nsIDocument* doc = mStart.Container()->OwnerDoc();
   NS_ENSURE_TRUE(doc, NS_ERROR_UNEXPECTED);
   doc->FlushPendingNotifications(FlushType::Frames);
 
   // Recheck whether we're still in the document
   NS_ENSURE_TRUE(mStart.Container()->IsInUncomposedDoc(), NS_ERROR_UNEXPECTED);
 
-  RefPtr<nsFontFaceList> fontFaceList = new nsFontFaceList();
+  // A table to map gfxFontEntry objects to InspectorFontFace objects.
+  // (We hold on to the InspectorFontFaces strongly due to the nsAutoPtrs
+  // in the nsClassHashtable, until we move them out into aResult at the end
+  // of the function.)
+  nsLayoutUtils::UsedFontFaceTable fontFaces;
 
   RangeSubtreeIterator iter;
   nsresult rv = iter.Init(this);
   NS_ENSURE_SUCCESS(rv, rv);
 
   while (!iter.IsDone()) {
     // only collect anything if the range is not collapsed
     nsCOMPtr<nsINode> node = iter.GetCurrentNode();
@@ -3505,30 +3507,35 @@ nsRange::GetUsedFontFaces(nsIDOMFontFace
       continue;
     }
 
     if (content->IsNodeOfType(nsINode::eTEXT)) {
        if (node == startContainer) {
          int32_t offset = startContainer == endContainer ?
            mEnd.Offset() : content->GetText()->GetLength();
          nsLayoutUtils::GetFontFacesForText(frame, mStart.Offset(), offset,
-                                            true, fontFaceList);
+                                            true, fontFaces);
          continue;
        }
        if (node == endContainer) {
          nsLayoutUtils::GetFontFacesForText(frame, 0, mEnd.Offset(),
-                                            true, fontFaceList);
+                                            true, fontFaces);
          continue;
        }
     }
 
-    nsLayoutUtils::GetFontFacesForFrames(frame, fontFaceList);
+    nsLayoutUtils::GetFontFacesForFrames(frame, fontFaces);
   }
 
-  fontFaceList.forget(aResult);
+  // Take ownership of the InspectorFontFaces in the table and move them into
+  // the aResult outparam.
+  for (auto iter = fontFaces.Iter(); !iter.Done(); iter.Next()) {
+    aResult.AppendElement(Move(iter.Data()));
+  }
+
   return NS_OK;
 }
 
 nsINode*
 nsRange::GetRegisteredCommonAncestor()
 {
   MOZ_ASSERT(IsInSelection(),
              "GetRegisteredCommonAncestor only valid for range in selection");
--- a/dom/base/nsRange.h
+++ b/dom/base/nsRange.h
@@ -28,16 +28,17 @@
 namespace mozilla {
 class ErrorResult;
 namespace dom {
 struct ClientRectsAndTexts;
 class DocGroup;
 class DocumentFragment;
 class DOMRect;
 class DOMRectList;
+class InspectorFontFace;
 class Selection;
 } // namespace dom
 } // namespace mozilla
 
 class nsRange final : public nsIDOMRange,
                       public nsStubMutationObserver,
                       public nsWrapperCache,
                       // For linking together selection-associated ranges.
@@ -275,17 +276,18 @@ public:
     int32_t indexInParent = parentNode->IndexOf(aNode);
     if (NS_WARN_IF(indexInParent < 0)) {
       return nullptr;
     }
     *aOffset = static_cast<uint32_t>(indexInParent);
     return parentNode;
   }
 
-  NS_IMETHOD GetUsedFontFaces(nsIDOMFontFaceList** aResult);
+  nsresult GetUsedFontFaces(
+      nsTArray<nsAutoPtr<mozilla::dom::InspectorFontFace>>& aResult);
 
   // nsIMutationObserver methods
   NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
   NS_DECL_NSIMUTATIONOBSERVER_PARENTCHAINCHANGED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
 
--- a/dom/base/test/bug704320.sjs
+++ b/dom/base/test/bug704320.sjs
@@ -189,22 +189,16 @@ function createPolicyTest(policy, option
                     onload="incrementLoad2(\'img\', 2);">\n\
             <img src="http://example.com/tests/dom/base/test/bug704320_counter.sjs?type=img"\n\
                     onload="incrementLoad2(\'img\', 2);">\n\
           </body>\n\
           </html>';
 }
 
 function handleRequest(request, response) {
-  if (request.method == 'HEAD') {
-    // respond to a HEAD request with a 418 so that we can easily distinguish
-    // HSTS priming responses and ignore them
-    response.setStatusLine('1.1', 418, "I'm a teapot");
-    return;
-  }
   var sharedKey = 'bug704320.sjs';
   var params = request.queryString.split('&');
   var action = params[0].split('=')[1];
 
   if (action === 'create-1st-level-iframe') {
     // ?action=create-1st-level-iframe&scheme-from=http&scheme-to=https&policy=origin
     var schemeFrom = params[1].split('=')[1];
     var schemeTo = params[2].split('=')[1];
--- a/dom/base/test/referrerHelper.js
+++ b/dom/base/test/referrerHelper.js
@@ -20,19 +20,16 @@ window.addEventListener("message", funct
 /**
  * helper to perform an XHR.
  */
 function doXHR(url, onSuccess, onFail) {
   var xhr = new XMLHttpRequest();
   xhr.onload = function () {
     if (xhr.status == 200) {
       onSuccess(xhr);
-    } else if (xhr.status == 418) {
-      // Ignore HSTS priming responses
-      return;
     } else {
       onFail(xhr);
     }
   };
   xhr.open('GET', url, true);
   xhr.send(null);
 }
 
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -512,16 +512,20 @@ DOMInterfaces = {
     'wrapperCache': False,
 },
 
 'InputStream': {
     'nativeType': 'nsIInputStream',
     'notflattened': True
 },
 
+'InspectorFontFace': {
+    'wrapperCache': False,
+},
+
 'IntersectionObserver': {
     'nativeType': 'mozilla::dom::DOMIntersectionObserver',
 },
 
 'IntersectionObserverEntry': {
     'nativeType': 'mozilla::dom::DOMIntersectionObserverEntry',
     'headerFile': 'DOMIntersectionObserver.h',
 },
--- a/dom/console/Console.cpp
+++ b/dom/console/Console.cpp
@@ -2,16 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/Console.h"
 #include "mozilla/dom/ConsoleInstance.h"
 #include "mozilla/dom/ConsoleBinding.h"
+#include "ConsoleCommon.h"
 
 #include "mozilla/dom/BlobBinding.h"
 #include "mozilla/dom/Exceptions.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/FunctionBinding.h"
 #include "mozilla/dom/Performance.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/StructuredCloneHolder.h"
@@ -290,34 +291,16 @@ public:
 private:
   ~ConsoleCallData()
   {
     AssertIsOnOwningThread();
     MOZ_ASSERT(mStatus != eInUse);
   }
 };
 
-// This class is used to clear any exception at the end of this method.
-class ClearException
-{
-public:
-  explicit ClearException(JSContext* aCx)
-    : mCx(aCx)
-  {
-  }
-
-  ~ClearException()
-  {
-    JS_ClearPendingException(mCx);
-  }
-
-private:
-  JSContext* mCx;
-};
-
 class ConsoleRunnable : public WorkerProxyToMainThreadRunnable
                       , public StructuredCloneHolderBase
 {
 public:
   explicit ConsoleRunnable(Console* aConsole)
     : WorkerProxyToMainThreadRunnable(GetCurrentThreadWorkerPrivate())
     , mConsole(aConsole)
   {}
@@ -531,17 +514,17 @@ private:
   }
 
   bool
   PreDispatch(JSContext* aCx) override
   {
     mWorkerPrivate->AssertIsOnWorkerThread();
     mCallData->AssertIsOnOwningThread();
 
-    ClearException ce(aCx);
+    ConsoleCommon::ClearException ce(aCx);
 
     JS::Rooted<JSObject*> arguments(aCx,
       JS_NewArrayObject(aCx, mCallData->mCopiedArguments.Length()));
     if (NS_WARN_IF(!arguments)) {
       return false;
     }
 
     JS::Rooted<JS::Value> arg(aCx);
@@ -618,17 +601,17 @@ private:
     mCallData = nullptr;
   }
 
   void
   ProcessCallData(JSContext* aCx)
   {
     AssertIsOnMainThread();
 
-    ClearException ce(aCx);
+    ConsoleCommon::ClearException ce(aCx);
 
     JS::Rooted<JS::Value> argumentsValue(aCx);
     if (!Read(aCx, &argumentsValue)) {
       return;
     }
 
     MOZ_ASSERT(argumentsValue.isObject());
 
@@ -676,17 +659,17 @@ public:
   {
     MOZ_ASSERT(aConsole);
   }
 
 private:
   bool
   PreDispatch(JSContext* aCx) override
   {
-    ClearException ce(aCx);
+    ConsoleCommon::ClearException ce(aCx);
 
     JS::Rooted<JSObject*> arguments(aCx,
       JS_NewArrayObject(aCx, mArguments.Length()));
     if (NS_WARN_IF(!arguments)) {
       return false;
     }
 
     JS::Rooted<JS::Value> arg(aCx);
@@ -708,17 +691,17 @@ private:
   }
 
   void
   RunConsole(JSContext* aCx, nsPIDOMWindowOuter* aOuterWindow,
              nsPIDOMWindowInner* aInnerWindow) override
   {
     AssertIsOnMainThread();
 
-    ClearException ce(aCx);
+    ConsoleCommon::ClearException ce(aCx);
 
     // Now we could have the correct window (if we are not window-less).
     mClonedData.mParent = aInnerWindow;
 
     JS::Rooted<JS::Value> argumentsValue(aCx);
     bool ok = Read(aCx, &argumentsValue);
     mClonedData.mParent = nullptr;
 
@@ -1009,17 +992,17 @@ Console::StringMethod(const GlobalObject
                                 aMethodString);
 }
 
 void
 Console::StringMethodInternal(JSContext* aCx, const nsAString& aLabel,
                               MethodName aMethodName,
                               const nsAString& aMethodString)
 {
-  ClearException ce(aCx);
+  ConsoleCommon::ClearException ce(aCx);
 
   Sequence<JS::Value> data;
   SequenceRooter<JS::Value> rooter(aCx, &data);
 
   JS::Rooted<JS::Value> value(aCx);
   if (!dom::ToJSValue(aCx, aLabel, &value)) {
     return;
   }
@@ -1032,17 +1015,17 @@ Console::StringMethodInternal(JSContext*
 }
 
 /* static */ void
 Console::TimeStamp(const GlobalObject& aGlobal,
                    const JS::Handle<JS::Value> aData)
 {
   JSContext* cx = aGlobal.Context();
 
-  ClearException ce(cx);
+  ConsoleCommon::ClearException ce(cx);
 
   Sequence<JS::Value> data;
   SequenceRooter<JS::Value> rooter(cx, &data);
 
   if (aData.isString() && !data.AppendElement(aData, fallible)) {
     return;
   }
 
@@ -1108,17 +1091,17 @@ Console::ProfileMethodInternal(JSContext
     // Here we are in a worker thread.
     RefPtr<ConsoleProfileRunnable> runnable =
       new ConsoleProfileRunnable(this, aMethodName, aAction, aData);
 
     runnable->Dispatch(aCx);
     return;
   }
 
-  ClearException ce(aCx);
+  ConsoleCommon::ClearException ce(aCx);
 
   RootedDictionary<ConsoleProfileEvent> event(aCx);
   event.mAction = aAction;
 
   event.mArguments.Construct();
   Sequence<JS::Value>& sequence = event.mArguments.Value();
 
   for (uint32_t i = 0; i < aData.Length(); ++i) {
@@ -1259,17 +1242,17 @@ Console::MethodInternal(JSContext* aCx, 
   if (!ShouldProceed(aMethodName)) {
     return;
   }
 
   AssertIsOnOwningThread();
 
   RefPtr<ConsoleCallData> callData(new ConsoleCallData());
 
-  ClearException ce(aCx);
+  ConsoleCommon::ClearException ce(aCx);
 
   if (NS_WARN_IF(!callData->Initialize(aCx, aMethodName, aMethodString,
                                        aData, this))) {
     return;
   }
 
   OriginAttributes oa;
 
@@ -1525,17 +1508,17 @@ Console::PopulateConsoleNotificationInTh
 
   JS::Rooted<JSObject*> targetScope(aCx, aTargetScope);
 
   ConsoleStackEntry frame;
   if (aData->mTopStackFrame) {
     frame = *aData->mTopStackFrame;
   }
 
-  ClearException ce(aCx);
+  ConsoleCommon::ClearException ce(aCx);
   RootedDictionary<ConsoleEvent> event(aCx);
 
   event.mAddonId = aData->mAddonId;
 
   event.mID.Construct();
   event.mInnerID.Construct();
 
   if (aData->mIDType == ConsoleCallData::eString) {
@@ -2183,17 +2166,17 @@ Console::ArgumentsToValueList(const Sequ
 }
 
 uint32_t
 Console::IncreaseCounter(JSContext* aCx, const Sequence<JS::Value>& aArguments,
                          nsAString& aCountLabel)
 {
   AssertIsOnOwningThread();
 
-  ClearException ce(aCx);
+  ConsoleCommon::ClearException ce(aCx);
 
   MOZ_ASSERT(!aArguments.IsEmpty());
 
   JS::Rooted<JS::Value> labelValue(aCx, aArguments[0]);
   JS::Rooted<JSString*> jsString(aCx, JS::ToString(aCx, labelValue));
   if (!jsString) {
     return 0; // We cannot continue.
   }
@@ -2219,17 +2202,17 @@ Console::IncreaseCounter(JSContext* aCx,
   }
   return entry.Data();
 }
 
 JS::Value
 Console::CreateCounterValue(JSContext* aCx, const nsAString& aCountLabel,
                             uint32_t aCountValue) const
 {
-  ClearException ce(aCx);
+  ConsoleCommon::ClearException ce(aCx);
 
   if (aCountValue == MAX_PAGE_COUNTERS) {
     RootedDictionary<ConsoleCounterError> error(aCx);
 
     JS::Rooted<JS::Value> value(aCx);
     if (!ToJSValue(aCx, error, &value)) {
       return JS::UndefinedValue();
     }
new file mode 100644
--- /dev/null
+++ b/dom/console/ConsoleCommon.h
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_ConsoleCommon_h
+#define mozilla_dom_ConsoleCommon_h
+
+#include "nsString.h"
+
+namespace mozilla {
+namespace dom {
+namespace ConsoleCommon {
+
+// This class is used to clear any exception at the end of this method.
+class MOZ_RAII ClearException
+{
+public:
+  explicit ClearException(JSContext* aCx)
+    : mCx(aCx)
+  {
+  }
+
+  ~ClearException()
+  {
+    JS_ClearPendingException(mCx);
+  }
+
+private:
+  JSContext* mCx;
+};
+
+} // namespace ConsoleCommon
+} // namespace dom
+} // namespace mozilla
+
+#endif /* mozilla_dom_ConsoleCommon_h */
--- a/dom/console/ConsoleInstance.cpp
+++ b/dom/console/ConsoleInstance.cpp
@@ -1,16 +1,18 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/ConsoleInstance.h"
 #include "mozilla/dom/ConsoleBinding.h"
+#include "ConsoleCommon.h"
+#include "ConsoleUtils.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(ConsoleInstance, mConsole)
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(ConsoleInstance)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(ConsoleInstance)
@@ -40,16 +42,33 @@ PrefToValue(const nsCString& aPref)
   if (NS_WARN_IF(index < 0)) {
     return ConsoleLogLevel::All;
   }
 
   MOZ_ASSERT(index < (int)ConsoleLogLevel::EndGuard_);
   return static_cast<ConsoleLogLevel>(index);
 }
 
+ConsoleUtils::Level
+WebIDLevelToConsoleUtilsLevel(ConsoleLevel aLevel)
+{
+  switch (aLevel) {
+    case ConsoleLevel::Log:
+      return ConsoleUtils::eLog;
+    case ConsoleLevel::Warning:
+      return ConsoleUtils::eWarning;
+    case ConsoleLevel::Error:
+      return ConsoleUtils::eError;
+    default:
+      break;
+  }
+
+  return ConsoleUtils::eLog;
+}
+
 } // anonymous
 
 ConsoleInstance::ConsoleInstance(const ConsoleInstanceOptions& aOptions)
   : mConsole(new Console(nullptr))
 {
   mConsole->mConsoleID = aOptions.mConsoleID;
   mConsole->mPassedInnerID = aOptions.mInnerID;
 
@@ -127,17 +146,17 @@ ConsoleInstance::TimeEnd(JSContext* aCx,
 {
   mConsole->StringMethodInternal(aCx, aLabel, Console::MethodTimeEnd,
                                  NS_LITERAL_STRING("timeEnd"));
 }
 
 void
 ConsoleInstance::TimeStamp(JSContext* aCx, const JS::Handle<JS::Value> aData)
 {
-  ClearException ce(aCx);
+  ConsoleCommon::ClearException ce(aCx);
 
   Sequence<JS::Value> data;
   SequenceRooter<JS::Value> rooter(aCx, &data);
 
   if (aData.isString() && !data.AppendElement(aData, fallible)) {
     return;
   }
 
@@ -179,10 +198,27 @@ ConsoleInstance::Count(JSContext* aCx, c
 void
 ConsoleInstance::Clear(JSContext* aCx)
 {
   const Sequence<JS::Value> data;
   mConsole->MethodInternal(aCx, Console::MethodClear,
                            NS_LITERAL_STRING("clear"), data);
 }
 
+void
+ConsoleInstance::ReportForServiceWorkerScope(const nsAString& aScope,
+                                             const nsAString& aMessage,
+                                             const nsAString& aFilename,
+                                             uint32_t aLineNumber,
+                                             uint32_t aColumnNumber,
+                                             ConsoleLevel aLevel)
+{
+  if (!NS_IsMainThread()) {
+    return;
+  }
+
+  ConsoleUtils::ReportForServiceWorkerScope(aScope, aMessage, aFilename,
+                                            aLineNumber, aColumnNumber,
+                                            WebIDLevelToConsoleUtilsLevel(aLevel));
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/console/ConsoleInstance.h
+++ b/dom/console/ConsoleInstance.h
@@ -89,16 +89,24 @@ public:
   Assert(JSContext* aCx, bool aCondition, const Sequence<JS::Value>& aData);
 
   void
   Count(JSContext* aCx, const nsAString& aLabel);
 
   void
   Clear(JSContext* aCx);
 
+  // For testing only.
+  void ReportForServiceWorkerScope(const nsAString& aScope,
+                                   const nsAString& aMessage,
+                                   const nsAString& aFilename,
+                                   uint32_t aLineNumber,
+                                   uint32_t aColumnNumber,
+                                   ConsoleLevel aLevel);
+
 private:
   ~ConsoleInstance();
 
   RefPtr<Console> mConsole;
 };
 
 } // dom namespace
 } // mozilla namespace
new file mode 100644
--- /dev/null
+++ b/dom/console/ConsoleUtils.cpp
@@ -0,0 +1,168 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ConsoleUtils.h"
+#include "ConsoleCommon.h"
+
+#include "mozilla/ClearOnShutdown.h"
+#include "NullPrincipal.h"
+
+namespace mozilla {
+namespace dom {
+
+namespace {
+
+StaticRefPtr<ConsoleUtils> gConsoleUtilsService;
+
+}
+
+/* static */ ConsoleUtils*
+ConsoleUtils::GetOrCreate()
+{
+  if (!gConsoleUtilsService) {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    gConsoleUtilsService = new ConsoleUtils();
+    ClearOnShutdown(&gConsoleUtilsService);
+  }
+
+  return gConsoleUtilsService;
+}
+
+ConsoleUtils::ConsoleUtils() = default;
+ConsoleUtils::~ConsoleUtils() = default;
+
+/* static */ void
+ConsoleUtils::ReportForServiceWorkerScope(const nsAString& aScope,
+                                          const nsAString& aMessage,
+                                          const nsAString& aFilename,
+                                          uint32_t aLineNumber,
+                                          uint32_t aColumnNumber,
+                                          Level aLevel)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  RefPtr<ConsoleUtils> service = ConsoleUtils::GetOrCreate();
+  if (NS_WARN_IF(!service)) {
+    return;
+  }
+
+  service->ReportForServiceWorkerScopeInternal(aScope, aMessage, aFilename,
+                                               aLineNumber, aColumnNumber,
+                                               aLevel);
+}
+
+void
+ConsoleUtils::ReportForServiceWorkerScopeInternal(const nsAString& aScope,
+                                                  const nsAString& aMessage,
+                                                  const nsAString& aFilename,
+                                                  uint32_t aLineNumber,
+                                                  uint32_t aColumnNumber,
+                                                  Level aLevel)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  AutoJSAPI jsapi;
+  jsapi.Init();
+
+  JSContext* cx = jsapi.cx();
+
+  ConsoleCommon::ClearException ce(cx);
+  JS::Rooted<JSObject*> global(cx, GetOrCreateSandbox(cx));
+  if (NS_WARN_IF(!global)) {
+    return;
+  }
+
+  // The GetOrCreateSandbox call returns a proxy to the actual sandbox object.
+  // We don't need a proxy here.
+  global = js::UncheckedUnwrap(global);
+
+  JSAutoCompartment ac(cx, global);
+
+  RootedDictionary<ConsoleEvent> event(cx);
+
+  event.mID.Construct();
+  event.mID.Value().SetAsString() = aScope;
+
+  event.mInnerID.Construct();
+  event.mInnerID.Value().SetAsString() = NS_LITERAL_STRING("ServiceWorker");
+
+  switch (aLevel) {
+    case eLog:
+      event.mLevel = NS_LITERAL_STRING("log");
+      break;
+
+    case eWarning:
+      event.mLevel = NS_LITERAL_STRING("warn");
+      break;
+
+    case eError:
+      event.mLevel = NS_LITERAL_STRING("error");
+      break;
+  }
+
+  event.mFilename = aFilename;
+  event.mLineNumber = aLineNumber;
+  event.mColumnNumber = aColumnNumber;
+  event.mTimeStamp = JS_Now() / PR_USEC_PER_MSEC;
+
+  JS::Rooted<JS::Value> messageValue(cx);
+  if (!dom::ToJSValue(cx, aMessage, &messageValue)) {
+    return;
+  }
+
+  event.mArguments.Construct();
+  if (!event.mArguments.Value().AppendElement(messageValue, fallible)) {
+    return;
+  }
+
+  nsCOMPtr<nsIConsoleAPIStorage> storage =
+    do_GetService("@mozilla.org/consoleAPI-storage;1");
+
+  if (NS_WARN_IF(!storage)) {
+    return;
+  }
+
+  JS::Rooted<JS::Value> eventValue(cx);
+  if (!ToJSValue(cx, event, &eventValue)) {
+    return;
+  }
+
+  // This is a legacy property.
+  JS::Rooted<JSObject*> eventObj(cx, &eventValue.toObject());
+  if (NS_WARN_IF(!JS_DefineProperty(cx, eventObj, "wrappedJSObject", eventObj,
+                                    JSPROP_ENUMERATE))) {
+    return;
+  }
+
+  storage->RecordEvent(NS_LITERAL_STRING("ServiceWorker"), aScope, eventValue);
+}
+
+JSObject*
+ConsoleUtils::GetOrCreateSandbox(JSContext* aCx)
+{
+  AssertIsOnMainThread();
+
+  if (!mSandbox) {
+    nsIXPConnect* xpc = nsContentUtils::XPConnect();
+    MOZ_ASSERT(xpc, "This should never be null!");
+
+    RefPtr<NullPrincipal> nullPrincipal = NullPrincipal::Create();
+
+    JS::Rooted<JSObject*> sandbox(aCx);
+    nsresult rv = xpc->CreateSandbox(aCx, nullPrincipal, sandbox.address());
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return nullptr;
+    }
+
+    mSandbox = new JSObjectHolder(aCx, sandbox);
+  }
+
+  return mSandbox->GetJSObject();
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/console/ConsoleUtils.h
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_ConsoleUtils_h
+#define mozilla_dom_ConsoleUtils_h
+
+#include "mozilla/JSObjectHolder.h"
+#include "nsISupportsImpl.h"
+#include "nsString.h"
+
+namespace mozilla {
+namespace dom {
+
+class ConsoleUtils final
+{
+public:
+  NS_INLINE_DECL_REFCOUNTING(ConsoleUtils)
+
+  enum Level {
+    eLog,
+    eWarning,
+    eError,
+  };
+
+  // Main-thread only, reports a console message from a ServiceWorker.
+  static void
+  ReportForServiceWorkerScope(const nsAString& aScope,
+                              const nsAString& aMessage,
+                              const nsAString& aFilename,
+                              uint32_t aLineNumber,
+                              uint32_t aColumnNumber,
+                              Level aLevel);
+
+private:
+  ConsoleUtils();
+  ~ConsoleUtils();
+
+  static ConsoleUtils*
+  GetOrCreate();
+
+  JSObject*
+  GetOrCreateSandbox(JSContext* aCx);
+
+  void
+  ReportForServiceWorkerScopeInternal(const nsAString& aScope,
+                                      const nsAString& aMessage,
+                                      const nsAString& aFilename,
+                                      uint32_t aLineNumber,
+                                      uint32_t aColumnNumber,
+                                      Level aLevel);
+
+  RefPtr<JSObjectHolder> mSandbox;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif /* mozilla_dom_ConsoleUtils_h */
--- a/dom/console/moz.build
+++ b/dom/console/moz.build
@@ -19,22 +19,24 @@ EXPORTS += [
 
 EXPORTS.mozilla += [
     'ConsoleReportCollector.h',
 ]
 
 EXPORTS.mozilla.dom += [
     'Console.h',
     'ConsoleInstance.h',
+    'ConsoleUtils.h',
 ]
 
 UNIFIED_SOURCES += [
     'Console.cpp',
     'ConsoleInstance.cpp',
     'ConsoleReportCollector.cpp',
+    'ConsoleUtils.cpp',
 ]
 
 EXTRA_COMPONENTS += [
     'ConsoleAPI.manifest',
     'ConsoleAPIStorage.js',
 ]
 
 LOCAL_INCLUDES += [
new file mode 100644
--- /dev/null
+++ b/dom/console/tests/xpcshell/test_reportForServiceWorkerScope.js
@@ -0,0 +1,34 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+add_task(async function() {
+  let p = new Promise(resolve => {
+    function consoleListener() {
+      Services.obs.addObserver(this, "console-api-log-event");
+    }
+
+    consoleListener.prototype  = {
+      observe: function(aSubject, aTopic, aData) {
+        let obj = aSubject.wrappedJSObject;
+        Assert.ok(obj.arguments[0] === "Hello world!", "Message received!");
+        Assert.ok(obj.ID === "scope", "The ID is the scope");
+        Assert.ok(obj.innerID === "ServiceWorker", "The innerID is ServiceWorker");
+        Assert.ok(obj.filename === "filename", "The filename matches");
+        Assert.ok(obj.lineNumber === 42, "The lineNumber matches");
+        Assert.ok(obj.columnNumber === 24, "The columnNumber matches");
+        Assert.ok(obj.level === "error", "The level is correct");
+
+        Services.obs.removeObserver(this, "console-api-log-event");
+        resolve();
+      }
+    };
+
+    new consoleListener();
+  });
+
+  let ci = console.createInstance();
+  ci.reportForServiceWorkerScope("scope", "Hello world!", "filename", 42, 24, "error");
+  await p;
+});
--- a/dom/console/tests/xpcshell/xpcshell.ini
+++ b/dom/console/tests/xpcshell/xpcshell.ini
@@ -1,5 +1,6 @@
 [DEFAULT]
 head =
 support-files =
 
 [test_basic.js]
+[test_reportForServiceWorkerScope.js]
--- a/dom/events/EventStates.h
+++ b/dom/events/EventStates.h
@@ -145,18 +145,19 @@ public:
    *
    * @return Whether the object has all states from aEventStates
    */
   bool HasAllStates(EventStates aEventStates) const
   {
     return (mStates & aEventStates.mStates) == aEventStates.mStates;
   }
 
-  // We only need that method for inDOMUtils::GetContentState.
-  // If inDOMUtils::GetContentState is removed, this method should be removed.
+  // We only need that method for InspectorUtils::GetContentState.
+  // If InspectorUtils::GetContentState is removed, this method should
+  // be removed.
   InternalType GetInternalValue() const {
     return mStates;
   }
 
   /**
    * Method used to get the appropriate state representation for Servo.
    */
   ServoType ServoValue() const
--- a/dom/html/test/test_anchor_ping.html
+++ b/dom/html/test/test_anchor_ping.html
@@ -33,25 +33,20 @@ addLoadEvent(function () {
 });
 
 let tests = [
 
   // Ensure that sending pings is enabled.
   function setup() {
     Services.prefs.setBoolPref("browser.send_pings", true);
     Services.prefs.setIntPref("browser.send_pings.max_per_link", -1);
-    Services.prefs.setBoolPref("security.mixed_content.block_active_content", false);
-    // The server we create can't handle the priming HEAD requests
-    Services.prefs.setBoolPref("security.mixed_content.send_hsts_priming", false);
 
     SimpleTest.registerCleanupFunction(() => {
       Services.prefs.clearUserPref("browser.send_pings");
       Services.prefs.clearUserPref("browser.send_pings.max_per_link");
-      Services.prefs.clearUserPref("security.mixed_content.block_active_content");
-      Services.prefs.clearUserPref("security.mixed_content.send_hsts_priming");
     });
   },
 
   // If both the address of the document containing the hyperlink being audited
   // and ping URL have the same origin then the request must include a Ping-From
   // HTTP header with, as its value, the address of the document containing the
   // hyperlink, and a Ping-To HTTP header with, as its value, the target URL.
   // The request must not include a Referer (sic) HTTP header.
@@ -144,17 +139,17 @@ let tests = [
       return {to: "https://example.com/"};
     });
 
     // Start the server and run the test.
     server.start(-1);
 
     // The referrer will be loaded using a secure channel.
     navigate("https://example.com/chrome/dom/html/test/" +
-             "file_anchor_ping.html?" + "http://localhost:" +
+             "file_anchor_ping.html?" + "http://127.0.0.1:" +
              server.identity.primaryPort + ping);
 
     // Wait until the ping has been sent.
     await promisePing;
 
     // Cleanup.
     await stopServer(server);
   },
--- a/dom/interfaces/base/domstubs.idl
+++ b/dom/interfaces/base/domstubs.idl
@@ -75,12 +75,8 @@ interface nsIDOMRect;
 interface nsIDOMCSSStyleRule;
 interface nsIDOMCSSStyleRuleCollection;
 
 // Range
 interface nsIDOMRange;
 
 // Crypto
 interface nsIDOMCrypto;
-
-// Used font face (for inspector)
-interface nsIDOMFontFace;
-interface nsIDOMFontFaceList;
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -3561,24 +3561,24 @@ ContentParent::RecvIsSecureURI(const uin
                                  nullptr, aIsSecureURI);
   if (NS_FAILED(rv)) {
     return IPC_FAIL_NO_REASON(this);
   }
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
-ContentParent::RecvAccumulateMixedContentHSTS(const URIParams& aURI, const bool& aActive, const bool& aHSTSPriming,
+ContentParent::RecvAccumulateMixedContentHSTS(const URIParams& aURI, const bool& aActive,
                                               const OriginAttributes& aOriginAttributes)
 {
   nsCOMPtr<nsIURI> ourURI = DeserializeURI(aURI);
   if (!ourURI) {
     return IPC_FAIL_NO_REASON(this);
   }
-  nsMixedContentBlocker::AccumulateMixedContentHSTS(ourURI, aActive, aHSTSPriming, aOriginAttributes);
+  nsMixedContentBlocker::AccumulateMixedContentHSTS(ourURI, aActive, aOriginAttributes);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 ContentParent::RecvLoadURIExternal(const URIParams& uri,
                                    PBrowserParent* windowContext)
 {
   nsCOMPtr<nsIExternalProtocolService> extProtService(do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID));
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -889,17 +889,16 @@ private:
 
   virtual mozilla::ipc::IPCResult RecvIsSecureURI(const uint32_t& aType, const URIParams& aURI,
                                                   const uint32_t& aFlags,
                                                   const OriginAttributes& aOriginAttributes,
                                                   bool* aIsSecureURI) override;
 
   virtual mozilla::ipc::IPCResult RecvAccumulateMixedContentHSTS(const URIParams& aURI,
                                                                  const bool& aActive,
-                                                                 const bool& aHSTSPriming,
                                                                  const OriginAttributes& aOriginAttributes) override;
 
   virtual bool DeallocPHalParent(PHalParent*) override;
 
   virtual bool
   DeallocPHeapSnapshotTempFileHelperParent(PHeapSnapshotTempFileHelperParent*) override;
 
   virtual PCycleCollectWithLogsParent*
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -738,17 +738,17 @@ parent:
     async PRemoteSpellcheckEngine();
 
     async InitCrashReporter(Shmem shmem, NativeThreadId tid);
 
     sync IsSecureURI(uint32_t aType, URIParams aURI, uint32_t aFlags,
                      OriginAttributes aOriginAttributes)
         returns (bool isSecureURI);
 
-    async AccumulateMixedContentHSTS(URIParams aURI, bool aActive, bool aHasHSTSPriming,
+    async AccumulateMixedContentHSTS(URIParams aURI, bool aActive,
                                      OriginAttributes aOriginAttributes);
 
     nested(inside_cpow) async PHal();
 
     async PHeapSnapshotTempFileHelper();
 
     async PNecko();
 
--- a/dom/manifest/ValueExtractor.jsm
+++ b/dom/manifest/ValueExtractor.jsm
@@ -4,19 +4,22 @@
 /*
  * Helper functions extract values from manifest members
  * and reports conformance violations.
  */
 /*globals Components*/
 'use strict';
 const {
   classes: Cc,
-  interfaces: Ci
+  interfaces: Ci,
+  utils: Cu
 } = Components;
 
+Cu.importGlobalProperties(["InspectorUtils"]);
+
 function ValueExtractor(aConsole, aBundle) {
   this.console = aConsole;
   this.domBundle = aBundle;
 }
 
 ValueExtractor.prototype = {
   // This function takes a 'spec' object and destructures
   // it to extract a value. If the value is of th wrong type, it
@@ -43,20 +46,18 @@ ValueExtractor.prototype = {
     const shouldTrim = expectedType === 'string' && value && trim;
     if (shouldTrim) {
       return value.trim() || undefined;
     }
     return value;
   },
   extractColorValue(spec) {
     const value = this.extractValue(spec);
-    const DOMUtils = Cc['@mozilla.org/inspector/dom-utils;1']
-      .getService(Ci.inIDOMUtils);
     let color;
-    if (DOMUtils.isValidCSSColor(value)) {
+    if (InspectorUtils.isValidCSSColor(value)) {
       color = value;
     } else if (value) {
       this.console.warn(this.domBundle.formatStringFromName("ManifestInvalidCSSColor",
                                                             [spec.property, value],
                                                             2));
     }
     return color;
   }
--- a/dom/security/nsContentSecurityManager.cpp
+++ b/dom/security/nsContentSecurityManager.cpp
@@ -10,18 +10,16 @@
 #include "nsIChannel.h"
 #include "nsIHttpChannelInternal.h"
 #include "nsIStreamListener.h"
 #include "nsILoadInfo.h"
 #include "nsIOService.h"
 #include "nsContentUtils.h"
 #include "nsCORSListenerProxy.h"
 #include "nsIStreamListener.h"
-#include "nsIDocument.h"
-#include "nsMixedContentBlocker.h"
 #include "nsCDefaultURIFixup.h"
 #include "nsIURIFixup.h"
 #include "nsIImageLoadingContent.h"
 
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/TabChild.h"
 
 NS_IMPL_ISUPPORTS(nsContentSecurityManager,
@@ -507,23 +505,16 @@ DoContentSecurityChecks(nsIChannel* aCha
         (contentPolicyType == nsIContentPolicy::TYPE_DOCUMENT ||
          contentPolicyType == nsIContentPolicy::TYPE_SUBDOCUMENT)) {
       // for docshell loads we might have to return SHOW_ALT.
       return NS_ERROR_CONTENT_BLOCKED_SHOW_ALT;
     }
     return NS_ERROR_CONTENT_BLOCKED;
   }
 
-  if (nsMixedContentBlocker::sSendHSTSPriming) {
-    rv = nsMixedContentBlocker::MarkLoadInfoForPriming(uri,
-                                                       requestingContext,
-                                                       aLoadInfo);
-    return rv;
-  }
-
   return NS_OK;
 }
 
 /*
  * Based on the security flags provided in the loadInfo of the channel,
  * doContentSecurityCheck() performs the following content security checks
  * before opening the channel:
  *
--- a/dom/security/nsMixedContentBlocker.cpp
+++ b/dom/security/nsMixedContentBlocker.cpp
@@ -53,73 +53,23 @@ enum nsMixedContentBlockerMessageType {
 // iframes, websockets, XHR) enabled?
 bool nsMixedContentBlocker::sBlockMixedScript = false;
 
 bool nsMixedContentBlocker::sBlockMixedObjectSubrequest = false;
 
 // Is mixed display content blocking (images, audio, video, <a ping>) enabled?
 bool nsMixedContentBlocker::sBlockMixedDisplay = false;
 
-// Do we move HSTS before mixed-content
-bool nsMixedContentBlocker::sUseHSTS = false;
-// Do we send an HSTS priming request
-bool nsMixedContentBlocker::sSendHSTSPriming = false;
-// Default HSTS Priming failure timeout to 7 days, in seconds
-uint32_t nsMixedContentBlocker::sHSTSPrimingCacheTimeout = (60 * 60 * 24 * 7);
-
-bool
-IsEligibleForHSTSPriming(nsIURI* aContentLocation) {
-  bool isHttpScheme = false;
-  nsresult rv = aContentLocation->SchemeIs("http", &isHttpScheme);
-  NS_ENSURE_SUCCESS(rv, false);
-  if (!isHttpScheme) {
-    return false;
-  }
-
-  int32_t port = -1;
-  rv = aContentLocation->GetPort(&port);
-  NS_ENSURE_SUCCESS(rv, false);
-  int32_t defaultPort = NS_GetDefaultPort("https");
-
-  if (port != -1 && port != defaultPort) {
-    // HSTS priming requests are only sent if the port is the default port
-    return false;
-  }
-
-  nsAutoCString hostname;
-  rv = aContentLocation->GetHost(hostname);
-  NS_ENSURE_SUCCESS(rv, false);
-
-  PRNetAddr hostAddr;
-  return (PR_StringToNetAddr(hostname.get(), &hostAddr) != PR_SUCCESS);
-}
-
 enum MixedContentHSTSState {
   MCB_HSTS_PASSIVE_NO_HSTS   = 0,
   MCB_HSTS_PASSIVE_WITH_HSTS = 1,
   MCB_HSTS_ACTIVE_NO_HSTS    = 2,
   MCB_HSTS_ACTIVE_WITH_HSTS  = 3
 };
 
-// Similar to the existing mixed-content HSTS, except MCB_HSTS_*_NO_HSTS is
-// broken into two distinct states, indicating whether we plan to send a priming
-// request or not. If we decided not go send a priming request, it could be
-// because it is a type we do not support, or because we cached a previous
-// negative response.
-enum MixedContentHSTSPrimingState {
-  eMCB_HSTS_PASSIVE_WITH_HSTS  = 0,
-  eMCB_HSTS_ACTIVE_WITH_HSTS   = 1,
-  eMCB_HSTS_PASSIVE_NO_PRIMING = 2,
-  eMCB_HSTS_PASSIVE_DO_PRIMING = 3,
-  eMCB_HSTS_ACTIVE_NO_PRIMING  = 4,
-  eMCB_HSTS_ACTIVE_DO_PRIMING  = 5,
-  eMCB_HSTS_PASSIVE_UPGRADE    = 6,
-  eMCB_HSTS_ACTIVE_UPGRADE     = 7,
-};
-
 // Fired at the document that attempted to load mixed content.  The UI could
 // handle this event, for example, by displaying an info bar that offers the
 // choice to reload the page with mixed content permitted.
 class nsMixedContentEvent : public Runnable
 {
 public:
   nsMixedContentEvent(nsISupports* aContext,
                       MixedContentTypes aType,
@@ -259,28 +209,16 @@ nsMixedContentBlocker::nsMixedContentBlo
                                "security.mixed_content.block_active_content");
 
   Preferences::AddBoolVarCache(&sBlockMixedObjectSubrequest,
                                "security.mixed_content.block_object_subrequest");
 
   // Cache the pref for mixed display blocking
   Preferences::AddBoolVarCache(&sBlockMixedDisplay,
                                "security.mixed_content.block_display_content");
-
-  // Cache the pref for HSTS
-  Preferences::AddBoolVarCache(&sUseHSTS,
-                               "security.mixed_content.use_hsts");
-
-  // Cache the pref for sending HSTS priming
-  Preferences::AddBoolVarCache(&sSendHSTSPriming,
-                               "security.mixed_content.send_hsts_priming");
-
-  // Cache the pref for HSTS priming failure cache time
-  Preferences::AddUintVarCache(&sHSTSPrimingCacheTimeout,
-                               "security.mixed_content.hsts_priming_cache_timeout");
 }
 
 nsMixedContentBlocker::~nsMixedContentBlocker()
 {
 }
 
 NS_IMPL_ISUPPORTS(nsMixedContentBlocker, nsIContentPolicy, nsIChannelEventSink)
 
@@ -400,36 +338,16 @@ nsMixedContentBlocker::AsyncOnChannelRed
                   requestingPrincipal,
                   &decision);
   if (NS_FAILED(rv)) {
     autoCallback.DontCallback();
     aOldChannel->Cancel(NS_ERROR_DOM_BAD_URI);
     return NS_BINDING_FAILED;
   }
 
-  if (nsMixedContentBlocker::sSendHSTSPriming) {
-    // The LoadInfo passed in is for the original channel, HSTS priming needs to
-    // be set on the new channel, if required. If the redirect changes
-    // http->https, or vice-versa, the need for priming may change.
-    nsCOMPtr<nsILoadInfo> newLoadInfo;
-    rv = aNewChannel->GetLoadInfo(getter_AddRefs(newLoadInfo));
-    NS_ENSURE_SUCCESS(rv, rv);
-    if (newLoadInfo) {
-      rv = nsMixedContentBlocker::MarkLoadInfoForPriming(newUri,
-                                                         requestingContext,
-                                                         newLoadInfo);
-      if (NS_FAILED(rv)) {
-        decision = REJECT_REQUEST;
-        newLoadInfo->ClearHSTSPriming();
-      }
-    } else {
-      decision = REJECT_REQUEST;
-    }
-  }
-
   // If the channel is about to load mixed content, abort the channel
   if (!NS_CP_ACCEPTED(decision)) {
     autoCallback.DontCallback();
     aOldChannel->Cancel(NS_ERROR_DOM_BAD_URI);
     return NS_BINDING_FAILED;
   }
 
   return NS_OK;
@@ -941,71 +859,41 @@ nsMixedContentBlocker::ShouldLoad(bool a
 
   OriginAttributes originAttributes;
   if (principal) {
     originAttributes = principal->OriginAttributesRef();
   } else if (aRequestPrincipal) {
     originAttributes = aRequestPrincipal->OriginAttributesRef();
   }
 
-  bool active = (classification == eMixedScript);
-  bool doHSTSPriming = false;
-  if (IsEligibleForHSTSPriming(aContentLocation)) {
-    bool hsts = false;
-    bool cached = false;
-    nsCOMPtr<nsISiteSecurityService> sss =
-      do_GetService(NS_SSSERVICE_CONTRACTID, &rv);
-    NS_ENSURE_SUCCESS(rv, rv);
-    rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HSTS, aContentLocation,
-        0, originAttributes, &cached, nullptr, &hsts);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    if (hsts && sUseHSTS) {
-      // assume we will be upgraded later
-      Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING_2,
-          (active) ? MixedContentHSTSPrimingState::eMCB_HSTS_ACTIVE_UPGRADE
-                   : MixedContentHSTSPrimingState::eMCB_HSTS_PASSIVE_UPGRADE);
-      *aDecision = ACCEPT;
-      return NS_OK;
-    }
-
-    // Send a priming request if the result is not already cached and priming
-    // requests are allowed
-    if (!cached && sSendHSTSPriming) {
-      // add this URI as a priming location
-      doHSTSPriming = true;
-      document->AddHSTSPrimingLocation(innerContentLocation,
-          HSTSPrimingState::eHSTS_PRIMING_ALLOW);
-      *aDecision = ACCEPT;
-    }
-  }
 
   // At this point we know that the request is mixed content, and the only
   // question is whether we block it.  Record telemetry at this point as to
   // whether HSTS would have fixed things by making the content location
   // into an HTTPS URL.
   //
   // Note that we count this for redirects as well as primary requests. This
   // will cause some degree of double-counting, especially when mixed content
   // is not blocked (e.g., for images).  For more detail, see:
   //   https://bugzilla.mozilla.org/show_bug.cgi?id=1198572#c19
   //
   // We do not count requests aHadInsecureImageRedirect=true, since these are
   // just an artifact of the image caching system.
+  bool active = (classification == eMixedScript);
   if (!aHadInsecureImageRedirect) {
     if (XRE_IsParentProcess()) {
-      AccumulateMixedContentHSTS(innerContentLocation, active, doHSTSPriming,
+      AccumulateMixedContentHSTS(innerContentLocation, active,
                                  originAttributes);
     } else {
       // Ask the parent process to do the same call
       mozilla::dom::ContentChild* cc = mozilla::dom::ContentChild::GetSingleton();
       if (cc) {
         mozilla::ipc::URIParams uri;
         SerializeURI(innerContentLocation, uri);
-        cc->SendAccumulateMixedContentHSTS(uri, active, doHSTSPriming,
+        cc->SendAccumulateMixedContentHSTS(uri, active,
                                            originAttributes);
       }
     }
   }
 
   // set hasMixedContentObjectSubrequest on this object if necessary
   if (aContentType == TYPE_OBJECT_SUBREQUEST) {
     rootDoc->SetHasMixedContentObjectSubrequest(true);
@@ -1039,23 +927,17 @@ nsMixedContentBlocker::ShouldLoad(bool a
       } else {
         // User has overriden the pref and the root is not https;
         // mixed display content was allowed on an https subframe.
         if (NS_SUCCEEDED(stateRV)) {
           eventSink->OnSecurityChange(aRequestingContext, (state | nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT));
         }
       }
     } else {
-      if (doHSTSPriming) {
-        document->AddHSTSPrimingLocation(innerContentLocation,
-            HSTSPrimingState::eHSTS_PRIMING_BLOCK);
-        *aDecision = nsIContentPolicy::ACCEPT;
-      } else {
-        *aDecision = nsIContentPolicy::REJECT_REQUEST;
-      }
+      *aDecision = nsIContentPolicy::REJECT_REQUEST;
       LogMixedContentMessage(classification, aContentLocation, rootDoc, eBlocked);
       if (!rootDoc->GetHasMixedDisplayContentBlocked() && NS_SUCCEEDED(stateRV)) {
         rootDoc->SetHasMixedDisplayContentBlocked(true);
         eventSink->OnSecurityChange(aRequestingContext, (state | nsIWebProgressListener::STATE_BLOCKED_MIXED_DISPLAY_CONTENT));
       }
     }
     return NS_OK;
 
@@ -1091,23 +973,17 @@ nsMixedContentBlocker::ShouldLoad(bool a
         // mixed active content was allowed on an https subframe.
         if (NS_SUCCEEDED(stateRV)) {
           eventSink->OnSecurityChange(aRequestingContext, (state | nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT));
         }
         return NS_OK;
       }
     } else {
       //User has not overriden the pref by Disabling protection. Reject the request and update the security state.
-      if (doHSTSPriming) {
-        document->AddHSTSPrimingLocation(innerContentLocation,
-            HSTSPrimingState::eHSTS_PRIMING_BLOCK);
-        *aDecision = nsIContentPolicy::ACCEPT;
-      } else {
-        *aDecision = nsIContentPolicy::REJECT_REQUEST;
-      }
+      *aDecision = nsIContentPolicy::REJECT_REQUEST;
       LogMixedContentMessage(classification, aContentLocation, rootDoc, eBlocked);
       // See if the pref will change here. If it will, only then do we need to call OnSecurityChange() to update the UI.
       if (rootDoc->GetHasMixedActiveContentBlocked()) {
         return NS_OK;
       }
       rootDoc->SetHasMixedActiveContentBlocked(true);
 
       // The user has not overriden the pref, so make sure they still have an option by calling eventSink
@@ -1159,18 +1035,17 @@ nsMixedContentBlocker::ShouldProcess(uin
                     aRequestingContext, aMimeGuess, aExtra, aRequestPrincipal,
                     aDecision);
 }
 
 // Record information on when HSTS would have made mixed content not mixed
 // content (regardless of whether it was actually blocked)
 void
 nsMixedContentBlocker::AccumulateMixedContentHSTS(
-  nsIURI* aURI, bool aActive, bool aHasHSTSPriming,
-  const OriginAttributes& aOriginAttributes)
+  nsIURI* aURI, bool aActive, const OriginAttributes& aOriginAttributes)
 {
   // This method must only be called in the parent, because
   // nsSiteSecurityService is only available in the parent
   if (!XRE_IsParentProcess()) {
     MOZ_ASSERT(false);
     return;
   }
 
@@ -1188,106 +1063,24 @@ nsMixedContentBlocker::AccumulateMixedCo
 
   // states: would upgrade, would prime, hsts info cached
   // active, passive
   //
   if (!aActive) {
     if (!hsts) {
       Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS,
                             MCB_HSTS_PASSIVE_NO_HSTS);
-      if (aHasHSTSPriming) {
-        Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING_2,
-                              eMCB_HSTS_PASSIVE_DO_PRIMING);
-      } else {
-        Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING_2,
-                              eMCB_HSTS_PASSIVE_NO_PRIMING);
-      }
     }
     else {
       Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS,
                             MCB_HSTS_PASSIVE_WITH_HSTS);
-      Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING_2,
-                            eMCB_HSTS_PASSIVE_WITH_HSTS);
     }
   } else {
     if (!hsts) {
       Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS,
                             MCB_HSTS_ACTIVE_NO_HSTS);
-      if (aHasHSTSPriming) {
-        Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING_2,
-                              eMCB_HSTS_ACTIVE_DO_PRIMING);
-      } else {
-        Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING_2,
-                              eMCB_HSTS_ACTIVE_NO_PRIMING);
-      }
     }
     else {
       Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS,
                             MCB_HSTS_ACTIVE_WITH_HSTS);
-      Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING_2,
-                            eMCB_HSTS_ACTIVE_WITH_HSTS);
     }
   }
 }
-
-//static
-nsresult
-nsMixedContentBlocker::MarkLoadInfoForPriming(nsIURI* aURI,
-                                              nsISupports* aRequestingContext,
-                                              nsILoadInfo* aLoadInfo)
-{
-  nsresult rv;
-  bool sendPriming = false;
-  bool mixedContentWouldBlock = false;
-  rv = GetHSTSPrimingFromRequestingContext(aURI,
-                                           aRequestingContext,
-                                           &sendPriming,
-                                           &mixedContentWouldBlock);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  if (sendPriming) {
-    aLoadInfo->SetHSTSPriming(mixedContentWouldBlock);
-  }
-
-  return NS_OK;
-}
-
-//static
-nsresult
-nsMixedContentBlocker::GetHSTSPrimingFromRequestingContext(nsIURI* aURI,
-    nsISupports* aRequestingContext,
-    bool* aSendPrimingRequest,
-    bool* aMixedContentWouldBlock)
-{
-  *aSendPrimingRequest = false;
-  *aMixedContentWouldBlock = false;
-  // If we marked for priming, we used the innermost URI, so get that
-  nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(aURI);
-  if (!innerURI) {
-    NS_ERROR("Can't get innerURI from aContentLocation");
-    return NS_ERROR_CONTENT_BLOCKED;
-  }
-
-  bool isHttp = false;
-  innerURI->SchemeIs("http", &isHttp);
-  if (!isHttp) {
-    // there is nothign to do
-    return NS_OK;
-  }
-
-  // If the DocShell was marked for HSTS priming, propagate that to the LoadInfo
-  nsCOMPtr<nsIDocShell> docShell = NS_CP_GetDocShellFromContext(aRequestingContext);
-  if (!docShell) {
-    return NS_OK;
-  }
-  nsCOMPtr<nsIDocument> document = docShell->GetDocument();
-  if (!document) {
-    return NS_OK;
-  }
-
-  HSTSPrimingState status = document->GetHSTSPrimingStateForLocation(innerURI);
-  if (status != HSTSPrimingState::eNO_HSTS_PRIMING) {
-    *aSendPrimingRequest = (status != HSTSPrimingState::eNO_HSTS_PRIMING);
-    *aMixedContentWouldBlock = (status == HSTSPrimingState::eHSTS_PRIMING_BLOCK);
-  }
-
-  return NS_OK;
-}
--- a/dom/security/nsMixedContentBlocker.h
+++ b/dom/security/nsMixedContentBlocker.h
@@ -64,50 +64,16 @@ public:
                              nsIURI* aRequestingLocation,
                              nsISupports* aRequestingContext,
                              const nsACString& aMimeGuess,
                              nsISupports* aExtra,
                              nsIPrincipal* aRequestPrincipal,
                              int16_t* aDecision);
   static void AccumulateMixedContentHSTS(nsIURI* aURI,
                                          bool aActive,
-                                         bool aHasHSTSPriming,
                                          const OriginAttributes& aOriginAttributes);
-  /* If the document associated with aRequestingContext requires priming for
-   * aURI, propagate that to the LoadInfo so the HttpChannel will find out about
-   * it.
-   *
-   * @param aURI The URI associated with the load
-   * @param aRequestingContext the requesting context passed to ShouldLoad
-   * @param aLoadInfo the LoadInfo for the load
-   */
-  static nsresult MarkLoadInfoForPriming(nsIURI* aURI,
-                                         nsISupports* aRequestingContext,
-                                         nsILoadInfo* aLoadInfo);
-
-  /* Given a context, return whether HSTS was marked on the document associated
-   * with the load for the given URI. This is used by MarkLoadInfoForPriming and
-   * directly by the image loader to determine whether to allow a load to occur
-   * from the cache.
-   *
-   * @param aURI The URI associated with the load
-   * @param aRequestingContext the requesting context passed to ShouldLoad
-   * @param aSendPrimingRequest out true if priming is required on the channel
-   * @param aMixedContentWouldBlock out true if mixed content would block
-   */
-  static nsresult GetHSTSPrimingFromRequestingContext(nsIURI* aURI,
-                                                      nsISupports* aRequestingContext,
-                                                      bool* aSendPrimingRequest,
-                                                      bool* aMixedContentWouldBlock);
-
 
   static bool sBlockMixedScript;
   static bool sBlockMixedObjectSubrequest;
   static bool sBlockMixedDisplay;
-  // Do we move HSTS before mixed-content
-  static bool sUseHSTS;
-  // Do we send an HSTS priming request
-  static bool sSendHSTSPriming;
-  // Default HSTS Priming failure timeout in seconds
-  static uint32_t sHSTSPrimingCacheTimeout;
 };
 
 #endif /* nsMixedContentBlocker_h___ */
--- a/dom/security/test/csp/test_referrerdirective.html
+++ b/dom/security/test/csp/test_referrerdirective.html
@@ -111,18 +111,16 @@ var referrerDirectiveTests = {
   }
 };
 
 SimpleTest.waitForExplicitFinish();
 // have to disable mixed content blocking to test https->http referrers.
 SpecialPowers.pushPrefEnv({
     'set': [['security.mixed_content.block_active_content',   false],
             ['security.mixed_content.block_display_content',  false],
-            ['security.mixed_content.send_hsts_priming',  false],
-            ['security.mixed_content.use_hsts',  false],
     ]
     },
     function() {
       // each of the iframes we create will call us back when its contents are loaded.
       window.addEventListener("message", referrerDirectiveTests.onIframeComplete.bind(window));
 
       // one iframe created for each test case
       for (var id in testData) {
deleted file mode 100644
--- a/dom/security/test/hsts/browser.ini
+++ /dev/null
@@ -1,25 +0,0 @@
-[DEFAULT]
-skip-if = true # Bug 1425968 - Depends on an expiring Telemetry probe and feature is being removed
-support-files =
-  head.js
-  file_priming-top.html
-  file_testserver.sjs
-  file_1x1.png
-  file_priming.js
-  file_stylesheet.css
-
-[browser_hsts-priming_allow_active.js]
-[browser_hsts-priming_block_active.js]
-[browser_hsts-priming_hsts_after_mixed.js]
-skip-if = os == "linux" # Bug 1311239
-[browser_hsts-priming_allow_display.js]
-[browser_hsts-priming_block_display.js]
-[browser_hsts-priming_block_active_css.js]
-[browser_hsts-priming_block_active_with_redir_same.js]
-[browser_hsts-priming_no-duplicates.js]
-[browser_hsts-priming_cache-timeout.js]
-[browser_hsts-priming_timeout.js]
-[browser_hsts-priming_no-non-standard-ports.js]
-[browser_hsts-priming_no-ip-address.js]
-[browser_hsts-priming_include-subdomains.js]
-skip-if = os == "linux" # Bug 1376238
deleted file mode 100644
--- a/dom/security/test/hsts/browser_hsts-priming_allow_active.js
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Description of the test:
- *   Check that HSTS priming occurs correctly with mixed content when active
- *   content is allowed.
- */
-'use strict';
-
-var expected_telemetry = {
-  "histograms": {
-    "MIXED_CONTENT_HSTS_PRIMING_RESULT": 3,
-    "MIXED_CONTENT_HSTS_PRIMING_REQUESTS": 6,
-    "HSTS_UPGRADE_SOURCE": [ 0,0,1,0,0,0,0,0,0 ]
-  },
-  "keyed-histograms": {
-    "HSTS_PRIMING_REQUEST_DURATION": {
-      "success": 1,
-      "failure": 2,
-    },
-  }
-};
-
-//jscs:disable
-add_task(async function() {
-  //jscs:enable
-  Services.obs.addObserver(Observer, "console-api-log-event");
-  Services.obs.addObserver(Observer, "http-on-examine-response");
-  registerCleanupFunction(do_cleanup);
-
-  let which = "allow_active";
-
-  SetupPrefTestEnvironment(which);
-  clear_hists(expected_telemetry);
-
-  for (let server of Object.keys(test_servers)) {
-    await execute_test(server, test_settings[which].mimetype);
-  }
-
-  test_telemetry(expected_telemetry);
-
-  SpecialPowers.popPrefEnv();
-});
deleted file mode 100644
--- a/dom/security/test/hsts/browser_hsts-priming_allow_display.js
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Description of the test:
- *   Check that HSTS priming occurs correctly with mixed content when display
- *   content is allowed.
- */
-'use strict';
-
-var expected_telemetry = {
-  "histograms": {
-    "MIXED_CONTENT_HSTS_PRIMING_RESULT": 3,
-    "MIXED_CONTENT_HSTS_PRIMING_REQUESTS": 6,
-    "HSTS_UPGRADE_SOURCE": [ 0,0,1,0,0,0,0,0,0 ]
-  },
-  "keyed-histograms": {
-    "HSTS_PRIMING_REQUEST_DURATION": {
-      "success": 1,
-      "failure": 2,
-    },
-  }
-};
-
-//jscs:disable
-add_task(async function() {
-  //jscs:enable
-  Services.obs.addObserver(Observer, "console-api-log-event");
-  Services.obs.addObserver(Observer, "http-on-examine-response");
-  registerCleanupFunction(do_cleanup);
-
-  let which = "allow_display";
-
-  SetupPrefTestEnvironment(which);
-  clear_hists(expected_telemetry);
-
-  for (let server of Object.keys(test_servers)) {
-    await execute_test(server, test_settings[which].mimetype);
-  }
-
-  test_telemetry(expected_telemetry);
-
-  SpecialPowers.popPrefEnv();
-});
deleted file mode 100644
--- a/dom/security/test/hsts/browser_hsts-priming_block_active.js
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Description of the test:
- *   Check that HSTS priming occurs correctly with mixed content when active
- *   content is blocked.
- */
-'use strict';
-
-var expected_telemetry = {
-  "histograms": {
-    "MIXED_CONTENT_HSTS_PRIMING_RESULT": 3,
-    "MIXED_CONTENT_HSTS_PRIMING_REQUESTS": 6,
-    "HSTS_UPGRADE_SOURCE": [ 0,0,1,0,0,0,0,0,0 ]
-  },
-  "keyed-histograms": {
-    "HSTS_PRIMING_REQUEST_DURATION": {
-      "success": 1,
-      "failure": 2,
-    },
-  }
-};
-
-//jscs:disable
-add_task(async function() {
-  //jscs:enable
-  Services.obs.addObserver(Observer, "console-api-log-event");
-  Services.obs.addObserver(Observer, "http-on-examine-response");
-  registerCleanupFunction(do_cleanup);
-
-  let which = "block_active";
-
-  SetupPrefTestEnvironment(which);
-  clear_hists(expected_telemetry);
-
-  for (let server of Object.keys(test_servers)) {
-    await execute_test(server, test_settings[which].mimetype);
-  }
-
-  test_telemetry(expected_telemetry);
-
-  SpecialPowers.popPrefEnv();
-});
deleted file mode 100644
--- a/dom/security/test/hsts/browser_hsts-priming_block_active_css.js
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Description of the test:
- *   Check that HSTS priming occurs correctly with mixed content when active
- *   content is blocked for css.
- */
-'use strict';
-
-var expected_telemetry = {
-  "histograms": {
-    "MIXED_CONTENT_HSTS_PRIMING_RESULT": 3,
-    "MIXED_CONTENT_HSTS_PRIMING_REQUESTS": 6,
-    "HSTS_UPGRADE_SOURCE": [ 0,0,1,0,0,0,0,0,0 ]
-  },
-  "keyed-histograms": {
-    "HSTS_PRIMING_REQUEST_DURATION": {
-      "success": 1,
-      "failure": 2,
-    },
-  }
-};
-
-//jscs:disable
-add_task(async function() {
-  //jscs:enable
-  Services.obs.addObserver(Observer, "console-api-log-event");
-  Services.obs.addObserver(Observer, "http-on-examine-response");
-  registerCleanupFunction(do_cleanup);
-
-  let which = "block_active_css";
-
-  SetupPrefTestEnvironment(which);
-  clear_hists(expected_telemetry);
-
-  for (let server of Object.keys(test_servers)) {
-    await execute_test(server, test_settings[which].mimetype);
-  }
-
-  test_telemetry(expected_telemetry);
-
-  SpecialPowers.popPrefEnv();
-});
deleted file mode 100644
--- a/dom/security/test/hsts/browser_hsts-priming_block_active_with_redir_same.js
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Description of the test:
- *   Check that HSTS priming occurs correctly with mixed content when active
- *   content is blocked and redirect to the same host should still upgrade.
- */
-'use strict';
-
-var expected_telemetry = {
-  "histograms": {
-    "MIXED_CONTENT_HSTS_PRIMING_RESULT": 3,
-    "MIXED_CONTENT_HSTS_PRIMING_REQUESTS": 6,
-    "HSTS_UPGRADE_SOURCE": [ 0,0,1,0,0,0,0,0,0 ]
-  },
-  "keyed-histograms": {
-    "HSTS_PRIMING_REQUEST_DURATION": {
-      "success": 1,
-      "failure": 2,
-    },
-  }
-};
-
-//jscs:disable
-add_task(async function() {
-  //jscs:enable
-  Services.obs.addObserver(Observer, "console-api-log-event");
-  Services.obs.addObserver(Observer, "http-on-examine-response");
-  registerCleanupFunction(do_cleanup);
-
-  let which = "block_active_with_redir_same";
-
-  SetupPrefTestEnvironment(which);
-  clear_hists(expected_telemetry);
-
-  for (let server of Object.keys(test_servers)) {
-    await execute_test(server, test_settings[which].mimetype);
-  }
-
-  test_telemetry(expected_telemetry);
-
-  SpecialPowers.popPrefEnv();
-});
deleted file mode 100644
--- a/dom/security/test/hsts/browser_hsts-priming_block_display.js
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Description of the test:
- *   Check that HSTS priming occurs correctly with mixed content when display
- *   content is blocked.
- */
-'use strict';
-
-var expected_telemetry = {
-  "histograms": {
-    "MIXED_CONTENT_HSTS_PRIMING_RESULT": 3,
-    "MIXED_CONTENT_HSTS_PRIMING_REQUESTS": 6,
-    "HSTS_UPGRADE_SOURCE": [ 0,0,1,0,0,0,0,0,0 ]
-  },
-  "keyed-histograms": {
-    "HSTS_PRIMING_REQUEST_DURATION": {
-      "success": 1,
-      "failure": 2,
-    },
-  }
-};
-
-//jscs:disable
-add_task(async function() {
-  //jscs:enable
-  Services.obs.addObserver(Observer, "console-api-log-event");
-  Services.obs.addObserver(Observer, "http-on-examine-response");
-  registerCleanupFunction(do_cleanup);
-
-  let which = "block_display";
-
-  SetupPrefTestEnvironment(which);
-  clear_hists(expected_telemetry);
-
-  for (let server of Object.keys(test_servers)) {
-    await execute_test(server, test_settings[which].mimetype);
-  }
-
-  test_telemetry(expected_telemetry);
-
-  SpecialPowers.popPrefEnv();
-});
deleted file mode 100644
--- a/dom/security/test/hsts/browser_hsts-priming_cache-timeout.js
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Description of the test:
- * Test that the network.hsts_priming.cache_timeout preferene causes the cache
- * to timeout
- */
-'use strict';
-
-var expected_telemetry = {
-  "histograms": {
-    "MIXED_CONTENT_HSTS_PRIMING_RESULT": 2,
-    "MIXED_CONTENT_HSTS_PRIMING_REQUESTS": 4,
-  },
-  "keyed-histograms": {
-    "HSTS_PRIMING_REQUEST_DURATION": {
-      "failure": 2,
-    },
-  }
-};
-
-//jscs:disable
-add_task(async function() {
-  //jscs:enable
-  Observer.add_observers(Services);
-  registerCleanupFunction(do_cleanup);
-
-  let which = "block_display";
-
-  SetupPrefTestEnvironment(which, [["security.mixed_content.hsts_priming_cache_timeout", 1]]);
-  clear_hists(expected_telemetry);
-
-  await execute_test("no-ssl", test_settings[which].mimetype);
-
-  let pre_promise = performance.now();
-
-  while ((performance.now() - pre_promise) < 1000) {
-    await new Promise(function (resolve) {
-      setTimeout(resolve, 1000);
-    });
-  }
-
-  // clear the fact that we saw a priming request
-  test_settings[which].priming = {};
-
-  await execute_test("no-ssl", test_settings[which].mimetype);
-  is(test_settings[which].priming["no-ssl"], true,
-    "Correctly send a priming request after expiration.");
-
-  test_telemetry(expected_telemetry);
-
-  SpecialPowers.popPrefEnv();
-});
deleted file mode 100644
--- a/dom/security/test/hsts/browser_hsts-priming_hsts_after_mixed.js
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Description of the test:
- *   Check that HSTS priming occurs correctly with mixed content when the
- *   mixed-content blocks before HSTS.
- */
-'use strict';
-
-var expected_telemetry = {
-  "histograms": {
-    "MIXED_CONTENT_HSTS_PRIMING_RESULT": 3,
-    "MIXED_CONTENT_HSTS_PRIMING_REQUESTS": 6,
-    "HSTS_UPGRADE_SOURCE": [ 0,0,0,0,0,0,0,0,0 ]
-  },
-  "keyed-histograms": {
-    "HSTS_PRIMING_REQUEST_DURATION": {
-      "success": 1,
-      "failure": 2,
-    },
-  }
-};
-
-//jscs:disable
-add_task(async function() {
-  //jscs:enable
-  Services.obs.addObserver(Observer, "console-api-log-event");
-  Services.obs.addObserver(Observer, "http-on-examine-response");
-  registerCleanupFunction(do_cleanup);
-
-  let which = "hsts_after_mixed";
-
-  SetupPrefTestEnvironment(which);
-  clear_hists(expected_telemetry);
-
-  for (let server of Object.keys(test_servers)) {
-    await execute_test(server, test_settings[which].mimetype);
-  }
-
-  test_telemetry(expected_telemetry);
-
-  SpecialPowers.popPrefEnv();
-});
deleted file mode 100644
--- a/dom/security/test/hsts/browser_hsts-priming_include-subdomains.js
+++ /dev/null
@@ -1,55 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-/*
- * Description of the test:
- *   If the top-level domain sends the STS header but does not have
- *   includeSubdomains, HSTS priming requests should still be sent to
- *   subdomains.
- */
-'use strict';
-
-var expected_telemetry = {
-  "histograms": {
-    "MIXED_CONTENT_HSTS_PRIMING_RESULT": 2,
-    "MIXED_CONTENT_HSTS_PRIMING_REQUESTS": 4,
-    "HSTS_UPGRADE_SOURCE": [ 0,0,2,0,0,0,0,0,0 ]
-  },
-  "keyed-histograms": {
-    "HSTS_PRIMING_REQUEST_DURATION": {
-      "success": 2,
-    },
-  }
-};
-
-//jscs:disable
-add_task(async function() {
-  //jscs:enable
-  Observer.add_observers(Services);
-  registerCleanupFunction(do_cleanup);
-
-  // add the top-level server
-  test_servers['top-level'] = {
-    host: 'example.com',
-    response: true,
-    id: 'top-level',
-  };
-  test_settings.block_active.result['top-level'] = 'secure';
-
-  let which = "block_active";
-
-  SetupPrefTestEnvironment(which);
-  clear_hists(expected_telemetry);
-
-  await execute_test("top-level", test_settings[which].mimetype);
-
-  await execute_test("prime-hsts", test_settings[which].mimetype);
-
-  ok("prime-hsts" in test_settings[which].priming,
-     "HSTS priming on a subdomain when top-level does not includeSubDomains");
-
-  test_telemetry(expected_telemetry);
-
-  SpecialPowers.popPrefEnv();
-});
deleted file mode 100644
--- a/dom/security/test/hsts/browser_hsts-priming_no-duplicates.js
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Description of the test:
- *   Only one request should be sent per host, even if we run the test more
- *   than once.
- */
-'use strict';
-
-var expected_telemetry = {
-  "histograms": {
-    "MIXED_CONTENT_HSTS_PRIMING_RESULT": 3,
-    "MIXED_CONTENT_HSTS_PRIMING_REQUESTS": 8,
-    "HSTS_UPGRADE_SOURCE": [ 0,0,2,0,0,0,0,0,0 ]
-  },
-  "keyed-histograms": {
-    "HSTS_PRIMING_REQUEST_DURATION": {
-      "success": 1,
-      "failure": 2,
-    },
-  }
-};
-
-//jscs:disable
-add_task(async function() {
-  //jscs:enable
-  Observer.add_observers(Services);
-  registerCleanupFunction(do_cleanup);
-
-  let which = "block_display";
-
-  SetupPrefTestEnvironment(which);
-  clear_hists(expected_telemetry);
-
-  for (let server of Object.keys(test_servers)) {
-    await execute_test(server, test_settings[which].mimetype);
-  }
-
-  // run the tests twice to validate the cache is being used
-  for (let server of Object.keys(test_servers)) {
-    await execute_test(server, test_settings[which].mimetype);
-  }
-
-  test_telemetry(expected_telemetry);
-
-  SpecialPowers.popPrefEnv();
-});
deleted file mode 100644
--- a/dom/security/test/hsts/browser_hsts-priming_no-ip-address.js
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Description of the test:
- *   If the top-level domain sends the STS header but does not have
- *   includeSubdomains, HSTS priming requests should still be sent to
- *   subdomains.
- */
-'use strict';
-
-var expected_telemetry = {
-  "histograms": {
-    "MIXED_CONTENT_HSTS_PRIMING_RESULT": 0,
-    "MIXED_CONTENT_HSTS_PRIMING_REQUESTS": 1,
-  },
-  "keyed-histograms": {
-  }
-};
-
-//jscs:disable
-add_task(async function() {
-  //jscs:enable
-  Observer.add_observers(Services);
-  registerCleanupFunction(do_cleanup);
-
-  // add the top-level server
-  test_servers['localhost-ip'] = {
-    host: '127.0.0.2',
-    response: true,
-    id: 'localhost-ip',
-  };
-  test_settings.block_active.result['localhost-ip'] = 'blocked';
-
-  let which = "block_active";
-
-  SetupPrefTestEnvironment(which);
-  clear_hists(expected_telemetry);
-
-  await execute_test("localhost-ip", test_settings[which].mimetype);
-
-  test_telemetry(expected_telemetry);
-
-  SpecialPowers.popPrefEnv();
-});
deleted file mode 100644
--- a/dom/security/test/hsts/browser_hsts-priming_no-non-standard-ports.js
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Description of the test:
- *   If the top-level domain sends the STS header but does not have
- *   includeSubdomains, HSTS priming requests should still be sent to
- *   subdomains.
- */
-'use strict';
-
-var expected_telemetry = {
-  "histograms": {
-    "MIXED_CONTENT_HSTS_PRIMING_RESULT": 1,
-    "MIXED_CONTENT_HSTS_PRIMING_REQUESTS": 3,
-    "HSTS_UPGRADE_SOURCE": [ 0,0,1,0,0,0,0,0,0 ]
-  },
-  "keyed-histograms": {
-    "HSTS_PRIMING_REQUEST_DURATION": {
-      "success": 1,
-    },
-  }
-};
-
-//jscs:disable
-add_task(async function() {
-  //jscs:enable
-  Observer.add_observers(Services);
-  registerCleanupFunction(do_cleanup);
-
-  // add the top-level server
-  test_servers['non-standard-port'] = {
-    host: 'test1.example.com:1234',
-    response: true,
-    id: 'non-standard-port',
-  };
-  test_settings.block_active.result['non-standard-port'] = 'blocked';
-
-  let which = "block_active";
-
-  SetupPrefTestEnvironment(which);
-  clear_hists(expected_telemetry);
-
-  await execute_test("non-standard-port", test_settings[which].mimetype);
-
-  await execute_test("prime-hsts", test_settings[which].mimetype);
-
-  ok("prime-hsts" in test_settings[which_test].priming, "Sent priming request on standard port after non-standard was not primed");
-
-  test_telemetry(expected_telemetry);
-
-  SpecialPowers.popPrefEnv();
-});
deleted file mode 100644
--- a/dom/security/test/hsts/browser_hsts-priming_timeout.js
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Description of the test:
- *   Only one request should be sent per host, even if we run the test more
- *   than once.
- */
-'use strict';
-
-var expected_telemetry = {
-  "histograms": {
-    "MIXED_CONTENT_HSTS_PRIMING_RESULT": 3,
-    "MIXED_CONTENT_HSTS_PRIMING_REQUESTS": 3,
-  },
-  "keyed-histograms": {
-    "HSTS_PRIMING_REQUEST_DURATION": {
-      "failure": 3,
-    },
-  }
-};
-
-//jscs:disable
-add_task(async function() {
-  //jscs:enable
-  Observer.add_observers(Services);
-  registerCleanupFunction(do_cleanup);
-
-  let which = "timeout";
-
-  SetupPrefTestEnvironment(which, [["security.mixed_content.hsts_priming_request_timeout",
-                1000]]);
-  clear_hists(expected_telemetry);
-
-  for (let server of Object.keys(test_servers)) {
-    await execute_test(server, test_settings[which].mimetype);
-  }
-
-  test_telemetry(expected_telemetry);
-
-  SpecialPowers.popPrefEnv();
-});
deleted file mode 100644
index 1ba31ba1a62313908f41f844a9fb2e74663a4cd2..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
--- a/dom/security/test/hsts/file_priming-top.html
+++ /dev/null
@@ -1,89 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<head>
-  <title>Bug 1246540</title>
-  <meta http-equiv='content-type' content="text/html;charset=utf-8" />
-</head>
-<body>
-  <p id="display"></p>
-  <div id="content" style="visibility: hidden">
-  </div>
-
-<script type="text/javascript">
-/*
- * Description of the test:
- * Attempt to load an insecure resource. If the resource responds to HSTS
- * priming with an STS header, the load should continue securely.
- * If it does not, the load should continue be blocked or continue insecurely.
- */
-
-function parse_query_string() {
-  var q = {};
-  document.location.search.substr(1).
-    split('&').forEach(function (item, idx, ar) {
-      let [k, v] = item.split('=');
-      q[k] = unescape(v);
-    });
-  return q;
-}
-
-var args = parse_query_string();
-
-var subresources = {
-  css: { mimetype: 'text/css', file: 'file_stylesheet.css' },
-  img: { mimetype: 'image/png', file: 'file_1x1.png' },
-  script: { mimetype: 'text/javascript', file: 'file_priming.js' },
-};
-
-function handler(ev) {
-  console.log("HSTS_PRIMING: Blocked "+args.id);
-  let elem = document.getElementById(args.id);
-  elem.parentElement.removeChild(elem);
-}
-
-function loadCss(src) {
-  let head = document.getElementsByTagName("head")[0];
-  let link = document.createElement("link");
-  link.setAttribute("rel", "stylesheet");
-  link.setAttribute("id", args.id);
-  link.setAttribute("type", subresources[args.type].mimetype);
-  link.setAttribute("href", src);
-  link.onerror = handler;
-  head.appendChild(link);
-}
-
-function loadResource(src) {
-  let content = document.getElementById("content");
-  let testElem = document.createElement(args.type);
-  testElem.setAttribute("id", args.id);
-  testElem.setAttribute("charset", "UTF-8");
-  testElem.onerror = handler;
-  content.appendChild(testElem);
-  testElem.src = src;
-}
-
-function loadTest() {
-  let subresource = subresources[args.type];
-
-  let src = "http://"
-    + args.host
-    + "/browser/dom/security/test/hsts/file_testserver.sjs"
-    + "?file=" +escape("browser/dom/security/test/hsts/" + subresource.file)
-    + "&primer=" + escape(args.id)
-    + "&mimetype=" + escape(subresource.mimetype)
-    + "&timeout=" + escape(args.timeout)
-    ;
-  if (args.type == 'css') {
-    loadCss(src);
-    return;
-  }
-
-  loadResource(src);
-}
-
-// start running the tests
-loadTest();
-
-</script>
-</body>
-</html>
deleted file mode 100644
--- a/dom/security/test/hsts/file_priming.js
+++ /dev/null
@@ -1,4 +0,0 @@
-function completed() {
-  return;
-}
-completed();
deleted file mode 100644
--- a/dom/security/test/hsts/file_stylesheet.css
+++ /dev/null
@@ -1,1 +0,0 @@
-body {}
deleted file mode 100644
--- a/dom/security/test/hsts/file_testserver.sjs
+++ /dev/null
@@ -1,84 +0,0 @@
-// SJS file for HSTS mochitests
-
-Components.utils.import("resource://gre/modules/NetUtil.jsm");
-Components.utils.importGlobalProperties(["URLSearchParams"]);
-
-function loadFromFile(path) {
-  // Load the HTML to return in the response from file.
-  // Since it's relative to the cwd of the test runner, we start there and
-  // append to get to the actual path of the file.
-  var testFile =
-    Components.classes["@mozilla.org/file/directory_service;1"].
-    getService(Components.interfaces.nsIProperties).
-    get("CurWorkD", Components.interfaces.nsIFile);
-  var dirs = path.split("/");
-  for (var i = 0; i < dirs.length; i++) {
-    testFile.append(dirs[i]);
-  }
-  var testFileStream =
-    Components.classes["@mozilla.org/network/file-input-stream;1"].
-    createInstance(Components.interfaces.nsIFileInputStream);
-  testFileStream.init(testFile, -1, 0, 0);
-  var test = NetUtil.readInputStreamToString(testFileStream, testFileStream.available());
-  return test;
-}
-
-function handleRequest(request, response)
-{
-  const query = new URLSearchParams(request.queryString);
-
-  var timeout = parseInt(query.get('timeout'));
-  response.processAsync();
-
-  timer = Components.classes["@mozilla.org/timer;1"].createInstance(Components.interfaces.nsITimer);
-  timer.initWithCallback(function()
-    {
-      if (!response) {
-        return;
-      }
-
-      // avoid confusing cache behaviors
-      response.setHeader("Cache-Control", "no-cache", false);
-
-      redir = query.get('redir');
-      if (redir == 'same') {
-        query.delete("redir");
-        response.setStatus(302);
-        let newURI = request.uri;
-        newURI.queryString = query.serialize();
-        response.setHeader("Location", newURI.spec)
-        response.write('xyzzy');
-        response.finish();
-        return;
-      }
-
-      // if we have a priming header, check for required behavior
-      // and set header appropriately
-      if (request.hasHeader('Upgrade-Insecure-Requests')) {
-        var expected = query.get('primer');
-        if (expected == 'prime-hsts' || expected == 'top-level') {
-          // set it for 5 minutes
-          response.setHeader("Strict-Transport-Security", "max-age="+(60*5), false);
-        } else if (expected == 'reject-upgrade') {
-          response.setHeader("Strict-Transport-Security", "max-age=0", false);
-        }
-        response.write('xyzzy');
-        response.finish();
-        return;
-      }
-
-      var file = query.get('file');
-      if (file) {
-        var mimetype = unescape(query.get('mimetype'));
-        response.setHeader("Content-Type", mimetype, false);
-        let contents = loadFromFile(unescape(file));
-        response.write(contents);
-        response.finish();
-        return;
-      }
-
-      response.setHeader("Content-Type", "application/json", false);
-      response.write('{}');
-      response.finish();
-    }, timeout, Components.interfaces.nsITimer.TYPE_ONE_SHOT);
-}
deleted file mode 100644
--- a/dom/security/test/hsts/head.js
+++ /dev/null
@@ -1,543 +0,0 @@
-/*
- * Description of the tests:
- *   Check that HSTS priming occurs correctly with mixed content
- *
- *   This test uses three hostnames, each of which treats an HSTS priming
- *   request differently.
- *   * no-ssl never returns an ssl response
- *   * reject-upgrade returns an ssl response, but with no STS header
- *   * prime-hsts returns an ssl response with the appropriate STS header
- *
- *   For each server, test that it response appropriately when the we allow
- *   or block active or display content, as well as when we send an hsts priming
- *   request, but do not change the order of mixed-content and HSTS.
- *
- *   Test use http-on-examine-response, so must be run in browser context.
- */
-'use strict';
-
-var { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
-
-var TOP_URI = "https://example.com/browser/dom/security/test/hsts/file_priming-top.html";
-
-var test_servers = {
-  // a test server that does not support TLS
-  'no-ssl': {
-    host: 'example.co.jp',
-    response: false,
-    id: 'no-ssl',
-  },
-  // a test server which does not support STS upgrade
-  'reject-upgrade': {
-    host: 'example.org',
-    response: true,
-    id: 'reject-upgrade',
-  },
-  // a test server when sends an STS header when priming
-  'prime-hsts': {
-    host: 'test1.example.com',
-    response: true,
-    id: 'prime-hsts'
-  },
-};
-
-var test_settings = {
-  // mixed active content is allowed, priming will upgrade
-  allow_active: {
-    block_active: false,
-    block_display: false,
-    use_hsts: true,
-    send_hsts_priming: true,
-    type: 'script',
-    timeout: 0,
-    result: {
-      'no-ssl': 'insecure',
-      'reject-upgrade': 'insecure',
-      'prime-hsts': 'secure',
-    },
-  },
-  // mixed active content is blocked, priming will upgrade
-  block_active: {
-    block_active: true,
-    block_display: false,
-    use_hsts: true,
-    send_hsts_priming: true,
-    type: 'script',
-    timeout: 0,
-    result: {
-      'no-ssl': 'blocked',
-      'reject-upgrade': 'blocked',
-      'prime-hsts': 'secure',
-    },
-  },
-  // keep the original order of mixed-content and HSTS, but send
-  // priming requests
-  hsts_after_mixed: {
-    block_active: true,
-    block_display: false,
-    use_hsts: false,
-    send_hsts_priming: true,
-    type: 'script',
-    timeout: 0,
-    result: {
-      'no-ssl': 'blocked',
-      'reject-upgrade': 'blocked',
-      'prime-hsts': 'blocked',
-    },
-  },
-  // mixed display content is allowed, priming will upgrade
-  allow_display: {
-    block_active: true,
-    block_display: false,
-    use_hsts: true,
-    send_hsts_priming: true,
-    type: 'img',
-    timeout: 0,
-    result: {
-      'no-ssl': 'insecure',
-      'reject-upgrade': 'insecure',
-      'prime-hsts': 'secure',
-    },
-  },
-  // mixed display content is blocked, priming will upgrade
-  block_display: {
-    block_active: true,
-    block_display: true,
-    use_hsts: true,
-    send_hsts_priming: true,
-    type: 'img',
-    timeout: 0,
-    result: {
-      'no-ssl': 'blocked',
-      'reject-upgrade': 'blocked',
-      'prime-hsts': 'secure',
-    },
-  },
-  // mixed active content is blocked, priming will upgrade (css)
-  block_active_css: {
-    block_active: true,
-    block_display: true,
-    use_hsts: true,
-    send_hsts_priming: true,
-    type: 'css',
-    timeout: 0,
-    result: {
-      'no-ssl': 'blocked',
-      'reject-upgrade': 'blocked',
-      'prime-hsts': 'secure',
-    },
-  },
-  // mixed active content is blocked, priming will upgrade
-  // redirect to the same host
-  block_active_with_redir_same: {
-    block_active: true,
-    block_display: false,
-    use_hsts: true,
-    send_hsts_priming: true,
-    type: 'script',
-    redir: 'same',
-    timeout: 0,
-    result: {
-      'no-ssl': 'blocked',
-      'reject-upgrade': 'blocked',
-      'prime-hsts': 'secure',
-    },
-  },
-  // mixed active content is blocked, priming will upgrade
-  // redirect to the same host
-  timeout: {
-    block_active: true,
-    block_display: true,
-    use_hsts: true,
-    send_hsts_priming: true,
-    type: 'script',
-    timeout: 100000,
-    result: {
-      'no-ssl': 'blocked',
-      'reject-upgrade': 'blocked',
-      'prime-hsts': 'blocked',
-    },
-  },
-}
-// track which test we are on
-var which_test = "";
-
-/**
- * A stream listener that just forwards all calls
- */
-var StreamListener = function(subject) {
-  let channel = subject.QueryInterface(Ci.nsIHttpChannel);
-  let traceable = subject.QueryInterface(Ci.nsITraceableChannel);
-
-  this.uri = channel.URI.asciiSpec;
-  this.listener = traceable.setNewListener(this);
-  return this;
-};
-
-// Next three methods are part of `nsIStreamListener` interface and are
-// invoked by `nsIInputStreamPump.asyncRead`.
-StreamListener.prototype.onDataAvailable = function(request, context, input, offset, count) {
-  if (request.status == Cr.NS_ERROR_ABORT) {
-    this.listener = null;
-    return Cr.NS_SUCCESS;
-  }
-  let listener = this.listener;
-  if (listener) {
-    try {
-      let rv = listener.onDataAvailable(request, context, input, offset, count);
-      if (rv != Cr.NS_ERROR_ABORT) {
-        // If the channel gets canceled, we sometimes get NS_ERROR_ABORT here.
-        // Anything else is an error.
-        return rv;
-      }
-    } catch (e) {
-      if (e != Cr.NS_ERROR_ABORT) {
-        return e;
-      }
-    }
-  }
-  return Cr.NS_SUCCESS;
-};
-
-// Next two methods implement `nsIRequestObserver` interface and are invoked
-// by `nsIInputStreamPump.asyncRead`.
-StreamListener.prototype.onStartRequest = function(request, context) {
-  if (request.status == Cr.NS_ERROR_ABORT) {
-    this.listener = null;
-    return Cr.NS_SUCCESS;
-  }
-  let listener = this.listener;
-  if (listener) {
-    try {
-      let rv = listener.onStartRequest(request, context);
-      if (rv != Cr.NS_ERROR_ABORT) {
-        // If the channel gets canceled, we sometimes get NS_ERROR_ABORT here.
-        // Anything else is an error.
-        return rv;
-      }
-    } catch (e) {
-      if (e != Cr.NS_ERROR_ABORT) {
-        return e;
-      }
-    }
-  }
-  return Cr.NS_SUCCESS;
-};
-
-// Called to signify the end of an asynchronous request. We only care to
-// discover errors.
-StreamListener.prototype.onStopRequest = function(request, context, status) {
-  if (status == Cr.NS_ERROR_ABORT) {
-    this.listener = null;
-    return Cr.NS_SUCCESS;
-  }
-  let listener = this.listener;
-  if (listener) {
-    try {
-      let rv = listener.onStopRequest(request, context, status);
-      if (rv != Cr.NS_ERROR_ABORT) {
-        // If the channel gets canceled, we sometimes get NS_ERROR_ABORT here.
-        // Anything else is an error.
-        return rv;
-      }
-    } catch (e) {
-      if (e != Cr.NS_ERROR_ABORT) {
-        return e;
-      }
-    }
-  }
-  return Cr.NS_SUCCESS;
-};
-
-var Observer = {
-  listeners: {},
-  observe: function (subject, topic, data) {
-    switch (topic) {
-      case 'console-api-log-event':
-        return Observer.console_api_log_event(subject, topic, data);
-      case 'http-on-examine-response':
-        return Observer.http_on_examine_response(subject, topic, data);
-      case 'http-on-modify-request':
-        return Observer.http_on_modify_request(subject, topic, data);
-    }
-    throw "Can't handle topic "+topic;
-  },
-  add_observers: function (services, include_on_modify = false) {
-    services.obs.addObserver(Observer, "console-api-log-event");
-    services.obs.addObserver(Observer, "http-on-examine-response");
-    services.obs.addObserver(Observer, "http-on-modify-request");
-  },
-  cleanup: function () {
-    this.listeners = {};
-  },
-  // When a load is blocked which results in an error event within a page, the
-  // test logs to the console.
-  console_api_log_event: function (subject, topic, data) {
-    var message = subject.wrappedJSObject.arguments[0];
-    // when we are blocked, this will match the message we sent to the console,
-    // ignore everything else.
-    var re = RegExp(/^HSTS_PRIMING: Blocked ([-\w]+).*$/);
-    if (!re.test(message)) {
-      return;
-    }
-
-    let id = message.replace(re, '$1');
-    let curTest =test_servers[id];
-
-    if (!curTest) {
-      ok(false, "HSTS priming got a console message blocked, "+
-          "but doesn't match expectations "+id+" (msg="+message);
-      return;
-    }
-
-    is("blocked", test_settings[which_test].result[curTest.id], "HSTS priming "+
-        which_test+":"+curTest.id+" expected "+
-        test_settings[which_test].result[curTest.id]+", got blocked");
-    test_settings[which_test].finished[curTest.id] = "blocked";
-  },
-  get_current_test: function(uri) {
-    for (let item in test_servers) {
-      let re = RegExp('https?://'+test_servers[item].host+'.*\/browser/dom/security/test/hsts/file_testserver.sjs');
-      if (re.test(uri)) {
-        return test_servers[item];
-      }
-    }
-    return null;
-  },
-  http_on_modify_request: function (subject, topic, data) {
-    let channel = subject.QueryInterface(Ci.nsIHttpChannel);
-    let uri = channel.URI.asciiSpec;
-
-    let curTest = this.get_current_test(channel.URI.asciiSpec);
-
-    if (!curTest) {
-      return;
-    }
-    
-    if (!(uri in this.listeners)) {
-      // Add an nsIStreamListener to ensure that the listener is not NULL
-      this.listeners[uri] = new StreamListener(subject);
-    }
-
-    if (channel.requestMethod != 'HEAD') {
-      return;
-    }
-    if (typeof ok === 'undefined') {
-      // we are in the wrong thread and ok and is not available
-      return;
-    }
-    ok(!(curTest.id in test_settings[which_test].priming), "Already saw a priming request for " + curTest.id);
-    test_settings[which_test].priming[curTest.id] = true;
-  },
-  // When we see a response come back, peek at the response and test it is secure
-  // or insecure as needed. Addtionally, watch the response for priming requests.
-  http_on_examine_response: function (subject, topic, data) {
-    let channel = subject.QueryInterface(Ci.nsIHttpChannel);
-    let curTest = this.get_current_test(channel.URI.asciiSpec);
-    let uri = channel.URI.asciiSpec;
-
-    if (!curTest) {
-      return;
-    }
-
-    let result = (channel.URI.asciiSpec.startsWith('https:')) ? "secure" : "insecure";
-
-    // This is a priming request, go ahead and validate we were supposed to see
-    // a response from the server
-    if (channel.requestMethod == 'HEAD') {
-      is(true, curTest.response, "HSTS priming response found " + curTest.id);
-      return;
-    }
-
-    // This is the response to our query, make sure it matches
-    is(result, test_settings[which_test].result[curTest.id],
-        "HSTS priming result " + which_test + ":" + curTest.id);
-    test_settings[which_test].finished[curTest.id] = result;
-    if (this.listeners[uri]) {
-      this.listeners[uri] = undefined;
-    }
-  },
-};
-
-// opens `uri' in a new tab and focuses it.
-// returns the newly opened tab
-function openTab(uri) {
-  let tab = BrowserTestUtils.addTab(gBrowser, uri);
-
-  // select tab and make sure its browser is focused
-  gBrowser.selectedTab = tab;
-  tab.ownerGlobal.focus();
-
-  return tab;
-}
-
-function clear_sts_data() {
-  for (let test in test_servers) {
-    SpecialPowers.cleanUpSTSData('http://'+test_servers[test].host);
-  }
-}
-
-var oldCanRecord = Services.telemetry.canRecordExtended;
-
-function do_cleanup() {
-  clear_sts_data();
-
-  Services.obs.removeObserver(Observer, "console-api-log-event");
-  Services.obs.removeObserver(Observer, "http-on-examine-response");
-
-  Services.telemetry.canRecordExtended = oldCanRecord;
-
-  Observer.cleanup();
-}
-
-function SetupPrefTestEnvironment(which, additional_prefs) {
-  which_test = which;
-  clear_sts_data();
-
-  var settings = test_settings[which];
-  // priming counts how many priming requests we saw
-  settings.priming = {};
-  // priming counts how many tests were finished
-  settings.finished= {};
-
-  var prefs = [["security.mixed_content.block_active_content",
-                settings.block_active],
-               ["security.mixed_content.block_display_content",
-                settings.block_display],
-               ["security.mixed_content.use_hsts",
-                settings.use_hsts],
-               ["security.mixed_content.send_hsts_priming",
-                settings.send_hsts_priming],
-  ];
-
-  if (additional_prefs) {
-    for (let idx in additional_prefs) {
-      prefs.push(additional_prefs[idx]);
-    }
-  }
-
-  Services.telemetry.canRecordExtended = true;
-
-  SpecialPowers.pushPrefEnv({'set': prefs});
-}
-
-// make the top-level test uri
-function build_test_uri(base_uri, host, test_id, type, timeout) {
-  return base_uri +
-          "?host=" + escape(host) +
-          "&id=" + escape(test_id) +
-          "&type=" + escape(type) +
-          "&timeout=" + escape(timeout)
-    ;
-}
-
-// open a new tab, load the test, and wait for it to finish
-async function execute_test(test, mimetype) {
-  var src = build_test_uri(TOP_URI, test_servers[test].host,
-      test, test_settings[which_test].type,
-      test_settings[which_test].timeout);
-
-  await BrowserTestUtils.withNewTab(src, () => {});
-}
-
-/* Expected should look something like this:
- * The numbers are the sum of all telemetry values.
-  var expected_telemetry = {
-    "histograms": {
-      "MIXED_CONTENT_HSTS_PRIMING_RESULT": 6,
-      "HSTS_PRIMING_REQUESTS": 10,
-      "HSTS_UPGRADE_SOURCE": [ 0,0,2,0,0,0,0,0,0 ]
-    },
-    "keyed-histograms": {
-      "HSTS_PRIMING_REQUEST_DURATION": {
-        "success": 1,
-        "failure": 2,
-      },
-    }
-  };
- */
-function test_telemetry(expected) {
-  for (let key in expected['histograms']) {
-    let hs = undefined;
-    try {
-      let hist = Services.telemetry.getHistogramById(key);
-      hs = hist.snapshot();
-      hist.clear();
-    } catch(e) {
-      ok(false, "Caught exception trying to get histogram for key " + key + ":" + e);
-      continue;
-    }
-
-    if (!hs) {
-      ok(false, "No histogram found for key " + key);
-      continue;
-    }
-
-    if (Array.isArray(expected['histograms'][key])) {
-      var is_ok = true;
-      if (expected['histograms'][key].length != hs.counts.length) {
-        ok(false, "Histogram lengths match");
-        continue;
-      }
-
-      for (let idx in expected['histograms'][key]) {
-        is_ok = (hs.counts[idx] >= expected['histograms'][key][idx]);
-        if (!is_ok) {
-          break;
-        }
-      }
-      ok(is_ok, "Histogram counts match for " + key + " - Got " + hs.counts + ", expected " + expected['histograms'][key]);
-    } else {
-      // there may have been other background requests processed
-      ok(hs.counts.reduce(sum) >= expected['histograms'][key], "Histogram counts match expected, got " + hs.counts.reduce(sum) + ", expected at least " + expected['histograms'][key]);
-    }
-  }
-
-  for (let key in expected['keyed-histograms']) {
-    let hs = undefined;
-    try {
-      let hist = Services.telemetry.getKeyedHistogramById(key);
-      hs = hist.snapshot();
-      hist.clear();
-    } catch(e) {
-      ok(false, "Caught exception trying to get histogram for key " + key + " :" + e);
-      continue;
-    }
-
-    if (!hs) {
-      ok(false, "No keyed histogram found for key " + key);
-      continue;
-    }
-
-    for (let hist_key in expected['keyed-histograms'][key]) {
-      ok(hist_key in hs, "Keyed histogram exists with key");
-      if (hist_key in hs) {
-        ok(hs[hist_key].counts.reduce(sum) >= expected['keyed-histograms'][key][hist_key], "Keyed histogram counts match expected got " + hs[hist_key].counts.reduce(sum) + ", expected at least " + expected['keyed-histograms'][key][hist_key])
-      }
-    }
-  }
-}
-
-function sum(a, b) {
-  return a+b;
-}
-
-function clear_hists(hists) {
-  for (let key in hists['histograms']) {
-    try {
-      let hist = Services.telemetry.getHistogramById(key);
-      hist.clear();
-    } catch(e) {
-      continue;
-    }
-  }
-
-  for (let key in hists['keyed-histograms']) {
-    try {
-      let hist = Services.telemetry.getKeyedHistogramById(key);
-      hist.clear();
-    } catch(e) {
-      continue;
-    }
-  }
-}
--- a/dom/security/test/mixedcontentblocker/test_main.html
+++ b/dom/security/test/mixedcontentblocker/test_main.html
@@ -157,19 +157,16 @@ https://bugzilla.mozilla.org/show_bug.cg
         testsToRun["imageLeavePicture"] = true;
         break;
 
     }
     checkTestsCompleted();
   }
 
   function startTest() {
-    // Set prefs to use mixed-content before HSTS
-    SpecialPowers.pushPrefEnv({'set': [["security.mixed_content.use_hsts", false],
-                                       ["security.mixed_content.send_hsts_priming", false]]});
     //Set the first set of mixed content settings and increment the counter.
     changePrefs([], function() {
       //listen for a messages from the mixed content test harness
       window.addEventListener("message", receiveMessage);
 
       //Kick off test
       reloadFrame();
     });
--- a/dom/security/test/mixedcontentblocker/test_redirect.html
+++ b/dom/security/test/mixedcontentblocker/test_redirect.html
@@ -31,20 +31,15 @@ function receiveMessage(event) {
   if (testcounter < 2) {
     return;
   }
   window.removeEventListener("message", receiveMessage);
   SimpleTest.finish();
 }
 
 function startTest() {
-  SpecialPowers.pushPrefEnv({
-  	'set': [["security.mixed_content.use_hsts", false],
-            ["security.mixed_content.send_hsts_priming", false]]
-  }, function () {
-    let testframe = document.getElementById("testframe");
-    testframe.src = PATH + "file_redirect.html";
-  });
+  let testframe = document.getElementById("testframe");
+  testframe.src = PATH + "file_redirect.html";
 }
 
 </script>
 </body>
 </html>
--- a/dom/security/test/moz.build
+++ b/dom/security/test/moz.build
@@ -26,10 +26,9 @@ MOCHITEST_MANIFESTS += [
 MOCHITEST_CHROME_MANIFESTS += [
     'general/chrome.ini',
 ]
 
 BROWSER_CHROME_MANIFESTS += [
     'cors/browser.ini',
     'csp/browser.ini',
     'general/browser.ini',
-    'hsts/browser.ini',
 ]
--- a/dom/tests/mochitest/chrome/test_parsingMode.html
+++ b/dom/tests/mochitest/chrome/test_parsingMode.html
@@ -7,31 +7,29 @@
 <script type="application/javascript">
   SimpleTest.waitForExplicitFinish();
   function run() {
     const Cc = Components.classes;
     const Ci = Components.interfaces;
 
     const sss = Cc["@mozilla.org/content/style-sheet-service;1"]
       .getService(Ci.nsIStyleSheetService);
-    const domutils = Cc["@mozilla.org/inspector/dom-utils;1"]
-      .getService(Ci.inIDOMUtils);
     const utils = window.QueryInterface(Ci.nsIInterfaceRequestor)
       .getInterface(Ci.nsIDOMWindowUtils);
 
     const userUrl = encodeURI("data:text/css,body { color: seagreen; -moz-window-transform: none }");
     utils.loadSheetUsingURIString(userUrl, sss.USER_SHEET);
 
     const agentUrl = encodeURI("data:text/css,body { color: tomato; }");
     utils.loadSheetUsingURIString(agentUrl, sss.AGENT_SHEET);
 
     const authorUrl = "chrome://mochikit/content/tests/SimpleTest/test.css";
 
     let results = [];
-    for (let sheet of domutils.getAllStyleSheets(document)) {
+    for (let sheet of InspectorUtils.getAllStyleSheets(document)) {
       if (sheet.href === agentUrl) {
         is(sheet.parsingMode, "agent", "agent sheet has expected mode");
         results[sss.AGENT_SHEET] = 1;
       } else if (sheet.href === userUrl) {
         is(sheet.parsingMode, "user", "user sheet has expected mode");
         is(sheet.cssRules[0].style.length, 2, "Chrome-only properties are parsed in user sheet")
         results[sss.USER_SHEET] = 1;
       } else if (sheet.href === authorUrl) {
@@ -43,17 +41,17 @@
         continue;
       } else {
         // Ignore sheets we don't care about.
         continue;
       }
 
       // Check that re-parsing preserves the mode.
       let mode = sheet.parsingMode;
-      domutils.parseStyleSheet(sheet, "body { color: chartreuse; }");
+      InspectorUtils.parseStyleSheet(sheet, "body { color: chartreuse; }");
       is(sheet.parsingMode, mode,
          "check that re-parsing preserved mode " + mode);
     }
 
     ok(results[sss.AGENT_SHEET] && results[sss.USER_SHEET] &&
       results[sss.AUTHOR_SHEET],
       "all sheets seen");
 
--- a/dom/webidl/CSSLexer.webidl
+++ b/dom/webidl/CSSLexer.webidl
@@ -103,17 +103,17 @@ dictionary CSSToken {
   //    symbol     The symbol text.
   DOMString text;
 };
 
 /**
  * CSSLexer is an interface to the CSS lexer.  It tokenizes an
  * input stream and returns CSS tokens.
  *
- * @see inIDOMUtils.getCSSLexer to create an instance of the lexer.
+ * @see InspectorUtils.getCSSLexer to create an instance of the lexer.
  */
 [ChromeOnly]
 interface CSSLexer
 {
   /**
    * The line number of the most recently returned token.  Line
    * numbers are 0-based.
    */
--- a/dom/webidl/Console.webidl
+++ b/dom/webidl/Console.webidl
@@ -181,8 +181,19 @@ dictionary ConsoleInstanceOptions {
   ConsoleLogLevel maxLogLevel;
 
   // String pref name which contains the level to use for maxLogLevel. If the
   // pref doesn't exist, gets removed or it is used in workers, the maxLogLevel
   // will default to the value passed to this constructor (or "all" if it wasn't
   // specified).
   DOMString maxLogLevelPref = "";
 };
+
+enum ConsoleLevel { "log", "warning", "error" };
+
+// this interface is just for testing
+partial interface ConsoleInstance {
+  [ChromeOnly]
+  void reportForServiceWorkerScope(DOMString scope, DOMString message,
+                                   DOMString filename, unsigned long lineNumber,
+                                   unsigned long columnNumber,
+                                   ConsoleLevel level);
+};
--- a/dom/webidl/InspectorUtils.webidl
+++ b/dom/webidl/InspectorUtils.webidl
@@ -1,29 +1,116 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/.
  */
-dictionary InspectorRGBTriple {
-  /*
-   * NOTE: Using octet for RGB components is not generally OK, because
-   * they can be outside the 0-255 range, but for backwards-compatible
-   * named colors (which is what we use this dictionary for) the 0-255
-   * assumption is fine.
-   */
-  octet r = 0;
-  octet g = 0;
-  octet b = 0;
+
+/**
+ * A collection of utility methods for use by devtools.
+ *
+ * See InspectorUtils.h for documentation on these methods.
+ */
+[ChromeOnly]
+namespace InspectorUtils {
+  sequence<StyleSheet> getAllStyleSheets(Document document);
+  sequence<CSSRule> getCSSStyleRules(
+    Element element,
+    [TreatNullAs=EmptyString] optional DOMString pseudo = "");
+  unsigned long getRuleLine(CSSRule rule);
+  unsigned long getRuleColumn(CSSRule rule);
+  unsigned long getRelativeRuleLine(CSSRule rule);
+  [NewObject] CSSLexer getCSSLexer(DOMString text);
+  unsigned long getSelectorCount(CSSStyleRule rule);
+  [Throws] DOMString getSelectorText(CSSStyleRule rule,
+                                     unsigned long selectorIndex);
+  [Throws] unsigned long long getSpecificity(CSSStyleRule rule,
+                                             unsigned long selectorIndex);
+  [Throws] boolean selectorMatchesElement(
+      Element element,
+      CSSStyleRule rule,
+      unsigned long selectorIndex,
+      [TreatNullAs=EmptyString] optional DOMString pseudo = "");
+  boolean isInheritedProperty(DOMString property);
+  sequence<DOMString> getCSSPropertyNames(optional PropertyNamesOptions options);
+  [Throws] sequence<DOMString> getCSSValuesForProperty(DOMString property);
+  [Throws] DOMString rgbToColorName(octet r, octet g, octet b);
+  InspectorRGBATuple? colorToRGBA(DOMString colorString);
+  boolean isValidCSSColor(DOMString colorString);
+  [Throws] sequence<DOMString> getSubpropertiesForCSSProperty(DOMString property);
+  [Throws] boolean cssPropertyIsShorthand(DOMString property);
+
+  // TODO: Change this to use an enum.
+  const unsigned long TYPE_LENGTH = 0;
+  const unsigned long TYPE_PERCENTAGE = 1;
+  const unsigned long TYPE_COLOR = 2;
+  const unsigned long TYPE_URL = 3;
+  const unsigned long TYPE_ANGLE = 4;
+  const unsigned long TYPE_FREQUENCY = 5;
+  const unsigned long TYPE_TIME = 6;
+  const unsigned long TYPE_GRADIENT = 7;
+  const unsigned long TYPE_TIMING_FUNCTION = 8;
+  const unsigned long TYPE_IMAGE_RECT = 9;
+  const unsigned long TYPE_NUMBER = 10;
+  [Throws] boolean cssPropertySupportsType(DOMString property, unsigned long type);
+
+  boolean isIgnorableWhitespace(CharacterData dataNode);
+  Node? getParentForNode(Node node, boolean showingAnonymousContent);
+  [NewObject] NodeList getChildrenForNode(Node node,
+                                          boolean showingAnonymousContent);
+  sequence<DOMString> getBindingURLs(Element element);
+  [Throws] void setContentState(Element element, unsigned long long state);
+  [Throws] void removeContentState(
+      Element element,
+      unsigned long long state,
+      optional boolean clearActiveDocument = false);
+  unsigned long long getContentState(Element element);
+  [NewObject, Throws] sequence<InspectorFontFace> getUsedFontFaces(Range range);
+  sequence<DOMString> getCSSPseudoElementNames();
+  void addPseudoClassLock(Element element,
+                          DOMString pseudoClass,
+                          optional boolean enabled = true);
+  void removePseudoClassLock(Element element, DOMString pseudoClass);
+  boolean hasPseudoClassLock(Element element, DOMString pseudoClass);
+  void clearPseudoClassLocks(Element element);
+  [Throws] void parseStyleSheet(CSSStyleSheet sheet, DOMString input);
+  void scrollElementIntoView(Element element);
+};
+
+dictionary PropertyNamesOptions {
+  boolean includeAliases = false;
 };
 
 dictionary InspectorRGBATuple {
   /*
    * NOTE: This tuple is in the normal 0-255-sized RGB space but can be
    * fractional and may extend outside the 0-255 range.
    *
    * a is in the range 0 - 1.
    */
   double r = 0;
   double g = 0;
   double b = 0;
   double a = 1;
 };
+
+[ChromeOnly]
+interface InspectorFontFace {
+  // An indication of how we found this font during font-matching.
+  // Note that the same physical font may have been found in multiple ways within a range.
+  readonly attribute boolean fromFontGroup;
+  readonly attribute boolean fromLanguagePrefs;
+  readonly attribute boolean fromSystemFallback;
+
+  // available for all fonts
+  readonly attribute DOMString name; // full font name as obtained from the font resource
+  readonly attribute DOMString CSSFamilyName; // a family name that could be used in CSS font-family
+                                              // (not necessarily the actual name that was used,
+                                              // due to aliases, generics, localized names, etc)
+
+  // meaningful only when the font is a user font defined using @font-face
+  readonly attribute CSSFontFaceRule? rule; // null if no associated @font-face rule
+  readonly attribute long srcIndex; // index in the rule's src list, -1 if no @font-face rule
+  readonly attribute DOMString URI; // empty string if not a downloaded font, i.e. local
+  readonly attribute DOMString localName; // empty string  if not a src:local(...) rule
+  readonly attribute DOMString format; // as per http://www.w3.org/TR/css3-webfonts/#referencing
+  readonly attribute DOMString metadata; // XML metadata from WOFF file (if any)
+};
--- a/dom/xbl/test/test_bug398492.xul
+++ b/dom/xbl/test/test_bug398492.xul
@@ -27,20 +27,20 @@ https://bugzilla.mozilla.org/show_bug.cg
   <hbox id="testbox" style="-moz-binding: url(#test)">Text</hbox>
   
   <!-- test code goes here -->
   <script type="application/javascript"><![CDATA[
 
     /** Test for Bug 398492 **/
     SimpleTest.waitForExplicitFinish();
 
+    const InspectorUtils = SpecialPowers.InspectorUtils;
+
     function getXBLParent(node) {
-      var utils = Components.classes["@mozilla.org/inspector/dom-utils;1"]
-                            .getService(Components.interfaces.inIDOMUtils);
-      return utils.getParentForNode(node, true);
+      return SpecialPowers.unwrap(InspectorUtils.getParentForNode(node, true));
     }
 
     addLoadEvent(function() {
       var n = $("testbox");
       var kid = n.firstChild;
       var anonKid = document.getAnonymousNodes(n)[0];
       is(anonKid instanceof XULElement, true, "Must be a XUL element");
       is(anonKid, getXBLParent(kid), "Unexpected anonymous nodes");
--- a/editor/libeditor/HTMLEditor.h
+++ b/editor/libeditor/HTMLEditor.h
@@ -125,18 +125,16 @@ public:
                      bool aSuppressTransaction) override;
   virtual nsresult SetAttributeOrEquivalent(Element* aElement,
                                             nsAtom* aAttribute,
                                             const nsAString& aValue,
                                             bool aSuppressTransaction) override;
   using EditorBase::RemoveAttributeOrEquivalent;
   using EditorBase::SetAttributeOrEquivalent;
 
-  nsresult MouseMove(nsIDOMMouseEvent* aMouseEvent);
-
   // nsStubMutationObserver overrides
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
 
   // nsIHTMLEditor methods
   NS_DECL_NSIHTMLEDITOR
 
@@ -439,16 +437,40 @@ public:
   {
     return mDefaultParagraphSeparator;
   }
   void SetDefaultParagraphSeparator(ParagraphSeparator aSep)
   {
     mDefaultParagraphSeparator = aSep;
   }
 
+  /**
+   * event callback when a mouse button is pressed
+   * @param aX      [IN] horizontal position of the pointer
+   * @param aY      [IN] vertical position of the pointer
+   * @param aTarget [IN] the element triggering the event
+   * @param aMouseEvent [IN] the event
+   */
+  nsresult OnMouseDown(int32_t aX, int32_t aY, nsIDOMElement* aTarget,
+                       nsIDOMEvent* aMouseEvent);
+
+  /**
+   * event callback when a mouse button is released
+   * @param aX      [IN] horizontal position of the pointer
+   * @param aY      [IN] vertical position of the pointer
+   * @param aTarget [IN] the element triggering the event
+   */
+  nsresult OnMouseUp(int32_t aX, int32_t aY, nsIDOMElement* aTarget);
+
+  /**
+   * event callback when the mouse pointer is moved
+   * @param aMouseEvent [IN] the event
+   */
+  nsresult OnMouseMove(nsIDOMMouseEvent* aMouseEvent);
+
 protected:
   class BlobReader final : public nsIEditorBlobListener
   {
   public:
     BlobReader(dom::BlobImpl* aBlob, HTMLEditor* aHTMLEditor,
                bool aIsSafe, nsIDOMDocument* aSourceDoc,
                nsIDOMNode* aDestinationNode, int32_t aDestOffset,
                bool aDoDeleteSelection);
--- a/editor/libeditor/HTMLEditorEventListener.cpp
+++ b/editor/libeditor/HTMLEditorEventListener.cpp
@@ -57,17 +57,17 @@ HTMLEditorEventListener::MouseUp(nsIDOMM
   nsresult rv = aMouseEvent->AsEvent()->GetTarget(getter_AddRefs(target));
   NS_ENSURE_SUCCESS(rv, rv);
   NS_ENSURE_TRUE(target, NS_ERROR_NULL_POINTER);
   nsCOMPtr<nsIDOMElement> element = do_QueryInterface(target);
 
   int32_t clientX, clientY;
   aMouseEvent->GetClientX(&clientX);
   aMouseEvent->GetClientY(&clientY);
-  htmlEditor->MouseUp(clientX, clientY, element);
+  htmlEditor->OnMouseUp(clientX, clientY, element);
 
   return EditorEventListener::MouseUp(aMouseEvent);
 }
 
 nsresult
 HTMLEditorEventListener::MouseDown(nsIDOMMouseEvent* aMouseEvent)
 {
   if (NS_WARN_IF(!aMouseEvent) || DetachedFromEditor()) {
@@ -189,17 +189,17 @@ HTMLEditorEventListener::MouseDown(nsIDO
       aMouseEvent->AsEvent()->PreventDefault();
       return NS_OK;
     }
   } else if (!isContextClick && buttonNumber == 0 && clickCount == 1) {
     // if the target element is an image, we have to display resizers
     int32_t clientX, clientY;
     aMouseEvent->GetClientX(&clientX);
     aMouseEvent->GetClientY(&clientY);
-    htmlEditor->MouseDown(clientX, clientY, element, aMouseEvent->AsEvent());
+    htmlEditor->OnMouseDown(clientX, clientY, element, aMouseEvent->AsEvent());
   }
 
   return EditorEventListener::MouseDown(aMouseEvent);
 }
 
 nsresult
 HTMLEditorEventListener::MouseClick(nsIDOMMouseEvent* aMouseEvent)
 {
--- a/editor/libeditor/HTMLEditorObjectResizer.cpp
+++ b/editor/libeditor/HTMLEditorObjectResizer.cpp
@@ -116,17 +116,17 @@ ResizerMouseMotionListener::HandleEvent(
     //non-ui event passed in.  bad things.
     return NS_OK;
   }
 
   // Don't do anything special if not an HTML object resizer editor
   RefPtr<HTMLEditor> htmlEditor = mHTMLEditorWeak.get();
   if (htmlEditor) {
     // check if we have to redisplay a resizing shadow
-    htmlEditor->MouseMove(mouseEvent);
+    htmlEditor->OnMouseMove(mouseEvent);
   }
 
   return NS_OK;
 }
 
 /******************************************************************************
  * mozilla::HTMLEditor
  ******************************************************************************/
@@ -543,21 +543,21 @@ HTMLEditor::StartResizing(nsIDOMElement*
     result = target->AddEventListener(NS_LITERAL_STRING("mousemove"),
                                       mMouseMotionListenerP, true);
     NS_ASSERTION(NS_SUCCEEDED(result),
                  "failed to register mouse motion listener");
   }
   return result;
 }
 
-NS_IMETHODIMP
-HTMLEditor::MouseDown(int32_t aClientX,
-                      int32_t aClientY,
-                      nsIDOMElement* aTarget,
-                      nsIDOMEvent* aEvent)
+nsresult
+HTMLEditor::OnMouseDown(int32_t aClientX,
+                        int32_t aClientY,
+                        nsIDOMElement* aTarget,
+                        nsIDOMEvent* aEvent)
 {
   bool anonElement = false;
   if (aTarget && NS_SUCCEEDED(aTarget->HasAttribute(NS_LITERAL_STRING("_moz_anonclass"), &anonElement)))
     // we caught a click on an anonymous element
     if (anonElement) {
       nsAutoString anonclass;
       nsresult rv =
         aTarget->GetAttribute(NS_LITERAL_STRING("_moz_anonclass"), anonclass);
@@ -575,20 +575,20 @@ HTMLEditor::MouseDown(int32_t aClientX,
         mOriginalX = aClientX;
         mOriginalY = aClientY;
         return GrabberClicked();
       }
     }
   return NS_OK;
 }
 
-NS_IMETHODIMP
-HTMLEditor::MouseUp(int32_t aClientX,
-                    int32_t aClientY,
-                    nsIDOMElement* aTarget)
+nsresult
+HTMLEditor::OnMouseUp(int32_t aClientX,
+                      int32_t aClientY,
+                      nsIDOMElement* aTarget)
 {
   if (mIsResizing) {
     // we are resizing and release the mouse button, so let's
     // end the resizing process
     mIsResizing = false;
     HideShadowAndInfo();
     SetFinalSize(aClientX, aClientY);
   } else if (mIsMoving || mGrabberClicked) {
@@ -800,28 +800,18 @@ HTMLEditor::GetNewResizingHeight(int32_t
                                  int32_t aY)
 {
   int32_t resized = mResizedObjectHeight +
                      GetNewResizingIncrement(aX, aY, kHeight) *
                          mHeightIncrementFactor;
   return std::max(resized, 1);
 }
 
-NS_IMETHODIMP
-HTMLEditor::MouseMove(nsIDOMEvent* aMouseEvent)
-{
-  nsCOMPtr<nsIDOMMouseEvent> mouseEvent = do_QueryInterface(aMouseEvent);
-  if (NS_WARN_IF(!mouseEvent)) {
-    return NS_OK;
-  }
-  return MouseMove(mouseEvent);
-}
-
 nsresult
-HTMLEditor::MouseMove(nsIDOMMouseEvent* aMouseEvent)
+HTMLEditor::OnMouseMove(nsIDOMMouseEvent* aMouseEvent)
 {
   MOZ_ASSERT(aMouseEvent);
 
   if (mIsResizing) {
     // we are resizing and the mouse pointer's position has changed
     // we have to resdisplay the shadow
     int32_t clientX, clientY;
     aMouseEvent->GetClientX(&clientX);
--- a/editor/nsIHTMLObjectResizer.idl
+++ b/editor/nsIHTMLObjectResizer.idl
@@ -42,35 +42,10 @@ interface nsIHTMLObjectResizer : nsISupp
    * Hide resizers if they are visible
    */
   void hideResizers();
 
   /**
    * Refresh visible resizers
    */
   void refreshResizers();
-
-  /**
-   * event callback when a mouse button is pressed
-   * @param aX      [IN] horizontal position of the pointer
-   * @param aY      [IN] vertical position of the pointer
-   * @param aTarget [IN] the element triggering the event
-   * @param aMouseEvent [IN] the event
-   */
-  void mouseDown(in long aX, in long aY,
-                 in nsIDOMElement aTarget, in nsIDOMEvent aMouseEvent);
-
-  /**
-   * event callback when a mouse button is released
-   * @param aX      [IN] horizontal position of the pointer
-   * @param aY      [IN] vertical position of the pointer
-   * @param aTarget [IN] the element triggering the event
-   */
-  void mouseUp(in long aX, in long aY,
-               in nsIDOMElement aTarget);
-
-  /**
-   * event callback when the mouse pointer is moved
-   * @param aMouseEvent [IN] the event
-   */
-  void mouseMove(in nsIDOMEvent aMouseEvent);
 };
 
--- a/gfx/2d/DrawTargetTiled.h
+++ b/gfx/2d/DrawTargetTiled.h
@@ -37,16 +37,17 @@ class DrawTargetTiled : public DrawTarge
 {
 public:
   DrawTargetTiled();
 
   bool Init(const TileSet& mTiles);
 
   virtual bool IsTiledDrawTarget() const override { return true; }
 
+  virtual bool IsCaptureDT() const override { return mTiles[0].mDrawTarget->IsCaptureDT(); }
   virtual DrawTargetType GetType() const override { return mTiles[0].mDrawTarget->GetType(); }
   virtual BackendType GetBackendType() const override { return mTiles[0].mDrawTarget->GetBackendType(); }
   virtual already_AddRefed<SourceSurface> Snapshot() override;
   virtual void DetachAllSnapshots() override;
   virtual IntSize GetSize() override {
     MOZ_ASSERT(mRect.Width() > 0 && mRect.Height() > 0);
     return IntSize(mRect.XMost(), mRect.YMost());
   }
--- a/gfx/2d/FilterNodeSoftware.cpp
+++ b/gfx/2d/FilterNodeSoftware.cpp
@@ -608,63 +608,17 @@ already_AddRefed<DataSourceSurface>
 FilterNodeSoftware::GetOutput(const IntRect &aRect)
 {
   MOZ_ASSERT(GetOutputRectInRect(aRect).Contains(aRect));
 
   if (aRect.Overflows()) {
     return nullptr;
   }
 
-  if (!mCachedRect.Contains(aRect)) {
-    RequestRect(aRect);
-    mCachedOutput = Render(mRequestedRect);
-    if (!mCachedOutput) {
-      mCachedRect = IntRect();
-      mRequestedRect = IntRect();
-      return nullptr;
-    }
-    mCachedRect = mRequestedRect;
-    mRequestedRect = IntRect();
-  } else {
-    MOZ_ASSERT(mCachedOutput, "cached rect but no cached output?");
-  }
-  return GetDataSurfaceInRect(mCachedOutput, mCachedRect, aRect, EDGE_MODE_NONE);
-}
-
-void
-FilterNodeSoftware::RequestRect(const IntRect &aRect)
-{
-  if (mRequestedRect.Contains(aRect)) {
-    // Bail out now. Otherwise pathological filters can spend time exponential
-    // in the number of primitives, e.g. if each primitive takes the
-    // previous primitive as its two inputs.
-    return;
-  }
-  mRequestedRect = mRequestedRect.Union(aRect);
-  RequestFromInputsForRect(aRect);
-}
-
-void
-FilterNodeSoftware::RequestInputRect(uint32_t aInputEnumIndex, const IntRect &aRect)
-{
-  if (aRect.Overflows()) {
-    return;
-  }
-
-  int32_t inputIndex = InputIndex(aInputEnumIndex);
-  if (inputIndex < 0 || (uint32_t)inputIndex >= NumberOfSetInputs()) {
-    gfxDevCrash(LogReason::FilterInputError) << "Invalid input " << inputIndex << " vs. " << NumberOfSetInputs();
-    return;
-  }
-  if (mInputSurfaces[inputIndex]) {
-    return;
-  }
-  RefPtr<FilterNodeSoftware> filter = mInputFilters[inputIndex];
-  MOZ_ASSERT(filter, "missing input");
-  filter->RequestRect(filter->GetOutputRectInRect(aRect));
+  return Render(aRect);
 }
 
 SurfaceFormat
 FilterNodeSoftware::DesiredFormat(SurfaceFormat aCurrentFormat,
                                   FormatHint aFormatHint)
 {
   if (aCurrentFormat == SurfaceFormat::A8 && aFormatHint == CAN_HANDLE_A8) {
     return SurfaceFormat::A8;
@@ -813,60 +767,18 @@ FilterNodeSoftware::GetInputRectInRect(u
 }
 
 size_t
 FilterNodeSoftware::NumberOfSetInputs()
 {
   return std::max(mInputSurfaces.size(), mInputFilters.size());
 }
 
-void
-FilterNodeSoftware::AddInvalidationListener(FilterInvalidationListener* aListener)
-{
-  MOZ_ASSERT(aListener, "null listener");
-  mInvalidationListeners.push_back(aListener);
-}
-
-void
-FilterNodeSoftware::RemoveInvalidationListener(FilterInvalidationListener* aListener)
-{
-  MOZ_ASSERT(aListener, "null listener");
-  std::vector<FilterInvalidationListener*>::iterator it =
-    std::find(mInvalidationListeners.begin(), mInvalidationListeners.end(), aListener);
-  mInvalidationListeners.erase(it);
-}
-
-void
-FilterNodeSoftware::FilterInvalidated(FilterNodeSoftware* aFilter)
-{
-  Invalidate();
-}
-
-void
-FilterNodeSoftware::Invalidate()
-{
-  mCachedOutput = nullptr;
-  mCachedRect = IntRect();
-  for (std::vector<FilterInvalidationListener*>::iterator it = mInvalidationListeners.begin();
-       it != mInvalidationListeners.end(); it++) {
-    (*it)->FilterInvalidated(this);
-  }
-}
-
 FilterNodeSoftware::~FilterNodeSoftware()
 {
-  MOZ_ASSERT(!mInvalidationListeners.size(),
-             "All invalidation listeners should have unsubscribed themselves by now!");
-
-  for (std::vector<RefPtr<FilterNodeSoftware> >::iterator it = mInputFilters.begin();
-       it != mInputFilters.end(); it++) {
-    if (*it) {
-      (*it)->RemoveInvalidationListener(this);
-    }
-  }
 }
 
 void
 FilterNodeSoftware::SetInput(uint32_t aIndex, FilterNode *aFilter)
 {
   if (aFilter && aFilter->GetBackendType() != FILTER_BACKEND_SOFTWARE) {
     MOZ_ASSERT(false, "can only take software filters as inputs");
     return;
@@ -890,28 +802,21 @@ FilterNodeSoftware::SetInput(uint32_t aI
     gfxDevCrash(LogReason::FilterInputSet) << "Invalid set " << inputIndex;
     return;
   }
   if ((uint32_t)inputIndex >= NumberOfSetInputs()) {
     mInputSurfaces.resize(inputIndex + 1);
     mInputFilters.resize(inputIndex + 1);
   }
   mInputSurfaces[inputIndex] = aSurface;
-  if (mInputFilters[inputIndex]) {
-    mInputFilters[inputIndex]->RemoveInvalidationListener(this);
-  }
-  if (aFilter) {
-    aFilter->AddInvalidationListener(this);
-  }
   mInputFilters[inputIndex] = aFilter;
   if (!aSurface && !aFilter && (size_t)inputIndex == NumberOfSetInputs()) {
     mInputSurfaces.resize(inputIndex);
     mInputFilters.resize(inputIndex);
   }
-  Invalidate();
 }
 
 FilterNodeBlendSoftware::FilterNodeBlendSoftware()
  : mBlendMode(BLEND_MODE_MULTIPLY)
 {}
 
 int32_t
 FilterNodeBlendSoftware::InputIndex(uint32_t aInputEnumIndex)
@@ -923,17 +828,16 @@ FilterNodeBlendSoftware::InputIndex(uint
   }
 }
 
 void
 FilterNodeBlendSoftware::SetAttribute(uint32_t aIndex, uint32_t aBlendMode)
 {
   MOZ_ASSERT(aIndex == ATT_BLEND_BLENDMODE);
   mBlendMode = static_cast<BlendMode>(aBlendMode);
-  Invalidate();
 }
 
 static CompositionOp ToBlendOp(BlendMode aOp)
 {
   switch (aOp) {
   case BLEND_MODE_MULTIPLY:
     return CompositionOp::OP_MULTIPLY;
   case BLEND_MODE_SCREEN:
@@ -1027,23 +931,16 @@ FilterNodeBlendSoftware::Render(const In
   }
 
   Rect r(0, 0, size.width, size.height);
   dt->DrawSurface(input2, r, r, DrawSurfaceOptions(), DrawOptions(1.0f, ToBlendOp(mBlendMode)));
   dt->Flush();
   return target.forget();
 }
 
-void
-FilterNodeBlendSoftware::RequestFromInputsForRect(const IntRect &aRect)
-{
-  RequestInputRect(IN_BLEND_IN, aRect);
-  RequestInputRect(IN_BLEND_IN2, aRect);
-}
-
 IntRect
 FilterNodeBlendSoftware::GetOutputRectInRect(const IntRect& aRect)
 {
   return GetInputRectInRect(IN_BLEND_IN, aRect).Union(
     GetInputRectInRect(IN_BLEND_IN2, aRect)).Intersect(aRect);
 }
 
 FilterNodeTransformSoftware::FilterNodeTransformSoftware()
@@ -1059,25 +956,23 @@ FilterNodeTransformSoftware::InputIndex(
   }
 }
 
 void
 FilterNodeTransformSoftware::SetAttribute(uint32_t aIndex, uint32_t aFilter)
 {
   MOZ_ASSERT(aIndex == ATT_TRANSFORM_FILTER);
   mSamplingFilter = static_cast<SamplingFilter>(aFilter);
-  Invalidate();
 }
 
 void
 FilterNodeTransformSoftware::SetAttribute(uint32_t aIndex, const Matrix &aMatrix)
 {
   MOZ_ASSERT(aIndex == ATT_TRANSFORM_MATRIX);
   mMatrix = aMatrix;
-  Invalidate();
 }
 
 IntRect
 FilterNodeTransformSoftware::SourceRectForOutputRect(const IntRect &aRect)
 {
   if (aRect.IsEmpty()) {
     return IntRect();
   }
@@ -1142,22 +1037,16 @@ FilterNodeTransformSoftware::Render(cons
   dt->SetTransform(transform);
   dt->DrawSurface(input, r, r, DrawSurfaceOptions(mSamplingFilter));
 
   dt->Flush();
   surf->Unmap();
   return surf.forget();
 }
 
-void
-FilterNodeTransformSoftware::RequestFromInputsForRect(const IntRect &aRect)
-{
-  RequestInputRect(IN_TRANSFORM_IN, SourceRectForOutputRect(aRect));
-}
-
 IntRect
 FilterNodeTransformSoftware::GetOutputRectInRect(const IntRect& aRect)
 {
   IntRect srcRect = SourceRectForOutputRect(aRect);
   if (srcRect.IsEmpty()) {
     return IntRect();
   }
 
@@ -1185,26 +1074,24 @@ FilterNodeMorphologySoftware::InputIndex
 
 void
 FilterNodeMorphologySoftware::SetAttribute(uint32_t aIndex,
                                            const IntSize &aRadii)
 {
   MOZ_ASSERT(aIndex == ATT_MORPHOLOGY_RADII);
   mRadii.width = std::min(std::max(aRadii.width, 0), 100000);
   mRadii.height = std::min(std::max(aRadii.height, 0), 100000);
-  Invalidate();
 }
 
 void
 FilterNodeMorphologySoftware::SetAttribute(uint32_t aIndex,
                                            uint32_t aOperator)
 {
   MOZ_ASSERT(aIndex == ATT_MORPHOLOGY_OPERATOR);
   mOperator = static_cast<MorphologyOperator>(aOperator);
-  Invalidate();
 }
 
 static already_AddRefed<DataSourceSurface>
 ApplyMorphology(const IntRect& aSourceRect, DataSourceSurface* aInput,
                 const IntRect& aDestRect, int32_t rx, int32_t ry,
                 MorphologyOperator aOperator)
 {
   IntRect srcRect = aSourceRect - aDestRect.TopLeft();
@@ -1283,24 +1170,16 @@ FilterNodeMorphologySoftware::Render(con
 
   if (rx == 0 && ry == 0) {
     return input.forget();
   }
 
   return ApplyMorphology(srcRect, input, aRect, rx, ry, mOperator);
 }
 
-void
-FilterNodeMorphologySoftware::RequestFromInputsForRect(const IntRect &aRect)
-{
-  IntRect srcRect = aRect;
-  srcRect.Inflate(mRadii);
-  RequestInputRect(IN_MORPHOLOGY_IN, srcRect);
-}
-
 IntRect
 FilterNodeMorphologySoftware::GetOutputRectInRect(const IntRect& aRect)
 {
   IntRect inflatedSourceRect = aRect;
   inflatedSourceRect.Inflate(mRadii);
   IntRect inputRect = GetInputRectInRect(IN_MORPHOLOGY_IN, inflatedSourceRect);
   if (mOperator == MORPHOLOGY_OPERATOR_ERODE) {
     inputRect.Deflate(mRadii);
@@ -1320,26 +1199,24 @@ FilterNodeColorMatrixSoftware::InputInde
 }
 
 void
 FilterNodeColorMatrixSoftware::SetAttribute(uint32_t aIndex,
                                             const Matrix5x4 &aMatrix)
 {
   MOZ_ASSERT(aIndex == ATT_COLOR_MATRIX_MATRIX);
   mMatrix = aMatrix;
-  Invalidate();
 }
 
 void
 FilterNodeColorMatrixSoftware::SetAttribute(uint32_t aIndex,
                                             uint32_t aAlphaMode)
 {
   MOZ_ASSERT(aIndex == ATT_COLOR_MATRIX_ALPHA_MODE);
   mAlphaMode = (AlphaMode)aAlphaMode;
-  Invalidate();
 }
 
 static already_AddRefed<DataSourceSurface>
 Premultiply(DataSourceSurface* aSurface)
 {
   if (aSurface->GetFormat() == SurfaceFormat::A8) {
     RefPtr<DataSourceSurface> surface(aSurface);
     return surface.forget();
@@ -1419,37 +1296,30 @@ FilterNodeColorMatrixSoftware::Render(co
 
   if (mAlphaMode == ALPHA_MODE_PREMULTIPLIED) {
     result = Premultiply(result);
   }
 
   return result.forget();
 }
 
-void
-FilterNodeColorMatrixSoftware::RequestFromInputsForRect(const IntRect &aRect)
-{
-  RequestInputRect(IN_COLOR_MATRIX_IN, aRect);
-}
-
 IntRect
 FilterNodeColorMatrixSoftware::GetOutputRectInRect(const IntRect& aRect)
 {
   if (mMatrix._54 > 0.0f) {
     return aRect;
   }
   return GetInputRectInRect(IN_COLOR_MATRIX_IN, aRect);
 }
 
 void
 FilterNodeFloodSoftware::SetAttribute(uint32_t aIndex, const Color &aColor)
 {
   MOZ_ASSERT(aIndex == ATT_FLOOD_COLOR);
   mColor = aColor;
-  Invalidate();
 }
 
 static uint32_t
 ColorToBGRA(const Color& aColor)
 {
   union {
     uint32_t color;
     uint8_t components[4];
@@ -1542,17 +1412,16 @@ FilterNodeTileSoftware::InputIndex(uint3
 
 void
 FilterNodeTileSoftware::SetAttribute(uint32_t aIndex,
                                      const IntRect &aSourceRect)
 {
   MOZ_ASSERT(aIndex == ATT_TILE_SOURCE_RECT);
   mSourceRect.SetRect(int32_t(aSourceRect.X()), int32_t(aSourceRect.Y()),
                       int32_t(aSourceRect.Width()), int32_t(aSourceRect.Height()));
-  Invalidate();
 }
 
 namespace {
 struct CompareIntRects
 {
   bool operator()(const IntRect& a, const IntRect& b) const
   {
     if (a.X() != b.X()) {
@@ -1631,25 +1500,16 @@ FilterNodeTileSoftware::Render(const Int
 
       CopyRect(input, target, srcRect - srcRect.TopLeft(), destRect.TopLeft() - aRect.TopLeft());
     }
   }
 
   return target.forget();
 }
 
-void
-FilterNodeTileSoftware::RequestFromInputsForRect(const IntRect &aRect)
-{
-  // Do not request anything.
-  // Source rects for the tile filter can be discontinuous with large gaps
-  // between them. Requesting those from our input filter might cause it to
-  // render the whole bounding box of all of them, which would be wasteful.
-}
-
 IntRect
 FilterNodeTileSoftware::GetOutputRectInRect(const IntRect& aRect)
 {
   return aRect;
 }
 
 FilterNodeComponentTransferSoftware::FilterNodeComponentTransferSoftware()
  : mDisableR(true)
@@ -1673,17 +1533,16 @@ FilterNodeComponentTransferSoftware::Set
       mDisableB = aDisable;
       break;
     case ATT_TRANSFER_DISABLE_A:
       mDisableA = aDisable;
       break;
     default:
       MOZ_CRASH("GFX: FilterNodeComponentTransferSoftware::SetAttribute");
   }
-  Invalidate();
 }
 
 void
 FilterNodeComponentTransferSoftware::GenerateLookupTable(ptrdiff_t aComponent,
                                                          uint8_t aTables[4][256],
                                                          bool aDisabled)
 {
   if (aDisabled) {
@@ -1800,22 +1659,16 @@ FilterNodeComponentTransferSoftware::Ren
     TransferComponents<1>(input, target, &lookupTables[B8G8R8A8_COMPONENT_BYTEOFFSET_A]);
   } else {
     TransferComponents<4>(input, target, lookupTables);
   }
 
   return target.forget();
 }
 
-void
-FilterNodeComponentTransferSoftware::RequestFromInputsForRect(const IntRect &aRect)
-{
-  RequestInputRect(IN_TRANSFER_IN, aRect);
-}
-
 IntRect
 FilterNodeComponentTransferSoftware::GetOutputRectInRect(const IntRect& aRect)
 {
   if (mDisableA) {
     return GetInputRectInRect(IN_TRANSFER_IN, aRect);
   }
   return aRect;
 }
@@ -1846,17 +1699,16 @@ FilterNodeTableTransferSoftware::SetAttr
       mTableB = table;
       break;
     case ATT_TABLE_TRANSFER_TABLE_A:
       mTableA = table;
       break;
     default:
       MOZ_CRASH("GFX: FilterNodeTableTransferSoftware::SetAttribute");
   }
-  Invalidate();
 }
 
 void
 FilterNodeTableTransferSoftware::FillLookupTable(ptrdiff_t aComponent,
                                                  uint8_t aTable[256])
 {
   switch (aComponent) {
     case B8G8R8A8_COMPONENT_BYTEOFFSET_R:
@@ -1915,17 +1767,16 @@ FilterNodeDiscreteTransferSoftware::SetA
       mTableB = discrete;
       break;
     case ATT_DISCRETE_TRANSFER_TABLE_A:
       mTableA = discrete;
       break;
     default:
       MOZ_CRASH("GFX: FilterNodeDiscreteTransferSoftware::SetAttribute");
   }
-  Invalidate();
 }
 
 void
 FilterNodeDiscreteTransferSoftware::FillLookupTable(ptrdiff_t aComponent,
                                                     uint8_t aTable[256])
 {
   switch (aComponent) {
     case B8G8R8A8_COMPONENT_BYTEOFFSET_R:
@@ -2004,17 +1855,16 @@ FilterNodeLinearTransferSoftware::SetAtt
       mSlopeA = aValue;
       break;
     case ATT_LINEAR_TRANSFER_INTERCEPT_A:
       mInterceptA = aValue;
       break;
     default:
       MOZ_CRASH("GFX: FilterNodeLinearTransferSoftware::SetAttribute");
   }
-  Invalidate();
 }
 
 void
 FilterNodeLinearTransferSoftware::FillLookupTable(ptrdiff_t aComponent,
                                                   uint8_t aTable[256])
 {
   switch (aComponent) {
     case B8G8R8A8_COMPONENT_BYTEOFFSET_R:
@@ -2098,17 +1948,16 @@ FilterNodeGammaTransferSoftware::SetAttr
       mExponentA = aValue;
       break;
     case ATT_GAMMA_TRANSFER_OFFSET_A:
       mOffsetA = aValue;
       break;
     default:
       MOZ_CRASH("GFX: FilterNodeGammaTransferSoftware::SetAttribute");
   }
-  Invalidate();
 }
 
 void
 FilterNodeGammaTransferSoftware::FillLookupTable(ptrdiff_t aComponent,
                                                  uint8_t aTable[256])
 {
   switch (aComponent) {
     case B8G8R8A8_COMPONENT_BYTEOFFSET_R:
@@ -2160,174 +2009,144 @@ FilterNodeConvolveMatrixSoftware::InputI
 }
 
 void
 FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex,
                                                const IntSize &aKernelSize)
 {
   MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_KERNEL_SIZE);
   mKernelSize = aKernelSize;
-  Invalidate();
 }
 
 void
 FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex,
                                                const Float *aMatrix,
                                                uint32_t aSize)
 {
   MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_KERNEL_MATRIX);
   mKernelMatrix = std::vector<Float>(aMatrix, aMatrix + aSize);
-  Invalidate();
 }
 
 void
 FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex, Float aValue)
 {
   switch (aIndex) {
     case ATT_CONVOLVE_MATRIX_DIVISOR:
       mDivisor = aValue;
       break;
     case ATT_CONVOLVE_MATRIX_BIAS:
       mBias = aValue;
       break;
     default:
       MOZ_CRASH("GFX: FilterNodeConvolveMatrixSoftware::SetAttribute");
   }
-  Invalidate();
 }
 
 void
 FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex, const Size &aKernelUnitLength)
 {
   switch (aIndex) {
     case ATT_CONVOLVE_MATRIX_KERNEL_UNIT_LENGTH:
       mKernelUnitLength = aKernelUnitLength;
       break;
     default:
       MOZ_CRASH("GFX: FilterNodeConvolveMatrixSoftware::SetAttribute");
   }
-  Invalidate();
 }
 
 void
 FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex,
                                                const IntPoint &aTarget)
 {
   MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_TARGET);
   mTarget = aTarget;
-  Invalidate();
 }
 
 void
 FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex,
                                                const IntRect &aSourceRect)
 {
   MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_SOURCE_RECT);
   mSourceRect = aSourceRect;
-  Invalidate();
 }
 
 void
 FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex,
                                                uint32_t aEdgeMode)
 {
   MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_EDGE_MODE);
   mEdgeMode = static_cast<ConvolveMatrixEdgeMode>(aEdgeMode);
-  Invalidate();
 }
 
 void
 FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex,
                                                bool aPreserveAlpha)
 {
   MOZ_ASSERT(aIndex == ATT_CONVOLVE_MATRIX_PRESERVE_ALPHA);
   mPreserveAlpha = aPreserveAlpha;
-  Invalidate();
 }
 
 #ifdef DEBUG
-static bool sColorSamplingAccessControlEnabled = false;
-static uint8_t* sColorSamplingAccessControlStart = nullptr;
-static uint8_t* sColorSamplingAccessControlEnd = nullptr;
-
-struct DebugOnlyAutoColorSamplingAccessControl
+static inline void
+DebugOnlyCheckColorSamplingAccess(const uint8_t* aSampleAddress, const uint8_t* aBoundsBegin, const uint8_t* aBoundsEnd)
 {
-  explicit DebugOnlyAutoColorSamplingAccessControl(DataSourceSurface* aSurface)
-  {
-    sColorSamplingAccessControlStart = aSurface->GetData();
-    sColorSamplingAccessControlEnd = sColorSamplingAccessControlStart +
-      aSurface->Stride() * aSurface->GetSize().height;
-    sColorSamplingAccessControlEnabled = true;
-  }
-
-  ~DebugOnlyAutoColorSamplingAccessControl()
-  {
-    sColorSamplingAccessControlEnabled = false;
-  }
-};
-
-static inline void
-DebugOnlyCheckColorSamplingAccess(const uint8_t* aSampleAddress)
-{
-  if (sColorSamplingAccessControlEnabled) {
-    MOZ_ASSERT(aSampleAddress >= sColorSamplingAccessControlStart, "accessing before start");
-    MOZ_ASSERT(aSampleAddress < sColorSamplingAccessControlEnd, "accessing after end");
-  }
+  MOZ_ASSERT(aSampleAddress >= aBoundsBegin, "accessing before start");
+  MOZ_ASSERT(aSampleAddress < aBoundsEnd, "accessing after end");
 }
 #else
-typedef DebugOnly<DataSourceSurface*> DebugOnlyAutoColorSamplingAccessControl;
-#define DebugOnlyCheckColorSamplingAccess(address)
+#define DebugOnlyCheckColorSamplingAccess(address, boundsBegin, boundsEnd)
 #endif
 
 static inline uint8_t
-ColorComponentAtPoint(const uint8_t *aData, int32_t aStride, int32_t x, int32_t y, size_t bpp, ptrdiff_t c)
+ColorComponentAtPoint(const uint8_t *aData, int32_t aStride, const uint8_t *aBoundsBegin, const uint8_t *aBoundsEnd, int32_t x, int32_t y, size_t bpp, ptrdiff_t c)
 {
-  DebugOnlyCheckColorSamplingAccess(&aData[y * aStride + bpp * x + c]);
+  DebugOnlyCheckColorSamplingAccess(&aData[y * aStride + bpp * x + c], aBoundsBegin, aBoundsEnd);
   return aData[y * aStride + bpp * x + c];
 }
 
 static inline int32_t
-ColorAtPoint(const uint8_t *aData, int32_t aStride, int32_t x, int32_t y)
+ColorAtPoint(const uint8_t *aData, int32_t aStride, const uint8_t *aBoundsBegin, const uint8_t *aBoundsEnd, int32_t x, int32_t y)
 {
-  DebugOnlyCheckColorSamplingAccess(aData + y * aStride + 4 * x);
+  DebugOnlyCheckColorSamplingAccess(aData + y * aStride + 4 * x, aBoundsBegin, aBoundsEnd);
   return *(uint32_t*)(aData + y * aStride + 4 * x);
 }
 
 // Accepts fractional x & y and does bilinear interpolation.
 // Only call this if the pixel (floor(x)+1, floor(y)+1) is accessible.
 static inline uint8_t
-ColorComponentAtPoint(const uint8_t *aData, int32_t aStride, Float x, Float y, size_t bpp, ptrdiff_t c)
+ColorComponentAtPoint(const uint8_t *aData, int32_t aStride, const uint8_t *aBoundsBegin, const uint8_t *aBoundsEnd, Float x, Float y, size_t bpp, ptrdiff_t c)
 {
   const uint32_t f = 256;
   const int32_t lx = floor(x);
   const int32_t ly = floor(y);
   const int32_t tux = uint32_t((x - lx) * f);
   const int32_t tlx = f - tux;
   const int32_t tuy = uint32_t((y - ly) * f);
   const int32_t tly = f - tuy;
-  const uint8_t &cll = ColorComponentAtPoint(aData, aStride, lx,     ly,     bpp, c);
-  const uint8_t &cul = ColorComponentAtPoint(aData, aStride, lx + 1, ly,     bpp, c);
-  const uint8_t &clu = ColorComponentAtPoint(aData, aStride, lx,     ly + 1, bpp, c);
-  const uint8_t &cuu = ColorComponentAtPoint(aData, aStride, lx + 1, ly + 1, bpp, c);
+  const uint8_t &cll = ColorComponentAtPoint(aData, aStride, aBoundsBegin, aBoundsEnd, lx,     ly,     bpp, c);
+  const uint8_t &cul = ColorComponentAtPoint(aData, aStride, aBoundsBegin, aBoundsEnd, lx + 1, ly,     bpp, c);
+  const uint8_t &clu = ColorComponentAtPoint(aData, aStride, aBoundsBegin, aBoundsEnd, lx,     ly + 1, bpp, c);
+  const uint8_t &cuu = ColorComponentAtPoint(aData, aStride, aBoundsBegin, aBoundsEnd, lx + 1, ly + 1, bpp, c);
   return ((cll * tlx + cul * tux) * tly +
           (clu * tlx + cuu * tux) * tuy + f * f / 2) / (f * f);
 }
 
 static int32_t
 ClampToNonZero(int32_t a)
 {
   return a * (a >= 0);
 }
 
 template<typename CoordType>
 static void
 ConvolvePixel(const uint8_t *aSourceData,
               uint8_t *aTargetData,
               int32_t aWidth, int32_t aHeight,
               int32_t aSourceStride, int32_t aTargetStride,
+              const uint8_t* aSourceBegin, const uint8_t* aSourceEnd,
               int32_t aX, int32_t aY,
               const int32_t *aKernel,
               int32_t aBias, int32_t shiftL, int32_t shiftR,
               bool aPreserveAlpha,
               int32_t aOrderX, int32_t aOrderY,
               int32_t aTargetX, int32_t aTargetY,
               CoordType aKernelUnitLengthX,
               CoordType aKernelUnitLengthY)
@@ -2341,17 +2160,17 @@ ConvolvePixel(const uint8_t *aSourceData
   int32_t roundingAddition = shiftL == 0 ? 0 : 1 << (shiftL - 1);
 
   for (int32_t y = 0; y < aOrderY; y++) {
     CoordType sampleY = aY + (y - aTargetY) * aKernelUnitLengthY;
     for (int32_t x = 0; x < aOrderX; x++) {
       CoordType sampleX = aX + (x - aTargetX) * aKernelUnitLengthX;
       for (int32_t i = 0; i < channels; i++) {
         sum[i] += aKernel[aOrderX * y + x] *
-          ColorComponentAtPoint(aSourceData, aSourceStride,
+          ColorComponentAtPoint(aSourceData, aSourceStride, aSourceBegin, aSourceEnd,
                                 sampleX, sampleY, 4, offsets[i]);
       }
     }
   }
   for (int32_t i = 0; i < channels; i++) {
     int32_t clamped = umin(ClampToNonZero(sum[i] + aBias), 255 << shiftL >> shiftR);
     aTargetData[aY * aTargetStride + 4 * aX + offsets[i]] =
       (clamped + roundingAddition) << shiftR >> shiftL;
@@ -2449,34 +2268,34 @@ FilterNodeConvolveMatrixSoftware::DoRend
 
   RefPtr<DataSourceSurface> input =
     GetInputDataSourceSurface(IN_CONVOLVE_MATRIX_IN, srcRect, NEED_COLOR_CHANNELS, mEdgeMode, &mSourceRect);
 
   if (!input) {
     return nullptr;
   }
 
-  DebugOnlyAutoColorSamplingAccessControl accessControl(input);
-
   RefPtr<DataSourceSurface> target =
     Factory::CreateDataSourceSurface(aRect.Size(), SurfaceFormat::B8G8R8A8, true);
   if (MOZ2D_WARN_IF(!target)) {
     return nullptr;
   }
 
   IntPoint offset = aRect.TopLeft() - srcRect.TopLeft();
 
   DataSourceSurface::ScopedMap sourceMap(input, DataSourceSurface::READ);
   DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::WRITE);
   if (MOZ2D_WARN_IF(!sourceMap.IsMapped() || !targetMap.IsMapped())) {
     return nullptr;
   }
 
   uint8_t* sourceData = DataAtOffset(input, sourceMap.GetMappedSurface(), offset);
   int32_t sourceStride = sourceMap.GetStride();
+  uint8_t* sourceBegin = sourceMap.GetData();
+  uint8_t* sourceEnd = sourceBegin + sourceStride * input->GetSize().height;
   uint8_t* targetData = targetMap.GetData();
   int32_t targetStride = targetMap.GetStride();
 
   // Why exactly are we reversing the kernel?
   std::vector<Float> kernel = ReversedVector(mKernelMatrix);
   kernel = ScaledVector(kernel, mDivisor);
   Float maxResultAbs = std::max(MaxVectorSum(kernel) + mBias,
                                 MaxVectorSum(ScaledVector(kernel, -1)) - mBias);
@@ -2493,33 +2312,27 @@ FilterNodeConvolveMatrixSoftware::DoRend
   for (size_t i = 0; i < kernel.size(); i++) {
     intKernel[i] = NS_lround(kernel[i] * factorFromShifts);
   }
   int32_t bias = NS_lround(mBias * 255 * factorFromShifts);
 
   for (int32_t y = 0; y < aRect.Height(); y++) {
     for (int32_t x = 0; x < aRect.Width(); x++) {
       ConvolvePixel(sourceData, targetData,
-                    aRect.Width(), aRect.Height(), sourceStride, targetStride,
+                    aRect.Width(), aRect.Height(), sourceStride, targetStride, sourceBegin, sourceEnd,
                     x, y, intKernel, bias, shiftL, shiftR, mPreserveAlpha,
                     mKernelSize.width, mKernelSize.height, mTarget.x, mTarget.y,
                     aKernelUnitLengthX, aKernelUnitLengthY);
     }
   }
   delete[] intKernel;
 
   return target.forget();
 }
 
-void
-FilterNodeConvolveMatrixSoftware::RequestFromInputsForRect(const IntRect &aRect)
-{
-  RequestInputRect(IN_CONVOLVE_MATRIX_IN, InflatedSourceRect(aRect));
-}
-
 IntRect
 FilterNodeConvolveMatrixSoftware::InflatedSourceRect(const IntRect &aDestRect)
 {
   if (aDestRect.IsEmpty()) {
     return IntRect();
   }
 
   IntMargin margin;
@@ -2576,33 +2389,31 @@ FilterNodeDisplacementMapSoftware::Input
 }
 
 void
 FilterNodeDisplacementMapSoftware::SetAttribute(uint32_t aIndex,
                                                 Float aScale)
 {
   MOZ_ASSERT(aIndex == ATT_DISPLACEMENT_MAP_SCALE);
   mScale = aScale;
-  Invalidate();
 }
 
 void
 FilterNodeDisplacementMapSoftware::SetAttribute(uint32_t aIndex, uint32_t aValue)
 {
   switch (aIndex) {
     case ATT_DISPLACEMENT_MAP_X_CHANNEL:
       mChannelX = static_cast<ColorChannel>(aValue);
       break;
     case ATT_DISPLACEMENT_MAP_Y_CHANNEL:
       mChannelY = static_cast<ColorChannel>(aValue);
       break;
     default:
       MOZ_CRASH("GFX: FilterNodeDisplacementMapSoftware::SetAttribute");
   }
-  Invalidate();
 }
 
 already_AddRefed<DataSourceSurface>
 FilterNodeDisplacementMapSoftware::Render(const IntRect& aRect)
 {
   IntRect srcRect = InflatedSourceOrDestRect(aRect);
   RefPtr<DataSourceSurface> input =
     GetInputDataSourceSurface(IN_DISPLACEMENT_MAP_IN, srcRect, NEED_COLOR_CHANNELS);
@@ -2620,16 +2431,18 @@ FilterNodeDisplacementMapSoftware::Rende
   DataSourceSurface::ScopedMap mapMap(map, DataSourceSurface::READ);
   DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::WRITE);
   if (MOZ2D_WARN_IF(!(inputMap.IsMapped() && mapMap.IsMapped() && targetMap.IsMapped()))) {
     return nullptr;
   }
 
   uint8_t* sourceData = DataAtOffset(input, inputMap.GetMappedSurface(), offset);
   int32_t sourceStride = inputMap.GetStride();
+  uint8_t* sourceBegin = inputMap.GetData();
+  uint8_t* sourceEnd = sourceBegin + sourceStride * input->GetSize().height;
   uint8_t* mapData = mapMap.GetData();
   int32_t mapStride = mapMap.GetStride();
   uint8_t* targetData = targetMap.GetData();
   int32_t targetStride = targetMap.GetStride();
 
   static const ptrdiff_t channelMap[4] = {
                              B8G8R8A8_COMPONENT_BYTEOFFSET_R,
                              B8G8R8A8_COMPONENT_BYTEOFFSET_G,
@@ -2645,33 +2458,26 @@ FilterNodeDisplacementMapSoftware::Rende
     for (int32_t x = 0; x < aRect.Width(); x++) {
       uint32_t mapIndex = y * mapStride + 4 * x;
       uint32_t targIndex = y * targetStride + 4 * x;
       int32_t sourceX = x +
         scaleOver255 * mapData[mapIndex + xChannel] + scaleAdjustment;
       int32_t sourceY = y +
         scaleOver255 * mapData[mapIndex + yChannel] + scaleAdjustment;
       *(uint32_t*)(targetData + targIndex) =
-        ColorAtPoint(sourceData, sourceStride, sourceX, sourceY);
+        ColorAtPoint(sourceData, sourceStride, sourceBegin, sourceEnd, sourceX, sourceY);
     }
 
     // Keep valgrind happy.
     PodZero(&targetData[y * targetStride + 4 * aRect.Width()], targetStride - 4 * aRect.Width());
   }
 
   return target.forget();
 }
 
-void
-FilterNodeDisplacementMapSoftware::RequestFromInputsForRect(const IntRect &aRect)
-{
-  RequestInputRect(IN_DISPLACEMENT_MAP_IN, InflatedSourceOrDestRect(aRect));
-  RequestInputRect(IN_DISPLACEMENT_MAP_IN2, aRect);
-}
-
 IntRect
 FilterNodeDisplacementMapSoftware::InflatedSourceOrDestRect(const IntRect &aDestOrSourceRect)
 {
   IntRect sourceOrDestRect = aDestOrSourceRect;
   sourceOrDestRect.Inflate(ceil(fabs(mScale) / 2));
   return sourceOrDestRect;
 }
 
@@ -2702,39 +2508,36 @@ FilterNodeTurbulenceSoftware::SetAttribu
   switch (aIndex) {
     case ATT_TURBULENCE_BASE_FREQUENCY:
       mBaseFrequency = aBaseFrequency;
       break;
     default:
       MOZ_CRASH("GFX: FilterNodeTurbulenceSoftware::SetAttribute");
       break;
   }
-  Invalidate();
 }
 
 void
 FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex, const IntRect &aRect)
 {
   switch (aIndex) {
     case ATT_TURBULENCE_RECT:
       mRenderRect = aRect;
       break;
     default:
       MOZ_CRASH("GFX: FilterNodeTurbulenceSoftware::SetAttribute");
       break;
   }
-  Invalidate();
 }
 
 void
 FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex, bool aStitchable)
 {
   MOZ_ASSERT(aIndex == ATT_TURBULENCE_STITCHABLE);
   mStitchable = aStitchable;
-  Invalidate();
 }
 
 void
 FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex, uint32_t aValue)
 {
   switch (aIndex) {
     case ATT_TURBULENCE_NUM_OCTAVES:
       mNumOctaves = aValue;
@@ -2744,17 +2547,16 @@ FilterNodeTurbulenceSoftware::SetAttribu
       break;
     case ATT_TURBULENCE_TYPE:
       mType = static_cast<TurbulenceType>(aValue);
       break;
     default:
       MOZ_CRASH("GFX: FilterNodeTurbulenceSoftware::SetAttribute");
       break;
   }
-  Invalidate();
 }
 
 already_AddRefed<DataSourceSurface>
 FilterNodeTurbulenceSoftware::Render(const IntRect& aRect)
 {
   return FilterProcessing::RenderTurbulence(
     aRect.Size(), aRect.TopLeft(), mBaseFrequency,
     mSeed, mNumOctaves, mType, mStitchable, Rect(mRenderRect));
@@ -2788,18 +2590,16 @@ FilterNodeArithmeticCombineSoftware::Set
 {
   MOZ_ASSERT(aIndex == ATT_ARITHMETIC_COMBINE_COEFFICIENTS);
   MOZ_ASSERT(aSize == 4);
 
   mK1 = aFloat[0];
   mK2 = aFloat[1];
   mK3 = aFloat[2];
   mK4 = aFloat[3];
-
-  Invalidate();
 }
 
 already_AddRefed<DataSourceSurface>
 FilterNodeArithmeticCombineSoftware::Render(const IntRect& aRect)
 {
   RefPtr<DataSourceSurface> input1 =
     GetInputDataSourceSurface(IN_ARITHMETIC_COMBINE_IN, aRect, NEED_COLOR_CHANNELS);
   RefPtr<DataSourceSurface> input2 =
@@ -2820,23 +2620,16 @@ FilterNodeArithmeticCombineSoftware::Ren
     k1 = 0.0f;
     k3 = 0.0f;
     input2 = input1;
   }
 
   return FilterProcessing::ApplyArithmeticCombine(input1, input2, k1, k2, k3, k4);
 }
 
-void
-FilterNodeArithmeticCombineSoftware::RequestFromInputsForRect(const IntRect &aRect)
-{
-  RequestInputRect(IN_ARITHMETIC_COMBINE_IN, aRect);
-  RequestInputRect(IN_ARITHMETIC_COMBINE_IN2, aRect);
-}
-
 IntRect
 FilterNodeArithmeticCombineSoftware::GetOutputRectInRect(const IntRect& aRect)
 {
   if (mK4 > 0.0f) {
     return aRect;
   }
   IntRect rectFrom1 = GetInputRectInRect(IN_ARITHMETIC_COMBINE_IN, aRect).Intersect(aRect);
   IntRect rectFrom2 = GetInputRectInRect(IN_ARITHMETIC_COMBINE_IN2, aRect).Intersect(aRect);
@@ -2863,17 +2656,16 @@ FilterNodeCompositeSoftware::InputIndex(
   return aInputEnumIndex - IN_COMPOSITE_IN_START;
 }
 
 void
 FilterNodeCompositeSoftware::SetAttribute(uint32_t aIndex, uint32_t aCompositeOperator)
 {
   MOZ_ASSERT(aIndex == ATT_COMPOSITE_OPERATOR);
   mOperator = static_cast<CompositeOperator>(aCompositeOperator);
-  Invalidate();
 }
 
 already_AddRefed<DataSourceSurface>
 FilterNodeCompositeSoftware::Render(const IntRect& aRect)
 {
   RefPtr<DataSourceSurface> start =
     GetInputDataSourceSurface(IN_COMPOSITE_IN_START, aRect, NEED_COLOR_CHANNELS);
   RefPtr<DataSourceSurface> dest =
@@ -2910,24 +2702,16 @@ FilterNodeCompositeSoftware::Render(cons
           // no additional input can get rid of that transparency.
           return nullptr;
       }
     }
   }
   return dest.forget();
 }
 
-void
-FilterNodeCompositeSoftware::RequestFromInputsForRect(const IntRect &aRect)
-{
-  for (size_t inputIndex = 0; inputIndex < NumberOfSetInputs(); inputIndex++) {
-    RequestInputRect(IN_COMPOSITE_IN_START + inputIndex, aRect);
-  }
-}
-
 IntRect
 FilterNodeCompositeSoftware::GetOutputRectInRect(const IntRect& aRect)
 {
   IntRect rect;
   for (size_t inputIndex = 0; inputIndex < NumberOfSetInputs(); inputIndex++) {
     IntRect inputRect = GetInputRectInRect(IN_COMPOSITE_IN_START + inputIndex, aRect);
     if (mOperator == COMPOSITE_OPERATOR_IN && inputIndex > 0) {
       rect = rect.Intersect(inputRect);
@@ -3003,22 +2787,16 @@ FilterNodeBlurXYSoftware::Render(const I
       blur.Blur(channel3Map.GetData());
     }
     target = FilterProcessing::CombineColorChannels(channel0, channel1, channel2, channel3);
   }
 
   return GetDataSurfaceInRect(target, srcRect, aRect, EDGE_MODE_NONE);
 }
 
-void
-FilterNodeBlurXYSoftware::RequestFromInputsForRect(const IntRect &aRect)
-{
-  RequestInputRect(IN_GAUSSIAN_BLUR_IN, InflatedSourceOrDestRect(aRect));
-}
-
 IntRect
 FilterNodeBlurXYSoftware::InflatedSourceOrDestRect(const IntRect &aDestRect)
 {
   Size sigmaXY = StdDeviationXY();
   IntSize d = AlphaBoxBlur::CalculateBlurRadius(Point(sigmaXY.width, sigmaXY.height));
   IntRect srcRect = aDestRect;
   srcRect.Inflate(d);
   return srcRect;
@@ -3049,17 +2827,16 @@ FilterNodeGaussianBlurSoftware::SetAttri
 {
   switch (aIndex) {
     case ATT_GAUSSIAN_BLUR_STD_DEVIATION:
       mStdDeviation = ClampStdDeviation(aStdDeviation);
       break;
     default:
       MOZ_CRASH("GFX: FilterNodeGaussianBlurSoftware::SetAttribute");
   }
-  Invalidate();
 }
 
 Size
 FilterNodeGaussianBlurSoftware::StdDeviationXY()
 {
   return Size(mStdDeviation, mStdDeviation);
 }
 
@@ -3073,31 +2850,29 @@ FilterNodeDirectionalBlurSoftware::SetAt
 {
   switch (aIndex) {
     case ATT_DIRECTIONAL_BLUR_STD_DEVIATION:
       mStdDeviation = ClampStdDeviation(aStdDeviation);
       break;
     default:
       MOZ_CRASH("GFX: FilterNodeDirectionalBlurSoftware::SetAttribute");
   }
-  Invalidate();
 }
 
 void
 FilterNodeDirectionalBlurSoftware::SetAttribute(uint32_t aIndex,
                                                 uint32_t aBlurDirection)
 {
   switch (aIndex) {
     case ATT_DIRECTIONAL_BLUR_DIRECTION:
       mBlurDirection = (BlurDirection)aBlurDirection;
       break;
     default:
       MOZ_CRASH("GFX: FilterNodeDirectionalBlurSoftware::SetAttribute");
   }
-  Invalidate();
 }
 
 Size
 FilterNodeDirectionalBlurSoftware::StdDeviationXY()
 {
   float sigmaX = mBlurDirection == BLUR_DIRECTION_X ? mStdDeviation : 0;
   float sigmaY = mBlurDirection == BLUR_DIRECTION_Y ? mStdDeviation : 0;
   return Size(sigmaX, sigmaY);
@@ -3117,31 +2892,24 @@ FilterNodeCropSoftware::SetAttribute(uin
                                      const Rect &aSourceRect)
 {
   MOZ_ASSERT(aIndex == ATT_CROP_RECT);
   Rect srcRect = aSourceRect;
   srcRect.Round();
   if (!srcRect.ToIntRect(&mCropRect)) {
     mCropRect = IntRect();
   }
-  Invalidate();
 }
 
 already_AddRefed<DataSourceSurface>
 FilterNodeCropSoftware::Render(const IntRect& aRect)
 {
   return GetInputDataSourceSurface(IN_CROP_IN, aRect.Intersect(mCropRect));
 }
 
-void
-FilterNodeCropSoftware::RequestFromInputsForRect(const IntRect &aRect)
-{
-  RequestInputRect(IN_CROP_IN, aRect.Intersect(mCropRect));
-}
-
 IntRect
 FilterNodeCropSoftware::GetOutputRectInRect(const IntRect& aRect)
 {
   return GetInputRectInRect(IN_CROP_IN, aRect).Intersect(mCropRect);
 }
 
 int32_t
 FilterNodePremultiplySoftware::InputIndex(uint32_t aInputEnumIndex)
@@ -3155,22 +2923,16 @@ FilterNodePremultiplySoftware::InputInde
 already_AddRefed<DataSourceSurface>
 FilterNodePremultiplySoftware::Render(const IntRect& aRect)
 {
   RefPtr<DataSourceSurface> input =
     GetInputDataSourceSurface(IN_PREMULTIPLY_IN, aRect);
   return input ? Premultiply(input) : nullptr;
 }
 
-void
-FilterNodePremultiplySoftware::RequestFromInputsForRect(const IntRect &aRect)
-{
-  RequestInputRect(IN_PREMULTIPLY_IN, aRect);
-}
-
 IntRect
 FilterNodePremultiplySoftware::GetOutputRectInRect(const IntRect& aRect)
 {
   return GetInputRectInRect(IN_PREMULTIPLY_IN, aRect);
 }
 
 int32_t
 FilterNodeUnpremultiplySoftware::InputIndex(uint32_t aInputEnumIndex)
@@ -3184,22 +2946,16 @@ FilterNodeUnpremultiplySoftware::InputIn
 already_AddRefed<DataSourceSurface>
 FilterNodeUnpremultiplySoftware::Render(const IntRect& aRect)
 {
   RefPtr<DataSourceSurface> input =
     GetInputDataSourceSurface(IN_UNPREMULTIPLY_IN, aRect);
   return input ? Unpremultiply(input) : nullptr;
 }
 
-void
-FilterNodeUnpremultiplySoftware::RequestFromInputsForRect(const IntRect &aRect)
-{
-  RequestInputRect(IN_UNPREMULTIPLY_IN, aRect);
-}
-
 IntRect
 FilterNodeUnpremultiplySoftware::GetOutputRectInRect(const IntRect& aRect)
 {
   return GetInputRectInRect(IN_UNPREMULTIPLY_IN, aRect);
 }
 
 bool
 PointLightSoftware::SetAttribute(uint32_t aIndex, const Point3D &aPoint)
@@ -3278,17 +3034,18 @@ DistantLightSoftware::SetAttribute(uint3
 static inline Point3D Normalized(const Point3D &vec) {
   Point3D copy(vec);
   copy.Normalize();
   return copy;
 }
 
 template<typename LightType, typename LightingType>
 FilterNodeLightingSoftware<LightType, LightingType>::FilterNodeLightingSoftware(const char* aTypeName)
- : mSurfaceScale(0)
+ : mLock("FilterNodeLightingSoftware")
+ , mSurfaceScale(0)
 #if defined(MOZILLA_INTERNAL_API) && (defined(DEBUG) || defined(FORCE_BUILD_REFCNT_LOGGING))
  , mTypeName(aTypeName)
 #endif
 {}
 
 template<typename LightType, typename LightingType>
 int32_t
 FilterNodeLightingSoftware<LightType, LightingType>::InputIndex(uint32_t aInputEnumIndex)
@@ -3299,62 +3056,57 @@ FilterNodeLightingSoftware<LightType, Li
   }
 }
 
 template<typename LightType, typename LightingType>
 void
 FilterNodeLightingSoftware<LightType, LightingType>::SetAttribute(uint32_t aIndex, const Point3D &aPoint)
 {
   if (mLight.SetAttribute(aIndex, aPoint)) {
-    Invalidate();
     return;
   }
   MOZ_CRASH("GFX: FilterNodeLightingSoftware::SetAttribute point");
 }
 
 template<typename LightType, typename LightingType>
 void
 FilterNodeLightingSoftware<LightType, LightingType>::SetAttribute(uint32_t aIndex, Float aValue)
 {
   if (mLight.SetAttribute(aIndex, aValue) ||
       mLighting.SetAttribute(aIndex, aValue)) {
-    Invalidate();
     return;
   }
   switch (aIndex) {
     case ATT_LIGHTING_SURFACE_SCALE:
       mSurfaceScale = std::fpclassify(aValue) == FP_SUBNORMAL ? 0.0 : aValue;
       break;
     default:
       MOZ_CRASH("GFX: FilterNodeLightingSoftware::SetAttribute float");
   }
-  Invalidate();
 }
 
 template<typename LightType, typename LightingType>
 void
 FilterNodeLightingSoftware<LightType, LightingType>::SetAttribute(uint32_t aIndex, const Size &aKernelUnitLength)
 {
   switch (aIndex) {
     case ATT_LIGHTING_KERNEL_UNIT_LENGTH:
       mKernelUnitLength = aKernelUnitLength;
       break;
     default:
       MOZ_CRASH("GFX: FilterNodeLightingSoftware::SetAttribute size");
   }
-  Invalidate();
 }
 
 template<typename LightType, typename LightingType>
 void
 FilterNodeLightingSoftware<LightType, LightingType>::SetAttribute(uint32_t aIndex, const Color &aColor)
 {
   MOZ_ASSERT(aIndex == ATT_LIGHTING_COLOR);
   mColor = aColor;
-  Invalidate();
 }
 
 template<typename LightType, typename LightingType>
 IntRect
 FilterNodeLightingSoftware<LightType, LightingType>::GetOutputRectInRect(const IntRect& aRect)
 {
   return aRect;
 }
@@ -3431,41 +3183,41 @@ DistantLightSoftware::GetVectorToLight(c
 uint32_t
 DistantLightSoftware::GetColor(uint32_t aLightColor, const Point3D &aVectorToLight)
 {
   return aLightColor;
 }
 
 template<typename CoordType>
 static Point3D
-GenerateNormal(const uint8_t *data, int32_t stride,
+GenerateNormal(const uint8_t *data, int32_t stride, uint8_t* boundsBegin, uint8_t* boundsEnd,
                int32_t x, int32_t y, float surfaceScale,
                CoordType dx, CoordType dy)
 {
   const uint8_t *index = data + y * stride + x;
 
   CoordType zero = 0;
 
   // See this for source of constants:
   //   http://www.w3.org/TR/SVG11/filters.html#feDiffuseLightingElement
   int16_t normalX =
-    -1 * ColorComponentAtPoint(index, stride, -dx, -dy, 1, 0) +
-     1 * ColorComponentAtPoint(index, stride, dx, -dy, 1, 0) +
-    -2 * ColorComponentAtPoint(index, stride, -dx, zero, 1, 0) +
-     2 * ColorComponentAtPoint(index, stride, dx, zero, 1, 0) +
-    -1 * ColorComponentAtPoint(index, stride, -dx, dy, 1, 0) +
-     1 * ColorComponentAtPoint(index, stride, dx, dy, 1, 0);
+    -1 * ColorComponentAtPoint(index, stride, boundsBegin, boundsEnd, -dx, -dy, 1, 0) +
+     1 * ColorComponentAtPoint(index, stride, boundsBegin, boundsEnd, dx, -dy, 1, 0) +
+    -2 * ColorComponentAtPoint(index, stride, boundsBegin, boundsEnd, -dx, zero, 1, 0) +
+     2 * ColorComponentAtPoint(index, stride, boundsBegin, boundsEnd, dx, zero, 1, 0) +
+    -1 * ColorComponentAtPoint(index, stride, boundsBegin, boundsEnd, -dx, dy, 1, 0) +
+     1 * ColorComponentAtPoint(index, stride, boundsBegin, boundsEnd, dx, dy, 1, 0);
 
   int16_t normalY =
-    -1 * ColorComponentAtPoint(index, stride, -dx, -dy, 1, 0) +
-    -2 * ColorComponentAtPoint(index, stride, zero, -dy, 1, 0) +
-    -1 * ColorComponentAtPoint(index, stride, dx, -dy, 1, 0) +
-     1 * ColorComponentAtPoint(index, stride, -dx, dy, 1, 0) +
-     2 * ColorComponentAtPoint(index, stride, zero, dy, 1, 0) +
-     1 * ColorComponentAtPoint(index, stride, dx, dy, 1, 0);
+    -1 * ColorComponentAtPoint(index, stride, boundsBegin, boundsEnd, -dx, -dy, 1, 0) +
+    -2 * ColorComponentAtPoint(index, stride, boundsBegin, boundsEnd, zero, -dy, 1, 0) +
+    -1 * ColorComponentAtPoint(index, stride, boundsBegin, boundsEnd, dx, -dy, 1, 0) +
+     1 * ColorComponentAtPoint(index, stride, boundsBegin, boundsEnd, -dx, dy, 1, 0) +
+     2 * ColorComponentAtPoint(index, stride, boundsBegin, boundsEnd, zero, dy, 1, 0) +
+     1 * ColorComponentAtPoint(index, stride, boundsBegin, boundsEnd, dx, dy, 1, 0);
 
   Point3D normal;
   normal.x = -surfaceScale * normalX / 4.0f;
   normal.y = -surfaceScale * normalY / 4.0f;
   normal.z = 255;
   return Normalized(normal);
 }
 
@@ -3475,26 +3227,16 @@ FilterNodeLightingSoftware<LightType, Li
 {
   if (mKernelUnitLength.width == floor(mKernelUnitLength.width) &&
       mKernelUnitLength.height == floor(mKernelUnitLength.height)) {
     return DoRender(aRect, (int32_t)mKernelUnitLength.width, (int32_t)mKernelUnitLength.height);
   }
   return DoRender(aRect, mKernelUnitLength.width, mKernelUnitLength.height);
 }
 
-template<typename LightType, typename LightingType>
-void
-FilterNodeLightingSoftware<LightType, LightingType>::RequestFromInputsForRect(const IntRect &aRect)
-{
-  IntRect srcRect = aRect;
-  srcRect.Inflate(ceil(mKernelUnitLength.width),
-                  ceil(mKernelUnitLength.height));
-  RequestInputRect(IN_LIGHTING_IN, srcRect);
-}
-
 template<typename LightType, typename LightingType> template<typename CoordType>
 already_AddRefed<DataSourceSurface>
 FilterNodeLightingSoftware<LightType, LightingType>::DoRender(const IntRect& aRect,
                                                               CoordType aKernelUnitLengthX,
                                                               CoordType aKernelUnitLengthY)
 {
   MOZ_ASSERT(aKernelUnitLengthX > 0, "aKernelUnitLengthX can be a negative or zero value");
   MOZ_ASSERT(aKernelUnitLengthY > 0, "aKernelUnitLengthY can be a negative or zero value");
@@ -3515,18 +3257,16 @@ FilterNodeLightingSoftware<LightType, Li
   if (!input) {
     return nullptr;
   }
 
   if (input->GetFormat() != SurfaceFormat::A8) {
     input = FilterProcessing::ExtractAlpha(input);
   }
 
-  DebugOnlyAutoColorSamplingAccessControl accessControl(input);
-
   RefPtr<DataSourceSurface> target =
     Factory::CreateDataSourceSurface(size, SurfaceFormat::B8G8R8A8);
   if (MOZ2D_WARN_IF(!target)) {
     return nullptr;
   }
 
   IntPoint offset = aRect.TopLeft() - srcRect.TopLeft();
 
@@ -3534,29 +3274,33 @@ FilterNodeLightingSoftware<LightType, Li
   DataSourceSurface::ScopedMap sourceMap(input, DataSourceSurface::READ);
   DataSourceSurface::ScopedMap targetMap(target, DataSourceSurface::WRITE);
   if (MOZ2D_WARN_IF(!(sourceMap.IsMapped() && targetMap.IsMapped()))) {
     return nullptr;
   }
 
   uint8_t* sourceData = DataAtOffset(input, sourceMap.GetMappedSurface(), offset);
   int32_t sourceStride = sourceMap.GetStride();
+  uint8_t* sourceBegin = sourceMap.GetData();
+  uint8_t* sourceEnd = sourceBegin + sourceStride * input->GetSize().height;
   uint8_t* targetData = targetMap.GetData();
   int32_t targetStride = targetMap.GetStride();
 
+  MutexAutoLock lock(mLock);
+
   uint32_t lightColor = ColorToBGRA(mColor);
   mLight.Prepare();
   mLighting.Prepare();
 
   for (int32_t y = 0; y < size.height; y++) {
     for (int32_t x = 0; x < size.width; x++) {
       int32_t sourceIndex = y * sourceStride + x;
       int32_t targetIndex = y * targetStride + 4 * x;
 
-      Point3D normal = GenerateNormal(sourceData, sourceStride,
+      Point3D normal = GenerateNormal(sourceData, sourceStride, sourceBegin, sourceEnd,
                                       x, y, mSurfaceScale,
                                       aKernelUnitLengthX, aKernelUnitLengthY);
 
       IntPoint pointInFilterSpace(aRect.X() + x, aRect.Y() + y);
       Float Z = mSurfaceScale * sourceData[sourceIndex] / 255.0f;
       Point3D pt(pointInFilterSpace.x, pointInFilterSpace.y, Z);
       Point3D rayDir = mLight.GetVectorToLight(pt);
       uint32_t color = mLight.GetColor(lightColor, rayDir);
--- a/gfx/2d/FilterNodeSoftware.h
+++ b/gfx/2d/FilterNodeSoftware.h
@@ -3,45 +3,33 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef _MOZILLA_GFX_FILTERNODESOFTWARE_H_
 #define _MOZILLA_GFX_FILTERNODESOFTWARE_H_
 
 #include "Filters.h"
+#include "mozilla/Mutex.h"
 #include <vector>
 
 namespace mozilla {
 namespace gfx {
 
 class DataSourceSurface;
 class DrawTarget;
 struct DrawOptions;
 class FilterNodeSoftware;
 
 /**
- * Can be attached to FilterNodeSoftware instances using
- * AddInvalidationListener. FilterInvalidated is called whenever the output of
- * the observed filter may have changed; that is, whenever cached GetOutput()
- * results (and results derived from them) need to discarded.
- */
-class FilterInvalidationListener
-{
-public:
-  virtual void FilterInvalidated(FilterNodeSoftware* aFilter) = 0;
-};
-
-/**
  * This is the base class for the software (i.e. pure CPU, non-accelerated)
  * FilterNode implementation. The software implementation is backend-agnostic,
  * so it can be used as a fallback for all DrawTarget implementations.
  */
-class FilterNodeSoftware : public FilterNode,
-                           public FilterInvalidationListener
+class FilterNodeSoftware : public FilterNode
 {
 public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeSoftware, override)
   virtual ~FilterNodeSoftware();
 
   // Factory method, intended to be called from DrawTarget*::CreateFilter.
   static already_AddRefed<FilterNode> Create(FilterType aType);
 
@@ -50,22 +38,16 @@ public:
             const Point &aDestPoint, const DrawOptions &aOptions);
 
   virtual FilterBackend GetBackendType() override { return FILTER_BACKEND_SOFTWARE; }
   virtual void SetInput(uint32_t aIndex, SourceSurface *aSurface) override;
   virtual void SetInput(uint32_t aIndex, FilterNode *aFilter) override;
 
   virtual const char* GetName() { return "Unknown"; }
 
-  virtual void AddInvalidationListener(FilterInvalidationListener* aListener);
-  virtual void RemoveInvalidationListener(FilterInvalidationListener* aListener);
-
-  // FilterInvalidationListener implementation
-  virtual void FilterInvalidated(FilterNodeSoftware* aFilter) override;
-
 protected:
 
   // The following methods are intended to be overriden by subclasses.
 
   /**
    * Translates a *FilterInputs enum value into an index for the
    * mInputFilters / mInputSurfaces arrays. Returns -1 for invalid inputs.
    * If somebody calls SetInput(enumValue, input) with an enumValue for which
@@ -89,23 +71,16 @@ protected:
    * May return nullptr in error conditions or for an empty aRect.
    * Implementations are not required to allocate a new surface and may even
    * pass through input surfaces unchanged.
    * Callers need to treat the returned surface as immutable.
    */
   virtual already_AddRefed<DataSourceSurface> Render(const IntRect& aRect) = 0;
 
   /**
-   * Call RequestRect (see below) on any input filters with the desired input
-   * rect, so that the input filter knows what to cache the next time it
-   * renders.
-   */
-  virtual void RequestFromInputsForRect(const IntRect &aRect) {}
-
-  /**
    * This method provides a caching default implementation but can be overriden
    * by subclasses that don't want to cache their output. Those classes should
    * call Render(aRect) directly from here.
    */
   virtual already_AddRefed<DataSourceSurface> GetOutput(const IntRect &aRect);
 
   // The following methods are non-virtual helper methods.
 
@@ -148,75 +123,36 @@ protected:
 
   /**
    * Returns the intersection of the input filter's or surface's output rect
    * with aInRect.
    */
   IntRect GetInputRectInRect(uint32_t aInputEnumIndex, const IntRect& aInRect);
 
   /**
-   * Calls RequestRect on the specified input, if it's a filter.
-   */
-  void RequestInputRect(uint32_t aInputEnumIndex, const IntRect& aRect);
-
-  /**
    * Returns the number of set input filters or surfaces. Needed for filters
    * which can have an arbitrary number of inputs.
    */
   size_t NumberOfSetInputs();
 
   /**
-   * Discard the cached surface that was stored in the GetOutput default
-   * implementation. Needs to be called whenever attributes or inputs are set
-   * that might change the result of a Render() call.
-   */
-  void Invalidate();
-
-  /**
-   * Called in order to let this filter know what to cache during the next
-   * GetOutput call. Expected to call RequestRect on this filter's input
-   * filters.
-   */
-  void RequestRect(const IntRect &aRect);
-
-  /**
    * Set input filter and clear input surface for this input index, or set
    * input surface and clear input filter. One of aSurface and aFilter should
    * be null.
    */
   void SetInput(uint32_t aIndex, SourceSurface *aSurface,
                 FilterNodeSoftware *aFilter);
 
 protected:
   /**
    * mInputSurfaces / mInputFilters: For each input index, either a surface or
    * a filter is set, and the other is null.
    */
   std::vector<RefPtr<SourceSurface> > mInputSurfaces;
   std::vector<RefPtr<FilterNodeSoftware> > mInputFilters;
-
-  /**
-   * Weak pointers to our invalidation listeners, i.e. to those filters who
-   * have this filter as an input. Invalidation listeners are required to
-   * unsubscribe themselves from us when they let go of their reference to us.
-   * This ensures that the pointers in this array are never stale.
-   */
-  std::vector<FilterInvalidationListener*> mInvalidationListeners;
-
-  /**
-   * Stores the rect which we want to render and cache on the next call to
-   * GetOutput.
-   */
-  IntRect mRequestedRect;
-
-  /**
-   * Stores our cached output.
-   */
-  IntRect mCachedRect;
-  RefPtr<DataSourceSurface> mCachedOutput;
 };
 
 // Subclasses for specific filters.
 
 class FilterNodeTransformSoftware : public FilterNodeSoftware
 {
 public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeTransformSoftware, override)
@@ -225,17 +161,16 @@ public:
   using FilterNodeSoftware::SetAttribute;
   virtual void SetAttribute(uint32_t aIndex, uint32_t aGraphicsFilter) override;
   virtual void SetAttribute(uint32_t aIndex, const Matrix &aMatrix) override;
 
 protected:
   virtual already_AddRefed<DataSourceSurface> Render(const IntRect& aRect) override;
   virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
   virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
-  virtual void RequestFromInputsForRect(const IntRect &aRect) override;
   IntRect SourceRectForOutputRect(const IntRect &aRect);
 
 private:
   Matrix mMatrix;
   SamplingFilter mSamplingFilter;
 };
 
 class FilterNodeBlendSoftware : public FilterNodeSoftware
@@ -246,17 +181,16 @@ public:
   virtual const char* GetName() override { return "Blend"; }
   using FilterNodeSoftware::SetAttribute;
   virtual void SetAttribute(uint32_t aIndex, uint32_t aBlendMode) override;
 
 protected:
   virtual already_AddRefed<DataSourceSurface> Render(const IntRect& aRect) override;
   virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
   virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
-  virtual void RequestFromInputsForRect(const IntRect &aRect) override;
 
 private:
   BlendMode mBlendMode;
 };
 
 class FilterNodeMorphologySoftware : public FilterNodeSoftware
 {
 public:
@@ -266,17 +200,16 @@ public:
   using FilterNodeSoftware::SetAttribute;
   virtual void SetAttribute(uint32_t aIndex, const IntSize &aRadii) override;
   virtual void SetAttribute(uint32_t aIndex, uint32_t aOperator) override;
 
 protected:
   virtual already_AddRefed<DataSourceSurface> Render(const IntRect& aRect) override;
   virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
   virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
-  virtual void RequestFromInputsForRect(const IntRect &aRect) override;
 
 private:
   IntSize mRadii;
   MorphologyOperator mOperator;
 };
 
 class FilterNodeColorMatrixSoftware : public FilterNodeSoftware
 {
@@ -286,17 +219,16 @@ public:
   using FilterNodeSoftware::SetAttribute;
   virtual void SetAttribute(uint32_t aIndex, const Matrix5x4 &aMatrix) override;
   virtual void SetAttribute(uint32_t aIndex, uint32_t aAlphaMode) override;
 
 protected:
   virtual already_AddRefed<DataSourceSurface> Render(const IntRect& aRect) override;
   virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
   virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
-  virtual void RequestFromInputsForRect(const IntRect &aRect) override;
 
 private:
   Matrix5x4 mMatrix;
   AlphaMode mAlphaMode;
 };
 
 class FilterNodeFloodSoftware : public FilterNodeSoftware
 {
@@ -322,17 +254,16 @@ public:
   virtual const char* GetName() override { return "Tile"; }
   using FilterNodeSoftware::SetAttribute;
   virtual void SetAttribute(uint32_t aIndex, const IntRect &aSourceRect) override;
 
 protected:
   virtual already_AddRefed<DataSourceSurface> Render(const IntRect& aRect) override;
   virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
   virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
-  virtual void RequestFromInputsForRect(const IntRect &aRect) override;
 
 private:
   IntRect mSourceRect;
 };
 
 /**
  * Baseclass for the four different component transfer filters.
  */
@@ -344,17 +275,16 @@ public:
 
   using FilterNodeSoftware::SetAttribute;
   virtual void SetAttribute(uint32_t aIndex, bool aDisable) override;
 
 protected:
   virtual already_AddRefed<DataSourceSurface> Render(const IntRect& aRect) override;
   virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
   virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
-  virtual void RequestFromInputsForRect(const IntRect &aRect) override;
   virtual void GenerateLookupTable(ptrdiff_t aComponent, uint8_t aTables[4][256],
                                    bool aDisabled);
   virtual void FillLookupTable(ptrdiff_t aComponent, uint8_t aTable[256]) = 0;
 
   bool mDisableR;
   bool mDisableG;
   bool mDisableB;
   bool mDisableA;
@@ -469,17 +399,16 @@ public:
   virtual void SetAttribute(uint32_t aIndex, const IntPoint &aTarget) override;
   virtual void SetAttribute(uint32_t aIndex, uint32_t aEdgeMode) override;
   virtual void SetAttribute(uint32_t aIndex, bool aPreserveAlpha) override;
 
 protected:
   virtual already_AddRefed<DataSourceSurface> Render(const IntRect& aRect) override;
   virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
   virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
-  virtual void RequestFromInputsForRect(const IntRect &aRect) override;
 
 private:
   template<typename CoordType>
   already_AddRefed<DataSourceSurface> DoRender(const IntRect& aRect,
                                            CoordType aKernelUnitLengthX,
                                            CoordType aKernelUnitLengthY);
 
   IntRect InflatedSourceRect(const IntRect &aDestRect);
@@ -505,17 +434,16 @@ public:
   using FilterNodeSoftware::SetAttribute;
   virtual void SetAttribute(uint32_t aIndex, Float aScale) override;
   virtual void SetAttribute(uint32_t aIndex, uint32_t aValue) override;
 
 protected:
   virtual already_AddRefed<DataSourceSurface> Render(const IntRect& aRect) override;
   virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
   virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
-  virtual void RequestFromInputsForRect(const IntRect &aRect) override;
 
 private:
   IntRect InflatedSourceOrDestRect(const IntRect &aDestOrSourceRect);
 
   Float mScale;
   ColorChannel mChannelX;
   ColorChannel mChannelY;
 };
@@ -554,17 +482,16 @@ public:
   virtual const char* GetName() override { return "ArithmeticCombine"; }
   using FilterNodeSoftware::SetAttribute;
   virtual void SetAttribute(uint32_t aIndex, const Float* aFloat, uint32_t aSize) override;
 
 protected:
   virtual already_AddRefed<DataSourceSurface> Render(const IntRect& aRect) override;
   virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
   virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
-  virtual void RequestFromInputsForRect(const IntRect &aRect) override;
 
 private:
   Float mK1;
   Float mK2;
   Float mK3;
   Float mK4;
 };
 
@@ -576,34 +503,32 @@ public:
   virtual const char* GetName() override { return "Composite"; }
   using FilterNodeSoftware::SetAttribute;
   virtual void SetAttribute(uint32_t aIndex, uint32_t aOperator) override;
 
 protected:
   virtual already_AddRefed<DataSourceSurface> Render(const IntRect& aRect) override;
   virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
   virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
-  virtual void RequestFromInputsForRect(const IntRect &aRect) override;
 
 private:
   CompositeOperator mOperator;
 };
 
 // Base class for FilterNodeGaussianBlurSoftware and
 // FilterNodeDirectionalBlurSoftware.
 class FilterNodeBlurXYSoftware : public FilterNodeSoftware
 {
 public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeBlurXYSoftware, override)
 protected:
   virtual already_AddRefed<DataSourceSurface> Render(const IntRect& aRect) override;
   virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
   virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
   IntRect InflatedSourceOrDestRect(const IntRect &aDestRect);
-  virtual void RequestFromInputsForRect(const IntRect &aRect) override;
 
   // Implemented by subclasses.
   virtual Size StdDeviationXY() = 0;
 };
 
 class FilterNodeGaussianBlurSoftware : public FilterNodeBlurXYSoftware
 {
 public:
@@ -645,44 +570,41 @@ public:
   virtual const char* GetName() override { return "Crop"; }
   using FilterNodeSoftware::SetAttribute;
   virtual void SetAttribute(uint32_t aIndex, const Rect &aSourceRect) override;
 
 protected:
   virtual already_AddRefed<DataSourceSurface> Render(const IntRect& aRect) override;
   virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
   virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
-  virtual void RequestFromInputsForRect(const IntRect &aRect) override;
 
 private:
   IntRect mCropRect;
 };
 
 class FilterNodePremultiplySoftware : public FilterNodeSoftware
 {
 public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodePremultiplySoftware, override)
   virtual const char* GetName() override { return "Premultiply"; }
 protected:
   virtual already_AddRefed<DataSourceSurface> Render(const IntRect& aRect) override;
   virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
   virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
-  virtual void RequestFromInputsForRect(const IntRect &aRect) override;
 };
 
 class FilterNodeUnpremultiplySoftware : public FilterNodeSoftware
 {
 public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeUnpremultiplySoftware, override)
   virtual const char* GetName() override { return "Unpremultiply"; }
 protected:
   virtual already_AddRefed<DataSourceSurface> Render(const IntRect& aRect) override;
   virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
   virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
-  virtual void RequestFromInputsForRect(const IntRect &aRect) override;
 };
 
 template<typename LightType, typename LightingType>
 class FilterNodeLightingSoftware : public FilterNodeSoftware
 {
 public:
 #if defined(MOZILLA_INTERNAL_API) && (defined(DEBUG) || defined(FORCE_BUILD_REFCNT_LOGGING))
   // Helpers for refcounted
@@ -696,24 +618,24 @@ public:
   virtual void SetAttribute(uint32_t aIndex, const Size &) override;
   virtual void SetAttribute(uint32_t aIndex, const Point3D &) override;
   virtual void SetAttribute(uint32_t aIndex, const Color &) override;
 
 protected:
   virtual already_AddRefed<DataSourceSurface> Render(const IntRect& aRect) override;
   virtual IntRect GetOutputRectInRect(const IntRect& aRect) override;
   virtual int32_t InputIndex(uint32_t aInputEnumIndex) override;
-  virtual void RequestFromInputsForRect(const IntRect &aRect) override;
 
 private:
   template<typename CoordType>
   already_AddRefed<DataSourceSurface> DoRender(const IntRect& aRect,
                                            CoordType aKernelUnitLengthX,
                                            CoordType aKernelUnitLengthY);
 
+  Mutex mLock;
   LightType mLight;
   LightingType mLighting;
   Float mSurfaceScale;
   Size mKernelUnitLength;
   Color mColor;
 #if defined(MOZILLA_INTERNAL_API) && (defined(DEBUG) || defined(FORCE_BUILD_REFCNT_LOGGING))
   const char* mTypeName;
 #endif
--- a/gfx/layers/PaintThread.cpp
+++ b/gfx/layers/PaintThread.cpp
@@ -1,25 +1,32 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "PaintThread.h"
 
+#include <algorithm>
+
 #include "base/task.h"
+#include "gfxPlatform.h"
 #include "gfxPrefs.h"
 #include "GeckoProfiler.h"
 #include "mozilla/layers/CompositorBridgeChild.h"
 #include "mozilla/layers/ShadowLayers.h"
 #include "mozilla/layers/SyncObject.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/Preferences.h"
+#include "mozilla/SharedThreadPool.h"
 #include "mozilla/SyncRunnable.h"
+#include "nsIPropertyBag2.h"
+#include "nsServiceManagerUtils.h"
+#include "nsSystemInfo.h"
 
 // Uncomment the following line to dispatch sync runnables when
 // painting so that rasterization happens synchronously from
 // the perspective of the main thread
 // #define OMTP_FORCE_SYNC
 
 namespace mozilla {
 namespace layers {
@@ -93,60 +100,56 @@ CapturedTiledPaintState::Clear::ClearBuf
 
   mTarget->SetTransform(oldTransform);
 }
 
 StaticAutoPtr<PaintThread> PaintThread::sSingleton;
 StaticRefPtr<nsIThread> PaintThread::sThread;
 PlatformThreadId PaintThread::sThreadId;
 
-// RAII make sure we clean up and restore our draw targets
-// when we paint async.
-struct MOZ_STACK_CLASS AutoCapturedPaintSetup
-{
-  AutoCapturedPaintSetup(CapturedPaintState* aState, CompositorBridgeChild* aBridge)
-  : mState(aState)
-  , mTarget(aState->mTargetDual)
-  , mRestorePermitsSubpixelAA(mTarget->GetPermitSubpixelAA())
-  , mOldTransform(mTarget->GetTransform())
-  , mBridge(aBridge)
-  {
-    mTarget->SetTransform(aState->mCapture->GetTransform());
-    mTarget->SetPermitSubpixelAA(aState->mCapture->GetPermitSubpixelAA());
-  }
-
-  ~AutoCapturedPaintSetup()
-  {
-    mTarget->SetTransform(mOldTransform);
-    mTarget->SetPermitSubpixelAA(mRestorePermitsSubpixelAA);
-    mBridge->NotifyFinishedAsyncPaint(mState);
-  }
-
-  RefPtr<CapturedPaintState> mState;
-  RefPtr<DrawTarget> mTarget;
-  bool mRestorePermitsSubpixelAA;
-  Matrix mOldTransform;
-  RefPtr<CompositorBridgeChild> mBridge;
-};
-
 PaintThread::PaintThread()
-  : mInAsyncPaintGroup(false)
 {
 }
 
 void
 PaintThread::Release()
 {
 }
 
 void
 PaintThread::AddRef()
 {
 }
 
+/* static */ int32_t
+PaintThread::CalculatePaintWorkerCount()
+{
+  int32_t cpuCores = 1;
+  nsCOMPtr<nsIPropertyBag2> systemInfo = do_GetService(NS_SYSTEMINFO_CONTRACTID);
+  if (systemInfo) {
+    nsresult rv = systemInfo->GetPropertyAsInt32(NS_LITERAL_STRING("cpucores"), &cpuCores);
+    if (NS_FAILED(rv)) {
+      cpuCores = 1;
+    }
+  }
+
+  int32_t workerCount = gfxPrefs::LayersOMTPPaintWorkers();
+
+  // If not manually specified, default to (cpuCores * 3) / 4
+  if (workerCount < 1) {
+    workerCount = std::max((cpuCores * 3) / 4, 1);
+
+    if (workerCount > 32) {
+      workerCount = 32;
+    }
+  }
+
+  return workerCount;
+}
+
 /* static */ void
 PaintThread::Start()
 {
   PaintThread::sSingleton = new PaintThread();
 
   if (!PaintThread::sSingleton->Init()) {
     gfxCriticalNote << "Unable to start paint thread";
     PaintThread::sSingleton = nullptr;
@@ -160,16 +163,21 @@ PaintThread::Init()
 
   RefPtr<nsIThread> thread;
   nsresult rv = NS_NewNamedThread("PaintThread", getter_AddRefs(thread));
   if (NS_FAILED(rv)) {
     return false;
   }
   sThread = thread;
 
+  if (gfxPlatform::GetPlatform()->UsesTiling()) {
+    int32_t paintWorkerCount = PaintThread::CalculatePaintWorkerCount();
+    mPaintWorkers = SharedThreadPool::Get(NS_LITERAL_CSTRING("PaintWorker"), paintWorkerCount);
+  }
+
   nsCOMPtr<nsIRunnable> paintInitTask =
     NewRunnableMethod("PaintThread::InitOnPaintThread",
                       this, &PaintThread::InitOnPaintThread);
   SyncRunnable::DispatchToThread(sThread, paintInitTask);
   return true;
 }
 
 void
@@ -217,22 +225,20 @@ PaintThread::Get()
 }
 
 /* static */ bool
 PaintThread::IsOnPaintThread()
 {
   return sThreadId == PlatformThread::CurrentId();
 }
 
-void
-PaintThread::BeginLayerTransaction()
+bool
+PaintThread::IsOnPaintWorkerThread()
 {
-  MOZ_ASSERT(NS_IsMainThread());
-
-  MOZ_ASSERT(!mInAsyncPaintGroup);
+  return mPaintWorkers && mPaintWorkers->IsOnCurrentThread();
 }
 
 void
 PaintThread::PrepareBuffer(CapturedBufferState* aState)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aState);
 
@@ -261,26 +267,25 @@ PaintThread::PrepareBuffer(CapturedBuffe
 
 void
 PaintThread::AsyncPrepareBuffer(CompositorBridgeChild* aBridge,
                                 CapturedBufferState* aState)
 {
   MOZ_ASSERT(IsOnPaintThread());
   MOZ_ASSERT(aState);
 
-  if (!mInAsyncPaintGroup) {
-    mInAsyncPaintGroup = true;
-    PROFILER_TRACING("Paint", "Rasterize", TRACING_INTERVAL_START);
-  }
-
   if (!aState->PrepareBuffer()) {
     gfxCriticalNote << "Failed to prepare buffers on the paint thread.";
   }
 
-  aBridge->NotifyFinishedAsyncPaint(aState);
+  if (aBridge->NotifyFinishedAsyncWorkerPaint(aState)) {
+    // We need to dispatch this task to ourselves so it runs after
+    // AsyncEndLayer
+    DispatchEndLayerTransaction(aBridge);
+  }
 }
 
 void
 PaintThread::PaintContents(CapturedPaintState* aState,
                            PrepDrawTargetForPaintingCallback aCallback)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aState);
@@ -309,41 +314,48 @@ PaintThread::PaintContents(CapturedPaint
 void
 PaintThread::AsyncPaintContents(CompositorBridgeChild* aBridge,
                                 CapturedPaintState* aState,
                                 PrepDrawTargetForPaintingCallback aCallback)
 {
   MOZ_ASSERT(IsOnPaintThread());
   MOZ_ASSERT(aState);
 
-  if (!mInAsyncPaintGroup) {
-    mInAsyncPaintGroup = true;
-    PROFILER_TRACING("Paint", "Rasterize", TRACING_INTERVAL_START);
-  }
-
   DrawTarget* target = aState->mTargetDual;
   DrawTargetCapture* capture = aState->mCapture;
 
-  AutoCapturedPaintSetup setup(aState, aBridge);
+  Matrix oldTransform = target->GetTransform();
+  bool oldPermitsSubpixelAA = target->GetPermitSubpixelAA();
+
+  target->SetTransform(capture->GetTransform());
+  target->SetPermitSubpixelAA(capture->GetPermitSubpixelAA());
+
+  if (aCallback(aState)) {
+    // Draw all the things into the actual dest target.
+    target->DrawCapturedDT(capture, Matrix());
 
-  if (!aCallback(aState)) {
-    return;
+    if (!mDrawTargetsToFlush.Contains(target)) {
+      mDrawTargetsToFlush.AppendElement(target);
+    }
+
+    if (gfxPrefs::LayersOMTPReleaseCaptureOnMainThread()) {
+      // This should ensure the capture drawtarget, which may hold on to UnscaledFont objects,
+      // gets destroyed on the main thread (See bug 1404742). This assumes (unflushed) target
+      // DrawTargets do not themselves hold on to UnscaledFonts.
+      NS_ReleaseOnMainThreadSystemGroup("CapturePaintState::DrawTargetCapture", aState->mCapture.forget());
+    }
   }
 
-  // Draw all the things into the actual dest target.
-  target->DrawCapturedDT(capture, Matrix());
-  if (!mDrawTargetsToFlush.Contains(target)) {
-    mDrawTargetsToFlush.AppendElement(target);
-  }
+  target->SetTransform(oldTransform);
+  target->SetPermitSubpixelAA(oldPermitsSubpixelAA);
 
-  if (gfxPrefs::LayersOMTPReleaseCaptureOnMainThread()) {
-    // This should ensure the capture drawtarget, which may hold on to UnscaledFont objects,
-    // gets destroyed on the main thread (See bug 1404742). This assumes (unflushed) target
-    // DrawTargets do not themselves hold on to UnscaledFonts.
-    NS_ReleaseOnMainThreadSystemGroup("CapturePaintState::DrawTargetCapture", aState->mCapture.forget());
+  if (aBridge->NotifyFinishedAsyncWorkerPaint(aState)) {
+    // We need to dispatch this task to ourselves so it runs after
+    // AsyncEndLayer
+    DispatchEndLayerTransaction(aBridge);
   }
 }
 
 void
 PaintThread::PaintTiledContents(CapturedTiledPaintState* aState)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aState);
@@ -352,62 +364,82 @@ PaintThread::PaintTiledContents(Captured
   RefPtr<CapturedTiledPaintState> state(aState);
 
   cbc->NotifyBeginAsyncPaint(state);
 
   RefPtr<PaintThread> self = this;
   RefPtr<Runnable> task = NS_NewRunnableFunction("PaintThread::PaintTiledContents",
     [self, cbc, state]() -> void
   {
-    self->AsyncPaintTiledContents(cbc,
-                                  state);
+    self->AsyncPaintTiledContents(cbc, state);
   });
 
 #ifndef OMTP_FORCE_SYNC
-  sThread->Dispatch(task.forget());
+  mPaintWorkers->Dispatch(task.forget());
 #else
-  SyncRunnable::DispatchToThread(sThread, task);
+  SyncRunnable::DispatchToThread(mPaintWorkers, task);
 #endif
 }
 
 void
 PaintThread::AsyncPaintTiledContents(CompositorBridgeChild* aBridge,
                                      CapturedTiledPaintState* aState)
 {
-  MOZ_ASSERT(IsOnPaintThread());
+  MOZ_ASSERT(IsOnPaintWorkerThread());
   MOZ_ASSERT(aState);
 
-  if (!mInAsyncPaintGroup) {
-    mInAsyncPaintGroup = true;
-    PROFILER_TRACING("Paint", "Rasterize", TRACING_INTERVAL_START);
-  }
-
   for (auto& copy : aState->mCopies) {
     copy.CopyBuffer();
   }
 
   for (auto& clear : aState->mClears) {
     clear.ClearBuffer();
   }
 
-  DrawTarget* target = aState->mTargetTiled;
+  DrawTarget* target = aState->mTarget;
   DrawTargetCapture* capture = aState->mCapture;
 
   // Draw all the things into the actual dest target.
   target->DrawCapturedDT(capture, Matrix());
   target->Flush();
 
   if (gfxPrefs::LayersOMTPReleaseCaptureOnMainThread()) {
     // This should ensure the capture drawtarget, which may hold on to UnscaledFont objects,
     // gets destroyed on the main thread (See bug 1404742). This assumes (unflushed) target
     // DrawTargets do not themselves hold on to UnscaledFonts.
     NS_ReleaseOnMainThreadSystemGroup("CapturePaintState::DrawTargetCapture", aState->mCapture.forget());
   }
 
-  aBridge->NotifyFinishedAsyncPaint(aState);
+  {
+    RefPtr<CompositorBridgeChild> cbc(aBridge);
+    RefPtr<CapturedTiledPaintState> state(aState);
+
+    RefPtr<PaintThread> self = this;
+    RefPtr<Runnable> task = NS_NewRunnableFunction("PaintThread::AsyncPaintTiledContentsFinished",
+      [self, cbc, state]() -> void
+    {
+      self->AsyncPaintTiledContentsFinished(cbc, state);
+    });
+
+  #ifndef OMTP_FORCE_SYNC
+    sThread->Dispatch(task.forget());
+  #else
+    SyncRunnable::DispatchToThread(sThread, task);
+  #endif
+  }
+}
+
+void
+PaintThread::AsyncPaintTiledContentsFinished(CompositorBridgeChild* aBridge,
+                                             CapturedTiledPaintState* aState)
+{
+  MOZ_ASSERT(IsOnPaintThread());
+  if (aBridge->NotifyFinishedAsyncWorkerPaint(aState)) {
+    aBridge->NotifyFinishedAsyncEndLayerTransaction();
+  }
 }
 
 void
 PaintThread::EndLayer()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   RefPtr<PaintThread> self = this;
@@ -433,61 +465,68 @@ PaintThread::Dispatch(RefPtr<Runnable>& 
   SyncRunnable::DispatchToThread(sThread, aRunnable);
 #endif
 }
 
 void
 PaintThread::AsyncEndLayer()
 {
   MOZ_ASSERT(IsOnPaintThread());
+
   // Textureclient forces a flush once we "end paint", so
   // users of this texture expect all the drawing to be complete.
   // Force a flush now.
   for (size_t i = 0; i < mDrawTargetsToFlush.Length(); i++) {
     mDrawTargetsToFlush[i]->Flush();
   }
 
   mDrawTargetsToFlush.Clear();
 }
 
 void
 PaintThread::EndLayerTransaction(SyncObjectClient* aSyncObject)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   RefPtr<CompositorBridgeChild> cbc(CompositorBridgeChild::Get());
-  RefPtr<SyncObjectClient> syncObject(aSyncObject);
-
-  cbc->NotifyBeginAsyncEndLayerTransaction();
 
-  RefPtr<PaintThread> self = this;
-  RefPtr<Runnable> task = NS_NewRunnableFunction("PaintThread::AsyncEndLayerTransaction",
-    [self, cbc, syncObject]() -> void
-  {
-    self->AsyncEndLayerTransaction(cbc, syncObject);
-  });
+  if (cbc->NotifyBeginAsyncEndLayerTransaction(aSyncObject)) {
+    RefPtr<PaintThread> self = this;
+    RefPtr<Runnable> task = NS_NewRunnableFunction("PaintThread::AsyncEndLayerTransaction",
+      [self, cbc]() -> void
+    {
+      self->AsyncEndLayerTransaction(cbc);
+    });
 
-#ifndef OMTP_FORCE_SYNC
-  sThread->Dispatch(task.forget());
-#else
-  SyncRunnable::DispatchToThread(sThread, task);
-#endif
+  #ifndef OMTP_FORCE_SYNC
+    sThread->Dispatch(task.forget());
+  #else
+    SyncRunnable::DispatchToThread(sThread, task);
+  #endif
+  }
 }
 
 void
-PaintThread::AsyncEndLayerTransaction(CompositorBridgeChild* aBridge,
-                                      SyncObjectClient* aSyncObject)
+PaintThread::AsyncEndLayerTransaction(CompositorBridgeChild* aBridge)
 {
   MOZ_ASSERT(IsOnPaintThread());
-  MOZ_ASSERT(mInAsyncPaintGroup);
-
-  if (aSyncObject) {
-    aSyncObject->Synchronize();
-  }
-
-  mInAsyncPaintGroup = false;
-  PROFILER_TRACING("Paint", "Rasterize", TRACING_INTERVAL_END);
 
   aBridge->NotifyFinishedAsyncEndLayerTransaction();
 }
 
+void
+PaintThread::DispatchEndLayerTransaction(CompositorBridgeChild* aBridge)
+{
+  MOZ_ASSERT(IsOnPaintThread());
+
+  RefPtr<CompositorBridgeChild> cbc = aBridge;
+  RefPtr<PaintThread> self = this;
+  RefPtr<Runnable> task = NS_NewRunnableFunction("PaintThread::AsyncEndLayerTransaction",
+    [self, cbc]() -> void
+  {
+    self->AsyncEndLayerTransaction(cbc);
+  });
+
+  sThread->Dispatch(task.forget());
+}
+
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/PaintThread.h
+++ b/gfx/layers/PaintThread.h
@@ -10,16 +10,18 @@
 #include "base/platform_thread.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/layers/TextureClient.h"
 #include "RotatedBuffer.h"
 #include "nsThreadUtils.h"
 
+class nsIThreadPool;
+
 namespace mozilla {
 namespace gfx {
 class DrawTarget;
 class DrawTargetCapture;
 };
 
 namespace layers {
 
@@ -216,36 +218,38 @@ public:
 
     void ClearBuffer();
 
     RefPtr<gfx::DrawTarget> mTarget;
     RefPtr<gfx::DrawTarget> mTargetOnWhite;
     nsIntRegion mDirtyRegion;
   };
 
-  CapturedTiledPaintState(gfx::DrawTarget* aTargetTiled,
+  CapturedTiledPaintState()
+  {}
+  CapturedTiledPaintState(gfx::DrawTarget* aTarget,
                           gfx::DrawTargetCapture* aCapture)
-  : mTargetTiled(aTargetTiled)
+  : mTarget(aTarget)
   , mCapture(aCapture)
   {}
 
   template<typename F>
   void ForEachTextureClient(F aClosure) const
   {
     for (auto client : mClients) {
       aClosure(client);
     }
   }
 
   void DropTextureClients()
   {
     mClients.clear();
   }
 
-  RefPtr<gfx::DrawTarget> mTargetTiled;
+  RefPtr<gfx::DrawTarget> mTarget;
   RefPtr<gfx::DrawTargetCapture> mCapture;
   std::vector<Copy> mCopies;
   std::vector<Clear> mClears;
 
   std::vector<RefPtr<TextureClient>> mClients;
 
 protected:
   virtual ~CapturedTiledPaintState() {}
@@ -259,22 +263,17 @@ class PaintThread final
 
 public:
   static void Start();
   static void Shutdown();
   static PaintThread* Get();
 
   // Helper for asserts.
   static bool IsOnPaintThread();
-
-  // Must be called on the main thread. Signifies that a new layer transaction
-  // is beginning. This must be called immediately after FlushAsyncPaints, and
-  // before any new painting occurs, as there can't be any async paints queued
-  // or running while this is executing.
-  void BeginLayerTransaction();
+  bool IsOnPaintWorkerThread();
 
   void PrepareBuffer(CapturedBufferState* aState);
 
   void PaintContents(CapturedPaintState* aState,
                      PrepDrawTargetForPaintingCallback aCallback);
 
   void PaintTiledContents(CapturedTiledPaintState* aState);
 
@@ -299,36 +298,41 @@ public:
   // We're only temporarily using sync runnables so
   // Override release/addref but don't do anything.
   void Release();
   void AddRef();
 
 private:
   PaintThread();
 
+  static int32_t CalculatePaintWorkerCount();
+
   bool Init();
   void ShutdownOnPaintThread();
   void InitOnPaintThread();
 
   void AsyncPrepareBuffer(CompositorBridgeChild* aBridge,
                           CapturedBufferState* aState);
   void AsyncPaintContents(CompositorBridgeChild* aBridge,
                           CapturedPaintState* aState,
                           PrepDrawTargetForPaintingCallback aCallback);
   void AsyncPaintTiledContents(CompositorBridgeChild* aBridge,
                                CapturedTiledPaintState* aState);
+  void AsyncPaintTiledContentsFinished(CompositorBridgeChild* aBridge,
+                                       CapturedTiledPaintState* aState);
   void AsyncEndLayer();
-  void AsyncEndLayerTransaction(CompositorBridgeChild* aBridge,
-                                SyncObjectClient* aSyncObject);
+  void AsyncEndLayerTransaction(CompositorBridgeChild* aBridge);
+
+  void DispatchEndLayerTransaction(CompositorBridgeChild* aBridge);
 
   static StaticAutoPtr<PaintThread> sSingleton;
   static StaticRefPtr<nsIThread> sThread;
   static PlatformThreadId sThreadId;
 
-  bool mInAsyncPaintGroup;
+  RefPtr<nsIThreadPool> mPaintWorkers;
 
   // This shouldn't be very many elements, so a list should be fine.
   // Should only be accessed on the paint thread.
   nsTArray<RefPtr<gfx::DrawTarget>> mDrawTargetsToFlush;
 };
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/client/ClientLayerManager.cpp
+++ b/gfx/layers/client/ClientLayerManager.cpp
@@ -221,19 +221,16 @@ ClientLayerManager::CreateReadbackLayer(
   return layer.forget();
 }
 
 bool
 ClientLayerManager::BeginTransactionWithTarget(gfxContext* aTarget)
 {
   // Wait for any previous async paints to complete before starting to paint again.
   GetCompositorBridgeChild()->FlushAsyncPaints();
-  if (PaintThread::Get()) {
-    PaintThread::Get()->BeginLayerTransaction();
-  }
 
   MOZ_ASSERT(mForwarder, "ClientLayerManager::BeginTransaction without forwarder");
   if (!mForwarder->IPCOpen()) {
     gfxCriticalNote << "ClientLayerManager::BeginTransaction with IPC channel down. GPU process may have died.";
     return false;
   }
 
   mInTransaction = true;
--- a/gfx/layers/client/ClientPaintedLayer.cpp
+++ b/gfx/layers/client/ClientPaintedLayer.cpp
@@ -319,23 +319,17 @@ ClientLayerManager::CreatePaintedLayer()
 {
   return CreatePaintedLayerWithHint(NONE);
 }
 
 already_AddRefed<PaintedLayer>
 ClientLayerManager::CreatePaintedLayerWithHint(PaintedLayerCreationHint aHint)
 {
   NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
-  // The non-tiling ContentClient requires CrossProcessSemaphore which
-  // isn't implemented for OSX.
-#ifdef XP_MACOSX
-  if (true) {
-#else
-  if (gfxPrefs::LayersTilesEnabled()) {
-#endif
+  if (gfxPlatform::GetPlatform()->UsesTiling()) {
     RefPtr<ClientTiledPaintedLayer> layer = new ClientTiledPaintedLayer(this, aHint);
     CREATE_SHADOW(Painted);
     return layer.forget();
   } else {
     RefPtr<ClientPaintedLayer> layer = new ClientPaintedLayer(this, aHint);
     CREATE_SHADOW(Painted);
     return layer.forget();
   }
--- a/gfx/layers/client/TiledContentClient.cpp
+++ b/gfx/layers/client/TiledContentClient.cpp
@@ -985,18 +985,17 @@ void ClientMultiTiledLayerBuffer::Update
     }
   }
 
   oldRetainedTiles.Clear();
 
   nsIntRegion paintRegion = aPaintRegion;
   nsIntRegion dirtyRegion = aDirtyRegion;
   if (!paintRegion.IsEmpty()) {
-    MOZ_ASSERT(mPaintClears.size() == 0);
-    MOZ_ASSERT(mPaintCopies.size() == 0);
+    MOZ_ASSERT(mPaintStates.size() == 0);
     for (size_t i = 0; i < newTileCount; ++i) {
       const TileIntPoint tilePosition = newTiles.TilePosition(i);
 
       IntPoint tileOffset = GetTileOffset(tilePosition);
       nsIntRegion tileDrawRegion = IntRect(tileOffset, scaledTileSize);
       tileDrawRegion.AndWith(paintRegion);
 
       if (tileDrawRegion.IsEmpty()) {
@@ -1023,60 +1022,39 @@ void ClientMultiTiledLayerBuffer::Update
       tileset.mTileCount = mPaintTiles.size();
       RefPtr<DrawTarget> drawTarget = gfx::Factory::CreateTiledDrawTarget(tileset);
       if (!drawTarget || !drawTarget->IsValid()) {
         gfxDevCrash(LogReason::InvalidContext) << "Invalid tiled draw target";
         return;
       }
       drawTarget->SetTransform(Matrix());
 
-      if (aFlags & TilePaintFlags::Async) {
-        // Create a capture draw target
-        RefPtr<DrawTargetCapture> captureDT =
-          Factory::CreateCaptureDrawTarget(drawTarget->GetBackendType(),
-                                           drawTarget->GetSize(),
-                                           drawTarget->GetFormat());
+      // Draw into the tiled draw target
+      RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(drawTarget);
+      MOZ_ASSERT(ctx); // already checked the draw target above
+      ctx->SetMatrix(
+        ctx->CurrentMatrix().PreScale(mResolution, mResolution).PreTranslate(-mTilingOrigin));
 
-        // Draw into the capture target
-        RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(captureDT);
-        MOZ_ASSERT(ctx); // already checked the draw target above
-        ctx->SetMatrix(
-          ctx->CurrentMatrix().PreScale(mResolution, mResolution).PreTranslate(-mTilingOrigin));
-
-        mCallback(&mPaintedLayer, ctx, paintRegion, dirtyRegion,
-                  DrawRegionClip::DRAW, nsIntRegion(), mCallbackData);
-        ctx = nullptr;
+      mCallback(&mPaintedLayer, ctx, paintRegion, dirtyRegion,
+                DrawRegionClip::DRAW, nsIntRegion(), mCallbackData);
+      ctx = nullptr;
 
-        // Replay on the paint thread
-        RefPtr<CapturedTiledPaintState> capturedState =
-          new CapturedTiledPaintState(drawTarget,
-                                      captureDT);
-        capturedState->mClients = std::move(mPaintTilesTextureClients);
-        capturedState->mCopies = std::move(mPaintCopies);
-        capturedState->mClears = std::move(mPaintClears);
-
-        PaintThread::Get()->PaintTiledContents(capturedState);
+      if (aFlags & TilePaintFlags::Async) {
+        for (const auto& state : mPaintStates) {
+          PaintThread::Get()->PaintTiledContents(state);
+        }
         mManager->SetQueuedAsyncPaints();
+        MOZ_ASSERT(mPaintStates.size() > 0);
+        mPaintStates.clear();
       } else {
-        MOZ_ASSERT(mPaintCopies.size() == 0);
-        MOZ_ASSERT(mPaintClears.size() == 0);
-
-        // Draw into the tiled draw target
-        RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(drawTarget);
-        MOZ_ASSERT(ctx); // already checked the draw target above
-        ctx->SetMatrix(
-          ctx->CurrentMatrix().PreScale(mResolution, mResolution).PreTranslate(-mTilingOrigin));
-
-        mCallback(&mPaintedLayer, ctx, paintRegion, dirtyRegion,
-                  DrawRegionClip::DRAW, nsIntRegion(), mCallbackData);
+        MOZ_ASSERT(mPaintStates.size() == 0);
       }
 
       // Reset
       mPaintTiles.clear();
-      mPaintTilesTextureClients.clear();
       mTilingOrigin = IntPoint(std::numeric_limits<int32_t>::max(),
                                std::numeric_limits<int32_t>::max());
     }
 
     bool edgePaddingEnabled = gfxPrefs::TileEdgePaddingEnabled();
 
     for (uint32_t i = 0; i < mRetainedTiles.Length(); ++i) {
       TileClient& tile = mRetainedTiles[i];
@@ -1141,27 +1119,30 @@ ClientMultiTiledLayerBuffer::ValidateTil
       gfxPlatform::GetPlatform()->Optimal2DFormatForContent(content),
       TextureFlags::DISALLOW_BIGIMAGE | TextureFlags::IMMEDIATE_UPLOAD));
     MOZ_ASSERT(aTile.mAllocator);
   }
 
   nsIntRegion offsetScaledDirtyRegion = aDirtyRegion.MovedBy(-aTileOrigin);
   offsetScaledDirtyRegion.ScaleRoundOut(mResolution, mResolution);
 
+  std::vector<CapturedTiledPaintState::Copy> asyncPaintCopies;
+  std::vector<RefPtr<TextureClient>> asyncPaintClients;
+
   nsIntRegion extraPainted;
   RefPtr<TextureClient> backBufferOnWhite;
   RefPtr<TextureClient> backBuffer =
     aTile.GetBackBuffer(mCompositableClient,
                         offsetScaledDirtyRegion,
                         content, mode,
                         extraPainted,
                         aFlags,
                         &backBufferOnWhite,
-                        &mPaintCopies,
-                        &mPaintTilesTextureClients);
+                        &asyncPaintCopies,
+                        &asyncPaintClients);
 
   // Mark the area we need to paint in the back buffer as invalid in the
   // front buffer as they will become out of sync.
   aTile.mInvalidFront.OrWith(offsetScaledDirtyRegion);
 
   // Add backbuffer's invalid region to the dirty region to be painted.
   // This will be empty if we were able to copy from the front in to the back.
   nsIntRegion invalidBack = aTile.mInvalidBack;
@@ -1176,54 +1157,74 @@ ClientMultiTiledLayerBuffer::ValidateTil
   extraPainted.MoveBy(aTileOrigin);
   extraPainted.And(extraPainted, mNewValidRegion);
   mPaintedRegion.Or(mPaintedRegion, extraPainted);
 
   if (!backBuffer) {
     return false;
   }
 
-  gfx::Tile paintTile;
   RefPtr<DrawTarget> dt = backBuffer->BorrowDrawTarget();
   RefPtr<DrawTarget> dtOnWhite;
   if (backBufferOnWhite) {
     dtOnWhite = backBufferOnWhite->BorrowDrawTarget();
-    paintTile.mDrawTarget = Factory::CreateDualDrawTarget(dt, dtOnWhite);
-  } else {
-    paintTile.mDrawTarget = dt;
   }
-  paintTile.mTileOrigin = gfx::IntPoint(aTileOrigin.x, aTileOrigin.y);
+
   if (!dt || (backBufferOnWhite && !dtOnWhite)) {
     aTile.DiscardBuffers();
     return false;
   }
 
-  mPaintTiles.push_back(paintTile);
-  mTilingOrigin.x = std::min(mTilingOrigin.x, paintTile.mTileOrigin.x);
-  mTilingOrigin.y = std::min(mTilingOrigin.y, paintTile.mTileOrigin.y);
-
-  if (aFlags & TilePaintFlags::Async) {
-    mPaintTilesTextureClients.push_back(backBuffer);
-    if (backBufferOnWhite) {
-      mPaintTilesTextureClients.push_back(backBufferOnWhite);
-    }
+  RefPtr<DrawTarget> drawTarget;
+  if (dtOnWhite) {
+    drawTarget = Factory::CreateDualDrawTarget(dt, dtOnWhite);
+  } else {
+    drawTarget = dt;
   }
 
   auto clear = CapturedTiledPaintState::Clear{
     dt,
     dtOnWhite,
     offsetScaledDirtyRegion
   };
 
+  gfx::Tile paintTile;
+  paintTile.mTileOrigin = gfx::IntPoint(aTileOrigin.x, aTileOrigin.y);
+
   if (aFlags & TilePaintFlags::Async) {
-    mPaintClears.push_back(clear);
+    RefPtr<CapturedTiledPaintState> asyncPaint = new CapturedTiledPaintState();
+
+    RefPtr<DrawTargetCapture> captureDT =
+      Factory::CreateCaptureDrawTarget(drawTarget->GetBackendType(),
+                                       drawTarget->GetSize(),
+                                       drawTarget->GetFormat());
+    paintTile.mDrawTarget = captureDT;
+    asyncPaint->mTarget = drawTarget;
+    asyncPaint->mCapture = captureDT;
+
+    asyncPaint->mCopies = std::move(asyncPaintCopies);
+    asyncPaint->mClears.push_back(clear);
+
+    asyncPaint->mClients = std::move(asyncPaintClients);
+    asyncPaint->mClients.push_back(backBuffer);
+    if (backBufferOnWhite) {
+      asyncPaint->mClients.push_back(backBufferOnWhite);
+    }
+
+    mPaintStates.push_back(asyncPaint);
   } else {
+    paintTile.mDrawTarget = drawTarget;
     clear.ClearBuffer();
   }
 
+  mPaintTiles.push_back(paintTile);
+
+  mTilingOrigin.x = std::min(mTilingOrigin.x, paintTile.mTileOrigin.x);
+  mTilingOrigin.y = std::min(mTilingOrigin.y, paintTile.mTileOrigin.y);
+
   // The new buffer is now validated, remove the dirty region from it.
   aTile.mInvalidBack.SubOut(offsetScaledDirtyRegion);
 
   aTile.Flip();
 
   return true;
 }
 
--- a/gfx/layers/client/TiledContentClient.h
+++ b/gfx/layers/client/TiledContentClient.h
@@ -445,19 +445,17 @@ private:
   // completed then this is identical to mValidRegion.
   nsIntRegion mNewValidRegion;
 
   SharedFrameMetricsHelper*  mSharedFrameMetricsHelper;
 
   // Parameters that are collected during Update for a paint before they
   // are either executed or replayed on the paint thread.
   std::vector<gfx::Tile> mPaintTiles;
-  std::vector<RefPtr<TextureClient>> mPaintTilesTextureClients;
-  std::vector<CapturedTiledPaintState::Copy> mPaintCopies;
-  std::vector<CapturedTiledPaintState::Clear> mPaintClears;
+  std::vector<RefPtr<CapturedTiledPaintState>> mPaintStates;
 
   /**
    * While we're adding tiles, this is used to keep track of the position of
    * the top-left of the top-left-most tile.  When we come to wrap the tiles in
    * TiledDrawTarget we subtract the value of this member from each tile's
    * offset so that all the tiles have a positive offset, then add a
    * translation to the TiledDrawTarget to compensate.  This is important so
    * that the mRect of the TiledDrawTarget is always at a positive x/y
--- a/gfx/layers/ipc/CompositorBridgeChild.cpp
+++ b/gfx/layers/ipc/CompositorBridgeChild.cpp
@@ -1188,30 +1188,38 @@ CompositorBridgeChild::FlushAsyncPaints(
     mTotalFlushCount++;
 
     double ratio = double(mSlowFlushCount) / double(mTotalFlushCount);
     Telemetry::ScalarSet(Telemetry::ScalarID::GFX_OMTP_PAINT_WAIT_RATIO,
                          uint32_t(ratio * 100 * 100));
   }
 }
 
-void
-CompositorBridgeChild::NotifyBeginAsyncEndLayerTransaction()
+bool
+CompositorBridgeChild::NotifyBeginAsyncEndLayerTransaction(SyncObjectClient* aSyncObject)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MonitorAutoLock lock(mPaintLock);
 
   MOZ_ASSERT(!mOutstandingAsyncEndTransaction);
   mOutstandingAsyncEndTransaction = true;
+  mOutstandingAsyncSyncObject = aSyncObject;
+  return mOutstandingAsyncPaints == 0;
 }
 
 void
 CompositorBridgeChild::NotifyFinishedAsyncEndLayerTransaction()
 {
   MOZ_ASSERT(PaintThread::IsOnPaintThread());
+
+  if (mOutstandingAsyncSyncObject) {
+    mOutstandingAsyncSyncObject->Synchronize();
+    mOutstandingAsyncSyncObject = nullptr;
+  }
+
   MonitorAutoLock lock(mPaintLock);
 
   // Since this should happen after ALL paints are done and
   // at the end of a transaction, this should always be true.
   MOZ_RELEASE_ASSERT(mOutstandingAsyncPaints == 0);
   MOZ_ASSERT(mOutstandingAsyncEndTransaction);
 
   mOutstandingAsyncEndTransaction = false;
--- a/gfx/layers/ipc/CompositorBridgeChild.h
+++ b/gfx/layers/ipc/CompositorBridgeChild.h
@@ -245,38 +245,44 @@ public:
       aClient->AddPaintThreadRef();
       mTextureClientsForAsyncPaint.AppendElement(aClient);
     });
   }
 
   // Must only be called from the paint thread. Notifies the CompositorBridge
   // that the paint thread has finished an asynchronous paint request.
   template<typename CapturedState>
-  void NotifyFinishedAsyncPaint(CapturedState& aState)
+  bool NotifyFinishedAsyncWorkerPaint(CapturedState& aState)
   {
     MOZ_ASSERT(PaintThread::IsOnPaintThread());
 
     MonitorAutoLock lock(mPaintLock);
     mOutstandingAsyncPaints--;
 
     aState->ForEachTextureClient([] (auto aClient) {
       aClient->DropPaintThreadRef();
     });
     aState->DropTextureClients();
+
+    // If the main thread has completed queuing work and this was the
+    // last paint, then it is time to end the layer transaction and sync
+    return mOutstandingAsyncEndTransaction && mOutstandingAsyncPaints == 0;
   }
 
   // Must only be called from the main thread. Notifies the CompositorBridge
-  // that the paint thread is going to perform texture synchronization at the
-  // end of async painting, and should postpone messages if needed until
-  // finished.
-  void NotifyBeginAsyncEndLayerTransaction();
+  // that all work has been submitted to the paint thread or paint worker
+  // threads, and returns whether all paints are completed. If this returns
+  // true, then an AsyncEndLayerTransaction must be queued, otherwise once
+  // NotifyFinishedAsyncWorkerPaint returns true, an AsyncEndLayerTransaction
+  // must be executed.
+  bool NotifyBeginAsyncEndLayerTransaction(SyncObjectClient* aSyncObject);
 
   // Must only be called from the paint thread. Notifies the CompositorBridge
-  // that the paint thread has finished all async paints and texture syncs from
-  // a given transaction and may resume sending messages.
+  // that the paint thread has finished all async paints and and may do the
+  // requested texture sync and resume sending messages.
   void NotifyFinishedAsyncEndLayerTransaction();
 
   // Must only be called from the main thread. Notifies the CompoistorBridge
   // that a transaction is about to be sent, and if the paint thread is
   // currently painting, to begin delaying IPC messages.
   void PostponeMessagesIfAsyncPainting();
 
 private:
@@ -401,16 +407,17 @@ private:
 
   // Contains the number of outstanding asynchronous paints tied to a
   // PLayerTransaction on this bridge. This is R/W on both the main and paint
   // threads, and must be accessed within the paint lock.
   size_t mOutstandingAsyncPaints;
 
   // Whether we are waiting for an async paint end transaction
   bool mOutstandingAsyncEndTransaction;
+  RefPtr<SyncObjectClient> mOutstandingAsyncSyncObject;
 
   // True if this CompositorBridge is currently delaying its messages until the
   // paint thread completes. This is R/W on both the main and paint threads, and
   // must be accessed within the paint lock.
   bool mIsDelayingForAsyncPaints;
 
   uintptr_t mSlowFlushCount;
   uintptr_t mTotalFlushCount;
--- a/gfx/layers/wr/WebRenderUserData.cpp
+++ b/gfx/layers/wr/WebRenderUserData.cpp
@@ -180,21 +180,30 @@ WebRenderImageData::CreateAsyncImageWebR
                                                       const LayoutDeviceRect& aSCBounds,
                                                       const gfx::Matrix4x4& aSCTransform,
                                                       const gfx::MaybeIntSize& aScaleToSize,
                                                       const wr::ImageRendering& aFilter,
                                                       const wr::MixBlendMode& aMixBlendMode,
                                                       bool aIsBackfaceVisible)
 {
   MOZ_ASSERT(aContainer->IsAsync());
+
+  if (mPipelineId.isSome() && mContainer != aContainer) {
+    // In this case, we need to remove the existed pipeline and create new one
+    // because the ImageContainer is changed.
+    WrBridge()->RemovePipelineIdForCompositable(mPipelineId.ref());
+    mPipelineId.reset();
+  }
+
   if (!mPipelineId) {
     // Alloc async image pipeline id.
     mPipelineId = Some(WrBridge()->GetCompositorBridgeChild()->GetNextPipelineId());
     WrBridge()->AddPipelineIdForAsyncCompositable(mPipelineId.ref(),
                                                   aContainer->GetAsyncContainerHandle());
+    mContainer = aContainer;
   }
   MOZ_ASSERT(!mImageClient);
   MOZ_ASSERT(!mExternalImageId);
 
   // Push IFrame for async image pipeline.
   //
   // We don't push a stacking context for this async image pipeline here.
   // Instead, we do it inside the iframe that hosts the image. As a result,
--- a/gfx/tests/mochitest/test_font_whitelist.html
+++ b/gfx/tests/mochitest/test_font_whitelist.html
@@ -16,25 +16,24 @@ https://bugzilla.mozilla.org/show_bug.cg
 <span id="serif" style="font-family: serif; font-size: 64px;">M</span>
 <div id="content" style="display: none">
 
 </div>
 <script class="testbody" type="application/javascript">
 
 /** Test for Bug 1121643 **/
 
-const DOMUtils =  SpecialPowers.Cc["@mozilla.org/inspector/dom-utils;1"]
-                               .getService(SpecialPowers.Ci.inIDOMUtils);
+const InspectorUtils = SpecialPowers.InspectorUtils;
 
 // Given an element id, returns the first font face name encountered.
 let fontUsed = id => {
   let element = document.getElementById(id),
       range = document.createRange();
   range.selectNode(element);
-  return DOMUtils.getUsedFontFaces(range).item(0).CSSFamilyName;
+  return InspectorUtils.getUsedFontFaces(range)[0].CSSFamilyName;
 }
 
 // A map of the default mono, sans and serif fonts, obtained when
 // whitelisting is disabled.
 const fonts = { mono : fontUsed("mono"),
                 sans : fontUsed("sans"),
                 serif : fontUsed("serif") };
 
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -2595,17 +2595,17 @@ gfxPlatform::InitOMTPConfig()
   if (mContentBackend == BackendType::CAIRO) {
     omtp.ForceDisable(FeatureStatus::Broken, "OMTP is not supported when using cairo",
       NS_LITERAL_CSTRING("FEATURE_FAILURE_COMP_PREF"));
   }
 
   if (InSafeMode()) {
     omtp.ForceDisable(FeatureStatus::Blocked, "OMTP blocked by safe-mode",
                       NS_LITERAL_CSTRING("FEATURE_FAILURE_COMP_SAFEMODE"));
-  } else if (gfxPrefs::LayersTilesEnabled() && gfxPrefs::TileEdgePaddingEnabled()) {
+  } else if (gfxPlatform::UsesTiling() && gfxPrefs::TileEdgePaddingEnabled()) {
     omtp.ForceDisable(FeatureStatus::Blocked, "OMTP does not yet support tiling with edge padding",
                       NS_LITERAL_CSTRING("FEATURE_FAILURE_OMTP_TILING"));
   }
 
   if (omtp.IsEnabled()) {
     gfxVars::SetUseOMTP(true);
     reporter.SetSuccessful();
   }
@@ -2667,16 +2667,22 @@ gfxPlatform::UsesOffMainThreadCompositin
 
 #endif
     firstTime = false;
   }
 
   return result;
 }
 
+bool
+gfxPlatform::UsesTiling() const
+{
+  return gfxPrefs::LayersTilesEnabled();
+}
+
 /***
  * The preference "layout.frame_rate" has 3 meanings depending on the value:
  *
  * -1 = Auto (default), use hardware vsync or software vsync @ 60 hz if hw vsync fails.
  *  0 = ASAP mode - used during talos testing.
  *  X = Software vsync at a rate of X times per second.
  */
 already_AddRefed<mozilla::gfx::VsyncSource>
--- a/gfx/thebes/gfxPlatform.h
+++ b/gfx/thebes/gfxPlatform.h
@@ -575,16 +575,18 @@ public:
 
     virtual mozilla::gfx::SurfaceFormat Optimal2DFormatForContent(gfxContentType aContent);
 
     virtual gfxImageFormat OptimalFormatForContent(gfxContentType aContent);
 
     virtual gfxImageFormat GetOffscreenFormat()
     { return mozilla::gfx::SurfaceFormat::X8R8G8B8_UINT32; }
 
+    virtual bool UsesTiling() const;
+
     /**
      * Returns a logger if one is available and logging is enabled
      */
     static mozilla::LogModule* GetLog(eGfxLog aWhichLog);
 
     int GetScreenDepth() const { return mScreenDepth; }
     mozilla::gfx::IntSize GetScreenSize() const { return mScreenSize; }
 
--- a/gfx/thebes/gfxPlatformMac.cpp
+++ b/gfx/thebes/gfxPlatformMac.cpp
@@ -98,16 +98,24 @@ gfxPlatformMac::gfxPlatformMac()
     MacIOSurfaceLib::LoadLibrary();
 }
 
 gfxPlatformMac::~gfxPlatformMac()
 {
     gfxCoreTextShaper::Shutdown();
 }
 
+bool
+gfxPlatformMac::UsesTiling() const
+{
+    // The non-tiling ContentClient requires CrossProcessSemaphore which
+    // isn't implemented for OSX.
+    return true;
+}
+
 gfxPlatformFontList*
 gfxPlatformMac::CreatePlatformFontList()
 {
     gfxPlatformFontList* list = new gfxMacPlatformFontList();
     if (NS_SUCCEEDED(list->InitFontList())) {
         return list;
     }
     gfxPlatformFontList::Shutdown();
--- a/gfx/thebes/gfxPlatformMac.h
+++ b/gfx/thebes/gfxPlatformMac.h
@@ -21,16 +21,18 @@ class gfxPlatformMac : public gfxPlatfor
 public:
     gfxPlatformMac();
     virtual ~gfxPlatformMac();
 
     static gfxPlatformMac *GetPlatform() {
         return (gfxPlatformMac*) gfxPlatform::GetPlatform();
     }
 
+    bool UsesTiling() const override;
+
     virtual already_AddRefed<gfxASurface>
       CreateOffscreenSurface(const IntSize& aSize,
                              gfxImageFormat aFormat) override;
 
     gfxFontGroup*
     CreateFontGroup(const mozilla::FontFamilyList& aFontFamilyList,
                     const gfxFontStyle *aStyle,
                     gfxTextPerfMetrics* aTextPerf,
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -610,16 +610,17 @@ private:
   DECL_GFX_PREF(Once, "layers.mlgpu.enable-clear-view",        AdvancedLayersEnableClearView, bool, true);
   DECL_GFX_PREF(Once, "layers.mlgpu.enable-cpu-occlusion",     AdvancedLayersEnableCPUOcclusion, bool, true);
   DECL_GFX_PREF(Once, "layers.mlgpu.enable-depth-buffer",      AdvancedLayersEnableDepthBuffer, bool, false);
   DECL_GFX_PREF(Live, "layers.mlgpu.enable-invalidation",      AdvancedLayersUseInvalidation, bool, true);
   DECL_GFX_PREF(Once, "layers.mlgpu.enable-on-windows7",       AdvancedLayersEnableOnWindows7, bool, false);
   DECL_GFX_PREF(Once, "layers.mlgpu.enable-container-resizing", AdvancedLayersEnableContainerResizing, bool, true);
   DECL_GFX_PREF(Once, "layers.offmainthreadcomposition.force-disabled", LayersOffMainThreadCompositionForceDisabled, bool, false);
   DECL_GFX_PREF(Live, "layers.offmainthreadcomposition.frame-rate", LayersCompositionFrameRate, int32_t,-1);
+  DECL_GFX_PREF(Live, "layers.omtp.paint-workers",             LayersOMTPPaintWorkers, int32_t, 1);
   DECL_GFX_PREF(Live, "layers.omtp.release-capture-on-main-thread", LayersOMTPReleaseCaptureOnMainThread, bool, false);
   DECL_GFX_PREF(Live, "layers.orientation.sync.timeout",       OrientationSyncMillis, uint32_t, (uint32_t)0);
   DECL_GFX_PREF(Once, "layers.prefer-opengl",                  LayersPreferOpenGL, bool, false);
   DECL_GFX_PREF(Live, "layers.progressive-paint",              ProgressivePaint, bool, false);
   DECL_GFX_PREF(Live, "layers.shared-buffer-provider.enabled", PersistentBufferProviderSharedEnabled, bool, false);
   DECL_GFX_PREF(Live, "layers.single-tile.enabled",            LayersSingleTileEnabled, bool, true);
   DECL_GFX_PREF(Live, "layers.force-synchronous-resize",       LayersForceSynchronousResize, bool, true);
 
--- a/gfx/thebes/gfxUserFontSet.cpp
+++ b/gfx/thebes/gfxUserFontSet.cpp
@@ -714,17 +714,17 @@ gfxUserFontEntry::LoadPlatformFont(const
         gfxFontUtils::DetermineFontDataType(aFontData, aLength);
     Telemetry::Accumulate(Telemetry::WEBFONT_FONTTYPE, uint32_t(fontType));
 
     // Unwrap/decompress/sanitize or otherwise munge the downloaded data
     // to make a usable sfnt structure.
 
     // Because platform font activation code may replace the name table
     // in the font with a synthetic one, we save the original name so that
-    // it can be reported via the nsIDOMFontFace API.
+    // it can be reported via the InspectorUtils API.
     nsAutoString originalFullName;
 
     // Call the OTS sanitizer; this will also decode WOFF to sfnt
     // if necessary. The original data in aFontData is left unchanged.
     uint32_t saneLen;
     uint32_t fontCompressionRatio = 0;
     size_t computedSize = 0;
     const uint8_t* saneData =
@@ -780,17 +780,17 @@ gfxUserFontEntry::LoadPlatformFont(const
         if (!fe) {
             mFontSet->LogMessage(this, "not usable by platform");
         }
     }
 
     if (fe) {
         fe->mComputedSizeOfUserFont = computedSize;
 
-        // Save a copy of the metadata block (if present) for nsIDOMFontFace
+        // Save a copy of the metadata block (if present) for InspectorUtils
         // to use if required. Ownership of the metadata block will be passed
         // to the gfxUserFontData record below.
         FallibleTArray<uint8_t> metadata;
         uint32_t metaOrigLen = 0;
         uint8_t compression = gfxUserFontData::kUnknownCompression;
         if (fontType == GFX_USERFONT_WOFF) {
             CopyWOFFMetadata<WOFFHeader>(aFontData, aLength,
                                          &metadata, &metaOrigLen);
--- a/image/imgLoader.cpp
+++ b/image/imgLoader.cpp
@@ -680,29 +680,16 @@ ShouldLoadCachedImage(imgRequest* aImgRe
                                              aTriggeringPrincipal,
                                              &decision);
       if (NS_FAILED(rv) || !NS_CP_ACCEPTED(decision)) {
         return false;
       }
     }
   }
 
-  bool sendPriming = false;
-  bool mixedContentWouldBlock = false;
-  rv = nsMixedContentBlocker::GetHSTSPrimingFromRequestingContext(contentLocation,
-      aLoadingContext, &sendPriming, &mixedContentWouldBlock);
-  if (NS_FAILED(rv)) {
-    return false;
-  }
-  if (sendPriming && mixedContentWouldBlock) {
-    // if either of the securty checks above would cause a priming request, we
-    // can't load this image from the cache, so go ahead and return false here
-    return false;
-  }
-
   return true;
 }
 
 // Returns true if this request is compatible with the given CORS mode on the
 // given loading principal, and false if the request may not be reused due
 // to CORS.  Also checks the Referrer Policy, since requests with different
 // referrers/policies may generate different responses.
 static bool
@@ -2726,18 +2713,18 @@ imgLoader::GetMIMETypeFromContent(nsIReq
 
 /* static */
 nsresult
 imgLoader::GetMimeTypeFromContent(const char* aContents,
                                   uint32_t aLength,
                                   nsACString& aContentType)
 {
   /* Is it a GIF? */
-  if (aLength >= 6 && (!nsCRT::strncmp(aContents, "GIF87a", 6) ||
-                       !nsCRT::strncmp(aContents, "GIF89a", 6))) {
+  if (aLength >= 6 && (!strncmp(aContents, "GIF87a", 6) ||
+                       !strncmp(aContents, "GIF89a", 6))) {
     aContentType.AssignLiteral(IMAGE_GIF);
 
   /* or a PNG? */
   } else if (aLength >= 8 && ((unsigned char)aContents[0]==0x89 &&
                               (unsigned char)aContents[1]==0x50 &&
                               (unsigned char)aContents[2]==0x4E &&
                               (unsigned char)aContents[3]==0x47 &&
                               (unsigned char)aContents[4]==0x0D &&
@@ -2764,17 +2751,17 @@ imgLoader::GetMimeTypeFromContent(const 
    * Minor version offset 3. Offset 4 must be nullptr.
    */
   } else if (aLength >= 5 &&
              ((unsigned char) aContents[0])==0x4a &&
              ((unsigned char) aContents[1])==0x47 &&
              ((unsigned char) aContents[4])==0x00 ) {
     aContentType.AssignLiteral(IMAGE_ART);
 
-  } else if (aLength >= 2 && !nsCRT::strncmp(aContents, "BM", 2)) {
+  } else if (aLength >= 2 && !strncmp(aContents, "BM", 2)) {
     aContentType.AssignLiteral(IMAGE_BMP);
 
   // ICOs always begin with a 2-byte 0 followed by a 2-byte 1.
   // CURs begin with 2-byte 0 followed by 2-byte 2.
   } else if (aLength >= 4 && (!memcmp(aContents, "\000\000\001\000", 4) ||
                               !memcmp(aContents, "\000\000\002\000", 4))) {
     aContentType.AssignLiteral(IMAGE_ICO);
 
--- a/ipc/glue/BackgroundUtils.cpp
+++ b/ipc/glue/BackgroundUtils.cpp
@@ -393,21 +393,17 @@ LoadInfoToLoadInfoArgs(nsILoadInfo *aLoa
       redirectChainIncludingInternalRedirects,
       redirectChain,
       ancestorPrincipals,
       aLoadInfo->AncestorOuterWindowIDs(),
       aLoadInfo->CorsUnsafeHeaders(),
       aLoadInfo->GetForcePreflight(),
       aLoadInfo->GetIsPreflight(),
       aLoadInfo->GetLoadTriggeredFromExternal(),
-      aLoadInfo->GetServiceWorkerTaintingSynthesized(),
-      aLoadInfo->GetForceHSTSPriming(),
-      aLoadInfo->GetMixedContentWouldBlock(),
-      aLoadInfo->GetIsHSTSPriming(),
-      aLoadInfo->GetIsHSTSPrimingUpgrade()
+      aLoadInfo->GetServiceWorkerTaintingSynthesized()
       );
 
   return NS_OK;
 }
 
 nsresult
 LoadInfoArgsToLoadInfo(const OptionalLoadInfoArgs& aOptionalLoadInfoArgs,
                        nsILoadInfo** outLoadInfo)
@@ -503,21 +499,17 @@ LoadInfoArgsToLoadInfo(const OptionalLoa
                           redirectChainIncludingInternalRedirects,
                           redirectChain,
                           Move(ancestorPrincipals),
                           loadInfoArgs.ancestorOuterWindowIDs(),
                           loadInfoArgs.corsUnsafeHeaders(),
                           loadInfoArgs.forcePreflight(),
                           loadInfoArgs.isPreflight(),
                           loadInfoArgs.loadTriggeredFromExternal(),
-                          loadInfoArgs.serviceWorkerTaintingSynthesized(),
-                          loadInfoArgs.forceHSTSPriming(),
-                          loadInfoArgs.mixedContentWouldBlock(),
-                          loadInfoArgs.isHSTSPriming(),
-                          loadInfoArgs.isHSTSPrimingUpgrade()
+                          loadInfoArgs.serviceWorkerTaintingSynthesized()
                           );
 
    loadInfo.forget(outLoadInfo);
    return NS_OK;
 }
 
 } // namespace ipc
 } // namespace mozilla
--- a/js/src/devtools/rootAnalysis/analyzeRoots.js
+++ b/js/src/devtools/rootAnalysis/analyzeRoots.js
@@ -78,19 +78,24 @@ function isGCType(type)
         return isGCType(type.Type);
     return false;
 }
 
 function isUnrootedType(type)
 {
     if (type.Kind == "Pointer")
         return isGCType(type.Type);
-    else if (type.Kind == "Array")
+    else if (type.Kind == "Array") {
+        if (!type.Type) {
+            printErr("Received Array Kind with no Type");
+            printErr(JSON.stringify(type));
+            printErr(getBacktrace({args: true, locals: true}));
+        }
         return isUnrootedType(type.Type);
-    else if (type.Kind == "CSU")
+    } else if (type.Kind == "CSU")
         return type.Name in gcPointers;
     else
         return false;
 }
 
 function expressionUsesVariable(exp, variable)
 {
     if (exp.Kind == "Var" && sameVariable(exp.Variable, variable))
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -1769,17 +1769,19 @@ GCMarker::processMarkStackTop(SliceBudge
                 // Save the rest of this value array for later and start scanning obj2's children.
                 pushValueArray(obj, vp, end);
                 obj = obj2;
                 goto scan_obj;
             }
         } else if (v.isSymbol()) {
             traverseEdge(obj, v.toSymbol());
         } else if (v.isPrivateGCThing()) {
-            traverseEdge(obj, v.toGCCellPtr());
+            // v.toGCCellPtr cannot be inlined, so construct one manually.
+            Cell* cell = v.toGCThing();
+            traverseEdge(obj, JS::GCCellPtr(cell, cell->getTraceKind()));
         }
     }
     return;
 
   scan_obj:
     {
         AssertShouldMarkInZone(obj);
 
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/modules/bug1429031.js
@@ -0,0 +1,20 @@
+// |jit-test| error: ReferenceError
+
+let moduleRepo = {};
+setModuleResolveHook(function(module, specifier) {
+    return moduleRepo[specifier];
+});
+assertEq = function(a, b) {}
+evaluate(`
+let a = moduleRepo['a'] = parseModule(
+    'export var a = 1;'
+);
+let b = moduleRepo['b'] = parseModule(
+    \`import * as ns from 'a';
+     export var x = ns.a + ns.b;\`
+);
+b.declarationInstantiation();
+let ns = getModuleEnvironmentValue(b, "ns");
+assertEq(ns.a, 1);
+while ( t.ArrayType() ) 1;
+`);
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -10837,17 +10837,22 @@ IonBuilder::getPropTryModuleNamespace(bo
         trackOptimizationOutcome(TrackedOutcome::UnknownProperty);
         return Ok();
     }
 
     obj->setImplicitlyUsedUnchecked();
     MConstant* envConst = constant(ObjectValue(*env));
     uint32_t slot = shape->slot();
     uint32_t nfixed = env->numFixedSlots();
-    MOZ_TRY(loadSlot(envConst, slot, nfixed, types->getKnownMIRType(), barrier, types));
+
+    MIRType rvalType = types->getKnownMIRType();
+    if (barrier != BarrierKind::NoBarrier || IsNullOrUndefined(rvalType))
+        rvalType = MIRType::Value;
+
+    MOZ_TRY(loadSlot(envConst, slot, nfixed, rvalType, barrier, types));
 
     trackOptimizationSuccess();
     *emitted = true;
     return Ok();
 }
 
 MInstruction*
 IonBuilder::loadUnboxedProperty(MDefinition* obj, size_t offset, JSValueType unboxedType,
--- a/js/src/vm/Shape.cpp
+++ b/js/src/vm/Shape.cpp
@@ -1979,16 +1979,23 @@ Shape::dump(js::GenericPrinter& out) con
 #define DUMP_FLAG(name, display) if (flags & name) out.put(&(" " #display)[first]), first = 0
         DUMP_FLAG(IN_DICTIONARY, in_dictionary);
 #undef  DUMP_FLAG
         out.putChar(')');
     }
 }
 
 void
+Shape::dump() const
+{
+    Fprinter out(stderr);
+    dump(out);
+}
+
+void
 Shape::dumpSubtree(int level, js::GenericPrinter& out) const
 {
     if (!parent) {
         MOZ_ASSERT(level == 0);
         MOZ_ASSERT(JSID_IS_EMPTY(propid_));
         out.printf("class %s emptyShape\n", getObjectClass()->name);
     } else {
         out.printf("%*sid ", level, "");
--- a/js/src/vm/Shape.h
+++ b/js/src/vm/Shape.h
@@ -1145,16 +1145,17 @@ class Shape : public gc::TenuredCell
         if (res)
             flags |= CACHED_BIG_ENOUGH_FOR_SHAPE_TABLE;
         flags |= HAS_CACHED_BIG_ENOUGH_FOR_SHAPE_TABLE;
         return res;
     }
 
 #ifdef DEBUG
     void dump(js::GenericPrinter& out) const;
+    void dump() const;
     void dumpSubtree(int level, js::GenericPrinter& out) const;
 #endif
 
     void sweep();
     void finalize(FreeOp* fop);
     void removeChild(Shape* child);
 
     static const JS::TraceKind TraceKind = JS::TraceKind::Shape;
--- a/js/xpconnect/src/Sandbox.cpp
+++ b/js/xpconnect/src/Sandbox.cpp
@@ -31,16 +31,17 @@
 #include "mozilla/dom/BlobBinding.h"
 #include "mozilla/dom/cache/CacheStorage.h"
 #include "mozilla/dom/CSSBinding.h"
 #include "mozilla/dom/DirectoryBinding.h"
 #include "mozilla/dom/DOMPrefs.h"
 #include "mozilla/dom/IndexedDatabaseManager.h"
 #include "mozilla/dom/Fetch.h"
 #include "mozilla/dom/FileBinding.h"
+#include "mozilla/dom/InspectorUtilsBinding.h"
 #include "mozilla/dom/MessageChannelBinding.h"
 #include "mozilla/dom/MessagePortBinding.h"
 #include "mozilla/dom/PromiseBinding.h"
 #include "mozilla/dom/RequestBinding.h"
 #include "mozilla/dom/ResponseBinding.h"
 #ifdef MOZ_WEBRTC
 #include "mozilla/dom/RTCIdentityProviderRegistrar.h"
 #endif
@@ -940,16 +941,18 @@ xpc::GlobalProperties::Parse(JSContext* 
         } else if (!strcmp(name.ptr(), "fetch")) {
             fetch = true;
         } else if (!strcmp(name.ptr(), "caches")) {
             caches = true;
         } else if (!strcmp(name.ptr(), "FileReader")) {
             fileReader = true;
         } else if (!strcmp(name.ptr(), "MessageChannel")) {
             messageChannel = true;
+        } else if (!strcmp(name.ptr(), "InspectorUtils")) {
+            inspectorUtils = true;
         } else {
             JS_ReportErrorUTF8(cx, "Unknown property name: %s", name.ptr());
             return false;
         }
     }
     return true;
 }
 
@@ -1022,16 +1025,20 @@ xpc::GlobalProperties::Define(JSContext*
     if (fileReader && !dom::FileReaderBinding::GetConstructorObject(cx))
         return false;
 
     if (messageChannel &&
         (!dom::MessageChannelBinding::GetConstructorObject(cx) ||
          !dom::MessagePortBinding::GetConstructorObject(cx)))
         return false;
 
+    if (inspectorUtils &&
+        !dom::InspectorUtilsBinding::GetConstructorObject(cx))
+        return false;
+
     return true;
 }
 
 bool
 xpc::GlobalProperties::DefineInXPCComponents(JSContext* cx, JS::HandleObject obj)
 {
     if (indexedDB &&
         !IndexedDatabaseManager::DefineIndexedDB(cx, obj))
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -2692,16 +2692,17 @@ struct GlobalProperties {
     bool Directory : 1;
     bool File : 1;
     bool crypto : 1;
     bool rtcIdentityProvider : 1;
     bool fetch : 1;
     bool caches : 1;
     bool fileReader: 1;
     bool messageChannel: 1;
+    bool inspectorUtils : 1;
 private:
     bool Define(JSContext* cx, JS::HandleObject obj);
 };
 
 // Infallible.
 already_AddRefed<nsIXPCComponents_utils_Sandbox>
 NewSandboxConstructor();
 
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -83,17 +83,16 @@
 #include "mozilla/dom/Element.h"
 #include "nsCanvasFrame.h"
 #include "gfxDrawable.h"
 #include "gfxEnv.h"
 #include "gfxUtils.h"
 #include "nsDataHashtable.h"
 #include "nsTableWrapperFrame.h"
 #include "nsTextFrame.h"
-#include "nsFontFaceList.h"
 #include "nsFontInflationData.h"
 #include "nsSVGIntegrationUtils.h"
 #include "nsSVGUtils.h"
 #include "SVGImageContext.h"
 #include "SVGTextFrame.h"
 #include "nsStyleStructInlines.h"
 #include "nsStyleTransformMatrix.h"
 #include "nsIFrameInlines.h"
@@ -128,16 +127,17 @@
 #include "mozilla/layers/WebRenderLayerManager.h"
 #include "prenv.h"
 #include "RetainedDisplayListBuilder.h"
 #include "DisplayListChecker.h"
 #include "TextDrawTarget.h"
 #include "nsDeckFrame.h"
 #include "nsIEffectiveTLDService.h" // for IsInStyloBlocklist
 #include "mozilla/StylePrefs.h"
+#include "mozilla/dom/InspectorFontFace.h"
 
 #ifdef MOZ_XUL
 #include "nsXULPopupManager.h"
 #endif
 
 #include "GeckoProfiler.h"
 #include "nsAnimationManager.h"
 #include "nsTransitionManager.h"
@@ -7994,61 +7994,86 @@ nsLayoutUtils::AssertTreeOnlyEmptyNextIn
     for (; !childFrames.AtEnd(); childFrames.Next()) {
       nsLayoutUtils::AssertTreeOnlyEmptyNextInFlows(childFrames.get());
     }
   }
 }
 #endif
 
 static void
-GetFontFacesForFramesInner(nsIFrame* aFrame, nsFontFaceList* aFontFaceList)
+GetFontFacesForFramesInner(nsIFrame* aFrame,
+                           nsLayoutUtils::UsedFontFaceTable& aFontFaces)
 {
   NS_PRECONDITION(aFrame, "NULL frame pointer");
 
   if (aFrame->IsTextFrame()) {
     if (!aFrame->GetPrevContinuation()) {
       nsLayoutUtils::GetFontFacesForText(aFrame, 0, INT32_MAX, true,
-                                         aFontFaceList);
+                                         aFontFaces);
     }
     return;
   }
 
   nsIFrame::ChildListID childLists[] = { nsIFrame::kPrincipalList,
                                          nsIFrame::kPopupList };
   for (size_t i = 0; i < ArrayLength(childLists); ++i) {
     nsFrameList children(aFrame->GetChildList(childLists[i]));
     for (nsFrameList::Enumerator e(children); !e.AtEnd(); e.Next()) {
       nsIFrame* child = e.get();
       child = nsPlaceholderFrame::GetRealFrameFor(child);
-      GetFontFacesForFramesInner(child, aFontFaceList);
-    }
-  }
-}
-
-/* static */
-nsresult
+      GetFontFacesForFramesInner(child, aFontFaces);
+    }
+  }
+}
+
+/* static */ nsresult
 nsLayoutUtils::GetFontFacesForFrames(nsIFrame* aFrame,
-                                     nsFontFaceList* aFontFaceList)
+                                     UsedFontFaceTable& aFontFaces)
 {
   NS_PRECONDITION(aFrame, "NULL frame pointer");
 
   while (aFrame) {
-    GetFontFacesForFramesInner(aFrame, aFontFaceList);
+    GetFontFacesForFramesInner(aFrame, aFontFaces);
     aFrame = GetNextContinuationOrIBSplitSibling(aFrame);
   }
 
   return NS_OK;
 }
 
-/* static */
-nsresult
+static void
+AddFontsFromTextRun(gfxTextRun* aTextRun,
+                    uint32_t aOffset,
+                    uint32_t aLength,
+                    nsLayoutUtils::UsedFontFaceTable& aFontFaces)
+{
+  gfxTextRun::Range range(aOffset, aOffset + aLength);
+  gfxTextRun::GlyphRunIterator iter(aTextRun, range);
+  while (iter.NextRun()) {
+    gfxFontEntry *fe = iter.GetGlyphRun()->mFont->GetFontEntry();
+    // if we have already listed this face, just make sure the match type is
+    // recorded
+    InspectorFontFace* existingFace = aFontFaces.Get(fe);
+    if (existingFace) {
+      existingFace->AddMatchType(iter.GetGlyphRun()->mMatchType);
+    } else {
+      // A new font entry we haven't seen before
+      InspectorFontFace* ff =
+        new InspectorFontFace(fe, aTextRun->GetFontGroup(),
+                              iter.GetGlyphRun()->mMatchType);
+      aFontFaces.Put(fe, ff);
+    }
+  }
+}
+
+/* static */ nsresult
 nsLayoutUtils::GetFontFacesForText(nsIFrame* aFrame,
-                                   int32_t aStartOffset, int32_t aEndOffset,
+                                   int32_t aStartOffset,
+                                   int32_t aEndOffset,
                                    bool aFollowContinuations,
-                                   nsFontFaceList* aFontFaceList)
+                                   UsedFontFaceTable& aFontFaces)
 {
   NS_PRECONDITION(aFrame, "NULL frame pointer");
 
   if (!aFrame->IsTextFrame()) {
     return NS_OK;
   }
 
   nsTextFrame* curr = static_cast<nsTextFrame*>(aFrame);
@@ -8073,17 +8098,17 @@ nsLayoutUtils::GetFontFacesForText(nsIFr
         fend = std::min(next->GetContentEnd(), aEndOffset);
         next = fend < aEndOffset ?
           static_cast<nsTextFrame*>(next->GetNextContinuation()) : nullptr;
       }
     }
 
     uint32_t skipStart = iter.ConvertOriginalToSkipped(fstart);
     uint32_t skipEnd = iter.ConvertOriginalToSkipped(fend);
-    aFontFaceList->AddFontsFromTextRun(textRun, skipStart, skipEnd - skipStart);
+    AddFontsFromTextRun(textRun, skipStart, skipEnd - skipStart, aFontFaces);
     curr = next;
   } while (aFollowContinuations && curr);
 
   return NS_OK;
 }
 
 /* static */
 size_t
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -31,16 +31,17 @@
 #include "mozilla/ReflowOutput.h"
 #include "ImageContainer.h"
 #include "gfx2DGlue.h"
 #include "nsStyleConsts.h"
 #include "SVGImageContext.h"
 #include <limits>
 #include <algorithm>
 #include "gfxPoint.h"
+#include "nsClassHashtable.h"
 
 class gfxContext;
 class nsPresContext;
 class nsIContent;
 class nsAtom;
 class nsIScrollableFrame;
 class nsIDOMEvent;
 class nsRegion;
@@ -76,16 +77,17 @@ class EffectSet;
 struct ActiveScrolledRoot;
 namespace dom {
 class CanvasRenderingContext2D;
 class DOMRectList;
 class Element;
 class HTMLImageElement;