merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Fri, 18 Sep 2015 13:19:00 +0200
changeset 295792 37c7812ce0e6d10c7e7182f12e752832835e1d67
parent 295753 67275f876c1555bb816ad1d9120b9f746b7e1345 (current diff)
parent 295791 1ab076078dc357724b011db374d035e7a1563b21 (diff)
child 295826 6a957b86f06b35dfbe41b16ae146d7549cf2ac8c
child 295860 ca7e2bde5018579720ab54bb85f739c33eb3417b
child 295870 5d90933874fc8c15f6f6d54015135274f46f64e6
push id5245
push userraliiev@mozilla.com
push dateThu, 29 Oct 2015 11:30:51 +0000
treeherdermozilla-beta@dac831dc1bd0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone43.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central a=merge
modules/libpref/init/all.js
--- a/accessible/windows/msaa/AccessibleWrap.cpp
+++ b/accessible/windows/msaa/AccessibleWrap.cpp
@@ -1194,87 +1194,79 @@ AccessibleWrap::Invoke(DISPID dispIdMemb
 
 void
 AccessibleWrap::GetNativeInterface(void** aOutAccessible)
 {
   *aOutAccessible = static_cast<IAccessible*>(this);
   NS_ADDREF_THIS();
 }
 
+void
+AccessibleWrap::FireWinEvent(Accessible* aTarget, uint32_t aEventType)
+{
+  static_assert(sizeof(gWinEventMap)/sizeof(gWinEventMap[0]) == nsIAccessibleEvent::EVENT_LAST_ENTRY,
+                "MSAA event map skewed");
+
+  NS_ASSERTION(aEventType > 0 && aEventType < ArrayLength(gWinEventMap), "invalid event type");
+
+  uint32_t winEvent = gWinEventMap[aEventType];
+  if (!winEvent)
+    return;
+
+  int32_t childID = GetChildIDFor(aTarget);
+  if (!childID)
+    return; // Can't fire an event without a child ID
+
+  HWND hwnd = GetHWNDFor(aTarget);
+  if (!hwnd) {
+    return;
+  }
+
+  // Fire MSAA event for client area window.
+  ::NotifyWinEvent(winEvent, hwnd, OBJID_CLIENT, childID);
+
+  // JAWS announces collapsed combobox navigation based on focus events.
+  if (aEventType == nsIAccessibleEvent::EVENT_SELECTION &&
+      Compatibility::IsJAWS()) {
+    roles::Role role = aTarget->IsProxy() ? aTarget->Proxy()->Role() :
+      aTarget->Role();
+    if (role == roles::COMBOBOX_OPTION) {
+      ::NotifyWinEvent(EVENT_OBJECT_FOCUS, hwnd, OBJID_CLIENT, childID);
+    }
+  }
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // Accessible
 
 nsresult
 AccessibleWrap::HandleAccEvent(AccEvent* aEvent)
 {
   nsresult rv = Accessible::HandleAccEvent(aEvent);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (IPCAccessibilityActive()) {
     return NS_OK;
   }
 
   uint32_t eventType = aEvent->GetEventType();
 
-  static_assert(sizeof(gWinEventMap)/sizeof(gWinEventMap[0]) == nsIAccessibleEvent::EVENT_LAST_ENTRY,
-                "MSAA event map skewed");
-
-  NS_ENSURE_TRUE(eventType > 0 && eventType < ArrayLength(gWinEventMap), NS_ERROR_FAILURE);
-
-  uint32_t winEvent = gWinEventMap[eventType];
-  if (!winEvent)
-    return NS_OK;
-
   // Means we're not active.
   NS_ENSURE_TRUE(!IsDefunct(), NS_ERROR_FAILURE);
 
   Accessible* accessible = aEvent->GetAccessible();
   if (!accessible)
     return NS_OK;
 
   if (eventType == nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED ||
       eventType == nsIAccessibleEvent::EVENT_FOCUS) {
     UpdateSystemCaretFor(accessible);
   }
 
-  int32_t childID = GetChildIDFor(accessible); // get the id for the accessible
-  if (!childID)
-    return NS_OK; // Can't fire an event without a child ID
-
-  HWND hWnd = GetHWNDFor(accessible);
-  NS_ENSURE_TRUE(hWnd, NS_ERROR_FAILURE);
-
-  nsAutoString tag;
-  nsAutoCString id;
-  nsIContent* cnt = accessible->GetContent();
-  if (cnt) {
-    cnt->NodeInfo()->NameAtom()->ToString(tag);
-    nsIAtom* aid = cnt->GetID();
-    if (aid)
-      aid->ToUTF8String(id);
-  }
-
-#ifdef A11Y_LOG
-  if (logging::IsEnabled(logging::ePlatforms)) {
-    printf("\n\nMSAA event: event: %d, target: %s@id='%s', childid: %d, hwnd: %p\n\n",
-           eventType, NS_ConvertUTF16toUTF8(tag).get(), id.get(),
-           childID, hWnd);
-  }
-#endif
-
-  // Fire MSAA event for client area window.
-  ::NotifyWinEvent(winEvent, hWnd, OBJID_CLIENT, childID);
-
-  // JAWS announces collapsed combobox navigation based on focus events.
-  if (Compatibility::IsJAWS()) {
-    if (eventType == nsIAccessibleEvent::EVENT_SELECTION &&
-      accessible->Role() == roles::COMBOBOX_OPTION) {
-      ::NotifyWinEvent(EVENT_OBJECT_FOCUS, hWnd, OBJID_CLIENT, childID);
-    }
-  }
+  FireWinEvent(accessible, eventType);
 
   return NS_OK;
 }
 
 DocProxyAccessibleWrap*
 AccessibleWrap::DocProxyWrapper() const
 {
   MOZ_ASSERT(IsProxy());
--- a/accessible/windows/msaa/AccessibleWrap.h
+++ b/accessible/windows/msaa/AccessibleWrap.h
@@ -154,16 +154,18 @@ public: // construction, destruction
   // Accessible
   virtual nsresult HandleAccEvent(AccEvent* aEvent);
   virtual void Shutdown() override;
 
   // Helper methods
   static int32_t GetChildIDFor(Accessible* aAccessible);
   static HWND GetHWNDFor(Accessible* aAccessible);
 
+  static void FireWinEvent(Accessible* aTarget, uint32_t aEventType);
+
   /**
    * System caret support: update the Windows caret position. 
    * The system caret works more universally than the MSAA caret
    * For example, Window-Eyes, JAWS, ZoomText and Windows Tablet Edition use it
    * We will use an invisible system caret.
    * Gecko is still responsible for drawing its own caret
    */
   void UpdateSystemCaretFor(Accessible* aAccessible);
--- a/accessible/windows/msaa/Platform.cpp
+++ b/accessible/windows/msaa/Platform.cpp
@@ -71,37 +71,46 @@ a11y::ProxyDestroyed(ProxyAccessible* aP
 #endif
 
   wrapper->Shutdown();
   aProxy->SetWrapper(0);
   wrapper->Release();
 }
 
 void
-a11y::ProxyEvent(ProxyAccessible*, uint32_t)
+a11y::ProxyEvent(ProxyAccessible* aTarget, uint32_t aEventType)
 {
+  AccessibleWrap::FireWinEvent(WrapperFor(aTarget), aEventType);
 }
 
 void
-a11y::ProxyStateChangeEvent(ProxyAccessible*, uint64_t, bool)
+a11y::ProxyStateChangeEvent(ProxyAccessible* aTarget, uint64_t, bool)
 {
+  AccessibleWrap::FireWinEvent(WrapperFor(aTarget),
+                               nsIAccessibleEvent::EVENT_STATE_CHANGE);
 }
 
 void
 a11y::ProxyCaretMoveEvent(ProxyAccessible* aTarget, int32_t aOffset)
 {
+  AccessibleWrap::FireWinEvent(WrapperFor(aTarget),
+                               nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED);
 }
 
 void
 a11y::ProxyTextChangeEvent(ProxyAccessible* aText, const nsString& aStr,
                            int32_t aStart, uint32_t aLen, bool aInsert, bool)
 {
   AccessibleWrap* wrapper = WrapperFor(aText);
   MOZ_ASSERT(wrapper);
   if (!wrapper) {
     return;
   }
 
   auto text = static_cast<HyperTextAccessibleWrap*>(wrapper->AsHyperText());
   if (text) {
     ia2AccessibleText::UpdateTextChangeData(text, aInsert, aStr, aStart, aLen);
   }
+
+  uint32_t eventType = aInsert ? nsIAccessibleEvent::EVENT_TEXT_INSERTED :
+    nsIAccessibleEvent::EVENT_TEXT_REMOVED;
+  AccessibleWrap::FireWinEvent(wrapper, eventType);
 }
--- a/browser/base/content/test/general/browser_domFullscreen_fullscreenMode.js
+++ b/browser/base/content/test/general/browser_domFullscreen_fullscreenMode.js
@@ -115,22 +115,41 @@ var gTests = [
     desc: "F11 key",
     affectsFullscreenMode: true,
     exitFunc: function () {
       executeSoon(() => EventUtils.synthesizeKey("VK_F11", {}));
     }
   }
 ];
 
+function checkState(expectedStates, contentStates) {
+  is(contentStates.inDOMFullscreen, expectedStates.inDOMFullscreen,
+     "The DOM fullscreen state of the content should match");
+  // TODO window.fullScreen is not updated as soon as the fullscreen
+  //      state flips in child process, hence checking it could cause
+  //      anonying intermittent failure. As we just want to confirm the
+  //      fullscreen state of the browser window, we can just check the
+  //      that on the chrome window below.
+  // is(contentStates.inFullscreen, expectedStates.inFullscreen,
+  //    "The fullscreen state of the content should match");
+  is(document.mozFullScreen, expectedStates.inDOMFullscreen,
+     "The DOM fullscreen state of the chrome should match");
+  is(window.fullScreen, expectedStates.inFullscreen,
+     "The fullscreen state of the chrome should match");
+}
+
+const kPage = "http://example.org/browser/browser/" +
+              "base/content/test/general/dummy_page.html";
+
 add_task(function* () {
   yield pushPrefs(
     ["full-screen-api.transition-duration.enter", "0 0"],
     ["full-screen-api.transition-duration.leave", "0 0"]);
 
-  let tab = gBrowser.addTab("about:robots");
+  let tab = gBrowser.addTab(kPage);
   let browser = tab.linkedBrowser;
   gBrowser.selectedTab = tab;
   yield waitForDocLoadComplete();
 
   registerCleanupFunction(() => {
     if (browser.contentWindow.fullScreen) {
       BrowserFullScreen();
     }
@@ -143,70 +162,60 @@ add_task(function* () {
   gMessageManager.addMessageListener(
     "Test:FullscreenChanged", captureUnexpectedFullscreenChange);
 
   // Wait for the document being activated, so that
   // fullscreen request won't be denied.
   yield new Promise(resolve => listenOneMessage("Test:Activated", resolve));
 
   for (let test of gTests) {
+    let contentStates;
     info("Testing exit DOM fullscreen via " + test.desc);
 
-    var { inDOMFullscreen, inFullscreen } = yield queryFullscreenState();
-    ok(!inDOMFullscreen, "Shouldn't have been in DOM fullscreen");
-    ok(!inFullscreen, "Shouldn't have been in fullscreen");
+    contentStates = yield queryFullscreenState();
+    checkState({inDOMFullscreen: false, inFullscreen: false}, contentStates);
 
     /* DOM fullscreen without fullscreen mode */
 
-    // Enter DOM fullscreen
+    info("> Enter DOM fullscreen");
     gMessageManager.sendAsyncMessage("Test:RequestFullscreen");
-    var { inDOMFullscreen, inFullscreen } =
-      yield waitForFullscreenChanges(FS_CHANGE_BOTH);
-    ok(inDOMFullscreen, "Should now be in DOM fullscreen");
-    ok(inFullscreen, "Should now be in fullscreen");
+    contentStates = yield waitForFullscreenChanges(FS_CHANGE_BOTH);
+    checkState({inDOMFullscreen: true, inFullscreen: true}, contentStates);
 
-    // Exit DOM fullscreen
+    info("> Exit DOM fullscreen");
     test.exitFunc();
-    var { inDOMFullscreen, inFullscreen } =
-      yield waitForFullscreenChanges(FS_CHANGE_BOTH);
-    ok(!inDOMFullscreen, "Should no longer be in DOM fullscreen");
-    ok(!inFullscreen, "Should no longer be in fullscreen");
+    contentStates = yield waitForFullscreenChanges(FS_CHANGE_BOTH);
+    checkState({inDOMFullscreen: false, inFullscreen: false}, contentStates);
 
     /* DOM fullscreen with fullscreen mode */
 
-    // Enter fullscreen mode
+    info("> Enter fullscreen mode");
     // Need to be asynchronous because sizemodechange event could be
     // dispatched synchronously, which would cause the event listener
     // miss that event and wait infinitely.
     executeSoon(() => BrowserFullScreen());
-    var { inDOMFullscreen, inFullscreen } =
-      yield waitForFullscreenChanges(FS_CHANGE_SIZE);
-    ok(!inDOMFullscreen, "Shouldn't have been in DOM fullscreen");
-    ok(inFullscreen, "Should now be in fullscreen mode");
+    contentStates = yield waitForFullscreenChanges(FS_CHANGE_SIZE);
+    checkState({inDOMFullscreen: false, inFullscreen: true}, contentStates);
 
-    // Enter DOM fullscreen
+    info("> Enter DOM fullscreen in fullscreen mode");
     gMessageManager.sendAsyncMessage("Test:RequestFullscreen");
-    var { inDOMFullscreen, inFullscreen } =
-      yield waitForFullscreenChanges(FS_CHANGE_DOM);
-    ok(inDOMFullscreen, "Should now be in DOM fullscreen");
-    ok(inFullscreen, "Should still be in fullscreen");
+    contentStates = yield waitForFullscreenChanges(FS_CHANGE_DOM);
+    checkState({inDOMFullscreen: true, inFullscreen: true}, contentStates);
 
-    // Exit DOM fullscreen
+    info("> Exit DOM fullscreen in fullscreen mode");
     test.exitFunc();
-    var { inDOMFullscreen, inFullscreen } =
-      yield waitForFullscreenChanges(test.affectsFullscreenMode ?
-                                     FS_CHANGE_BOTH : FS_CHANGE_DOM);
-    ok(!inDOMFullscreen, "Should no longer be in DOM fullscreen");
-    if (test.affectsFullscreenMode) {
-      ok(!inFullscreen, "Should no longer be in fullscreen mode");
-    } else {
-      ok(inFullscreen, "Should still be in fullscreen mode");
-    }
+    contentStates = yield waitForFullscreenChanges(
+      test.affectsFullscreenMode ? FS_CHANGE_BOTH : FS_CHANGE_DOM);
+    checkState({
+      inDOMFullscreen: false,
+      inFullscreen: !test.affectsFullscreenMode
+    }, contentStates);
 
     /* Cleanup */
 
     // Exit fullscreen mode if we are still in
-    if (browser.contentWindow.fullScreen) {
+    if (window.fullScreen) {
+      info("> Cleanup");
       executeSoon(() => BrowserFullScreen());
       yield waitForFullscreenChanges(FS_CHANGE_SIZE);
     }
   }
 });
--- a/build/mach_bootstrap.py
+++ b/build/mach_bootstrap.py
@@ -140,16 +140,17 @@ MACH_MODULES = [
     'testing/mochitest/mach_commands.py',
     'testing/xpcshell/mach_commands.py',
     'testing/talos/mach_commands.py',
     'testing/web-platform/mach_commands.py',
     'testing/xpcshell/mach_commands.py',
     'tools/docs/mach_commands.py',
     'tools/mercurial/mach_commands.py',
     'tools/mach_commands.py',
+    'tools/power/mach_commands.py',
     'mobile/android/mach_commands.py',
 ]
 
 
 CATEGORIES = {
     'build': {
         'short': 'Build Commands',
         'long': 'Interact with the build system',
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -227,22 +227,21 @@ static NS_DEFINE_CID(kAppShellCID, NS_AP
 #else
 #include <unistd.h> // for getpid()
 #endif
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using mozilla::dom::workers::ServiceWorkerManager;
 
-// True means sUseErrorPages and sInterceptionEnabled has been added to
+// True means sUseErrorPages has been added to
 // preferences var cache.
 static bool gAddedPreferencesVarCache = false;
 
 bool nsDocShell::sUseErrorPages = false;
-bool nsDocShell::sInterceptionEnabled = false;
 
 // Number of documents currently loading
 static int32_t gNumberOfDocumentsLoading = 0;
 
 // Global count of existing docshells.
 static int32_t gDocShellCount = 0;
 
 // Global count of docshells with the private attribute set
@@ -5531,19 +5530,16 @@ nsDocShell::Create()
   // Should we use XUL error pages instead of alerts if possible?
   mUseErrorPages =
     Preferences::GetBool("browser.xul.error_pages.enabled", mUseErrorPages);
 
   if (!gAddedPreferencesVarCache) {
     Preferences::AddBoolVarCache(&sUseErrorPages,
                                  "browser.xul.error_pages.enabled",
                                  mUseErrorPages);
-    Preferences::AddBoolVarCache(&sInterceptionEnabled,
-                                 "dom.serviceWorkers.interception.enabled",
-                                 false);
     gAddedPreferencesVarCache = true;
   }
 
   mDeviceSizeIsPageSize =
     Preferences::GetBool("docshell.device_size_is_page_size",
                          mDeviceSizeIsPageSize);
 
   nsCOMPtr<nsIObserverService> serv = services::GetObserverService();
@@ -13853,17 +13849,17 @@ nsDocShell::MaybeNotifyKeywordSearchLoad
 }
 
 NS_IMETHODIMP
 nsDocShell::ShouldPrepareForIntercept(nsIURI* aURI, bool aIsNavigate,
                                       bool* aShouldIntercept)
 {
   *aShouldIntercept = false;
   // Preffed off.
-  if (!sInterceptionEnabled) {
+  if (!nsContentUtils::ServiceWorkerInterceptionEnabled()) {
     return NS_OK;
   }
 
   // No in private browsing
   if (mInPrivateBrowsing) {
     return NS_OK;
   }
 
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -882,19 +882,16 @@ protected:
 
   // The orientation lock as described by
   // https://w3c.github.io/screen-orientation/
   mozilla::dom::ScreenOrientationInternal mOrientationLock;
 
   // Cached value of the "browser.xul.error_pages.enabled" preference.
   static bool sUseErrorPages;
 
-  // Cached value of the "dom.serviceWorkers.interception.enabled" preference.
-  static bool sInterceptionEnabled;
-
   bool mCreated;
   bool mAllowSubframes;
   bool mAllowPlugins;
   bool mAllowJavascript;
   bool mAllowMetaRedirects;
   bool mAllowImages;
   bool mAllowMedia;
   bool mAllowDNSPrefetch;
--- a/dom/base/nsContentSink.cpp
+++ b/dom/base/nsContentSink.cpp
@@ -1059,16 +1059,22 @@ void
 nsContentSink::ProcessOfflineManifest(const nsAString& aManifestSpec)
 {
   // Don't bother processing offline manifest for documents
   // without a docshell
   if (!mDocShell) {
     return;
   }
 
+  // If this document has been interecepted, let's skip the processing of the
+  // manifest.
+  if (nsContentUtils::IsControlledByServiceWorker(mDocument)) {
+    return;
+  }
+
   // If the docshell's in private browsing mode, we don't want to do any
   // manifest processing.
   nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(mDocShell);
   if (loadContext->UsePrivateBrowsing()) {
     return;
   }
 
   nsresult rv;
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -47,16 +47,17 @@
 #include "mozilla/dom/ipc/BlobParent.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/TabParent.h"
 #include "mozilla/dom/TextDecoder.h"
 #include "mozilla/dom/TouchEvent.h"
 #include "mozilla/dom/ShadowRoot.h"
 #include "mozilla/dom/WorkerPrivate.h"
+#include "mozilla/dom/workers/ServiceWorkerManager.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/EventStateManager.h"
 #include "mozilla/IMEStateManager.h"
 #include "mozilla/InternalMutationEvent.h"
 #include "mozilla/Likely.h"
 #include "mozilla/MouseEvents.h"
 #include "mozilla/Preferences.h"
@@ -262,16 +263,17 @@ bool nsContentUtils::sIsFrameTimingPrefE
 bool nsContentUtils::sIsPerformanceTimingEnabled = false;
 bool nsContentUtils::sIsResourceTimingEnabled = false;
 bool nsContentUtils::sIsUserTimingLoggingEnabled = false;
 bool nsContentUtils::sIsExperimentalAutocompleteEnabled = false;
 bool nsContentUtils::sEncodeDecodeURLHash = false;
 bool nsContentUtils::sGettersDecodeURLHash = false;
 bool nsContentUtils::sPrivacyResistFingerprinting = false;
 bool nsContentUtils::sSendPerformanceTimingNotifications = false;
+bool nsContentUtils::sSWInterceptionEnabled = false;
 
 uint32_t nsContentUtils::sHandlingInputTimeout = 1000;
 
 uint32_t nsContentUtils::sCookiesLifetimePolicy = nsICookieService::ACCEPT_NORMALLY;
 uint32_t nsContentUtils::sCookiesBehavior = nsICookieService::BEHAVIOR_ACCEPT;
 
 nsHtml5StringParser* nsContentUtils::sHTMLFragmentParser = nullptr;
 nsIParser* nsContentUtils::sXMLFragmentParser = nullptr;
@@ -554,16 +556,20 @@ nsContentUtils::Init()
                                "dom.url.encode_decode_hash", false);
 
   Preferences::AddBoolVarCache(&sGettersDecodeURLHash,
                                "dom.url.getters_decode_hash", false);
 
   Preferences::AddBoolVarCache(&sPrivacyResistFingerprinting,
                                "privacy.resistFingerprinting", false);
 
+  Preferences::AddBoolVarCache(&sSWInterceptionEnabled,
+                               "dom.serviceWorkers.interception.enabled",
+                               false);
+
   Preferences::AddUintVarCache(&sHandlingInputTimeout,
                                "dom.event.handling-user-input-time-limit",
                                1000);
 
   Preferences::AddBoolVarCache(&sSendPerformanceTimingNotifications,
                                "dom.performance.enable_notify_performance_timing", false);
 
   Preferences::AddUintVarCache(&sCookiesLifetimePolicy,
@@ -1732,19 +1738,47 @@ nsContentUtils::ParseLegacyFontSize(cons
       value = 3 + value;
     }
   }
 
   return clamped(value, 1, 7);
 }
 
 /* static */
+bool
+nsContentUtils::IsControlledByServiceWorker(nsIDocument* aDocument)
+{
+  if (!ServiceWorkerInterceptionEnabled() ||
+      nsContentUtils::IsInPrivateBrowsing(aDocument)) {
+    return false;
+  }
+
+  nsRefPtr<workers::ServiceWorkerManager> swm =
+    workers::ServiceWorkerManager::GetInstance();
+  MOZ_ASSERT(swm);
+
+  ErrorResult rv;
+  bool controlled = swm->IsControlled(aDocument, rv);
+  NS_WARN_IF(rv.Failed());
+
+  return !rv.Failed() && controlled;
+}
+
+/* static */
 void
 nsContentUtils::GetOfflineAppManifest(nsIDocument *aDocument, nsIURI **aURI)
 {
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aDocument);
+  *aURI = nullptr;
+
+  if (IsControlledByServiceWorker(aDocument)) {
+    return;
+  }
+
   Element* docElement = aDocument->GetRootElement();
   if (!docElement) {
     return;
   }
 
   nsAutoString manifestSpec;
   docElement->GetAttr(kNameSpaceID_None, nsGkAtoms::manifest, manifestSpec);
 
@@ -5225,17 +5259,16 @@ nsContentUtils::EnterMicroTask()
 }
 
 void
 nsContentUtils::LeaveMicroTask()
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (--sMicroTaskLevel == 0) {
     PerformMainThreadMicroTaskCheckpoint();
-    nsDocument::ProcessBaseElementQueue();
   }
 }
 
 bool
 nsContentUtils::IsInMicroTask()
 {
   MOZ_ASSERT(NS_IsMainThread());
   return sMicroTaskLevel != 0;
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -1951,16 +1951,24 @@ public:
    * Returns true if notification should be sent for peformance timing events.
    */
   static bool SendPerformanceTimingNotifications()
   {
     return sSendPerformanceTimingNotifications;
   }
 
   /*
+   * Returns true if ServiceWorker Interception is enabled by pref.
+   */
+  static bool ServiceWorkerInterceptionEnabled()
+  {
+    return sSWInterceptionEnabled;
+  }
+
+  /*
    * Returns true if the frame timing APIs are enabled.
    */
   static bool IsFrameTimingEnabled();
 
   /*
    * Returns true if URL setters should percent encode the Hash/Ref segment
    * and getters should return the percent decoded value of the segment
    */
@@ -1991,16 +1999,21 @@ public:
    * Returns true if the doc tree branch which contains aDoc contains any
    * plugins which we don't control event dispatch for, i.e. do any plugins
    * in the same tab as this document receive key events outside of our
    * control? This always returns false on MacOSX.
    */
   static bool HasPluginWithUncontrolledEventDispatch(nsIDocument* aDoc);
 
   /**
+   * Return true if this doc is controlled by a ServiceWorker.
+   */
+  static bool IsControlledByServiceWorker(nsIDocument* aDocument);
+
+  /**
    * Fire mutation events for changes caused by parsing directly into a
    * context node.
    *
    * @param aDoc the document of the node
    * @param aDest the destination node that got stuff appended to it
    * @param aOldChildCount the number of children the node had before parsing
    */
   static void FireMutationEventsForDirectParsing(nsIDocument* aDoc,
@@ -2602,16 +2615,17 @@ private:
   static bool sIsResourceTimingEnabled;
   static bool sIsUserTimingLoggingEnabled;
   static bool sIsFrameTimingPrefEnabled;
   static bool sIsExperimentalAutocompleteEnabled;
   static bool sEncodeDecodeURLHash;
   static bool sGettersDecodeURLHash;
   static bool sPrivacyResistFingerprinting;
   static bool sSendPerformanceTimingNotifications;
+  static bool sSWInterceptionEnabled;
   static uint32_t sCookiesLifetimePolicy;
   static uint32_t sCookiesBehavior;
 
   static nsHtml5StringParser* sHTMLFragmentParser;
   static nsIParser* sXMLFragmentParser;
   static nsIFragmentContentSink* sXMLFragmentSink;
 
   /**
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -5890,39 +5890,16 @@ nsDocument::RegisterUnresolvedElement(El
 
   nsRefPtr<Element>* elem = unresolved->AppendElement();
   *elem = aElement;
   aElement->AddStates(NS_EVENT_STATE_UNRESOLVED);
 
   return NS_OK;
 }
 
-namespace {
-
-class ProcessStackRunner final : public nsIRunnable
-{
-  ~ProcessStackRunner() {}
-public:
-  explicit ProcessStackRunner(bool aIsBaseQueue = false)
-    : mIsBaseQueue(aIsBaseQueue)
-  {
-  }
-  NS_DECL_ISUPPORTS
-  NS_IMETHOD Run() override
-  {
-    nsDocument::ProcessTopElementQueue(mIsBaseQueue);
-    return NS_OK;
-  }
-  bool mIsBaseQueue;
-};
-
-NS_IMPL_ISUPPORTS(ProcessStackRunner, nsIRunnable);
-
-} // namespace
-
 void
 nsDocument::EnqueueLifecycleCallback(nsIDocument::ElementCallbackType aType,
                                      Element* aCustomElement,
                                      LifecycleCallbackArgs* aArgs,
                                      CustomElementDefinition* aDefinition)
 {
   if (!mRegistry) {
     // The element might not belong to a document that
@@ -6014,20 +5991,17 @@ nsDocument::EnqueueLifecycleCallback(nsI
   }
 
   if (!elementData->mElementIsBeingCreated) {
     CustomElementData* lastData =
       sProcessingStack->SafeLastElement(nullptr);
 
     // A new element queue needs to be pushed if the queue at the
     // top of the stack is associated with another microtask level.
-    // Don't push a queue for the level 0 microtask (base element queue)
-    // because we don't want to process the queue until the
-    // microtask checkpoint.
-    bool shouldPushElementQueue = nsContentUtils::MicroTaskLevel() > 0 &&
+    bool shouldPushElementQueue =
       (!lastData || lastData->mAssociatedMicroTask <
          static_cast<int32_t>(nsContentUtils::MicroTaskLevel()));
 
     // Push a new element queue onto the processing stack when appropriate
     // (when we enter a new microtask).
     if (shouldPushElementQueue) {
       // Push a sentinel value on the processing stack to mark the
       // boundary between the element queues.
@@ -6040,49 +6014,32 @@ nsDocument::EnqueueLifecycleCallback(nsI
 
     // Add a script runner to pop and process the element queue at
     // the top of the processing stack.
     if (shouldPushElementQueue) {
       // Lifecycle callbacks enqueued by user agent implementation
       // should be invoked prior to returning control back to script.
       // Create a script runner to process the top of the processing
       // stack as soon as it is safe to run script.
-      nsContentUtils::AddScriptRunner(new ProcessStackRunner());
+      nsCOMPtr<nsIRunnable> runnable =
+        NS_NewRunnableFunction(&nsDocument::ProcessTopElementQueue);
+      nsContentUtils::AddScriptRunner(runnable);
     }
   }
 }
 
 // static
 void
-nsDocument::ProcessBaseElementQueue()
-{
-  // Prevent re-entrance. Also, if a microtask checkpoint is reached
-  // and there is no processing stack to process, then we are done.
-  if (sProcessingBaseElementQueue || !sProcessingStack) {
-    return;
-  }
-
-  MOZ_ASSERT(nsContentUtils::MicroTaskLevel() == 0);
-  sProcessingBaseElementQueue = true;
-  nsContentUtils::AddScriptRunner(new ProcessStackRunner(true));
-}
-
-// static
-void
-nsDocument::ProcessTopElementQueue(bool aIsBaseQueue)
+nsDocument::ProcessTopElementQueue()
 {
   MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
 
   nsTArray<nsRefPtr<CustomElementData>>& stack = *sProcessingStack;
   uint32_t firstQueue = stack.LastIndexOf((CustomElementData*) nullptr);
 
-  if (aIsBaseQueue && firstQueue != 0) {
-    return;
-  }
-
   for (uint32_t i = firstQueue + 1; i < stack.Length(); ++i) {
     // Callback queue may have already been processed in an earlier
     // element queue or in an element queue that was popped
     // off more recently.
     if (stack[i]->mAssociatedMicroTask != -1) {
       stack[i]->RunCallbackQueue();
       stack[i]->mAssociatedMicroTask = -1;
     }
@@ -6090,36 +6047,31 @@ nsDocument::ProcessTopElementQueue(bool 
 
   // If this was actually the base element queue, don't bother trying to pop
   // the first "queue" marker (sentinel).
   if (firstQueue != 0) {
     stack.SetLength(firstQueue);
   } else {
     // Don't pop sentinel for base element queue.
     stack.SetLength(1);
-    sProcessingBaseElementQueue = false;
   }
 }
 
 bool
 nsDocument::RegisterEnabled()
 {
   static bool sPrefValue =
     Preferences::GetBool("dom.webcomponents.enabled", false);
   return sPrefValue;
 }
 
 // static
 Maybe<nsTArray<nsRefPtr<mozilla::dom::CustomElementData>>>
 nsDocument::sProcessingStack;
 
-// static
-bool
-nsDocument::sProcessingBaseElementQueue;
-
 void
 nsDocument::RegisterElement(JSContext* aCx, const nsAString& aType,
                             const ElementRegistrationOptions& aOptions,
                             JS::MutableHandle<JSObject*> aRetval,
                             ErrorResult& rv)
 {
   if (!mRegistry) {
     rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
@@ -11729,37 +11681,60 @@ GetRootWindow(nsIDocument* aDoc)
   if (!docShell) {
     return nullptr;
   }
   nsCOMPtr<nsIDocShellTreeItem> rootItem;
   docShell->GetRootTreeItem(getter_AddRefs(rootItem));
   return rootItem ? rootItem->GetWindow() : nullptr;
 }
 
+static bool
+ShouldApplyFullscreenDirectly(nsIDocument* aDoc,
+                              nsPIDOMWindow* aRootWin)
+{
+  if (XRE_GetProcessType() == GeckoProcessType_Content) {
+    // If we are in the content process, we can apply the fullscreen
+    // state directly only if we have been in DOM fullscreen, because
+    // otherwise we always need to notify the chrome.
+    return nsContentUtils::GetRootDocument(aDoc)->IsFullScreenDoc();
+  } else {
+    // If we are in the chrome process, and the window has not been in
+    // fullscreen, we certainly need to make that fullscreen first.
+    bool fullscreen;
+    NS_WARN_IF(NS_FAILED(aRootWin->GetFullScreen(&fullscreen)));
+    if (!fullscreen) {
+      return false;
+    }
+    // The iterator not being at end indicates there is still some
+    // pending fullscreen request relates to this document. We have to
+    // push the request to the pending queue so requests are handled
+    // in the correct order.
+    PendingFullscreenRequestList::Iterator
+      iter(aDoc, PendingFullscreenRequestList::eDocumentsWithSameRoot);
+    if (!iter.AtEnd()) {
+      return false;
+    }
+    // We have to apply the fullscreen state directly in this case,
+    // because nsGlobalWindow::SetFullscreenInternal() will do nothing
+    // if it is already in fullscreen. If we do not apply the state but
+    // instead add it to the queue and wait for the window as normal,
+    // we would get stuck.
+    return true;
+  }
+}
+
 void
 nsDocument::RequestFullScreen(UniquePtr<FullscreenRequest>&& aRequest)
 {
   nsCOMPtr<nsPIDOMWindow> rootWin = GetRootWindow(this);
   if (!rootWin) {
     return;
   }
 
-  // If we have been in fullscreen, apply the new state directly.
-  // Note that we should check both condition, because if we are in
-  // child process, our window may not report to be in fullscreen.
-  // Also, it is possible that the root window reports that it is in
-  // fullscreen while there exists pending fullscreen request because
-  // of ongoing fullscreen transition. In that case, we shouldn't
-  // apply the state before any previous request.
-  if ((static_cast<nsGlobalWindow*>(rootWin.get())->FullScreen() &&
-       // The iterator being at end at the beginning indicates there is
-       // no pending fullscreen request which relates to this document.
-       PendingFullscreenRequestList::Iterator(
-         this, PendingFullscreenRequestList::eDocumentsWithSameRoot).AtEnd()) ||
-      nsContentUtils::GetRootDocument(this)->IsFullScreenDoc()) {
+  if (ShouldApplyFullscreenDirectly(this, rootWin)) {
     ApplyFullscreen(*aRequest);
     return;
   }
 
   // We don't need to check element ready before this point, because
   // if we called ApplyFullscreen, it would check that for us.
   Element* elem = aRequest->GetElement();
   if (!FullscreenElementReadyCheck(elem, aRequest->mIsCallerChrome)) {
@@ -13038,52 +13013,74 @@ nsDocument::ReportUseCounters()
     // certain features were removed.  Consider the case of a single
     // HTML document with several SVG images and/or iframes with
     // sub-documents of their own.  If we maintained a single set of use
     // counters and all the sub-documents use a particular feature, then
     // telemetry would indicate that we would be breaking N documents if
     // that feature were removed.  Whereas with a document/top-level
     // page split, we can see that N documents would be affected, but
     // only a single web page would be affected.
+
+    // The difference between the values of these two histograms and the
+    // related use counters below tell us how many pages did *not* use
+    // the feature in question.  For instance, if we see that a given
+    // session has destroyed 30 content documents, but a particular use
+    // counter shows only a count of 5, we can infer that the use
+    // counter was *not* used in 25 of those 30 documents.
+    //
+    // We do things this way, rather than accumulating a boolean flag
+    // for each use counter, to avoid sending histograms for features
+    // that don't get widely used.  Doing things in this fashion means
+    // smaller telemetry payloads and faster processing on the server
+    // side.
+    Telemetry::Accumulate(Telemetry::CONTENT_DOCUMENTS_DESTROYED, 1);
+    if (IsTopLevelContentDocument()) {
+      Telemetry::Accumulate(Telemetry::TOP_LEVEL_CONTENT_DOCUMENTS_DESTROYED, 1);
+    }
+
     for (int32_t c = 0;
          c < eUseCounter_Count; ++c) {
       UseCounter uc = static_cast<UseCounter>(c);
       
       Telemetry::ID id =
         static_cast<Telemetry::ID>(Telemetry::HistogramFirstUseCounter + uc * 2);
       bool value = GetUseCounter(uc);
 
-      if (sDebugUseCounters && value) {
-        const char* name = Telemetry::GetHistogramName(id);
-        if (name) {
-          printf("  %s", name);
-        } else {
-          printf("  #%d", id);
-        }
-        printf(": %d\n", value);
-      }
-
-      Telemetry::Accumulate(id, value);
-
-      if (IsTopLevelContentDocument()) {
-        id = static_cast<Telemetry::ID>(Telemetry::HistogramFirstUseCounter +
-                                        uc * 2 + 1);
-        value = GetUseCounter(uc) || GetChildDocumentUseCounter(uc);
-
-        if (sDebugUseCounters && value) {
+      if (value) {
+        if (sDebugUseCounters) {
           const char* name = Telemetry::GetHistogramName(id);
           if (name) {
             printf("  %s", name);
           } else {
             printf("  #%d", id);
           }
           printf(": %d\n", value);
         }
 
-        Telemetry::Accumulate(id, value);
+        Telemetry::Accumulate(id, 1);
+      }
+
+      if (IsTopLevelContentDocument()) {
+        id = static_cast<Telemetry::ID>(Telemetry::HistogramFirstUseCounter +
+                                        uc * 2 + 1);
+        value = GetUseCounter(uc) || GetChildDocumentUseCounter(uc);
+
+        if (value) {
+          if (sDebugUseCounters) {
+            const char* name = Telemetry::GetHistogramName(id);
+            if (name) {
+              printf("  %s", name);
+            } else {
+              printf("  #%d", id);
+            }
+            printf(": %d\n", value);
+          }
+
+          Telemetry::Accumulate(id, 1);
+        }
       }
     }
   }
 }
 
 XPathEvaluator*
 nsIDocument::XPathEvaluator()
 {
--- a/dom/base/nsDocument.h
+++ b/dom/base/nsDocument.h
@@ -1298,17 +1298,17 @@ public:
 
   virtual nsIDOMNode* AsDOMNode() override { return this; }
 
   virtual void EnqueueLifecycleCallback(nsIDocument::ElementCallbackType aType,
                                         Element* aCustomElement,
                                         mozilla::dom::LifecycleCallbackArgs* aArgs = nullptr,
                                         mozilla::dom::CustomElementDefinition* aDefinition = nullptr) override;
 
-  static void ProcessTopElementQueue(bool aIsBaseQueue = false);
+  static void ProcessTopElementQueue();
 
   void GetCustomPrototype(int32_t aNamespaceID,
                           nsIAtom* aAtom,
                           JS::MutableHandle<JSObject*> prototype)
   {
     if (!mRegistry) {
       prototype.set(nullptr);
       return;
@@ -1595,25 +1595,19 @@ private:
   // Array representing the processing stack in the custom elements
   // specification. The processing stack is conceptually a stack of
   // element queues. Each queue is represented by a sequence of
   // CustomElementData in this array, separated by nullptr that
   // represent the boundaries of the items in the stack. The first
   // queue in the stack is the base element queue.
   static mozilla::Maybe<nsTArray<nsRefPtr<mozilla::dom::CustomElementData>>> sProcessingStack;
 
-  // Flag to prevent re-entrance into base element queue as described in the
-  // custom elements speicification.
-  static bool sProcessingBaseElementQueue;
-
   static bool CustomElementConstructor(JSContext* aCx, unsigned aArgc, JS::Value* aVp);
 
 public:
-  static void ProcessBaseElementQueue();
-
   // Enqueue created callback or register upgrade candidate for
   // newly created custom elements, possibly extending an existing type.
   // ex. <x-button>, <button is="x-button> (type extension)
   virtual void SetupCustomElement(Element* aElement,
                                   uint32_t aNamespaceID,
                                   const nsAString* aTypeExtension) override;
 
   static bool IsWebComponentsEnabled(JSContext* aCx, JSObject* aObject);
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -630,17 +630,17 @@ public:
   virtual bool preventExtensions(JSContext* cx,
                                  JS::Handle<JSObject*> proxy,
                                  JS::ObjectOpResult& result) const override;
   virtual bool isExtensible(JSContext *cx, JS::Handle<JSObject*> proxy, bool *extensible)
                             const override;
   virtual bool has(JSContext *cx, JS::Handle<JSObject*> proxy,
                    JS::Handle<jsid> id, bool *bp) const override;
   virtual bool get(JSContext *cx, JS::Handle<JSObject*> proxy,
-                   JS::Handle<JSObject*> receiver,
+                   JS::Handle<JS::Value> receiver,
                    JS::Handle<jsid> id,
                    JS::MutableHandle<JS::Value> vp) const override;
   virtual bool set(JSContext *cx, JS::Handle<JSObject*> proxy,
                    JS::Handle<jsid> id, JS::Handle<JS::Value> v,
                    JS::Handle<JS::Value> receiver,
                    JS::ObjectOpResult &result) const override;
 
   // SpiderMonkey extensions
@@ -895,17 +895,17 @@ nsOuterWindowProxy::hasOwn(JSContext *cx
     return true;
   }
 
   return js::Wrapper::hasOwn(cx, proxy, id, bp);
 }
 
 bool
 nsOuterWindowProxy::get(JSContext *cx, JS::Handle<JSObject*> proxy,
-                        JS::Handle<JSObject*> receiver,
+                        JS::Handle<JS::Value> receiver,
                         JS::Handle<jsid> id,
                         JS::MutableHandle<JS::Value> vp) const
 {
   if (id == nsDOMClassInfo::sWrappedJSObject_id &&
       xpc::AccessCheck::isChrome(js::GetContextCompartment(cx))) {
     vp.set(JS::ObjectValue(*proxy));
     return true;
   }
--- a/dom/base/test/browser_use_counters.js
+++ b/dom/base/test/browser_use_counters.js
@@ -99,34 +99,38 @@ function waitForPageLoad(browser) {
 
 function grabHistogramsFromContent(browser, use_counter_middlefix) {
   return ContentTask.spawn(browser, { middlefix: use_counter_middlefix }, function* (arg) {
     let telemetry = Cc["@mozilla.org/base/telemetry;1"].getService(Ci.nsITelemetry);
     function snapshot_histogram(name) {
       return telemetry.getHistogramById(name).snapshot();
     }
 
-    let histogram_page_name = "USE_COUNTER_" + arg.middlefix + "_PAGE";
-    let histogram_document_name = "USE_COUNTER_" + arg.middlefix + "_DOCUMENT";
+    let histogram_page_name = "USE_COUNTER2_" + arg.middlefix + "_PAGE";
+    let histogram_document_name = "USE_COUNTER2_" + arg.middlefix + "_DOCUMENT";
     let histogram_page = snapshot_histogram(histogram_page_name);
     let histogram_document = snapshot_histogram(histogram_document_name);
-    return [histogram_page.sum, histogram_document.sum];
+    let histogram_docs = snapshot_histogram("CONTENT_DOCUMENTS_DESTROYED");
+    let histogram_toplevel_docs = snapshot_histogram("TOP_LEVEL_CONTENT_DOCUMENTS_DESTROYED");
+    return [histogram_page.sum, histogram_document.sum,
+            histogram_docs.sum, histogram_toplevel_docs.sum];
   });
 }
 
 var check_use_counter_iframe = Task.async(function* (file, use_counter_middlefix, check_documents=true) {
   info("checking " + file + " with histogram " + use_counter_middlefix);
 
   let newTab = gBrowser.addTab( "about:blank");
   gBrowser.selectedTab = newTab;
   newTab.linkedBrowser.stop();
 
   // Hold on to the current values of the telemetry histograms we're
   // interested in.
-  let [histogram_page_before, histogram_document_before] =
+  let [histogram_page_before, histogram_document_before,
+       histogram_docs_before, histogram_toplevel_docs_before] =
       yield grabHistogramsFromContent(gBrowser.selectedBrowser, use_counter_middlefix);
 
   gBrowser.selectedBrowser.loadURI(gHttpTestRoot + "file_use_counter_outer.html");
   yield waitForPageLoad(gBrowser.selectedBrowser);
 
   // Inject our desired file into the iframe of the newly-loaded page.
   yield ContentTask.spawn(gBrowser.selectedBrowser, { file: file }, function(opts) {
     Cu.import("resource://gre/modules/PromiseUtils.jsm");
@@ -156,37 +160,41 @@ var check_use_counter_iframe = Task.asyn
   gBrowser.removeTab(newTab);
 
   // The histograms only get recorded when the document actually gets
   // destroyed, which might not have happened yet due to GC/CC effects, etc.
   // Try to force document destruction.
   yield waitForDestroyedDocuments();
 
   // Grab histograms again and compare.
-  let [histogram_page_after, histogram_document_after] =
+  let [histogram_page_after, histogram_document_after,
+       histogram_docs_after, histogram_toplevel_docs_after] =
       yield grabHistogramsFromContent(gBrowser.selectedBrowser, use_counter_middlefix);
 
   is(histogram_page_after, histogram_page_before + 1,
      "page counts for " + use_counter_middlefix + " after are correct");
+  ok(histogram_toplevel_docs_after >= histogram_toplevel_docs_before + 1,
+     "top level document counts are correct");
   if (check_documents) {
     is(histogram_document_after, histogram_document_before + 1,
-       "document counts " + use_counter_middlefix + " after are correct");
+       "document counts for " + use_counter_middlefix + " after are correct");
   }
 });
 
 var check_use_counter_img = Task.async(function* (file, use_counter_middlefix) {
   info("checking " + file + " as image with histogram " + use_counter_middlefix);
 
   let newTab = gBrowser.addTab("about:blank");
   gBrowser.selectedTab = newTab;
   newTab.linkedBrowser.stop();
 
   // Hold on to the current values of the telemetry histograms we're
   // interested in.
-  let [histogram_page_before, histogram_document_before] =
+  let [histogram_page_before, histogram_document_before,
+       histogram_docs_before, histogram_toplevel_docs_before] =
       yield grabHistogramsFromContent(gBrowser.selectedBrowser, use_counter_middlefix);
 
   gBrowser.selectedBrowser.loadURI(gHttpTestRoot + "file_use_counter_outer.html");
   yield waitForPageLoad(gBrowser.selectedBrowser);
 
   // Inject our desired file into the img of the newly-loaded page.
   yield ContentTask.spawn(gBrowser.selectedBrowser, { file: file }, function(opts) {
     Cu.import("resource://gre/modules/PromiseUtils.jsm");
@@ -217,34 +225,42 @@ var check_use_counter_img = Task.async(f
   gBrowser.removeTab(newTab);
 
   // The histograms only get recorded when the document actually gets
   // destroyed, which might not have happened yet due to GC/CC effects, etc.
   // Try to force document destruction.
   yield waitForDestroyedDocuments();
 
   // Grab histograms again and compare.
-  let [histogram_page_after, histogram_document_after] =
+  let [histogram_page_after, histogram_document_after,
+       histogram_docs_after, histogram_toplevel_docs_after] =
       yield grabHistogramsFromContent(gBrowser.selectedBrowser, use_counter_middlefix);
   is(histogram_page_after, histogram_page_before + 1,
      "page counts for " + use_counter_middlefix + " after are correct");
   is(histogram_document_after, histogram_document_before + 1,
-     "document counts " + use_counter_middlefix + " after are correct");
+     "document counts for " + use_counter_middlefix + " after are correct");
+  ok(histogram_toplevel_docs_after >= histogram_toplevel_docs_before + 1,
+     "top level document counts are correct");
+  // 2 documents: one for the outer html page containing the <img> element, and
+  // one for the SVG image itself.
+  ok(histogram_docs_after >= histogram_docs_before + 2,
+     "document counts are correct");
 });
 
 var check_use_counter_direct = Task.async(function* (file, use_counter_middlefix, xfail=false) {
   info("checking " + file + " with histogram " + use_counter_middlefix);
 
   let newTab = gBrowser.addTab( "about:blank");
   gBrowser.selectedTab = newTab;
   newTab.linkedBrowser.stop();
 
   // Hold on to the current values of the telemetry histograms we're
   // interested in.
-  let [histogram_page_before, histogram_document_before] =
+  let [histogram_page_before, histogram_document_before,
+       histogram_docs_before, histogram_toplevel_docs_before] =
       yield grabHistogramsFromContent(gBrowser.selectedBrowser, use_counter_middlefix);
 
   gBrowser.selectedBrowser.loadURI(gHttpTestRoot + file);
   yield ContentTask.spawn(gBrowser.selectedBrowser, null, function*() {
     Cu.import("resource://gre/modules/PromiseUtils.jsm");
     yield new Promise(resolve => {
       let listener = () => {
         removeEventListener("load", listener, true);
@@ -262,15 +278,20 @@ var check_use_counter_direct = Task.asyn
   gBrowser.removeTab(newTab);
 
   // The histograms only get recorded when the document actually gets
   // destroyed, which might not have happened yet due to GC/CC effects, etc.
   // Try to force document destruction.
   yield waitForDestroyedDocuments();
 
   // Grab histograms again and compare.
-  let [histogram_page_after, histogram_document_after] =
+  let [histogram_page_after, histogram_document_after,
+       histogram_docs_after, histogram_toplevel_docs_after] =
       yield grabHistogramsFromContent(gBrowser.selectedBrowser, use_counter_middlefix);
   (xfail ? todo_is : is)(histogram_page_after, histogram_page_before + 1,
                          "page counts for " + use_counter_middlefix + " after are correct");
   (xfail ? todo_is : is)(histogram_document_after, histogram_document_before + 1,
-                         "document counts " + use_counter_middlefix + " after are correct");
+                         "document counts for " + use_counter_middlefix + " after are correct");
+  ok(histogram_toplevel_docs_after >= histogram_toplevel_docs_before + 1,
+     "top level document counts are correct");
+  ok(histogram_docs_after >= histogram_docs_before + 1,
+     "document counts are correct");
 });
--- a/dom/base/usecounters.py
+++ b/dom/base/usecounters.py
@@ -57,18 +57,18 @@ def generate_histograms(filename):
     items = collections.OrderedDict()
     for counter in read_conf(filename):
         def append_counter(name, desc):
             items[name] = { 'expires_in_version': 'never',
                             'kind' : 'boolean',
                             'description': desc }
 
         def append_counters(name, desc):
-            append_counter('USE_COUNTER_%s_DOCUMENT' % name, 'Whether a document %s' % desc)
-            append_counter('USE_COUNTER_%s_PAGE' % name, 'Whether a page %s' % desc)
+            append_counter('USE_COUNTER2_%s_DOCUMENT' % name, 'Whether a document %s' % desc)
+            append_counter('USE_COUNTER2_%s_PAGE' % name, 'Whether a page %s' % desc)
 
         if counter['type'] == 'method':
             method = '%s.%s' % (counter['interface_name'], counter['method_name'])
             append_counters(method.replace('.', '_').upper(), 'called %s' % method)
         elif counter['type'] == 'attribute':
             attr = '%s.%s' % (counter['interface_name'], counter['attribute_name'])
             counter_name = attr.replace('.', '_').upper()
             append_counters('%s_getter' % counter_name, 'got %s' % attr)
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -1694,18 +1694,18 @@ NativePropertyHooks sEmptyNativeProperty
   },
   prototypes::id::_ID_Count,
   constructors::id::_ID_Count,
   nullptr
 };
 
 bool
 GetPropertyOnPrototype(JSContext* cx, JS::Handle<JSObject*> proxy,
-                       JS::Handle<jsid> id, bool* found,
-                       JS::MutableHandle<JS::Value> vp)
+                       JS::Handle<JS::Value> receiver, JS::Handle<jsid> id,
+                       bool* found, JS::MutableHandle<JS::Value> vp)
 {
   JS::Rooted<JSObject*> proto(cx);
   if (!js::GetObjectProto(cx, proxy, &proto)) {
     return false;
   }
   if (!proto) {
     *found = false;
     return true;
@@ -1714,17 +1714,17 @@ GetPropertyOnPrototype(JSContext* cx, JS
   if (!JS_HasPropertyById(cx, proto, id, found)) {
     return false;
   }
 
   if (!*found) {
     return true;
   }
 
-  return JS_ForwardGetPropertyTo(cx, proto, id, proxy, vp);
+  return JS_ForwardGetPropertyTo(cx, proto, id, receiver, vp);
 }
 
 bool
 HasPropertyOnPrototype(JSContext* cx, JS::Handle<JSObject*> proxy,
                        JS::Handle<jsid> id, bool* has)
 {
   JS::Rooted<JSObject*> proto(cx);
   if (!js::GetObjectProto(cx, proxy, &proto)) {
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -1842,18 +1842,18 @@ UnforgeableValueOf(JSContext* cx, unsign
 bool
 ThrowingConstructor(JSContext* cx, unsigned argc, JS::Value* vp);
 
 bool
 ThrowConstructorWithoutNew(JSContext* cx, const char* name);
 
 bool
 GetPropertyOnPrototype(JSContext* cx, JS::Handle<JSObject*> proxy,
-                       JS::Handle<jsid> id, bool* found,
-                       JS::MutableHandle<JS::Value> vp);
+                       JS::Handle<JS::Value> receiver, JS::Handle<jsid> id,
+                       bool* found, JS::MutableHandle<JS::Value> vp);
 
 //
 bool
 HasPropertyOnPrototype(JSContext* cx, JS::Handle<JSObject*> proxy,
                        JS::Handle<jsid> id, bool* has);
 
 
 // Append the property names in "names" to "props". If
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -11034,17 +11034,17 @@ class CGDOMJSProxyHandler_hasOwn(ClassMe
             indexed=indexed,
             named=named)
 
 
 class CGDOMJSProxyHandler_get(ClassMethod):
     def __init__(self, descriptor):
         args = [Argument('JSContext*', 'cx'),
                 Argument('JS::Handle<JSObject*>', 'proxy'),
-                Argument('JS::Handle<JSObject*>', 'receiver'),
+                Argument('JS::Handle<JS::Value>', 'receiver'),
                 Argument('JS::Handle<jsid>', 'id'),
                 Argument('JS::MutableHandle<JS::Value>', 'vp')]
         ClassMethod.__init__(self, "get", "bool", args,
                              virtual=True, override=True, const=True)
         self.descriptor = descriptor
 
     def getBody(self):
         getUnforgeableOrExpando = dedent("""
@@ -11089,17 +11089,17 @@ class CGDOMJSProxyHandler_get(ClassMetho
             if self.descriptor.supportsIndexedProperties():
                 getNamed = CGIfWrapper(getNamed, "!IsArrayIndex(index)")
             getNamed = getNamed.define() + "\n"
         else:
             getNamed = ""
 
         getOnPrototype = dedent("""
             bool foundOnPrototype;
-            if (!GetPropertyOnPrototype(cx, proxy, id, &foundOnPrototype, vp)) {
+            if (!GetPropertyOnPrototype(cx, proxy, receiver, id, &foundOnPrototype, vp)) {
               return false;
             }
 
             if (foundOnPrototype) {
               return true;
             }
 
             """)
--- a/gfx/layers/client/TextureClient.cpp
+++ b/gfx/layers/client/TextureClient.cpp
@@ -219,17 +219,16 @@ TextureClient::RemoveFlags(TextureFlags 
     mActor->SendRecycleTexture(mFlags);
   }
 }
 
 void
 TextureClient::RecycleTexture(TextureFlags aFlags)
 {
   MOZ_ASSERT(GetFlags() & TextureFlags::RECYCLE);
-  MOZ_ASSERT(!HasRecycleCallback());
 
   mAddedToCompositableClient = false;
   if (mFlags != aFlags) {
     mFlags = aFlags;
     if (mValid && mActor && mActor->IPCOpen()) {
       mActor->SendRecycleTexture(mFlags);
     }
   }
--- a/gfx/layers/client/TextureClientRecycleAllocator.cpp
+++ b/gfx/layers/client/TextureClientRecycleAllocator.cpp
@@ -48,16 +48,34 @@ TextureClientRecycleAllocator::~TextureC
 }
 
 void
 TextureClientRecycleAllocator::SetMaxPoolSize(uint32_t aMax)
 {
   mMaxPooledSize = aMax;
 }
 
+class TextureClientRecycleTask : public Task
+{
+public:
+  explicit TextureClientRecycleTask(TextureClient* aClient, TextureFlags aFlags)
+    : mTextureClient(aClient)
+    , mFlags(aFlags)
+  {}
+
+  virtual void Run() override
+  {
+    mTextureClient->RecycleTexture(mFlags);
+  }
+
+private:
+  mozilla::RefPtr<TextureClient> mTextureClient;
+  TextureFlags mFlags;
+};
+
 already_AddRefed<TextureClient>
 TextureClientRecycleAllocator::CreateOrRecycle(gfx::SurfaceFormat aFormat,
                                                gfx::IntSize aSize,
                                                BackendSelector aSelector,
                                                TextureFlags aTextureFlags,
                                                TextureAllocationFlags aAllocFlags)
 {
   // TextureAllocationFlags is actually used only by ContentClient.
@@ -69,27 +87,28 @@ TextureClientRecycleAllocator::CreateOrR
 
   RefPtr<TextureClientHolder> textureHolder;
 
   {
     MutexAutoLock lock(mLock);
     if (!mPooledClients.empty()) {
       textureHolder = mPooledClients.top();
       mPooledClients.pop();
+      Task* task = nullptr;
       // If a pooled TextureClient is not compatible, release it.
       if (textureHolder->GetTextureClient()->GetFormat() != aFormat ||
           textureHolder->GetTextureClient()->GetSize() != aSize) {
-        TextureClientReleaseTask* task = new TextureClientReleaseTask(textureHolder->GetTextureClient());
+        // Release TextureClient.
+        task = new TextureClientReleaseTask(textureHolder->GetTextureClient());
         textureHolder->ClearTextureClient();
         textureHolder = nullptr;
-        // Release TextureClient.
-        mSurfaceAllocator->GetMessageLoop()->PostTask(FROM_HERE, task);
       } else {
-        textureHolder->GetTextureClient()->RecycleTexture(aTextureFlags);
+        task = new TextureClientRecycleTask(textureHolder->GetTextureClient(), aTextureFlags);
       }
+      mSurfaceAllocator->GetMessageLoop()->PostTask(FROM_HERE, task);
     }
   }
 
   if (!textureHolder) {
     // Allocate new TextureClient
     RefPtr<TextureClient> texture = Allocate(aFormat, aSize, aSelector, aTextureFlags, aAllocFlags);
     if (!texture) {
       return nullptr;
--- a/gfx/qcms/iccread.c
+++ b/gfx/qcms/iccread.c
@@ -307,27 +307,27 @@ qcms_bool qcms_profile_is_bogus(qcms_pro
 
 
        // Sum the values; they should add up to something close to white
        sum[0] = rX + gX + bX;
        sum[1] = rY + gY + bY;
        sum[2] = rZ + gZ + bZ;
 
        // Build our target vector (see mozilla bug 460629)
-       target[0] = 0.96420;
-       target[1] = 1.00000;
-       target[2] = 0.82491;
+       target[0] = 0.96420f;
+       target[1] = 1.00000f;
+       target[2] = 0.82491f;
 
        // Our tolerance vector - Recommended by Chris Murphy based on
        // conversion from the LAB space criterion of no more than 3 in any one
        // channel. This is similar to, but slightly more tolerant than Adobe's
        // criterion.
-       tolerance[0] = 0.02;
-       tolerance[1] = 0.02;
-       tolerance[2] = 0.04;
+       tolerance[0] = 0.02f;
+       tolerance[1] = 0.02f;
+       tolerance[2] = 0.04f;
 
        // Compare with our tolerance
        for (i = 0; i < 3; ++i) {
            if (!(((sum[i] - tolerance[i]) <= target[i]) &&
                  ((sum[i] + tolerance[i]) >= target[i])))
                return true;
        }
 
@@ -746,17 +746,17 @@ static struct lutType *read_tag_lutType(
 	lut->e02 = read_s15Fixed16Number(src, offset+20);
 	lut->e10 = read_s15Fixed16Number(src, offset+24);
 	lut->e11 = read_s15Fixed16Number(src, offset+28);
 	lut->e12 = read_s15Fixed16Number(src, offset+32);
 	lut->e20 = read_s15Fixed16Number(src, offset+36);
 	lut->e21 = read_s15Fixed16Number(src, offset+40);
 	lut->e22 = read_s15Fixed16Number(src, offset+44);
 
-	for (i = 0; i < lut->num_input_table_entries * in_chan; i++) {
+	for (i = 0; i < (uint32_t)(lut->num_input_table_entries * in_chan); i++) {
 		if (type == LUT8_TYPE) {
 			lut->input_table[i] = uInt8Number_to_float(read_uInt8Number(src, offset + 52 + i * entry_size));
 		} else {
 			lut->input_table[i] = uInt16Number_to_float(read_uInt16Number(src, offset + 52 + i * entry_size));
 		}
 	}
 
 	clut_offset = offset + 52 + lut->num_input_table_entries * in_chan * entry_size;
@@ -768,17 +768,17 @@ static struct lutType *read_tag_lutType(
 		} else {
 			lut->clut_table[i+0] = uInt16Number_to_float(read_uInt16Number(src, clut_offset + i*entry_size + 0));
 			lut->clut_table[i+1] = uInt16Number_to_float(read_uInt16Number(src, clut_offset + i*entry_size + 2));
 			lut->clut_table[i+2] = uInt16Number_to_float(read_uInt16Number(src, clut_offset + i*entry_size + 4));
 		}
 	}
 
 	output_offset = clut_offset + clut_size * out_chan * entry_size;
-	for (i = 0; i < lut->num_output_table_entries * out_chan; i++) {
+	for (i = 0; i < (uint32_t)(lut->num_output_table_entries * out_chan); i++) {
 		if (type == LUT8_TYPE) {
 			lut->output_table[i] = uInt8Number_to_float(read_uInt8Number(src, output_offset + i*entry_size));
 		} else {
 			lut->output_table[i] = uInt16Number_to_float(read_uInt16Number(src, output_offset + i*entry_size));
 		}
 	}
 
 	return lut;
@@ -1305,17 +1305,17 @@ void qcms_data_from_unicode_path(const w
 * this is a hardcode step just for "create_rgb_with_gamma", it is the only
 * requirement till now, maybe we can make this method more general in future,
 *
 * NOTE : some of the parameters below are hardcode, please refer to the ICC documentation.
 */
 #define ICC_PROFILE_HEADER_LENGTH 128
 void qcms_data_create_rgb_with_gamma(qcms_CIE_xyY white_point, qcms_CIE_xyYTRIPLE primaries, float gamma, void **mem, size_t *size)
 {
-	uint32_t length, offset, index, xyz_count, trc_count;
+	uint32_t length, index, xyz_count, trc_count;
 	size_t tag_table_offset, tag_data_offset;
 	void *data;
 	struct matrix colorants;
 
 	uint32_t TAG_XYZ[3] = {TAG_rXYZ, TAG_gXYZ, TAG_bXYZ};
 	uint32_t TAG_TRC[3] = {TAG_rTRC, TAG_gTRC, TAG_bTRC};
 
 	if ((mem == NULL) || (size == NULL))
--- a/gfx/qcms/moz.build
+++ b/gfx/qcms/moz.build
@@ -12,19 +12,16 @@ EXPORTS += [
 SOURCES += [
     'chain.c',
     'iccread.c',
     'matrix.c',
     'transform.c',
     'transform_util.c',
 ]
 
-# XXX: We should fix these warnings.
-ALLOW_COMPILER_WARNINGS = True
-
 FINAL_LIBRARY = 'xul'
 
 if CONFIG['GNU_CC']:
     CFLAGS += ['-Wno-missing-field-initializers']
 
 use_sse1 = False
 use_sse2 = False
 use_altivec = False
--- a/gfx/qcms/transform.c
+++ b/gfx/qcms/transform.c
@@ -254,19 +254,19 @@ compute_chromatic_adaption(struct CIE_XY
 
 /* from lcms: cmsAdaptionMatrix */
 // Returns the final chrmatic adaptation from illuminant FromIll to Illuminant ToIll
 // Bradford is assumed
 static struct matrix
 adaption_matrix(struct CIE_XYZ source_illumination, struct CIE_XYZ target_illumination)
 {
 	struct matrix lam_rigg = {{ // Bradford matrix
-	                         {  0.8951,  0.2664, -0.1614 },
-	                         { -0.7502,  1.7135,  0.0367 },
-	                         {  0.0389, -0.0685,  1.0296 }
+	                         {  0.8951f,  0.2664f, -0.1614f },
+	                         { -0.7502f,  1.7135f,  0.0367f },
+	                         {  0.0389f, -0.0685f,  1.0296f }
 	                         }};
 	return compute_chromatic_adaption(source_illumination, target_illumination, lam_rigg);
 }
 
 /* from lcms: cmsAdaptMatrixToD50 */
 static struct matrix adapt_matrix_to_D50(struct matrix r, qcms_CIE_xyY source_white_pt)
 {
 	struct CIE_XYZ Dn;
@@ -1389,17 +1389,17 @@ qcms_transform* qcms_transform_create(
 	} else {
 		assert(0 && "unexpected colorspace");
 		qcms_transform_release(transform);
 		return NULL;
 	}
 	return transform;
 }
 
-#if defined(__GNUC__) && !defined(__x86_64__) && !defined(__amd64__)
+#if defined(__GNUC__) && defined(__i386__)
 /* we need this to avoid crashes when gcc assumes the stack is 128bit aligned */
 __attribute__((__force_align_arg_pointer__))
 #endif
 void qcms_transform_data(qcms_transform *transform, void *src, void *dest, size_t length)
 {
 	transform->transform_fn(transform, src, dest, length);
 }
 
--- a/gfx/qcms/transform_util.c
+++ b/gfx/qcms/transform_util.c
@@ -1,21 +1,15 @@
-#define _ISOC99_SOURCE  /* for INFINITY */
-
 #include <math.h>
 #include <assert.h>
 #include <string.h> //memcpy
 #include "qcmsint.h"
 #include "transform_util.h"
 #include "matrix.h"
 
-#if !defined(INFINITY)
-#define INFINITY HUGE_VAL
-#endif
-
 #define PARAMETRIC_CURVE_TYPE 0x70617261 //'para'
 
 /* value must be a value between 0 and 1 */
 //XXX: is the above a good restriction to have?
 // the output range of this functions is 0..1
 float lut_interp_linear(double input_value, uint16_t *table, int length)
 {
 	int upper, lower;
@@ -126,17 +120,17 @@ void compute_curve_gamma_table_type_para
         float a, b, c, e, f;
         float y = parameter[0];
         if (count == 0) {
                 a = 1;
                 b = 0;
                 c = 0;
                 e = 0;
                 f = 0;
-                interval = -INFINITY;
+                interval = -1;
         } else if(count == 1) {
                 a = parameter[1];
                 b = parameter[2];
                 c = 0;
                 e = 0;
                 f = 0;
                 interval = -1 * parameter[2] / parameter[1];
         } else if(count == 2) {
@@ -162,22 +156,22 @@ void compute_curve_gamma_table_type_para
                 interval = parameter[4];
         } else {
                 assert(0 && "invalid parametric function type.");
                 a = 1;
                 b = 0;
                 c = 0;
                 e = 0;
                 f = 0;
-                interval = -INFINITY;
-        }       
+                interval = -1;
+        }
         for (X = 0; X < 256; X++) {
                 if (X >= interval) {
-                        // XXX The equations are not exactly as definied in the spec but are
-                        //     algebraic equivilent.
+                        // XXX The equations are not exactly as defined in the spec but are
+                        //     algebraically equivalent.
                         // TODO Should division by 255 be for the whole expression.
                         gamma_table[X] = clamp_float(pow(a * X / 255. + b, y) + c + e);
                 } else {
                         gamma_table[X] = clamp_float(c * X / 255. + f);
                 }
         }
 }
 
--- a/gfx/thebes/gfxFontPrefLangList.h
+++ b/gfx/thebes/gfxFontPrefLangList.h
@@ -22,14 +22,15 @@ FONT_PREF_LANG(Armenian, "x-armn", x_arm
 FONT_PREF_LANG(Bengali, "x-beng", x_beng),
 FONT_PREF_LANG(Canadian, "x-cans", x_cans),
 FONT_PREF_LANG(Ethiopic, "x-ethi", x_ethi),
 FONT_PREF_LANG(Georgian, "x-geor", x_geor),
 FONT_PREF_LANG(Gujarati, "x-gujr", x_gujr),
 FONT_PREF_LANG(Gurmukhi, "x-guru", x_guru),
 FONT_PREF_LANG(Khmer, "x-khmr", x_khmr),
 FONT_PREF_LANG(Malayalam, "x-mlym", x_mlym),
+FONT_PREF_LANG(Mathematics, "x-math", x_math),
 FONT_PREF_LANG(Oriya, "x-orya", x_orya),
 FONT_PREF_LANG(Telugu, "x-telu", x_telu),
 FONT_PREF_LANG(Kannada, "x-knda", x_knda),
 FONT_PREF_LANG(Sinhala, "x-sinh", x_sinh),
 FONT_PREF_LANG(Tibetan, "x-tibt", x_tibt),
 FONT_PREF_LANG(Others, "x-unicode", Unicode)
--- a/image/Downscaler.cpp
+++ b/image/Downscaler.cpp
@@ -276,10 +276,41 @@ Downscaler::DownscaleInputLine()
   // Shift the buffer. We're just moving pointers here, so this is cheap.
   mLinesInBuffer -= diff;
   mLinesInBuffer = max(mLinesInBuffer, 0);
   for (int32_t i = 0; i < mLinesInBuffer; ++i) {
     swap(mWindow[i], mWindow[filterLength - mLinesInBuffer + i]);
   }
 }
 
+Deinterlacer::Deinterlacer(const nsIntSize& aImageSize)
+  : mImageSize(aImageSize)
+  , mBuffer(MakeUnique<uint8_t[]>(mImageSize.width *
+                                  mImageSize.height *
+                                  sizeof(uint32_t)))
+{ }
+
+uint32_t
+Deinterlacer::RowSize() const
+{
+  return mImageSize.width * sizeof(uint32_t);
+}
+
+uint8_t*
+Deinterlacer::RowBuffer(uint32_t aRow)
+{
+  uint32_t offset = aRow * RowSize();
+  MOZ_ASSERT(offset < mImageSize.width * mImageSize.height * sizeof(uint32_t),
+             "Row is outside of image");
+  return mBuffer.get() + offset;
+}
+
+void
+Deinterlacer::PropagatePassToDownscaler(Downscaler& aDownscaler)
+{
+  for (int32_t row = 0 ; row < mImageSize.height ; ++row) {
+    memcpy(aDownscaler.RowBuffer(), RowBuffer(row), RowSize());
+    aDownscaler.CommitRow();
+  }
+}
+
 } // namespace image
 } // namespace mozilla
--- a/image/Downscaler.h
+++ b/image/Downscaler.h
@@ -157,12 +157,40 @@ public:
   void CommitRow() { }
   bool HasInvalidation() const { return false; }
   DownscalerInvalidRect TakeInvalidRect() { return DownscalerInvalidRect(); }
   void ResetForNextProgressivePass() { }
 };
 
 #endif // MOZ_ENABLE_SKIA
 
+/**
+ * Deinterlacer is a utility class to allow Downscaler to work with interlaced
+ * images.
+
+ * Since Downscaler needs to receive rows in top-to-bottom or
+ * bottom-to-top order, it can't natively handle interlaced images, in which the
+ * rows arrive in an interleaved order. Deinterlacer solves this problem by
+ * acting as an intermediate buffer that records decoded rows. Unlike
+ * Downscaler, it allows the rows to be written in arbitrary order. After each
+ * pass, calling PropagatePassToDownscaler() will downscale every buffered row
+ * in a single operation. The rows remain in the buffer, so rows that were
+ * written in one pass will be included in subsequent passes.
+ */
+class Deinterlacer
+{
+public:
+  explicit Deinterlacer(const nsIntSize& aImageSize);
+
+  uint8_t* RowBuffer(uint32_t aRow);
+  void PropagatePassToDownscaler(Downscaler& aDownscaler);
+
+private:
+  uint32_t RowSize() const;
+
+  nsIntSize mImageSize;
+  UniquePtr<uint8_t[]> mBuffer;
+};
+
 } // namespace image
 } // namespace mozilla
 
 #endif // mozilla_image_Downscaler_h
--- a/image/ImageFactory.cpp
+++ b/image/ImageFactory.cpp
@@ -34,17 +34,18 @@ ImageFactory::Initialize()
 
 static bool
 ShouldDownscaleDuringDecode(const nsCString& aMimeType)
 {
   DecoderType type = DecoderFactory::GetDecoderType(aMimeType.get());
   return type == DecoderType::JPEG ||
          type == DecoderType::ICON ||
          type == DecoderType::PNG ||
-         type == DecoderType::BMP;
+         type == DecoderType::BMP ||
+         type == DecoderType::GIF;
 }
 
 static uint32_t
 ComputeImageFlags(ImageURL* uri, const nsCString& aMimeType, bool isMultiPart)
 {
   nsresult rv;
 
   // We default to the static globals.
--- a/image/decoders/nsGIFDecoder2.cpp
+++ b/image/decoders/nsGIFDecoder2.cpp
@@ -94,16 +94,64 @@ nsGIFDecoder2::nsGIFDecoder2(RasterImage
 }
 
 nsGIFDecoder2::~nsGIFDecoder2()
 {
   free(mGIFStruct.local_colormap);
   free(mGIFStruct.hold);
 }
 
+nsresult
+nsGIFDecoder2::SetTargetSize(const nsIntSize& aSize)
+{
+  // Make sure the size is reasonable.
+  if (MOZ_UNLIKELY(aSize.width <= 0 || aSize.height <= 0)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  // Create a downscaler that we'll filter our output through.
+  mDownscaler.emplace(aSize);
+
+  return NS_OK;
+}
+
+uint8_t*
+nsGIFDecoder2::GetCurrentRowBuffer()
+{
+  if (!mDownscaler) {
+    MOZ_ASSERT(!mDeinterlacer, "Deinterlacer without downscaler?");
+    uint32_t bpp = mGIFStruct.images_decoded == 0 ? sizeof(uint32_t)
+                                                  : sizeof(uint8_t);
+    return mImageData + mGIFStruct.irow * mGIFStruct.width * bpp;
+  }
+
+  if (!mDeinterlacer) {
+    return mDownscaler->RowBuffer();
+  }
+
+  return mDeinterlacer->RowBuffer(mGIFStruct.irow);
+}
+
+uint8_t*
+nsGIFDecoder2::GetRowBuffer(uint32_t aRow)
+{
+  MOZ_ASSERT(mGIFStruct.images_decoded == 0,
+             "Calling GetRowBuffer on a frame other than the first suggests "
+             "we're deinterlacing animated frames");
+  MOZ_ASSERT(!mDownscaler || mDeinterlacer,
+             "Can't get buffer for a specific row if downscaling "
+             "but not deinterlacing");
+
+  if (mDownscaler) {
+    return mDeinterlacer->RowBuffer(aRow);
+  }
+
+  return mImageData + aRow * mGIFStruct.width * sizeof(uint32_t);
+}
+
 void
 nsGIFDecoder2::FinishInternal()
 {
   MOZ_ASSERT(!HasError(), "Shouldn't call FinishInternal after error!");
 
   // If the GIF got cut off, handle it anyway
   if (!IsMetadataDecode() && mGIFOpen) {
     if (mCurrentFrameIndex == mGIFStruct.images_decoded) {
@@ -123,16 +171,25 @@ nsGIFDecoder2::FlushImageData(uint32_t f
   nsIntRect r(mGIFStruct.x_offset, mGIFStruct.y_offset + fromRow,
               mGIFStruct.width, rows);
   PostInvalidation(r);
 }
 
 void
 nsGIFDecoder2::FlushImageData()
 {
+  if (mDownscaler) {
+    if (mDownscaler->HasInvalidation()) {
+      DownscalerInvalidRect invalidRect = mDownscaler->TakeInvalidRect();
+      PostInvalidation(invalidRect.mOriginalSizeRect,
+                       Some(invalidRect.mTargetSizeRect));
+    }
+    return;
+  }
+
   switch (mCurrentPass - mLastFlushedPass) {
     case 0:  // same pass
       if (mCurrentRow - mLastFlushedRow) {
         FlushImageData(mLastFlushedRow + 1, mCurrentRow - mLastFlushedRow);
       }
       break;
 
     case 1:  // one pass on - need to handle bottom & top rects
@@ -197,31 +254,54 @@ nsGIFDecoder2::BeginImageFrame(uint16_t 
     format = gfx::SurfaceFormat::B8G8R8X8;
   }
 
   IntRect frameRect(mGIFStruct.x_offset, mGIFStruct.y_offset,
                     mGIFStruct.width, mGIFStruct.height);
 
   CheckForTransparency(frameRect);
 
+  // Make sure there's no animation if we're downscaling.
+  MOZ_ASSERT_IF(mDownscaler, !GetImageMetadata().HasAnimation());
+
+  IntSize targetSize = mDownscaler ? mDownscaler->TargetSize()
+                                   : GetSize();
+
+  // Rescale the frame rect for the target size.
+  IntRect targetFrameRect = frameRect;
+  if (mDownscaler) {
+    IntSize originalSize = GetSize();
+    targetFrameRect.ScaleRoundOut(double(targetSize.width) / originalSize.width,
+                                  double(targetSize.height) / originalSize.height);
+  }
+
   // Use correct format, RGB for first frame, PAL for following frames
   // and include transparency to allow for optimization of opaque images
   nsresult rv = NS_OK;
   if (mGIFStruct.images_decoded) {
     // Image data is stored with original depth and palette.
-    rv = AllocateFrame(mGIFStruct.images_decoded, GetSize(),
-                       frameRect, format, aDepth);
+    rv = AllocateFrame(mGIFStruct.images_decoded, targetSize,
+                       targetFrameRect, format, aDepth);
   } else {
     // Regardless of depth of input, the first frame is decoded into 24bit RGB.
-    rv = AllocateFrame(mGIFStruct.images_decoded, GetSize(),
-                       frameRect, format);
+    rv = AllocateFrame(mGIFStruct.images_decoded, targetSize,
+                       targetFrameRect, format);
   }
 
   mCurrentFrameIndex = mGIFStruct.images_decoded;
 
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  if (mDownscaler) {
+    rv = mDownscaler->BeginFrame(frameRect.Size(), mImageData,
+                                 mGIFStruct.is_transparent);
+  }
+
   return rv;
 }
 
 
 //******************************************************************************
 void
 nsGIFDecoder2::EndImageFrame()
 {
@@ -232,20 +312,25 @@ nsGIFDecoder2::EndImageFrame()
     // Only need to flush first frame
     FlushImageData();
 
     // If the first frame is smaller in height than the entire image, send an
     // invalidation for the area it does not have data for.
     // This will clear the remaining bits of the placeholder. (Bug 37589)
     const uint32_t realFrameHeight = mGIFStruct.height + mGIFStruct.y_offset;
     if (realFrameHeight < mGIFStruct.screen_height) {
-      nsIntRect r(0, realFrameHeight,
-                  mGIFStruct.screen_width,
-                  mGIFStruct.screen_height - realFrameHeight);
-      PostInvalidation(r);
+      if (mDownscaler) {
+        IntRect targetRect = IntRect(IntPoint(), mDownscaler->TargetSize());
+        PostInvalidation(IntRect(IntPoint(), GetSize()), Some(targetRect));
+      } else {
+        nsIntRect r(0, realFrameHeight,
+                    mGIFStruct.screen_width,
+                    mGIFStruct.screen_height - realFrameHeight);
+        PostInvalidation(r);
+      }
     }
 
     // The first frame was preallocated with alpha; if it wasn't transparent, we
     // should fix that. We can also mark it opaque unconditionally if we didn't
     // actually see any transparent pixels - this test is only valid for the
     // first frame.
     if (!mGIFStruct.is_transparent || !mSawTransparency) {
       opacity = Opacity::OPAQUE;
@@ -286,18 +371,21 @@ nsGIFDecoder2::EndImageFrame()
 }
 
 
 //******************************************************************************
 // Send the data to the display front-end.
 uint32_t
 nsGIFDecoder2::OutputRow()
 {
-  int drow_start, drow_end;
-  drow_start = drow_end = mGIFStruct.irow;
+  // Initialize the region in which we're duplicating rows (for the
+  // Haeberli-inspired hack below) to |irow|, which is the row we're writing to
+  // now.
+  int drow_start = mGIFStruct.irow;
+  int drow_end = mGIFStruct.irow;
 
   // Protect against too much image data
   if ((unsigned)drow_start >= mGIFStruct.height) {
     NS_WARNING("GIF2.cpp::OutputRow - too much image data");
     return 0;
   }
 
   if (!mGIFStruct.images_decoded) {
@@ -324,18 +412,17 @@ nsGIFDecoder2::OutputRow()
         drow_start = 0;
       }
       if ((unsigned)drow_end >= mGIFStruct.height) {
         drow_end = mGIFStruct.height - 1;
       }
     }
 
     // Row to process
-    const uint32_t bpr = sizeof(uint32_t) * mGIFStruct.width;
-    uint8_t* rowp = mImageData + (mGIFStruct.irow * bpr);
+    uint8_t* rowp = GetCurrentRowBuffer();
 
     // Convert color indices to Cairo pixels
     uint8_t* from = rowp + mGIFStruct.width;
     uint32_t* to = ((uint32_t*)rowp) + mGIFStruct.width;
     uint32_t* cmap = mColormap;
     for (uint32_t c = mGIFStruct.width; c > 0; c--) {
       *--to = cmap[*--from];
     }
@@ -346,46 +433,70 @@ nsGIFDecoder2::OutputRow()
       for (uint32_t i = mGIFStruct.width; i > 0; i--) {
         if (*rgb++ == 0) {
           mSawTransparency = true;
           break;
         }
       }
     }
 
-    // Duplicate rows
+    // If we're downscaling but not deinterlacing, we're done with this row and
+    // can commit it now. Otherwise, we'll let Deinterlacer do the committing
+    // when we call PropagatePassToDownscaler() at the end of this pass.
+    if (mDownscaler && !mDeinterlacer) {
+      mDownscaler->CommitRow();
+    }
+
     if (drow_end > drow_start) {
-      // irow is the current row filled
+      // Duplicate rows if needed to reduce the "venetian blind" effect mentioned
+      // above. This writes out scanlines of the image in a way that isn't ordered
+      // vertically, which is incompatible with the filter that we use for
+      // downscale-during-decode, so we can't do this if we're downscaling.
+      MOZ_ASSERT_IF(mDownscaler, mDeinterlacer);
+      const uint32_t bpr = sizeof(uint32_t) * mGIFStruct.width;
       for (int r = drow_start; r <= drow_end; r++) {
+        // Skip the row we wrote to above; that's what we're copying *from*.
         if (r != int(mGIFStruct.irow)) {
-          memcpy(mImageData + (r * bpr), rowp, bpr);
+          memcpy(GetRowBuffer(r), rowp, bpr);
         }
       }
     }
   }
 
   mCurrentRow = drow_end;
   mCurrentPass = mGIFStruct.ipass;
   if (mGIFStruct.ipass == 1) {
     mLastFlushedPass = mGIFStruct.ipass;   // interlaced starts at 1
   }
 
   if (!mGIFStruct.interlaced) {
+    MOZ_ASSERT(!mDeinterlacer);
     mGIFStruct.irow++;
   } else {
     static const uint8_t kjump[5] = { 1, 8, 8, 4, 2 };
+    int currentPass = mGIFStruct.ipass;
+
     do {
       // Row increments resp. per 8,8,4,2 rows
       mGIFStruct.irow += kjump[mGIFStruct.ipass];
       if (mGIFStruct.irow >= mGIFStruct.height) {
         // Next pass starts resp. at row 4,2,1,0
         mGIFStruct.irow = 8 >> mGIFStruct.ipass;
         mGIFStruct.ipass++;
       }
     } while (mGIFStruct.irow >= mGIFStruct.height);
+
+    // We've finished a pass. If we're downscaling, it's time to propagate the
+    // rows we've decoded so far from our Deinterlacer to our Downscaler.
+    if (mGIFStruct.ipass > currentPass && mDownscaler) {
+      MOZ_ASSERT(mDeinterlacer);
+      mDeinterlacer->PropagatePassToDownscaler(*mDownscaler);
+      FlushImageData();
+      mDownscaler->ResetForNextProgressivePass();
+    }
   }
 
   return --mGIFStruct.rows_remaining;
 }
 
 //******************************************************************************
 // Perform Lempel-Ziv-Welch decoding
 bool
@@ -408,27 +519,23 @@ nsGIFDecoder2::DoLzw(const uint8_t* q)
   uint8_t firstchar = mGIFStruct.firstchar;
   int32_t datum     = mGIFStruct.datum;
   uint16_t* prefix  = mGIFStruct.prefix;
   uint8_t* stackp   = mGIFStruct.stackp;
   uint8_t* suffix   = mGIFStruct.suffix;
   uint8_t* stack    = mGIFStruct.stack;
   uint8_t* rowp     = mGIFStruct.rowp;
 
-  uint32_t bpr = mGIFStruct.width;
-  if (!mGIFStruct.images_decoded) {
-    bpr *= sizeof(uint32_t);
-  }
-  uint8_t* rowend   = mImageData + (bpr * mGIFStruct.irow) + mGIFStruct.width;
+  uint8_t* rowend = GetCurrentRowBuffer() + mGIFStruct.width;
 
 #define OUTPUT_ROW()                                        \
   PR_BEGIN_MACRO                                            \
     if (!OutputRow())                                       \
       goto END;                                             \
-    rowp = mImageData + mGIFStruct.irow * bpr;              \
+    rowp = GetCurrentRowBuffer();                           \
     rowend = rowp + mGIFStruct.width;                       \
   PR_END_MACRO
 
   for (const uint8_t* ch = q; count-- > 0; ch++) {
     // Feed the next byte into the decoder's 32-bit input buffer.
     datum += ((int32_t)* ch) << bits;
     bits += 8;
 
@@ -929,22 +1036,29 @@ nsGIFDecoder2::WriteInternal(const char*
       if (mGIFStruct.images_decoded == 1) {
         if (!HasAnimation()) {
           // We should've already called PostIsAnimated(); this must be a
           // corrupt animated image with a first frame timeout of zero. Signal
           // that we're animated now, before the first-frame decode early exit
           // below, so that RasterImage can detect that this happened.
           PostIsAnimated(/* aFirstFrameTimeout = */ 0);
         }
+
         if (IsFirstFrameDecode()) {
           // We're about to get a second frame, but we only want the first. Stop
           // decoding now.
           mGIFStruct.state = gif_done;
           break;
         }
+
+        if (mDownscaler) {
+          MOZ_ASSERT_UNREACHABLE("Doing downscale-during-decode "
+                                 "for an animated image?");
+          mDownscaler.reset();
+        }
       }
 
       // Get image offsets, with respect to the screen origin
       mGIFStruct.x_offset = GETINT16(q);
       mGIFStruct.y_offset = GETINT16(q + 2);
 
       // Get image width and height.
       mGIFStruct.width  = GETINT16(q + 4);
@@ -1020,36 +1134,44 @@ nsGIFDecoder2::WriteInternal(const char*
         memset(mColormap, 0, mColormapSize);
       }
 
       if (!mGIFStruct.images_decoded) {
         // Send a onetime invalidation for the first frame if it has a y-axis
         // offset. Otherwise, the area may never be refreshed and the
         // placeholder will remain on the screen. (Bug 37589)
         if (mGIFStruct.y_offset > 0) {
-          nsIntRect r(0, 0, mGIFStruct.screen_width, mGIFStruct.y_offset);
-          PostInvalidation(r);
+          if (mDownscaler) {
+            IntRect targetRect = IntRect(IntPoint(), mDownscaler->TargetSize());
+            PostInvalidation(IntRect(IntPoint(), GetSize()), Some(targetRect));
+          } else {
+            nsIntRect r(0, 0, mGIFStruct.screen_width, mGIFStruct.y_offset);
+            PostInvalidation(r);
+          }
         }
       }
 
       if (q[8] & 0x40) {
         mGIFStruct.interlaced = true;
         mGIFStruct.ipass = 1;
+        if (mDownscaler) {
+          mDeinterlacer.emplace(mDownscaler->OriginalSize());
+        }
       } else {
         mGIFStruct.interlaced = false;
         mGIFStruct.ipass = 0;
       }
 
       // Only apply the Haeberli display hack on the first frame
       mGIFStruct.progressive_display = (mGIFStruct.images_decoded == 0);
 
       // Clear state from last image
       mGIFStruct.irow = 0;
       mGIFStruct.rows_remaining = mGIFStruct.height;
-      mGIFStruct.rowp = mImageData;
+      mGIFStruct.rowp = GetCurrentRowBuffer();
 
       // Depth of colors is determined by colormap
       // (q[8] & 0x80) indicates local colormap
       // bits per pixel is (q[8]&0x07 + 1) when local colormap is set
       uint32_t depth = mGIFStruct.global_colormap_depth;
       if (q[8] & 0x80) {
         depth = (q[8]&0x07) + 1;
       }
--- a/image/decoders/nsGIFDecoder2.h
+++ b/image/decoders/nsGIFDecoder2.h
@@ -3,42 +3,48 @@
  * 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_image_decoders_nsGIFDecoder2_h
 #define mozilla_image_decoders_nsGIFDecoder2_h
 
 #include "Decoder.h"
+#include "Downscaler.h"
 
 #include "GIF2.h"
 #include "nsCOMPtr.h"
 
 namespace mozilla {
 namespace image {
 class RasterImage;
 
 //////////////////////////////////////////////////////////////////////
 // nsGIFDecoder2 Definition
 
 class nsGIFDecoder2 : public Decoder
 {
 public:
   ~nsGIFDecoder2();
 
+  nsresult SetTargetSize(const nsIntSize& aSize) override;
+
   virtual void WriteInternal(const char* aBuffer, uint32_t aCount) override;
   virtual void FinishInternal() override;
   virtual Telemetry::ID SpeedHistogram() override;
 
 private:
   friend class DecoderFactory;
 
   // Decoders should only be instantiated via DecoderFactory.
   explicit nsGIFDecoder2(RasterImage* aImage);
 
+  uint8_t*  GetCurrentRowBuffer();
+  uint8_t*  GetRowBuffer(uint32_t aRow);
+
   // These functions will be called when the decoder has a decoded row,
   // frame size information, etc.
   void      BeginGIF();
   nsresult  BeginImageFrame(uint16_t aDepth);
   void      EndImageFrame();
   void      FlushImageData();
   void      FlushImageData(uint32_t fromRow, uint32_t rows);
 
@@ -62,14 +68,16 @@ private:
 
   uint8_t mCurrentPass;
   uint8_t mLastFlushedPass;
   uint8_t mColorMask;        // Apply this to the pixel to keep within colormap
   bool mGIFOpen;
   bool mSawTransparency;
 
   gif_struct mGIFStruct;
+  Maybe<Downscaler> mDownscaler;
+  Maybe<Deinterlacer> mDeinterlacer;
 };
 
 } // namespace image
 } // namespace mozilla
 
 #endif // mozilla_image_decoders_nsGIFDecoder2_h
--- a/ipc/glue/SharedMemoryBasic_mach.cpp
+++ b/ipc/glue/SharedMemoryBasic_mach.cpp
@@ -5,16 +5,18 @@
  * 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 <map>
 
 #include <mach/vm_map.h>
 #include <mach/mach_port.h>
 #include <mach/mach_vm.h>
+#include <pthread.h>
+#include <unistd.h>
 #include "SharedMemoryBasic.h"
 #include "chrome/common/mach_ipc_mac.h"
 
 #include "mozilla/StaticMutex.h"
 
 #ifdef DEBUG
 #define LOG_ERROR(str, args...)                 \
   PR_BEGIN_MACRO                                \
--- a/js/ipc/JavaScriptBase.h
+++ b/js/ipc/JavaScriptBase.h
@@ -61,19 +61,18 @@ class JavaScriptBase : public WrapperOwn
     bool RecvHas(const uint64_t& objId, const JSIDVariant& id,
                    ReturnStatus* rs, bool* bp) {
         return Answer::RecvHas(ObjectId::deserialize(objId), id, rs, bp);
     }
     bool RecvHasOwn(const uint64_t& objId, const JSIDVariant& id,
                       ReturnStatus* rs, bool* bp) {
         return Answer::RecvHasOwn(ObjectId::deserialize(objId), id, rs, bp);
     }
-    bool RecvGet(const uint64_t& objId, const ObjectVariant& receiverVar,
-                   const JSIDVariant& id,
-                   ReturnStatus* rs, JSVariant* result) {
+    bool RecvGet(const uint64_t& objId, const JSVariant& receiverVar, const JSIDVariant& id,
+                 ReturnStatus* rs, JSVariant* result) {
         return Answer::RecvGet(ObjectId::deserialize(objId), receiverVar, id, rs, result);
     }
     bool RecvSet(const uint64_t& objId, const JSIDVariant& id, const JSVariant& value,
                  const JSVariant& receiverVar, ReturnStatus* rs) {
         return Answer::RecvSet(ObjectId::deserialize(objId), id, value, receiverVar, rs);
     }
 
     bool RecvIsExtensible(const uint64_t& objId, ReturnStatus* rs,
@@ -150,18 +149,17 @@ class JavaScriptBase : public WrapperOwn
     bool SendHas(const ObjectId& objId, const JSIDVariant& id,
                    ReturnStatus* rs, bool* bp) {
         return Base::SendHas(objId.serialize(), id, rs, bp);
     }
     bool SendHasOwn(const ObjectId& objId, const JSIDVariant& id,
                     ReturnStatus* rs, bool* bp) {
         return Base::SendHasOwn(objId.serialize(), id, rs, bp);
     }
-    bool SendGet(const ObjectId& objId, const ObjectVariant& receiverVar,
-                 const JSIDVariant& id,
+    bool SendGet(const ObjectId& objId, const JSVariant& receiverVar, const JSIDVariant& id,
                  ReturnStatus* rs, JSVariant* result) {
         return Base::SendGet(objId.serialize(), receiverVar, id, rs, result);
     }
     bool SendSet(const ObjectId& objId, const JSIDVariant& id, const JSVariant& value,
                  const JSVariant& receiverVar, ReturnStatus* rs) {
         return Base::SendSet(objId.serialize(), id, value, receiverVar, rs);
     }
 
--- a/js/ipc/PJavaScript.ipdl
+++ b/js/ipc/PJavaScript.ipdl
@@ -27,17 +27,17 @@ both:
     prio(high) sync PreventExtensions(uint64_t objId) returns (ReturnStatus rs);
     prio(high) sync GetPropertyDescriptor(uint64_t objId, JSIDVariant id) returns (ReturnStatus rs, PPropertyDescriptor result);
     prio(high) sync GetOwnPropertyDescriptor(uint64_t objId, JSIDVariant id) returns (ReturnStatus rs, PPropertyDescriptor result);
     prio(high) sync DefineProperty(uint64_t objId, JSIDVariant id, PPropertyDescriptor descriptor) returns (ReturnStatus rs);
     prio(high) sync Delete(uint64_t objId, JSIDVariant id) returns (ReturnStatus rs);
 
     prio(high) sync Has(uint64_t objId, JSIDVariant id) returns (ReturnStatus rs, bool has);
     prio(high) sync HasOwn(uint64_t objId, JSIDVariant id) returns (ReturnStatus rs, bool has);
-    prio(high) sync Get(uint64_t objId, ObjectVariant receiver, JSIDVariant id) returns (ReturnStatus rs, JSVariant result);
+    prio(high) sync Get(uint64_t objId, JSVariant receiver, JSIDVariant id) returns (ReturnStatus rs, JSVariant result);
     prio(high) sync Set(uint64_t objId, JSIDVariant id, JSVariant value, JSVariant receiver) returns (ReturnStatus rs);
 
     prio(high) sync IsExtensible(uint64_t objId) returns (ReturnStatus rs, bool result);
     prio(high) sync CallOrConstruct(uint64_t objId, JSParam[] argv, bool construct) returns (ReturnStatus rs, JSVariant result, JSParam[] outparams);
     prio(high) sync HasInstance(uint64_t objId, JSVariant v) returns (ReturnStatus rs, bool has);
     prio(high) sync ObjectClassIs(uint64_t objId, uint32_t classValue) returns (bool result);
     prio(high) sync ClassName(uint64_t objId) returns (nsCString name);
     prio(high) sync GetPrototype(uint64_t objId) returns (ReturnStatus rs, ObjectOrNullVariant result);
--- a/js/ipc/WrapperAnswer.cpp
+++ b/js/ipc/WrapperAnswer.cpp
@@ -267,35 +267,35 @@ WrapperAnswer::RecvHasOwn(const ObjectId
         return fail(jsapi, rs);
 
     if (!JS_HasOwnPropertyById(cx, obj, id, foundp))
         return fail(jsapi, rs);
     return ok(rs);
 }
 
 bool
-WrapperAnswer::RecvGet(const ObjectId& objId, const ObjectVariant& receiverVar,
+WrapperAnswer::RecvGet(const ObjectId& objId, const JSVariant& receiverVar,
                        const JSIDVariant& idVar, ReturnStatus* rs, JSVariant* result)
 {
     // We may run scripted getters.
     AutoEntryScript aes(xpc::NativeGlobal(scopeForTargetObjects()),
                         "Cross-Process Object Wrapper 'get'");
     aes.TakeOwnershipOfErrorReporting();
     JSContext* cx = aes.cx();
 
     // The outparam will be written to the buffer, so it must be set even if
     // the parent won't read it.
     *result = UndefinedVariant();
 
     RootedObject obj(cx, findObjectById(cx, objId));
     if (!obj)
         return fail(aes, rs);
 
-    RootedObject receiver(cx, fromObjectVariant(cx, receiverVar));
-    if (!receiver)
+    RootedValue receiver(cx);
+    if (!fromVariant(cx, receiverVar, &receiver))
         return fail(aes, rs);
 
     RootedId id(cx);
     if (!fromJSIDVariant(cx, idVar, &id))
         return fail(aes, rs);
 
     JS::RootedValue val(cx);
     if (!JS_ForwardGetPropertyTo(cx, obj, id, receiver, &val))
--- a/js/ipc/WrapperAnswer.h
+++ b/js/ipc/WrapperAnswer.h
@@ -34,17 +34,17 @@ class WrapperAnswer : public virtual Jav
     bool RecvDefineProperty(const ObjectId& objId, const JSIDVariant& id,
                             const PPropertyDescriptor& flags, ReturnStatus* rs);
     bool RecvDelete(const ObjectId& objId, const JSIDVariant& id, ReturnStatus* rs);
 
     bool RecvHas(const ObjectId& objId, const JSIDVariant& id,
                  ReturnStatus* rs, bool* foundp);
     bool RecvHasOwn(const ObjectId& objId, const JSIDVariant& id,
                     ReturnStatus* rs, bool* foundp);
-    bool RecvGet(const ObjectId& objId, const ObjectVariant& receiverVar,
+    bool RecvGet(const ObjectId& objId, const JSVariant& receiverVar,
                  const JSIDVariant& id,
                  ReturnStatus* rs, JSVariant* result);
     bool RecvSet(const ObjectId& objId, const JSIDVariant& id, const JSVariant& value,
                  const JSVariant& receiverVar, ReturnStatus* rs);
 
     bool RecvIsExtensible(const ObjectId& objId, ReturnStatus* rs,
                           bool* result);
     bool RecvCallOrConstruct(const ObjectId& objId, InfallibleTArray<JSParam>&& argv,
--- a/js/ipc/WrapperOwner.cpp
+++ b/js/ipc/WrapperOwner.cpp
@@ -106,17 +106,17 @@ class CPOWProxyHandler : public BaseProx
                                  AutoIdVector& props) const override;
     virtual bool delete_(JSContext* cx, HandleObject proxy, HandleId id,
                          ObjectOpResult& result) const override;
     virtual bool enumerate(JSContext* cx, HandleObject proxy, MutableHandleObject objp) const override;
     virtual bool preventExtensions(JSContext* cx, HandleObject proxy,
                                    ObjectOpResult& result) const override;
     virtual bool isExtensible(JSContext* cx, HandleObject proxy, bool* extensible) const override;
     virtual bool has(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) const override;
-    virtual bool get(JSContext* cx, HandleObject proxy, HandleObject receiver,
+    virtual bool get(JSContext* cx, HandleObject proxy, HandleValue receiver,
                      HandleId id, MutableHandleValue vp) const override;
     virtual bool set(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, JS::HandleValue v,
                      JS::HandleValue receiver, JS::ObjectOpResult& result) const override;
     virtual bool call(JSContext* cx, HandleObject proxy, const CallArgs& args) const override;
     virtual bool construct(JSContext* cx, HandleObject proxy, const CallArgs& args) const override;
 
     virtual bool getPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
                                        MutableHandle<JSPropertyDescriptor> desc) const override;
@@ -336,17 +336,17 @@ WrapperOwner::hasOwn(JSContext* cx, Hand
         return ipcfail(cx);
 
     LOG_STACK();
 
     return !!ok(cx, status);
 }
 
 bool
-CPOWProxyHandler::get(JSContext* cx, HandleObject proxy, HandleObject receiver,
+CPOWProxyHandler::get(JSContext* cx, HandleObject proxy, HandleValue receiver,
                       HandleId id, MutableHandleValue vp) const
 {
     FORWARD(get, (cx, proxy, receiver, id, vp));
 }
 
 static bool
 CPOWDOMQI(JSContext* cx, unsigned argc, Value* vp)
 {
@@ -453,23 +453,23 @@ WrapperOwner::DOMQI(JSContext* cx, JS::H
     if (!propDesc.value().isObject()) {
         MOZ_ASSERT_UNREACHABLE("We didn't get QueryInterface off a node");
         return Throw(cx, NS_ERROR_UNEXPECTED);
     }
     return JS_CallFunctionValue(cx, proxy, propDesc.value(), args, args.rval());
 }
 
 bool
-WrapperOwner::get(JSContext* cx, HandleObject proxy, HandleObject receiver,
+WrapperOwner::get(JSContext* cx, HandleObject proxy, HandleValue receiver,
                   HandleId id, MutableHandleValue vp)
 {
     ObjectId objId = idOf(proxy);
 
-    ObjectVariant receiverVar;
-    if (!toObjectVariant(cx, receiver, &receiverVar))
+    JSVariant receiverVar;
+    if (!toVariant(cx, receiver, &receiverVar))
         return false;
 
     JSIDVariant idVar;
     if (!toJSIDVariant(cx, id, &idVar))
         return false;
 
     AuxCPOWData* data = AuxCPOWDataOf(proxy);
     if (data->isDOMObject &&
--- a/js/ipc/WrapperOwner.h
+++ b/js/ipc/WrapperOwner.h
@@ -35,17 +35,17 @@ class WrapperOwner : public virtual Java
                         JS::Handle<JSPropertyDescriptor> desc,
                         JS::ObjectOpResult& result);
     bool ownPropertyKeys(JSContext* cx, JS::HandleObject proxy, JS::AutoIdVector& props);
     bool delete_(JSContext* cx, JS::HandleObject proxy, JS::HandleId id,
                  JS::ObjectOpResult& result);
     bool preventExtensions(JSContext* cx, JS::HandleObject proxy, JS::ObjectOpResult& result);
     bool isExtensible(JSContext* cx, JS::HandleObject proxy, bool* extensible);
     bool has(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, bool* bp);
-    bool get(JSContext* cx, JS::HandleObject proxy, JS::HandleObject receiver,
+    bool get(JSContext* cx, JS::HandleObject proxy, JS::HandleValue receiver,
              JS::HandleId id, JS::MutableHandleValue vp);
     bool set(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, JS::HandleValue v,
              JS::HandleValue receiver, JS::ObjectOpResult& result);
     bool callOrConstruct(JSContext* cx, JS::HandleObject proxy, const JS::CallArgs& args,
                          bool construct);
 
     // SpiderMonkey extensions.
     bool getPropertyDescriptor(JSContext* cx, JS::HandleObject proxy, JS::HandleId id,
@@ -120,17 +120,17 @@ class WrapperOwner : public virtual Java
                                     ReturnStatus* rs) = 0;
     virtual bool SendDelete(const ObjectId& objId, const JSIDVariant& id,
                             ReturnStatus* rs) = 0;
 
     virtual bool SendHas(const ObjectId& objId, const JSIDVariant& id,
                          ReturnStatus* rs, bool* bp) = 0;
     virtual bool SendHasOwn(const ObjectId& objId, const JSIDVariant& id,
                             ReturnStatus* rs, bool* bp) = 0;
-    virtual bool SendGet(const ObjectId& objId, const ObjectVariant& receiverVar,
+    virtual bool SendGet(const ObjectId& objId, const JSVariant& receiverVar,
                          const JSIDVariant& id,
                          ReturnStatus* rs, JSVariant* result) = 0;
     virtual bool SendSet(const ObjectId& objId, const JSIDVariant& id, const JSVariant& value,
                          const JSVariant& receiverVar, ReturnStatus* rs) = 0;
 
     virtual bool SendIsExtensible(const ObjectId& objId, ReturnStatus* rs,
                                   bool* result) = 0;
     virtual bool SendCallOrConstruct(const ObjectId& objId, const nsTArray<JSParam>& argv,
--- a/js/public/Class.h
+++ b/js/public/Class.h
@@ -353,17 +353,17 @@ typedef bool
                      JS::MutableHandleObject objp, JS::MutableHandle<Shape*> propp);
 typedef bool
 (* DefinePropertyOp)(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
                      JS::Handle<JSPropertyDescriptor> desc,
                      JS::ObjectOpResult& result);
 typedef bool
 (* HasPropertyOp)(JSContext* cx, JS::HandleObject obj, JS::HandleId id, bool* foundp);
 typedef bool
-(* GetPropertyOp)(JSContext* cx, JS::HandleObject obj, JS::HandleObject receiver, JS::HandleId id,
+(* GetPropertyOp)(JSContext* cx, JS::HandleObject obj, JS::HandleValue receiver, JS::HandleId id,
                   JS::MutableHandleValue vp);
 typedef bool
 (* SetPropertyOp)(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::HandleValue v,
                   JS::HandleValue receiver, JS::ObjectOpResult& result);
 typedef bool
 (* GetOwnPropertyOp)(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
                      JS::MutableHandle<JSPropertyDescriptor> desc);
 typedef bool
--- a/js/public/Proxy.h
+++ b/js/public/Proxy.h
@@ -288,17 +288,17 @@ class JS_FRIEND_API(BaseProxyHandler)
     /*
      * These standard internal methods are implemented, as a convenience, so
      * that ProxyHandler subclasses don't have to provide every single method.
      *
      * The base-class implementations work by calling getPropertyDescriptor().
      * They do not follow any standard. When in doubt, override them.
      */
     virtual bool has(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) const;
-    virtual bool get(JSContext* cx, HandleObject proxy, HandleObject receiver,
+    virtual bool get(JSContext* cx, HandleObject proxy, HandleValue receiver,
                      HandleId id, MutableHandleValue vp) const;
     virtual bool set(JSContext* cx, HandleObject proxy, HandleId id, HandleValue v,
                      HandleValue receiver, ObjectOpResult& result) const;
 
     /*
      * [[Call]] and [[Construct]] are standard internal methods but according
      * to the spec, they are not present on every object.
      *
@@ -389,17 +389,17 @@ class JS_FRIEND_API(DirectProxyHandler) 
                               ObjectOpResult& result) const override;
     virtual bool setImmutablePrototype(JSContext* cx, HandleObject proxy,
                                        bool* succeeded) const override;
     virtual bool preventExtensions(JSContext* cx, HandleObject proxy,
                                    ObjectOpResult& result) const override;
     virtual bool isExtensible(JSContext* cx, HandleObject proxy, bool* extensible) const override;
     virtual bool has(JSContext* cx, HandleObject proxy, HandleId id,
                      bool* bp) const override;
-    virtual bool get(JSContext* cx, HandleObject proxy, HandleObject receiver,
+    virtual bool get(JSContext* cx, HandleObject proxy, HandleValue receiver,
                      HandleId id, MutableHandleValue vp) const override;
     virtual bool set(JSContext* cx, HandleObject proxy, HandleId id, HandleValue v,
                      HandleValue receiver, ObjectOpResult& result) const override;
     virtual bool call(JSContext* cx, HandleObject proxy, const CallArgs& args) const override;
     virtual bool construct(JSContext* cx, HandleObject proxy, const CallArgs& args) const override;
 
     /* SpiderMonkey extensions. */
     virtual bool getPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -1788,17 +1788,17 @@ TypedObject::obj_hasProperty(JSContext* 
         *foundp = false;
         return true;
     }
 
     return HasProperty(cx, proto, id, foundp);
 }
 
 bool
-TypedObject::obj_getProperty(JSContext* cx, HandleObject obj, HandleObject receiver,
+TypedObject::obj_getProperty(JSContext* cx, HandleObject obj, HandleValue receiver,
                              HandleId id, MutableHandleValue vp)
 {
     Rooted<TypedObject*> typedObj(cx, &obj->as<TypedObject>());
 
     // Dispatch elements to obj_getElement:
     uint32_t index;
     if (IdIsIndex(id, &index))
         return obj_getElement(cx, obj, receiver, index, vp);
@@ -1845,17 +1845,17 @@ TypedObject::obj_getProperty(JSContext* 
         vp.setUndefined();
         return true;
     }
 
     return GetProperty(cx, proto, receiver, id, vp);
 }
 
 bool
-TypedObject::obj_getElement(JSContext* cx, HandleObject obj, HandleObject receiver,
+TypedObject::obj_getElement(JSContext* cx, HandleObject obj, HandleValue receiver,
                             uint32_t index, MutableHandleValue vp)
 {
     MOZ_ASSERT(obj->is<TypedObject>());
     Rooted<TypedObject*> typedObj(cx, &obj->as<TypedObject>());
     Rooted<TypeDescr*> descr(cx, &typedObj->typeDescr());
 
     switch (descr->kind()) {
       case type::Scalar:
--- a/js/src/builtin/TypedObject.h
+++ b/js/src/builtin/TypedObject.h
@@ -524,20 +524,20 @@ class TypedObject : public JSObject
                                    MutableHandleShape propp);
 
     static bool obj_defineProperty(JSContext* cx, HandleObject obj, HandleId id,
                                    Handle<JSPropertyDescriptor> desc,
                                    ObjectOpResult& result);
 
     static bool obj_hasProperty(JSContext* cx, HandleObject obj, HandleId id, bool* foundp);
 
-    static bool obj_getProperty(JSContext* cx, HandleObject obj, HandleObject receiver,
+    static bool obj_getProperty(JSContext* cx, HandleObject obj, HandleValue receiver,
                                 HandleId id, MutableHandleValue vp);
 
-    static bool obj_getElement(JSContext* cx, HandleObject obj, HandleObject receiver,
+    static bool obj_getElement(JSContext* cx, HandleObject obj, HandleValue receiver,
                                uint32_t index, MutableHandleValue vp);
 
     static bool obj_setProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v,
                                 HandleValue receiver, ObjectOpResult& result);
 
     static bool obj_getOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id,
                                              MutableHandle<JSPropertyDescriptor> desc);
 
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -6864,18 +6864,19 @@ ICGetProp_DOMProxyShadowed::Compiler::ge
     RootedShape shape(cx, proxy_->maybeShape());
     return New<ICGetProp_DOMProxyShadowed>(cx, space, getStubCode(), firstMonitorStub_, shape,
                                            proxy_->handler(), name_, pcOffset_);
 }
 
 static bool
 ProxyGet(JSContext* cx, HandleObject proxy, HandlePropertyName name, MutableHandleValue vp)
 {
+    RootedValue receiver(cx, ObjectValue(*proxy));
     RootedId id(cx, NameToId(name));
-    return Proxy::get(cx, proxy, proxy, id, vp);
+    return Proxy::get(cx, proxy, receiver, id, vp);
 }
 
 typedef bool (*ProxyGetFn)(JSContext* cx, HandleObject proxy, HandlePropertyName name,
                            MutableHandleValue vp);
 static const VMFunction ProxyGetInfo = FunctionInfo<ProxyGetFn>(ProxyGet);
 
 bool
 ICGetProp_DOMProxyShadowed::Compiler::generateStubCode(MacroAssembler& masm)
--- a/js/src/jit/IonCaches.cpp
+++ b/js/src/jit/IonCaches.cpp
@@ -1535,69 +1535,80 @@ static void
 PushObjectOpResult(MacroAssembler& masm)
 {
     static_assert(sizeof(ObjectOpResult) == sizeof(uintptr_t),
                   "ObjectOpResult size must match size reserved by masm.Push() here");
     masm.Push(ImmWord(ObjectOpResult::Uninitialized));
 }
 
 static bool
+ProxyGetProperty(JSContext* cx, HandleObject proxy, HandleId id, MutableHandleValue vp)
+{
+    RootedValue receiver(cx, ObjectValue(*proxy));
+    return Proxy::get(cx, proxy, receiver, id, vp);
+}
+
+static bool
+ProxyCallProperty(JSContext* cx, HandleObject proxy, HandleId id, MutableHandleValue vp)
+{
+    RootedValue receiver(cx, ObjectValue(*proxy));
+    return Proxy::callProp(cx, proxy, receiver, id, vp);
+}
+
+static bool
 EmitCallProxyGet(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& attacher,
                  PropertyName* name, LiveRegisterSet liveRegs, Register object,
                  TypedOrValueRegister output, jsbytecode* pc, void* returnAddr)
 {
     MOZ_ASSERT(output.hasValue());
     MacroAssembler::AfterICSaveLive aic = masm.icSaveLive(liveRegs);
 
     // Remaining registers should be free, but we need to use |object| still
     // so leave it alone.
     AllocatableRegisterSet regSet(RegisterSet::All());
     regSet.take(AnyRegister(object));
 
-    // Proxy::get(JSContext* cx, HandleObject proxy, HandleObject receiver, HandleId id,
-    //            MutableHandleValue vp)
+    // ProxyGetProperty(JSContext* cx, HandleObject proxy, HandleId id,
+    //                  MutableHandleValue vp)
     Register argJSContextReg = regSet.takeAnyGeneral();
     Register argProxyReg     = regSet.takeAnyGeneral();
     Register argIdReg        = regSet.takeAnyGeneral();
     Register argVpReg        = regSet.takeAnyGeneral();
 
     Register scratch         = regSet.takeAnyGeneral();
 
-    void* getFunction = JSOp(*pc) == JSOP_CALLPROP                      ?
-                            JS_FUNC_TO_DATA_PTR(void*, Proxy::callProp) :
-                            JS_FUNC_TO_DATA_PTR(void*, Proxy::get);
+    void* getFunction = JSOp(*pc) == JSOP_CALLPROP                        ?
+                            JS_FUNC_TO_DATA_PTR(void*, ProxyCallProperty) :
+                            JS_FUNC_TO_DATA_PTR(void*, ProxyGetProperty);
 
     // Push stubCode for marking.
     attacher.pushStubCodePointer(masm);
 
     // Push args on stack first so we can take pointers to make handles.
     masm.Push(UndefinedValue());
     masm.moveStackPtrTo(argVpReg);
 
     RootedId propId(cx, AtomToId(name));
     masm.Push(propId, scratch);
     masm.moveStackPtrTo(argIdReg);
 
-    // Pushing object and receiver.  Both are the same, so Handle to one is equivalent to
-    // handle to other.
-    masm.Push(object);
+    // Push the proxy. Also used as receiver.
     masm.Push(object);
     masm.moveStackPtrTo(argProxyReg);
 
     masm.loadJSContext(argJSContextReg);
 
     if (!masm.icBuildOOLFakeExitFrame(returnAddr, aic))
         return false;
     masm.enterFakeExitFrame(IonOOLProxyExitFrameLayoutToken);
 
     // Make the call.
     masm.setupUnalignedABICall(scratch);
     masm.passABIArg(argJSContextReg);
     masm.passABIArg(argProxyReg);
-    masm.passABIArg(argProxyReg);
     masm.passABIArg(argIdReg);
     masm.passABIArg(argVpReg);
     masm.callWithABI(getFunction);
 
     // Test for failure.
     masm.branchIfFalseBool(ReturnReg, masm.exceptionLabel());
 
     // Load the outparam vp[0] into output register(s).
@@ -2279,19 +2290,17 @@ EmitCallProxySet(JSContext* cx, MacroAss
     masm.Push(value);
     masm.moveStackPtrTo(argValueReg);
 
     masm.move32(Imm32(strict), argStrictReg);
 
     masm.Push(propId, scratch);
     masm.moveStackPtrTo(argIdReg);
 
-    // Pushing object and receiver.  Both are the same, so Handle to one is equivalent to
-    // handle to other.
-    masm.Push(object);
+    // Push object.
     masm.Push(object);
     masm.moveStackPtrTo(argProxyReg);
 
     masm.loadJSContext(argJSContextReg);
 
     if (!masm.icBuildOOLFakeExitFrame(returnAddr, aic))
         return false;
     masm.enterFakeExitFrame(IonOOLProxyExitFrameLayoutToken);
--- a/js/src/jit/JitFrames.cpp
+++ b/js/src/jit/JitFrames.cpp
@@ -1396,17 +1396,16 @@ MarkJitExitFrame(JSTracer* trc, const Ji
     }
 
     if (frame.isExitFrameLayout<IonOOLProxyExitFrameLayout>()) {
         IonOOLProxyExitFrameLayout* oolproxy = frame.exitFrame()->as<IonOOLProxyExitFrameLayout>();
         TraceRoot(trc, oolproxy->stubCode(), "ion-ool-proxy-code");
         TraceRoot(trc, oolproxy->vp(), "ion-ool-proxy-vp");
         TraceRoot(trc, oolproxy->id(), "ion-ool-proxy-id");
         TraceRoot(trc, oolproxy->proxy(), "ion-ool-proxy-proxy");
-        TraceRoot(trc, oolproxy->receiver(), "ion-ool-proxy-receiver");
         return;
     }
 
     if (frame.isExitFrameLayout<IonDOMExitFrameLayout>()) {
         IonDOMExitFrameLayout* dom = frame.exitFrame()->as<IonDOMExitFrameLayout>();
         TraceRoot(trc, dom->thisObjAddress(), "ion-dom-args");
         if (dom->isMethodFrame()) {
             IonDOMMethodExitFrameLayout* method =
--- a/js/src/jit/JitFrames.h
+++ b/js/src/jit/JitFrames.h
@@ -694,32 +694,29 @@ class IonOOLSetterOpExitFrameLayout : pu
         return offsetof(IonOOLSetterOpExitFrameLayout, result_);
     }
 
     static size_t Size() {
         return sizeof(IonOOLSetterOpExitFrameLayout);
     }
 };
 
-// Proxy::get(JSContext* cx, HandleObject proxy, HandleObject receiver, HandleId id,
-//            MutableHandleValue vp)
+// ProxyGetProperty(JSContext* cx, HandleObject proxy, HandleId id, MutableHandleValue vp)
+// ProxyCallProperty(JSContext* cx, HandleObject proxy, HandleId id, MutableHandleValue vp)
 // ProxySetProperty(JSContext* cx, HandleObject proxy, HandleId id, MutableHandleValue vp,
 //                  bool strict)
 class IonOOLProxyExitFrameLayout
 {
   protected: // only to silence a clang warning about unused private fields
     ExitFooterFrame footer_;
     ExitFrameLayout exit_;
 
     // The proxy object.
     JSObject* proxy_;
 
-    // Object for HandleObject
-    JSObject* receiver_;
-
     // id for HandleId
     jsid id_;
 
     // space for MutableHandleValue result
     // use two uint32_t so compiler doesn't align.
     uint32_t vp0_;
     uint32_t vp1_;
 
@@ -741,19 +738,16 @@ class IonOOLProxyExitFrameLayout
         return &stubCode_;
     }
     inline Value* vp() {
         return reinterpret_cast<Value*>(&vp0_);
     }
     inline jsid* id() {
         return &id_;
     }
-    inline JSObject** receiver() {
-        return &receiver_;
-    }
     inline JSObject** proxy() {
         return &proxy_;
     }
 };
 
 class IonDOMExitFrameLayout
 {
   protected: // only to silence a clang warning about unused private fields
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -2915,34 +2915,42 @@ JS_GetPropertyDescriptor(JSContext* cx, 
         return false;
     RootedId id(cx, AtomToId(atom));
     return atom && JS_GetPropertyDescriptorById(cx, obj, id, desc);
 }
 
 JS_PUBLIC_API(bool)
 JS_GetPropertyById(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp)
 {
-    return JS_ForwardGetPropertyTo(cx, obj, id, obj, vp);
-}
-
-JS_PUBLIC_API(bool)
-JS_ForwardGetPropertyTo(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::HandleObject onBehalfOf,
-                        JS::MutableHandleValue vp)
+    AssertHeapIsIdle(cx);
+    CHECK_REQUEST(cx);
+    assertSameCompartment(cx, obj, id);
+
+    return GetProperty(cx, obj, obj, id, vp);
+}
+
+JS_PUBLIC_API(bool)
+JS_ForwardGetPropertyTo(JSContext* cx, HandleObject obj, HandleId id, HandleValue onBehalfOf,
+                        MutableHandleValue vp)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj, id, onBehalfOf);
 
     return GetProperty(cx, obj, onBehalfOf, id, vp);
 }
 
 JS_PUBLIC_API(bool)
-JS_GetElement(JSContext* cx, HandleObject objArg, uint32_t index, MutableHandleValue vp)
-{
-    return JS_ForwardGetElementTo(cx, objArg, index, objArg, vp);
+JS_GetElement(JSContext* cx, HandleObject obj, uint32_t index, MutableHandleValue vp)
+{
+    AssertHeapIsIdle(cx);
+    CHECK_REQUEST(cx);
+    assertSameCompartment(cx, obj);
+
+    return GetElement(cx, obj, obj, index, vp);
 }
 
 JS_PUBLIC_API(bool)
 JS_ForwardGetElementTo(JSContext* cx, HandleObject obj, uint32_t index, HandleObject onBehalfOf,
                        MutableHandleValue vp)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -2848,17 +2848,17 @@ JS_GetPropertyDescriptor(JSContext* cx, 
 
 extern JS_PUBLIC_API(bool)
 JS_GetProperty(JSContext* cx, JS::HandleObject obj, const char* name, JS::MutableHandleValue vp);
 
 extern JS_PUBLIC_API(bool)
 JS_GetPropertyById(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::MutableHandleValue vp);
 
 extern JS_PUBLIC_API(bool)
-JS_ForwardGetPropertyTo(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::HandleObject onBehalfOf,
+JS_ForwardGetPropertyTo(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::HandleValue onBehalfOf,
                         JS::MutableHandleValue vp);
 
 extern JS_PUBLIC_API(bool)
 JS_SetProperty(JSContext* cx, JS::HandleObject obj, const char* name, JS::HandleValue v);
 
 extern JS_PUBLIC_API(bool)
 JS_SetPropertyById(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::HandleValue v);
 
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -372,17 +372,17 @@ proxy_LookupProperty(JSContext* cx, JS::
                     JS::MutableHandle<Shape*> propp);
 extern JS_FRIEND_API(bool)
 proxy_DefineProperty(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
                      JS::Handle<JSPropertyDescriptor> desc,
                      JS::ObjectOpResult& result);
 extern JS_FRIEND_API(bool)
 proxy_HasProperty(JSContext* cx, JS::HandleObject obj, JS::HandleId id, bool* foundp);
 extern JS_FRIEND_API(bool)
-proxy_GetProperty(JSContext* cx, JS::HandleObject obj, JS::HandleObject receiver, JS::HandleId id,
+proxy_GetProperty(JSContext* cx, JS::HandleObject obj, JS::HandleValue receiver, JS::HandleId id,
                   JS::MutableHandleValue vp);
 extern JS_FRIEND_API(bool)
 proxy_SetProperty(JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::HandleValue bp,
                   JS::HandleValue receiver, JS::ObjectOpResult& result);
 extern JS_FRIEND_API(bool)
 proxy_GetOwnPropertyDescriptor(JSContext* cx, JS::HandleObject obj, JS::HandleId id,
                                JS::MutableHandle<JSPropertyDescriptor> desc);
 extern JS_FRIEND_API(bool)
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -814,41 +814,76 @@ HasProperty(JSContext* cx, HandleObject 
  * ES6 [[Get]]. Get the value of the property `obj[id]`, or undefined if no
  * such property exists.
  *
  * Typically obj == receiver; if obj != receiver then the caller is most likely
  * a proxy using GetProperty to finish a property get that started out as
  * `receiver[id]`, and we've already searched the prototype chain up to `obj`.
  */
 inline bool
-GetProperty(JSContext* cx, HandleObject obj, HandleObject receiver, HandleId id,
+GetProperty(JSContext* cx, HandleObject obj, HandleValue receiver, HandleId id,
             MutableHandleValue vp);
 
 inline bool
-GetProperty(JSContext* cx, HandleObject obj, HandleObject receiver, PropertyName* name,
+GetProperty(JSContext* cx, HandleObject obj, HandleValue receiver, PropertyName* name,
             MutableHandleValue vp)
 {
     RootedId id(cx, NameToId(name));
     return GetProperty(cx, obj, receiver, id, vp);
 }
 
 inline bool
+GetProperty(JSContext* cx, HandleObject obj, HandleObject receiver, HandleId id,
+            MutableHandleValue vp)
+{
+    RootedValue receiverValue(cx, ObjectValue(*receiver));
+    return GetProperty(cx, obj, receiverValue, id, vp);
+}
+
+inline bool
+GetProperty(JSContext* cx, HandleObject obj, HandleObject receiver, PropertyName* name,
+            MutableHandleValue vp)
+{
+    RootedValue receiverValue(cx, ObjectValue(*receiver));
+    return GetProperty(cx, obj, receiverValue, name, vp);
+}
+
+inline bool
+GetElement(JSContext* cx, HandleObject obj, HandleValue receiver, uint32_t index,
+           MutableHandleValue vp);
+
+inline bool
 GetElement(JSContext* cx, HandleObject obj, HandleObject receiver, uint32_t index,
            MutableHandleValue vp);
 
 inline bool
-GetPropertyNoGC(JSContext* cx, JSObject* obj, JSObject* receiver, jsid id, Value* vp);
+GetPropertyNoGC(JSContext* cx, JSObject* obj, const Value& receiver, jsid id, Value* vp);
+
+inline bool
+GetPropertyNoGC(JSContext* cx, JSObject* obj, JSObject* receiver, jsid id, Value* vp)
+{
+    return GetPropertyNoGC(cx, obj, ObjectValue(*receiver), id, vp);
+}
+
+inline bool
+GetPropertyNoGC(JSContext* cx, JSObject* obj, const Value& receiver, PropertyName* name, Value* vp)
+{
+    return GetPropertyNoGC(cx, obj, receiver, NameToId(name), vp);
+}
 
 inline bool
 GetPropertyNoGC(JSContext* cx, JSObject* obj, JSObject* receiver, PropertyName* name, Value* vp)
 {
-    return GetPropertyNoGC(cx, obj, receiver, NameToId(name), vp);
+    return GetPropertyNoGC(cx, obj, ObjectValue(*receiver), name, vp);
 }
 
 inline bool
+GetElementNoGC(JSContext* cx, JSObject* obj, const Value& receiver, uint32_t index, Value* vp);
+
+inline bool
 GetElementNoGC(JSContext* cx, JSObject* obj, JSObject* receiver, uint32_t index, Value* vp);
 
 /*
  * ES6 [[Set]]. Carry out the assignment `obj[id] = v`.
  *
  * The `receiver` argument has to do with how [[Set]] interacts with the
  * prototype chain and proxies. It's hard to explain and ES6 doesn't really
  * try. Long story short, if you just want bog-standard assignment, pass
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -175,37 +175,51 @@ js::IsExtensible(ExclusiveContext* cx, H
 inline bool
 js::HasProperty(JSContext* cx, HandleObject obj, PropertyName* name, bool* found)
 {
     RootedId id(cx, NameToId(name));
     return HasProperty(cx, obj, id, found);
 }
 
 inline bool
-js::GetElement(JSContext* cx, HandleObject obj, HandleObject receiver, uint32_t index,
+js::GetElement(JSContext* cx, HandleObject obj, HandleValue receiver, uint32_t index,
                MutableHandleValue vp)
 {
     RootedId id(cx);
     if (!IndexToId(cx, index, &id))
         return false;
     return GetProperty(cx, obj, receiver, id, vp);
 }
 
 inline bool
-js::GetElementNoGC(JSContext* cx, JSObject* obj, JSObject* receiver, uint32_t index, Value* vp)
+js::GetElement(JSContext* cx, HandleObject obj, HandleObject receiver, uint32_t index,
+               MutableHandleValue vp)
+{
+    RootedValue receiverValue(cx, ObjectValue(*receiver));
+    return GetElement(cx, obj, receiverValue, index, vp);
+}
+
+inline bool
+js::GetElementNoGC(JSContext* cx, JSObject* obj, const Value& receiver, uint32_t index, Value* vp)
 {
     if (obj->getOps()->getProperty)
         return false;
 
     if (index > JSID_INT_MAX)
         return false;
     return GetPropertyNoGC(cx, obj, receiver, INT_TO_JSID(index), vp);
 }
 
 inline bool
+js::GetElementNoGC(JSContext* cx, JSObject* obj, JSObject* receiver, uint32_t index, Value* vp)
+{
+    return GetElementNoGC(cx, obj, ObjectValue(*receiver), index, vp);
+}
+
+inline bool
 js::DeleteProperty(JSContext* cx, HandleObject obj, HandleId id, ObjectOpResult& result)
 {
     MarkTypePropertyNonData(cx, obj, id);
     if (DeletePropertyOp op = obj->getOps()->deleteProperty)
         return op(cx, obj, id, result);
     return NativeDeleteProperty(cx, obj.as<NativeObject>(), id, result);
 }
 
--- a/js/src/jswrapper.h
+++ b/js/src/jswrapper.h
@@ -128,17 +128,17 @@ class JS_FRIEND_API(CrossCompartmentWrap
                               ObjectOpResult& result) const override;
 
     virtual bool setImmutablePrototype(JSContext* cx, HandleObject proxy,
                                        bool* succeeded) const override;
     virtual bool preventExtensions(JSContext* cx, HandleObject wrapper,
                                    ObjectOpResult& result) const override;
     virtual bool isExtensible(JSContext* cx, HandleObject wrapper, bool* extensible) const override;
     virtual bool has(JSContext* cx, HandleObject wrapper, HandleId id, bool* bp) const override;
-    virtual bool get(JSContext* cx, HandleObject wrapper, HandleObject receiver,
+    virtual bool get(JSContext* cx, HandleObject wrapper, HandleValue receiver,
                      HandleId id, MutableHandleValue vp) const override;
     virtual bool set(JSContext* cx, HandleObject wrapper, HandleId id, HandleValue v,
                      HandleValue receiver, ObjectOpResult& result) const override;
     virtual bool call(JSContext* cx, HandleObject wrapper, const CallArgs& args) const override;
     virtual bool construct(JSContext* cx, HandleObject wrapper, const CallArgs& args) const override;
 
     /* SpiderMonkey extensions. */
     virtual bool getPropertyDescriptor(JSContext* cx, HandleObject wrapper, HandleId id,
@@ -186,17 +186,17 @@ class JS_FRIEND_API(OpaqueCrossCompartme
                               ObjectOpResult& result) const override;
     virtual bool setImmutablePrototype(JSContext* cx, HandleObject wrapper,
                                        bool* succeeded) const override;
     virtual bool preventExtensions(JSContext* cx, HandleObject wrapper,
                                    ObjectOpResult& result) const override;
     virtual bool isExtensible(JSContext* cx, HandleObject wrapper, bool* extensible) const override;
     virtual bool has(JSContext* cx, HandleObject wrapper, HandleId id,
                      bool* bp) const override;
-    virtual bool get(JSContext* cx, HandleObject wrapper, HandleObject receiver,
+    virtual bool get(JSContext* cx, HandleObject wrapper, HandleValue receiver,
                      HandleId id, MutableHandleValue vp) const override;
     virtual bool set(JSContext* cx, HandleObject wrapper, HandleId id, HandleValue v,
                      HandleValue receiver, ObjectOpResult& result) const override;
     virtual bool call(JSContext* cx, HandleObject wrapper, const CallArgs& args) const override;
     virtual bool construct(JSContext* cx, HandleObject wrapper, const CallArgs& args) const override;
 
     /* SpiderMonkey extensions. */
     virtual bool getPropertyDescriptor(JSContext* cx, HandleObject wrapper, HandleId id,
--- a/js/src/proxy/BaseProxyHandler.cpp
+++ b/js/src/proxy/BaseProxyHandler.cpp
@@ -60,17 +60,17 @@ BaseProxyHandler::hasOwn(JSContext* cx, 
     Rooted<PropertyDescriptor> desc(cx);
     if (!getOwnPropertyDescriptor(cx, proxy, id, &desc))
         return false;
     *bp = !!desc.object();
     return true;
 }
 
 bool
-BaseProxyHandler::get(JSContext* cx, HandleObject proxy, HandleObject receiver,
+BaseProxyHandler::get(JSContext* cx, HandleObject proxy, HandleValue receiver,
                       HandleId id, MutableHandleValue vp) const
 {
     assertEnteredPolicy(cx, proxy, id, GET);
 
     Rooted<PropertyDescriptor> desc(cx);
     if (!getPropertyDescriptor(cx, proxy, id, &desc))
         return false;
     if (!desc.object()) {
--- a/js/src/proxy/CrossCompartmentWrapper.cpp
+++ b/js/src/proxy/CrossCompartmentWrapper.cpp
@@ -148,20 +148,20 @@ CrossCompartmentWrapper::hasOwn(JSContex
 {
     PIERCE(cx, wrapper,
            NOTHING,
            Wrapper::hasOwn(cx, wrapper, id, bp),
            NOTHING);
 }
 
 bool
-CrossCompartmentWrapper::get(JSContext* cx, HandleObject wrapper, HandleObject receiver,
+CrossCompartmentWrapper::get(JSContext* cx, HandleObject wrapper, HandleValue receiver,
                              HandleId id, MutableHandleValue vp) const
 {
-    RootedObject receiverCopy(cx, receiver);
+    RootedValue receiverCopy(cx, receiver);
     {
         AutoCompartment call(cx, wrappedObject(wrapper));
         if (!cx->compartment()->wrap(cx, &receiverCopy))
             return false;
 
         if (!Wrapper::get(cx, wrapper, receiverCopy, id, vp))
             return false;
     }
--- a/js/src/proxy/DirectProxyHandler.cpp
+++ b/js/src/proxy/DirectProxyHandler.cpp
@@ -212,17 +212,17 @@ bool
 DirectProxyHandler::hasOwn(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) const
 {
     assertEnteredPolicy(cx, proxy, id, GET);
     RootedObject target(cx, proxy->as<ProxyObject>().target());
     return HasOwnProperty(cx, target, id, bp);
 }
 
 bool
-DirectProxyHandler::get(JSContext* cx, HandleObject proxy, HandleObject receiver,
+DirectProxyHandler::get(JSContext* cx, HandleObject proxy, HandleValue receiver,
                         HandleId id, MutableHandleValue vp) const
 {
     assertEnteredPolicy(cx, proxy, id, GET);
     RootedObject target(cx, proxy->as<ProxyObject>().target());
     return GetProperty(cx, target, receiver, id, vp);
 }
 
 bool
--- a/js/src/proxy/OpaqueCrossCompartmentWrapper.cpp
+++ b/js/src/proxy/OpaqueCrossCompartmentWrapper.cpp
@@ -90,17 +90,17 @@ OpaqueCrossCompartmentWrapper::isExtensi
 bool
 OpaqueCrossCompartmentWrapper::has(JSContext* cx, HandleObject wrapper, HandleId id,
                                    bool* bp) const
 {
     return BaseProxyHandler::has(cx, wrapper, id, bp);
 }
 
 bool
-OpaqueCrossCompartmentWrapper::get(JSContext* cx, HandleObject wrapper, HandleObject receiver,
+OpaqueCrossCompartmentWrapper::get(JSContext* cx, HandleObject wrapper, HandleValue receiver,
                                    HandleId id, MutableHandleValue vp) const
 {
     return BaseProxyHandler::get(cx, wrapper, receiver, id, vp);
 }
 
 bool
 OpaqueCrossCompartmentWrapper::set(JSContext* cx, HandleObject wrapper, HandleId id,
                                    HandleValue v, HandleValue receiver,
--- a/js/src/proxy/Proxy.cpp
+++ b/js/src/proxy/Proxy.cpp
@@ -266,29 +266,29 @@ OuterizeValue(JSContext* cx, HandleValue
     if (v.isObject()) {
         RootedObject obj(cx, &v.toObject());
         return ObjectValue(*GetOuterObject(cx, obj));
     }
     return v;
 }
 
 bool
-Proxy::get(JSContext* cx, HandleObject proxy, HandleObject receiver_, HandleId id,
+Proxy::get(JSContext* cx, HandleObject proxy, HandleValue receiver_, HandleId id,
            MutableHandleValue vp)
 {
     JS_CHECK_RECURSION(cx, return false);
     const BaseProxyHandler* handler = proxy->as<ProxyObject>().handler();
     vp.setUndefined(); // default result if we refuse to perform this action
     AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET, true);
     if (!policy.allowed())
         return policy.returnValue();
 
     // Outerize the receiver. Proxy handlers shouldn't have to know about
     // the Window/WindowProxy distinction.
-    RootedObject receiver(cx, GetOuterObject(cx, receiver_));
+    RootedValue receiver(cx, OuterizeValue(cx, receiver_));
 
     if (handler->hasPrototype()) {
         bool own;
         if (!handler->hasOwn(cx, proxy, id, &own))
             return false;
         if (!own) {
             RootedObject proto(cx);
             if (!GetPrototype(cx, proxy, &proto))
@@ -298,17 +298,17 @@ Proxy::get(JSContext* cx, HandleObject p
             return GetProperty(cx, proto, receiver, id, vp);
         }
     }
 
     return handler->get(cx, proxy, receiver, id, vp);
 }
 
 bool
-Proxy::callProp(JSContext* cx, HandleObject proxy, HandleObject receiver, HandleId id,
+Proxy::callProp(JSContext* cx, HandleObject proxy, HandleValue receiver, HandleId id,
                 MutableHandleValue vp)
 {
     // The inline caches need an access point for JSOP_CALLPROP sites that accounts
     // for the possibility of __noSuchMethod__
     if (!Proxy::get(cx, proxy, receiver, id, vp))
         return false;
 
 #if JS_HAS_NO_SUCH_METHOD
@@ -589,17 +589,17 @@ js::proxy_DefineProperty(JSContext* cx, 
 
 bool
 js::proxy_HasProperty(JSContext* cx, JS::HandleObject obj, JS::HandleId id, bool* foundp)
 {
     return Proxy::has(cx, obj, id, foundp);
 }
 
 bool
-js::proxy_GetProperty(JSContext* cx, HandleObject obj, HandleObject receiver, HandleId id,
+js::proxy_GetProperty(JSContext* cx, HandleObject obj, HandleValue receiver, HandleId id,
                       MutableHandleValue vp)
 {
     return Proxy::get(cx, obj, receiver, id, vp);
 }
 
 bool
 js::proxy_SetProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v,
                       HandleValue receiver, ObjectOpResult& result)
--- a/js/src/proxy/Proxy.h
+++ b/js/src/proxy/Proxy.h
@@ -35,17 +35,17 @@ class Proxy
     static bool enumerate(JSContext* cx, HandleObject proxy, MutableHandleObject objp);
     static bool isExtensible(JSContext* cx, HandleObject proxy, bool* extensible);
     static bool preventExtensions(JSContext* cx, HandleObject proxy, ObjectOpResult& result);
     static bool getPrototype(JSContext* cx, HandleObject proxy, MutableHandleObject protop);
     static bool setPrototype(JSContext* cx, HandleObject proxy, HandleObject proto,
                              ObjectOpResult& result);
     static bool setImmutablePrototype(JSContext* cx, HandleObject proxy, bool* succeeded);
     static bool has(JSContext* cx, HandleObject proxy, HandleId id, bool* bp);
-    static bool get(JSContext* cx, HandleObject proxy, HandleObject receiver, HandleId id,
+    static bool get(JSContext* cx, HandleObject proxy, HandleValue receiver, HandleId id,
                     MutableHandleValue vp);
     static bool set(JSContext* cx, HandleObject proxy, HandleId id, HandleValue v,
                     HandleValue receiver, ObjectOpResult& result);
     static bool call(JSContext* cx, HandleObject proxy, const CallArgs& args);
     static bool construct(JSContext* cx, HandleObject proxy, const CallArgs& args);
 
     /* SpiderMonkey extensions. */
     static bool getPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
@@ -67,15 +67,16 @@ class Proxy
     static bool unwatch(JSContext* cx, HandleObject proxy, HandleId id);
 
     static bool getElements(JSContext* cx, HandleObject obj, uint32_t begin, uint32_t end,
                             ElementAdder* adder);
 
     static void trace(JSTracer* trc, JSObject* obj);
 
     /* IC entry path for handling __noSuchMethod__ on access. */
-    static bool callProp(JSContext* cx, HandleObject proxy, HandleObject reveiver, HandleId id,
+    static bool callProp(JSContext* cx, HandleObject proxy, HandleValue reveiver, HandleId id,
                          MutableHandleValue vp);
+
 };
 
 } /* namespace js */
 
 #endif /* proxy_Proxy_h */
--- a/js/src/proxy/ScriptedDirectProxyHandler.cpp
+++ b/js/src/proxy/ScriptedDirectProxyHandler.cpp
@@ -841,17 +841,17 @@ ScriptedDirectProxyHandler::has(JSContex
 
     // step 12
     *bp = success;
     return true;
 }
 
 // ES6 (22 May, 2014) 9.5.8 Proxy.[[GetP]](P, Receiver)
 bool
-ScriptedDirectProxyHandler::get(JSContext* cx, HandleObject proxy, HandleObject receiver,
+ScriptedDirectProxyHandler::get(JSContext* cx, HandleObject proxy, HandleValue receiver,
                                 HandleId id, MutableHandleValue vp) const
 {
     // step 2
     RootedObject handler(cx, GetDirectProxyHandlerObject(proxy));
 
     // step 3
     if (!handler) {
         JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED);
@@ -872,17 +872,17 @@ ScriptedDirectProxyHandler::get(JSContex
 
     // step 8-9
     RootedValue value(cx);
     if (!IdToStringOrSymbol(cx, id, &value))
         return false;
     Value argv[] = {
         ObjectOrNullValue(target),
         value,
-        ObjectOrNullValue(receiver)
+        receiver
     };
     RootedValue trapResult(cx);
     if (!Invoke(cx, ObjectValue(*handler), trap, ArrayLength(argv), argv, &trapResult))
         return false;
 
     // step 10-11
     Rooted<PropertyDescriptor> desc(cx);
     if (!GetOwnPropertyDescriptor(cx, target, id, &desc))
--- a/js/src/proxy/ScriptedDirectProxyHandler.h
+++ b/js/src/proxy/ScriptedDirectProxyHandler.h
@@ -39,17 +39,17 @@ class ScriptedDirectProxyHandler : publi
     virtual bool setImmutablePrototype(JSContext* cx, HandleObject proxy,
                                        bool* succeeded) const override;
 
     virtual bool preventExtensions(JSContext* cx, HandleObject proxy,
                                    ObjectOpResult& result) const override;
     virtual bool isExtensible(JSContext* cx, HandleObject proxy, bool* extensible) const override;
 
     virtual bool has(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) const override;
-    virtual bool get(JSContext* cx, HandleObject proxy, HandleObject receiver, HandleId id,
+    virtual bool get(JSContext* cx, HandleObject proxy, HandleValue receiver, HandleId id,
                      MutableHandleValue vp) const override;
     virtual bool set(JSContext* cx, HandleObject proxy, HandleId id, HandleValue v,
                      HandleValue receiver, ObjectOpResult& result) const override;
     virtual bool call(JSContext* cx, HandleObject proxy, const CallArgs& args) const override;
     virtual bool construct(JSContext* cx, HandleObject proxy, const CallArgs& args) const override;
 
     /* SpiderMonkey extensions. */
     virtual bool hasOwn(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) const override {
--- a/js/src/proxy/ScriptedIndirectProxyHandler.cpp
+++ b/js/src/proxy/ScriptedIndirectProxyHandler.cpp
@@ -280,25 +280,25 @@ ScriptedIndirectProxyHandler::hasOwn(JSC
         return false;
     if (!IsCallable(fval))
         return BaseProxyHandler::hasOwn(cx, proxy, id, bp);
     return Trap1(cx, handler, fval, id, &value) &&
            ValueToBool(value, bp);
 }
 
 bool
-ScriptedIndirectProxyHandler::get(JSContext* cx, HandleObject proxy, HandleObject receiver,
+ScriptedIndirectProxyHandler::get(JSContext* cx, HandleObject proxy, HandleValue receiver,
                                   HandleId id, MutableHandleValue vp) const
 {
     RootedObject handler(cx, GetIndirectProxyHandlerObject(proxy));
     RootedValue idv(cx);
     if (!IdToStringOrSymbol(cx, id, &idv))
         return false;
     JS::AutoValueArray<2> argv(cx);
-    argv[0].setObjectOrNull(receiver);
+    argv[0].set(receiver);
     argv[1].set(idv);
     RootedValue fval(cx);
     if (!GetDerivedTrap(cx, handler, cx->names().get, &fval))
         return false;
     if (!IsCallable(fval))
         return BaseProxyHandler::get(cx, proxy, receiver, id, vp);
     return Trap(cx, handler, fval, 2, argv.begin(), vp);
 }
--- a/js/src/proxy/ScriptedIndirectProxyHandler.h
+++ b/js/src/proxy/ScriptedIndirectProxyHandler.h
@@ -30,17 +30,17 @@ class ScriptedIndirectProxyHandler : pub
     virtual bool delete_(JSContext* cx, HandleObject proxy, HandleId id,
                          ObjectOpResult& result) const override;
     virtual bool enumerate(JSContext* cx, HandleObject proxy,
                            MutableHandleObject objp) const override;
     virtual bool preventExtensions(JSContext* cx, HandleObject proxy,
                                    ObjectOpResult& result) const override;
     virtual bool isExtensible(JSContext* cx, HandleObject proxy, bool* extensible) const override;
     virtual bool has(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) const override;
-    virtual bool get(JSContext* cx, HandleObject proxy, HandleObject receiver, HandleId id,
+    virtual bool get(JSContext* cx, HandleObject proxy, HandleValue receiver, HandleId id,
                      MutableHandleValue vp) const override;
     virtual bool set(JSContext* cx, HandleObject proxy, HandleId id, HandleValue v,
                      HandleValue receiver, ObjectOpResult& result) const override;
 
     /* SpiderMonkey extensions. */
     virtual bool getPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
                                        MutableHandle<JSPropertyDescriptor> desc) const override;
     virtual bool hasOwn(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) const override;
--- a/js/src/vm/Interpreter-inl.h
+++ b/js/src/vm/Interpreter-inl.h
@@ -448,64 +448,65 @@ GetObjectElementOperation(JSContext* cx,
     }
 #endif
 
     assertSameCompartmentDebugOnly(cx, res);
     return true;
 }
 
 static MOZ_ALWAYS_INLINE bool
-GetPrimitiveElementOperation(JSContext* cx, JSOp op, JS::HandleValue receiver,
+GetPrimitiveElementOperation(JSContext* cx, JSOp op, JS::HandleValue receiver_,
                              HandleValue key, MutableHandleValue res)
 {
     MOZ_ASSERT(op == JSOP_GETELEM || op == JSOP_CALLELEM);
 
     // FIXME: We shouldn't be boxing here or exposing the boxed object as
     //        receiver anywhere below (bug 603201).
-    RootedObject boxed(cx, ToObjectFromStack(cx, receiver));
+    RootedObject boxed(cx, ToObjectFromStack(cx, receiver_));
     if (!boxed)
         return false;
+    RootedValue receiver(cx, ObjectValue(*boxed));
 
     do {
         uint32_t index;
         if (IsDefinitelyIndex(key, &index)) {
-            if (GetElementNoGC(cx, boxed, boxed, index, res.address()))
+            if (GetElementNoGC(cx, boxed, receiver, index, res.address()))
                 break;
 
-            if (!GetElement(cx, boxed, boxed, index, res))
+            if (!GetElement(cx, boxed, receiver, index, res))
                 return false;
             break;
         }
 
         if (IsSymbolOrSymbolWrapper(key)) {
             RootedId id(cx, SYMBOL_TO_JSID(ToSymbolPrimitive(key)));
-            if (!GetProperty(cx, boxed, boxed, id, res))
+            if (!GetProperty(cx, boxed, receiver, id, res))
                 return false;
             break;
         }
 
         if (JSAtom* name = ToAtom<NoGC>(cx, key)) {
             if (name->isIndex(&index)) {
-                if (GetElementNoGC(cx, boxed, boxed, index, res.address()))
+                if (GetElementNoGC(cx, boxed, receiver, index, res.address()))
                     break;
             } else {
-                if (GetPropertyNoGC(cx, boxed, boxed, name->asPropertyName(), res.address()))
+                if (GetPropertyNoGC(cx, boxed, receiver, name->asPropertyName(), res.address()))
                     break;
             }
         }
 
         JSAtom* name = ToAtom<CanGC>(cx, key);
         if (!name)
             return false;
 
         if (name->isIndex(&index)) {
-            if (!GetElement(cx, boxed, boxed, index, res))
+            if (!GetElement(cx, boxed, receiver, index, res))
                 return false;
         } else {
-            if (!GetProperty(cx, boxed, boxed, name->asPropertyName(), res))
+            if (!GetProperty(cx, boxed, receiver, name->asPropertyName(), res))
                 return false;
         }
     } while (false);
 
     // Note: we don't call a __noSuchMethod__ hook when |this| was primitive.
 
     assertSameCompartmentDebugOnly(cx, res);
     return true;
@@ -550,18 +551,20 @@ GetElementOperation(JSContext* cx, JSOp 
             str = cx->staticStrings().getUnitStringForElement(cx, str, index);
             if (!str)
                 return false;
             res.setString(str);
             return true;
         }
     }
 
-    if (lref.isPrimitive())
-        return GetPrimitiveElementOperation(cx, op, lref, rref, res);
+    if (lref.isPrimitive()) {
+        RootedValue thisv(cx, lref);
+        return GetPrimitiveElementOperation(cx, op, thisv, rref, res);
+    }
 
     RootedObject thisv(cx, &lref.toObject());
     return GetObjectElementOperation(cx, op, thisv, thisv, rref, res);
 }
 
 static MOZ_ALWAYS_INLINE JSString*
 TypeOfOperation(const Value& v, JSRuntime* rt)
 {
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -250,17 +250,19 @@ GetPropertyOperation(JSContext* cx, Inte
     if (op == JSOP_CALLPROP) {
         // The __noSuchMethod__ code in CallProperty requires non-aliasing
         // v and vp arguments.
         RootedValue v(cx, lval);
         return CallProperty(cx, v, name, vp);
     }
 
     MOZ_ASSERT(op == JSOP_GETPROP || op == JSOP_LENGTH);
-    return GetProperty(cx, lval, name, vp);
+    // Copy lval, because it might alias vp.
+    RootedValue v(cx, lval);
+    return GetProperty(cx, v, name, vp);
 }
 
 static inline bool
 GetNameOperation(JSContext* cx, InterpreterFrame* fp, jsbytecode* pc, MutableHandleValue vp)
 {
     JSObject* obj = fp->scopeChain();
     PropertyName* name = fp->script()->getName(pc);
 
@@ -911,25 +913,25 @@ js::InternalConstructWithProvidedThis(JS
     if (!InternalConstruct(cx, args))
         return false;
 
     rval.set(args.rval());
     return true;
 }
 
 bool
-js::InvokeGetter(JSContext* cx, JSObject* obj, Value fval, MutableHandleValue rval)
+js::InvokeGetter(JSContext* cx, const Value& thisv, Value fval, MutableHandleValue rval)
 {
     /*
      * Invoke could result in another try to get or set the same id again, see
      * bug 355497.
      */
     JS_CHECK_RECURSION(cx, return false);
 
-    return Invoke(cx, ObjectValue(*obj), fval, 0, nullptr, rval);
+    return Invoke(cx, thisv, fval, 0, nullptr, rval);
 }
 
 bool
 js::InvokeSetter(JSContext* cx, const Value& thisv, Value fval, HandleValue v)
 {
     JS_CHECK_RECURSION(cx, return false);
 
     RootedValue ignored(cx);
@@ -4185,27 +4187,29 @@ js::GetProperty(JSContext* cx, HandleVal
             proto = GlobalObject::getOrCreateStringPrototype(cx, cx->global());
         } else if (v.isBoolean()) {
             proto = GlobalObject::getOrCreateBooleanPrototype(cx, cx->global());
         } else {
             MOZ_ASSERT(v.isSymbol());
             proto = GlobalObject::getOrCreateSymbolPrototype(cx, cx->global());
         }
         if (!proto)
-             return false;
+            return false;
 
         if (GetPropertyPure(cx, proto, NameToId(name), vp.address()))
             return true;
     }
 
     RootedObject obj(cx, ToObjectFromStack(cx, v));
     if (!obj)
         return false;
 
-    return GetProperty(cx, obj, obj, name, vp);
+    // Bug 603201: Pass primitive receiver here.
+    RootedValue receiver(cx, ObjectValue(*obj));
+    return GetProperty(cx, obj, receiver, name, vp);
 }
 
 bool
 js::CallProperty(JSContext* cx, HandleValue v, HandlePropertyName name, MutableHandleValue vp)
 {
     // __noSuchMethod__ code below depends on this.
     MOZ_ASSERT(v.address() != vp.address());
 
--- a/js/src/vm/Interpreter.h
+++ b/js/src/vm/Interpreter.h
@@ -75,17 +75,17 @@ extern bool
 Invoke(JSContext* cx, const Value& thisv, const Value& fval, unsigned argc, const Value* argv,
        MutableHandleValue rval);
 
 /*
  * These helpers take care of the infinite-recursion check necessary for
  * getter/setter calls.
  */
 extern bool
-InvokeGetter(JSContext* cx, JSObject* obj, Value fval, MutableHandleValue rval);
+InvokeGetter(JSContext* cx, const Value& thisv, Value fval, MutableHandleValue rval);
 
 extern bool
 InvokeSetter(JSContext* cx, const Value& thisv, Value fval, HandleValue v);
 
 // ES6 7.3.13 Construct(F, argumentsList, newTarget).  All parameters are
 // required, hopefully forcing callers to be careful not to (say) blindly pass
 // callee as |newTarget| when a different value should have been passed.
 //
--- a/js/src/vm/NativeObject.cpp
+++ b/js/src/vm/NativeObject.cpp
@@ -1189,17 +1189,17 @@ static bool IsAccessorDescriptor(unsigne
 static bool IsDataDescriptor(unsigned attrs) {
     MOZ_ASSERT((attrs & (JSPROP_IGNORE_VALUE | JSPROP_IGNORE_READONLY)) == 0);
     return !IsAccessorDescriptor(attrs);
 }
 
 template <AllowGC allowGC>
 static MOZ_ALWAYS_INLINE bool
 GetExistingProperty(JSContext* cx,
-                    typename MaybeRooted<JSObject*, allowGC>::HandleType receiver,
+                    typename MaybeRooted<Value, allowGC>::HandleType receiver,
                     typename MaybeRooted<NativeObject*, allowGC>::HandleType obj,
                     typename MaybeRooted<Shape*, allowGC>::HandleType shape,
                     typename MaybeRooted<Value, allowGC>::MutableHandleType vp);
 
 static bool
 GetExistingPropertyValue(ExclusiveContext* cx, HandleNativeObject obj, HandleId id,
                          HandleShape shape, MutableHandleValue vp)
 {
@@ -1208,17 +1208,18 @@ GetExistingPropertyValue(ExclusiveContex
         return true;
     }
     if (!cx->shouldBeJSContext())
         return false;
 
     MOZ_ASSERT(shape->propid() == id);
     MOZ_ASSERT(obj->contains(cx, shape));
 
-    return GetExistingProperty<CanGC>(cx->asJSContext(), obj, obj, shape, vp);
+    RootedValue receiver(cx, ObjectValue(*obj));
+    return GetExistingProperty<CanGC>(cx->asJSContext(), receiver, obj, shape, vp);
 }
 
 /*
  * If ES6 draft rev 37 9.1.6.3 ValidateAndApplyPropertyDescriptor step 4 would
  * return early, because desc is redundant with an existing own property obj[id],
  * then set *redundant = true and return true.
  */
 static bool
@@ -1639,17 +1640,17 @@ js::NativeHasProperty(JSContext* cx, Han
         pobj = &proto->as<NativeObject>();
     }
 }
 
 
 /*** [[Get]] *************************************************************************************/
 
 static inline bool
-CallGetter(JSContext* cx, HandleObject obj, HandleObject receiver, HandleShape shape,
+CallGetter(JSContext* cx, HandleObject obj, HandleValue receiver, HandleShape shape,
            MutableHandleValue vp)
 {
     MOZ_ASSERT(!shape->hasDefaultGetter());
 
     if (shape->hasGetterValue()) {
         Value fval = shape->getterValue();
         return InvokeGetter(cx, receiver, fval, vp);
     }
@@ -1657,17 +1658,17 @@ CallGetter(JSContext* cx, HandleObject o
     // In contrast to normal getters JSGetterOps always want the holder.
     RootedId id(cx, shape->propid());
     return CallJSGetterOp(cx, shape->getterOp(), obj, id, vp);
 }
 
 template <AllowGC allowGC>
 static MOZ_ALWAYS_INLINE bool
 GetExistingProperty(JSContext* cx,
-                    typename MaybeRooted<JSObject*, allowGC>::HandleType receiver,
+                    typename MaybeRooted<Value, allowGC>::HandleType receiver,
                     typename MaybeRooted<NativeObject*, allowGC>::HandleType obj,
                     typename MaybeRooted<Shape*, allowGC>::HandleType shape,
                     typename MaybeRooted<Value, allowGC>::MutableHandleType vp)
 {
     if (shape->hasSlot()) {
         vp.set(obj->getSlot(shape->slot()));
         MOZ_ASSERT_IF(!vp.isMagic(JS_UNINITIALIZED_LEXICAL) &&
                       !obj->isSingleton() &&
@@ -1696,17 +1697,17 @@ GetExistingProperty(JSContext* cx,
         }
     }
 
     if (!allowGC)
         return false;
 
     if (!CallGetter(cx,
                     MaybeRooted<JSObject*, allowGC>::toHandle(obj),
-                    MaybeRooted<JSObject*, allowGC>::toHandle(receiver),
+                    MaybeRooted<Value, allowGC>::toHandle(receiver),
                     MaybeRooted<Shape*, allowGC>::toHandle(shape),
                     MaybeRooted<Value, allowGC>::toMutableHandle(vp)))
     {
         return false;
     }
 
     // Ancient nonstandard extension: via the JSAPI it's possible to create a
     // data property that has both a slot and a getter. In that case, copy the
@@ -1716,17 +1717,18 @@ GetExistingProperty(JSContext* cx,
 
     return true;
 }
 
 bool
 js::NativeGetExistingProperty(JSContext* cx, HandleObject receiver, HandleNativeObject obj,
                               HandleShape shape, MutableHandleValue vp)
 {
-    return GetExistingProperty<CanGC>(cx, receiver, obj, shape, vp);
+    RootedValue receiverValue(cx, ObjectValue(*receiver));
+    return GetExistingProperty<CanGC>(cx, receiverValue, obj, shape, vp);
 }
 
 /*
  * Given pc pointing after a property accessing bytecode, return true if the
  * access is "property-detecting" -- that is, if we shouldn't warn about it
  * even if no such property is found and strict warnings are enabled.
  */
 static bool
@@ -1779,17 +1781,17 @@ enum IsNameLookup { NotNameLookup = fals
  *     out if that's what's happening and throw a ReferenceError if so.
  *
  * 3.  We also emit an optional warning for this. (It's not super useful on the
  *     web, as there are too many false positives, but anecdotally useful in
  *     Gecko code.)
  */
 static bool
 GetNonexistentProperty(JSContext* cx, HandleNativeObject obj, HandleId id,
-                       HandleObject receiver, IsNameLookup nameLookup, MutableHandleValue vp)
+                       HandleValue receiver, IsNameLookup nameLookup, MutableHandleValue vp)
 {
     vp.setUndefined();
 
     // Non-standard extension: Call the getProperty hook. If it sets vp to a
     // value other than undefined, we're done. If not, fall through to the
     // warning/error checks below.
     if (JSGetterOp getProperty = obj->getClass()->getProperty) {
         if (!CallJSGetterOp(cx, getProperty, obj, id, vp))
@@ -1844,24 +1846,24 @@ GetNonexistentProperty(JSContext* cx, Ha
     // Ok, bad undefined property reference: whine about it.
     RootedValue val(cx, IdToValue(id));
     return ReportValueErrorFlags(cx, flags, JSMSG_UNDEFINED_PROP, JSDVG_IGNORE_STACK, val,
                                     nullptr, nullptr, nullptr);
 }
 
 /* The NoGC version of GetNonexistentProperty, present only to make types line up. */
 bool
-GetNonexistentProperty(JSContext* cx, NativeObject* obj, jsid id, JSObject* receiver,
+GetNonexistentProperty(JSContext* cx, NativeObject* obj, jsid id, Value& receiver,
                        IsNameLookup nameLookup, FakeMutableHandle<Value> vp)
 {
     return false;
 }
 
 static inline bool
-GeneralizedGetProperty(JSContext* cx, HandleObject obj, HandleId id, HandleObject receiver,
+GeneralizedGetProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue receiver,
                        IsNameLookup nameLookup, MutableHandleValue vp)
 {
     JS_CHECK_RECURSION(cx, return false);
     if (nameLookup) {
         // When nameLookup is true, GetProperty implements ES6 rev 34 (2015 Feb
         // 20) 8.1.1.2.6 GetBindingValue, with step 3 (the call to HasProperty)
         // and step 6 (the call to Get) fused so that only a single lookup is
         // needed.
@@ -1876,30 +1878,30 @@ GeneralizedGetProperty(JSContext* cx, Ha
         if (!found)
             return ReportIsNotDefined(cx, id);
     }
 
     return GetProperty(cx, obj, receiver, id, vp);
 }
 
 static inline bool
-GeneralizedGetProperty(JSContext* cx, JSObject* obj, jsid id, JSObject* receiver,
+GeneralizedGetProperty(JSContext* cx, JSObject* obj, jsid id, const Value& receiver,
                        IsNameLookup nameLookup, FakeMutableHandle<Value> vp)
 {
     JS_CHECK_RECURSION_DONT_REPORT(cx, return false);
     if (nameLookup)
         return false;
     return GetPropertyNoGC(cx, obj, receiver, id, vp.address());
 }
 
 template <AllowGC allowGC>
 static MOZ_ALWAYS_INLINE bool
 NativeGetPropertyInline(JSContext* cx,
                         typename MaybeRooted<NativeObject*, allowGC>::HandleType obj,
-                        typename MaybeRooted<JSObject*, allowGC>::HandleType receiver,
+                        typename MaybeRooted<Value, allowGC>::HandleType receiver,
                         typename MaybeRooted<jsid, allowGC>::HandleType id,
                         IsNameLookup nameLookup,
                         typename MaybeRooted<Value, allowGC>::MutableHandleType vp)
 {
     typename MaybeRooted<NativeObject*, allowGC>::RootType pobj(cx, obj);
     typename MaybeRooted<Shape*, allowGC>::RootType shape(cx);
 
     // This loop isn't explicit in the spec algorithm. See the comment on step
@@ -1943,35 +1945,36 @@ NativeGetPropertyInline(JSContext* cx,
         if (proto->getOps()->getProperty)
             return GeneralizedGetProperty(cx, proto, id, receiver, nameLookup, vp);
 
         pobj = &proto->as<NativeObject>();
     }
 }
 
 bool
-js::NativeGetProperty(JSContext* cx, HandleNativeObject obj, HandleObject receiver, HandleId id,
+js::NativeGetProperty(JSContext* cx, HandleNativeObject obj, HandleValue receiver, HandleId id,
                       MutableHandleValue vp)
 {
     return NativeGetPropertyInline<CanGC>(cx, obj, receiver, id, NotNameLookup, vp);
 }
 
 bool
-js::NativeGetPropertyNoGC(JSContext* cx, NativeObject* obj, JSObject* receiver, jsid id, Value* vp)
+js::NativeGetPropertyNoGC(JSContext* cx, NativeObject* obj, const Value& receiver, jsid id, Value* vp)
 {
     AutoAssertNoException noexc(cx);
     return NativeGetPropertyInline<NoGC>(cx, obj, receiver, id, NotNameLookup, vp);
 }
 
 bool
 js::GetPropertyForNameLookup(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp)
 {
+    RootedValue receiver(cx, ObjectValue(*obj));
     if (obj->getOps()->getProperty)
-        return GeneralizedGetProperty(cx, obj, id, obj, NameLookup, vp);
-    return NativeGetPropertyInline<CanGC>(cx, obj.as<NativeObject>(), obj, id, NameLookup, vp);
+        return GeneralizedGetProperty(cx, obj, id, receiver, NameLookup, vp);
+    return NativeGetPropertyInline<CanGC>(cx, obj.as<NativeObject>(), receiver, id, NameLookup, vp);
 }
 
 
 /*** [[Set]] *************************************************************************************/
 
 static bool
 MaybeReportUndeclaredVarAssignment(JSContext* cx, JSString* propname)
 {
--- a/js/src/vm/NativeObject.h
+++ b/js/src/vm/NativeObject.h
@@ -1283,36 +1283,38 @@ extern bool
 NativeDefineProperty(ExclusiveContext* cx, HandleNativeObject obj, PropertyName* name,
                      HandleValue value, JSGetterOp getter, JSSetterOp setter,
                      unsigned attrs);
 
 extern bool
 NativeHasProperty(JSContext* cx, HandleNativeObject obj, HandleId id, bool* foundp);
 
 extern bool
-NativeGetProperty(JSContext* cx, HandleNativeObject obj, HandleObject receiver, HandleId id,
+NativeGetProperty(JSContext* cx, HandleNativeObject obj, HandleValue receiver, HandleId id,
                   MutableHandleValue vp);
 
 extern bool
-NativeGetPropertyNoGC(JSContext* cx, NativeObject* obj, JSObject* receiver, jsid id, Value* vp);
+NativeGetPropertyNoGC(JSContext* cx, NativeObject* obj, const Value& receiver, jsid id, Value* vp);
 
 extern bool
-NativeGetElement(JSContext* cx, HandleNativeObject obj, HandleObject receiver, uint32_t index,
+NativeGetElement(JSContext* cx, HandleNativeObject obj, HandleValue receiver, uint32_t index,
                  MutableHandleValue vp);
 
 inline bool
 NativeGetProperty(JSContext* cx, HandleNativeObject obj, HandleId id, MutableHandleValue vp)
 {
-    return NativeGetProperty(cx, obj, obj, id, vp);
+    RootedValue receiver(cx, ObjectValue(*obj));
+    return NativeGetProperty(cx, obj, receiver, id, vp);
 }
 
 inline bool
 NativeGetElement(JSContext* cx, HandleNativeObject obj, uint32_t index, MutableHandleValue vp)
 {
-    return NativeGetElement(cx, obj, obj, index, vp);
+    RootedValue receiver(cx, ObjectValue(*obj));
+    return NativeGetElement(cx, obj, receiver, index, vp);
 }
 
 bool
 SetPropertyByDefining(JSContext* cx, HandleObject obj, HandleId id, HandleValue v,
                       HandleValue receiver, ObjectOpResult& result);
 
 bool
 SetPropertyOnProto(JSContext* cx, HandleObject obj, HandleId id, HandleValue v,
@@ -1404,26 +1406,26 @@ inline bool
 js::HasProperty(JSContext* cx, HandleObject obj, HandleId id, bool* foundp)
 {
     if (HasPropertyOp op = obj->getOps()->hasProperty)
         return op(cx, obj, id, foundp);
     return NativeHasProperty(cx, obj.as<NativeObject>(), id, foundp);
 }
 
 inline bool
-js::GetProperty(JSContext* cx, HandleObject obj, HandleObject receiver, HandleId id,
+js::GetProperty(JSContext* cx, HandleObject obj, HandleValue receiver, HandleId id,
                 MutableHandleValue vp)
 {
     if (GetPropertyOp op = obj->getOps()->getProperty)
         return op(cx, obj, receiver, id, vp);
     return NativeGetProperty(cx, obj.as<NativeObject>(), receiver, id, vp);
 }
 
 inline bool
-js::GetPropertyNoGC(JSContext* cx, JSObject* obj, JSObject* receiver, jsid id, Value* vp)
+js::GetPropertyNoGC(JSContext* cx, JSObject* obj, const Value& receiver, jsid id, Value* vp)
 {
     if (obj->getOps()->getProperty)
         return false;
     return NativeGetPropertyNoGC(cx, &obj->as<NativeObject>(), receiver, id, vp);
 }
 
 inline bool
 js::SetProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v,
--- a/js/src/vm/ScopeObject.cpp
+++ b/js/src/vm/ScopeObject.cpp
@@ -499,21 +499,24 @@ with_DefineProperty(JSContext* cx, Handl
 static bool
 with_HasProperty(JSContext* cx, HandleObject obj, HandleId id, bool* foundp)
 {
     RootedObject actual(cx, &obj->as<DynamicWithObject>().object());
     return HasProperty(cx, actual, id, foundp);
 }
 
 static bool
-with_GetProperty(JSContext* cx, HandleObject obj, HandleObject receiver, HandleId id,
+with_GetProperty(JSContext* cx, HandleObject obj, HandleValue receiver, HandleId id,
                  MutableHandleValue vp)
 {
     RootedObject actual(cx, &obj->as<DynamicWithObject>().object());
-    return GetProperty(cx, actual, actual, id, vp);
+    RootedValue actualReceiver(cx, receiver);
+    if (receiver.isObject() && &receiver.toObject() == obj)
+        actualReceiver.setObject(*actual);
+    return GetProperty(cx, actual, actualReceiver, id, vp);
 }
 
 static bool
 with_SetProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v,
                  HandleValue receiver, ObjectOpResult& result)
 {
     RootedObject actual(cx, &obj->as<DynamicWithObject>().object());
     RootedValue actualReceiver(cx, receiver);
@@ -977,17 +980,17 @@ uninitialized_LookupProperty(JSContext* 
 static bool
 uninitialized_HasProperty(JSContext* cx, HandleObject obj, HandleId id, bool* foundp)
 {
     ReportUninitializedLexicalId(cx, id);
     return false;
 }
 
 static bool
-uninitialized_GetProperty(JSContext* cx, HandleObject obj, HandleObject receiver, HandleId id,
+uninitialized_GetProperty(JSContext* cx, HandleObject obj, HandleValue receiver, HandleId id,
                           MutableHandleValue vp)
 {
     ReportUninitializedLexicalId(cx, id);
     return false;
 }
 
 static bool
 uninitialized_SetProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v,
@@ -1619,17 +1622,17 @@ class DebugScopeProxy : public BaseProxy
                                  "Debugger scope");
             return false;
         }
 
         vp.setObject(*argsObj);
         return true;
     }
 
-    bool get(JSContext* cx, HandleObject proxy, HandleObject receiver, HandleId id,
+    bool get(JSContext* cx, HandleObject proxy, HandleValue receiver, HandleId id,
              MutableHandleValue vp) const override
     {
         Rooted<DebugScopeObject*> debugScope(cx, &proxy->as<DebugScopeObject>());
         Rooted<ScopeObject*> scope(cx, &proxy->as<DebugScopeObject>().scope());
 
         if (isMissingArguments(cx, id, *scope))
             return getMissingArguments(cx, *scope, vp);
 
--- a/js/src/vm/UnboxedObject.cpp
+++ b/js/src/vm/UnboxedObject.cpp
@@ -775,30 +775,32 @@ UnboxedPlainObject::obj_hasProperty(JSCo
         *foundp = false;
         return true;
     }
 
     return HasProperty(cx, proto, id, foundp);
 }
 
 /* static */ bool
-UnboxedPlainObject::obj_getProperty(JSContext* cx, HandleObject obj, HandleObject receiver,
+UnboxedPlainObject::obj_getProperty(JSContext* cx, HandleObject obj, HandleValue receiver,
                                     HandleId id, MutableHandleValue vp)
 {
     const UnboxedLayout& layout = obj->as<UnboxedPlainObject>().layout();
 
     if (const UnboxedLayout::Property* property = layout.lookup(id)) {
         vp.set(obj->as<UnboxedPlainObject>().getValue(*property));
         return true;
     }
 
     if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando()) {
         if (expando->containsShapeOrElement(cx, id)) {
             RootedObject nexpando(cx, expando);
-            RootedObject nreceiver(cx, (obj == receiver) ? expando : receiver.get());
+            RootedValue nreceiver(cx, receiver);
+            if (receiver.isObject() && &receiver.toObject() == obj)
+                nreceiver.setObject(*expando);
             return GetProperty(cx, nexpando, nreceiver, id, vp);
         }
     }
 
     RootedObject proto(cx, obj->getProto());
     if (!proto) {
         vp.setUndefined();
         return true;
@@ -1477,17 +1479,17 @@ UnboxedArrayObject::obj_hasProperty(JSCo
         *foundp = false;
         return true;
     }
 
     return HasProperty(cx, proto, id, foundp);
 }
 
 /* static */ bool
-UnboxedArrayObject::obj_getProperty(JSContext* cx, HandleObject obj, HandleObject receiver,
+UnboxedArrayObject::obj_getProperty(JSContext* cx, HandleObject obj, HandleValue receiver,
                                     HandleId id, MutableHandleValue vp)
 {
     if (obj->as<UnboxedArrayObject>().containsProperty(cx, id)) {
         if (JSID_IS_INT(id))
             vp.set(obj->as<UnboxedArrayObject>().getElement(JSID_TO_INT(id)));
         else
             vp.set(Int32Value(obj->as<UnboxedArrayObject>().length()));
         return true;
--- a/js/src/vm/UnboxedObject.h
+++ b/js/src/vm/UnboxedObject.h
@@ -245,17 +245,17 @@ class UnboxedPlainObject : public JSObje
                                    MutableHandleShape propp);
 
     static bool obj_defineProperty(JSContext* cx, HandleObject obj, HandleId id,
                                    Handle<JSPropertyDescriptor> desc,
                                    ObjectOpResult& result);
 
     static bool obj_hasProperty(JSContext* cx, HandleObject obj, HandleId id, bool* foundp);
 
-    static bool obj_getProperty(JSContext* cx, HandleObject obj, HandleObject receiver,
+    static bool obj_getProperty(JSContext* cx, HandleObject obj, HandleValue receiver,
                                 HandleId id, MutableHandleValue vp);
 
     static bool obj_setProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v,
                                 HandleValue receiver, ObjectOpResult& result);
 
     static bool obj_getOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id,
                                              MutableHandle<JSPropertyDescriptor> desc);
 
@@ -381,17 +381,17 @@ class UnboxedArrayObject : public JSObje
                                    MutableHandleShape propp);
 
     static bool obj_defineProperty(JSContext* cx, HandleObject obj, HandleId id,
                                    Handle<JSPropertyDescriptor> desc,
                                    ObjectOpResult& result);
 
     static bool obj_hasProperty(JSContext* cx, HandleObject obj, HandleId id, bool* foundp);
 
-    static bool obj_getProperty(JSContext* cx, HandleObject obj, HandleObject receiver,
+    static bool obj_getProperty(JSContext* cx, HandleObject obj, HandleValue receiver,
                                 HandleId id, MutableHandleValue vp);
 
     static bool obj_setProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v,
                                 HandleValue receiver, ObjectOpResult& result);
 
     static bool obj_getOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id,
                                              MutableHandle<JSPropertyDescriptor> desc);
 
--- a/js/xpconnect/src/Sandbox.cpp
+++ b/js/xpconnect/src/Sandbox.cpp
@@ -823,17 +823,17 @@ bool
 xpc::SandboxProxyHandler::hasOwn(JSContext* cx, JS::Handle<JSObject*> proxy,
                                  JS::Handle<jsid> id, bool* bp) const
 {
     return BaseProxyHandler::hasOwn(cx, proxy, id, bp);
 }
 
 bool
 xpc::SandboxProxyHandler::get(JSContext* cx, JS::Handle<JSObject*> proxy,
-                              JS::Handle<JSObject*> receiver,
+                              JS::Handle<JS::Value> receiver,
                               JS::Handle<jsid> id,
                               JS::MutableHandle<Value> vp) const
 {
     return BaseProxyHandler::get(cx, proxy, receiver, id, vp);
 }
 
 bool
 xpc::SandboxProxyHandler::set(JSContext* cx, JS::Handle<JSObject*> proxy,
--- a/js/xpconnect/wrappers/AddonWrapper.cpp
+++ b/js/xpconnect/wrappers/AddonWrapper.cpp
@@ -164,31 +164,28 @@ AddonWrapper<Base>::getOwnPropertyDescri
     if (desc.object())
         return true;
 
     return Base::getOwnPropertyDescriptor(cx, wrapper, id, desc);
 }
 
 template<typename Base>
 bool
-AddonWrapper<Base>::get(JSContext* cx, JS::Handle<JSObject*> wrapper, JS::Handle<JSObject*> receiver,
+AddonWrapper<Base>::get(JSContext* cx, JS::Handle<JSObject*> wrapper, JS::Handle<Value> receiver,
                         JS::Handle<jsid> id, JS::MutableHandle<JS::Value> vp) const
 {
     Rooted<JSPropertyDescriptor> desc(cx);
     if (!InterposeProperty(cx, wrapper, nullptr, id, &desc))
         return false;
 
     if (!desc.object())
         return Base::get(cx, wrapper, receiver, id, vp);
 
     if (desc.getter()) {
-        MOZ_ASSERT(desc.hasGetterObject());
-        AutoValueVector args(cx);
-        RootedValue fval(cx, ObjectValue(*desc.getterObject()));
-        return JS_CallFunctionValue(cx, receiver, fval, args, vp);
+        return Call(cx, receiver, desc.getterObject(), HandleValueArray::empty(), vp);
     } else {
         vp.set(desc.value());
         return true;
     }
 }
 
 template<typename Base>
 bool
--- a/js/xpconnect/wrappers/AddonWrapper.h
+++ b/js/xpconnect/wrappers/AddonWrapper.h
@@ -31,17 +31,17 @@ class AddonWrapper : public Base {
     virtual bool getOwnPropertyDescriptor(JSContext* cx, JS::Handle<JSObject*> wrapper,
                                           JS::Handle<jsid> id,
                                           JS::MutableHandle<JSPropertyDescriptor> desc) const override;
     virtual bool defineProperty(JSContext* cx, JS::HandleObject proxy, JS::HandleId id,
                                 JS::Handle<JSPropertyDescriptor> desc,
                                 JS::ObjectOpResult& result) const override;
     virtual bool delete_(JSContext* cx, JS::HandleObject proxy, JS::HandleId id,
                          JS::ObjectOpResult& result) const override;
-    virtual bool get(JSContext* cx, JS::Handle<JSObject*> wrapper, JS::Handle<JSObject*> receiver,
+    virtual bool get(JSContext* cx, JS::Handle<JSObject*> wrapper, JS::Handle<JS::Value> receiver,
                      JS::Handle<jsid> id, JS::MutableHandle<JS::Value> vp) const override;
     virtual bool set(JSContext* cx, JS::HandleObject wrapper, JS::HandleId id, JS::HandleValue v,
                      JS::HandleValue receiver, JS::ObjectOpResult& result) const override;
     virtual bool call(JSContext* cx, JS::Handle<JSObject*> wrapper,
                       const JS::CallArgs& args) const override;
 
     virtual bool getPropertyDescriptor(JSContext* cx, JS::Handle<JSObject*> wrapper,
                                        JS::Handle<jsid> id,
--- a/js/xpconnect/wrappers/WaiveXrayWrapper.cpp
+++ b/js/xpconnect/wrappers/WaiveXrayWrapper.cpp
@@ -45,18 +45,17 @@ WaiveXrayWrapper::getOwnPropertyDescript
                                            HandleId id, JS::MutableHandle<JSPropertyDescriptor> desc)
                                            const
 {
     return CrossCompartmentWrapper::getOwnPropertyDescriptor(cx, wrapper, id, desc) &&
            WrapperFactory::WaiveXrayAndWrap(cx, desc.value()) && WaiveAccessors(cx, desc);
 }
 
 bool
-WaiveXrayWrapper::get(JSContext* cx, HandleObject wrapper,
-                      HandleObject receiver, HandleId id,
+WaiveXrayWrapper::get(JSContext* cx, HandleObject wrapper, HandleValue receiver, HandleId id,
                       MutableHandleValue vp) const
 {
     return CrossCompartmentWrapper::get(cx, wrapper, receiver, id, vp) &&
            WrapperFactory::WaiveXrayAndWrap(cx, vp);
 }
 
 bool
 WaiveXrayWrapper::enumerate(JSContext* cx, HandleObject proxy,
--- a/js/xpconnect/wrappers/WaiveXrayWrapper.h
+++ b/js/xpconnect/wrappers/WaiveXrayWrapper.h
@@ -17,17 +17,17 @@ class WaiveXrayWrapper : public js::Cros
   public:
     explicit MOZ_CONSTEXPR WaiveXrayWrapper(unsigned flags) : js::CrossCompartmentWrapper(flags) { }
 
     virtual bool getOwnPropertyDescriptor(JSContext* cx, JS::Handle<JSObject*> wrapper,
                                           JS::Handle<jsid> id,
                                           JS::MutableHandle<JSPropertyDescriptor> desc) const override;
     virtual bool getPrototype(JSContext* cx, JS::Handle<JSObject*> wrapper,
                               JS::MutableHandle<JSObject*> protop) const override;
-    virtual bool get(JSContext* cx, JS::Handle<JSObject*> wrapper, JS::Handle<JSObject*> receiver,
+    virtual bool get(JSContext* cx, JS::Handle<JSObject*> wrapper, JS::Handle<JS::Value> receiver,
                      JS::Handle<jsid> id, JS::MutableHandle<JS::Value> vp) const override;
     virtual bool call(JSContext* cx, JS::Handle<JSObject*> wrapper,
                       const JS::CallArgs& args) const override;
     virtual bool construct(JSContext* cx, JS::Handle<JSObject*> wrapper,
                            const JS::CallArgs& args) const override;
 
     virtual bool enumerate(JSContext* cx, JS::Handle<JSObject*> proxy,
                            JS::MutableHandle<JSObject*> objp) const override;
--- a/js/xpconnect/wrappers/XrayWrapper.cpp
+++ b/js/xpconnect/wrappers/XrayWrapper.cpp
@@ -2087,23 +2087,28 @@ XrayWrapper<Base, Traits>::delete_(JSCon
     }
 
     return Traits::singleton.delete_(cx, wrapper, id, result);
 }
 
 template <typename Base, typename Traits>
 bool
 XrayWrapper<Base, Traits>::get(JSContext* cx, HandleObject wrapper,
-                               HandleObject receiver, HandleId id,
+                               HandleValue receiver, HandleId id,
                                MutableHandleValue vp) const
 {
     // Skip our Base if it isn't already ProxyHandler.
     // NB: None of the functions we call are prepared for the receiver not
     // being the wrapper, so ignore the receiver here.
-    return js::BaseProxyHandler::get(cx, wrapper, Traits::HasPrototype ? receiver : wrapper, id, vp);
+    RootedValue thisv(cx);
+    if (Traits::HasPrototype)
+      thisv = receiver;
+    else
+      thisv.setObject(*wrapper);
+    return js::BaseProxyHandler::get(cx, wrapper, thisv, id, vp);
 }
 
 template <typename Base, typename Traits>
 bool
 XrayWrapper<Base, Traits>::set(JSContext* cx, HandleObject wrapper, HandleId id, HandleValue v,
                                HandleValue receiver, ObjectOpResult& result) const
 {
     MOZ_ASSERT(!Traits::HasPrototype);
--- a/js/xpconnect/wrappers/XrayWrapper.h
+++ b/js/xpconnect/wrappers/XrayWrapper.h
@@ -441,17 +441,17 @@ class XrayWrapper : public Base {
                               JS::HandleObject proto, JS::ObjectOpResult& result) const override;
     virtual bool setImmutablePrototype(JSContext* cx, JS::HandleObject wrapper,
                                        bool* succeeded) const override;
     virtual bool preventExtensions(JSContext* cx, JS::Handle<JSObject*> wrapper,
                                    JS::ObjectOpResult& result) const override;
     virtual bool isExtensible(JSContext* cx, JS::Handle<JSObject*> wrapper, bool* extensible) const override;
     virtual bool has(JSContext* cx, JS::Handle<JSObject*> wrapper, JS::Handle<jsid> id,
                      bool* bp) const override;
-    virtual bool get(JSContext* cx, JS::Handle<JSObject*> wrapper, JS::Handle<JSObject*> receiver,
+    virtual bool get(JSContext* cx, JS::Handle<JSObject*> wrapper, JS::HandleValue receiver,
                      JS::Handle<jsid> id, JS::MutableHandle<JS::Value> vp) const override;
     virtual bool set(JSContext* cx, JS::Handle<JSObject*> wrapper, JS::Handle<jsid> id,
                      JS::Handle<JS::Value> v, JS::Handle<JS::Value> receiver,
                      JS::ObjectOpResult& result) const override;
     virtual bool call(JSContext* cx, JS::Handle<JSObject*> wrapper,
                       const JS::CallArgs& args) const override;
     virtual bool construct(JSContext* cx, JS::Handle<JSObject*> wrapper,
                            const JS::CallArgs& args) const override;
@@ -515,17 +515,17 @@ public:
     virtual bool getOwnPropertyDescriptor(JSContext* cx, JS::Handle<JSObject*> proxy,
                                           JS::Handle<jsid> id,
                                           JS::MutableHandle<JSPropertyDescriptor> desc) const override;
 
     // We just forward the high-level methods to the BaseProxyHandler versions
     // which implement them in terms of lower-level methods.
     virtual bool has(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
                      bool* bp) const override;
-    virtual bool get(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<JSObject*> receiver,
+    virtual bool get(JSContext* cx, JS::Handle<JSObject*> proxy, JS::HandleValue receiver,
                      JS::Handle<jsid> id, JS::MutableHandle<JS::Value> vp) const override;
     virtual bool set(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
                      JS::Handle<JS::Value> v, JS::Handle<JS::Value> receiver,
                      JS::ObjectOpResult& result) const override;
 
     virtual bool getPropertyDescriptor(JSContext* cx, JS::Handle<JSObject*> proxy,
                                        JS::Handle<jsid> id,
                                        JS::MutableHandle<JSPropertyDescriptor> desc) const override;
--- a/layout/base/nsDocumentViewer.cpp
+++ b/layout/base/nsDocumentViewer.cpp
@@ -3686,17 +3686,21 @@ nsDocumentViewer::Print(nsIPrintSettings
 
   // if we are printing another URL, then exit
   // the reason we check here is because this method can be called while
   // another is still in here (the printing dialog is a good example).
   // the only time we can print more than one job at a time is the regression tests
   if (GetIsPrinting()) {
     // Let the user know we are not ready to print.
     rv = NS_ERROR_NOT_AVAILABLE;
-    nsPrintEngine::ShowPrintErrorDialog(rv);
+
+    if (mPrintEngine) {
+      mPrintEngine->FirePrintingErrorEvent(rv);
+    }
+
     return rv;
   }
 
   nsAutoPtr<nsPrintEventDispatcher> beforeAndAfterPrint(
     new nsPrintEventDispatcher(mDocument));
   NS_ENSURE_STATE(!GetIsPrinting());
   // If we are hosting a full-page plugin, tell it to print
   // first. It shows its own native print UI.
--- a/layout/printing/nsPrintEngine.cpp
+++ b/layout/printing/nsPrintEngine.cpp
@@ -6,16 +6,17 @@
 #include "nsPrintEngine.h"
 
 #include "nsIStringBundle.h"
 #include "nsReadableUtils.h"
 #include "nsCRT.h"
 
 #include "mozilla/AsyncEventDispatcher.h"
 #include "mozilla/dom/Selection.h"
+#include "mozilla/dom/CustomEvent.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsPIDOMWindow.h"
 #include "nsIDocShell.h"
 #include "nsIFrame.h"
 #include "nsIURI.h"
 #include "nsITextToSubURI.h"
 #include "nsError.h"
 
@@ -116,16 +117,17 @@ static const char kPrintingPromptService
 #include "nsFocusManager.h"
 #include "nsRange.h"
 #include "nsCDefaultURIFixup.h"
 #include "nsIURIFixup.h"
 #include "mozilla/dom/Element.h"
 #include "nsContentList.h"
 #include "nsIChannel.h"
 #include "xpcpublic.h"
+#include "nsVariant.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 //-----------------------------------------------------
 // PR LOGGING
 #include "mozilla/Logging.h"
 
@@ -409,18 +411,19 @@ nsPrintEngine::CommonPrint(bool         
     if (aIsPrintPreview) {
       SetIsCreatingPrintPreview(false);
       SetIsPrintPreview(false);
     } else {
       SetIsPrinting(false);
     }
     if (mProgressDialogIsShown)
       CloseProgressDialog(aWebProgressListener);
-    if (rv != NS_ERROR_ABORT && rv != NS_ERROR_OUT_OF_MEMORY)
-      ShowPrintErrorDialog(rv, !aIsPrintPreview);
+    if (rv != NS_ERROR_ABORT && rv != NS_ERROR_OUT_OF_MEMORY) {
+      FirePrintingErrorEvent(rv);
+    }
     delete mPrt;
     mPrt = nullptr;
   }
 
   return rv;
 }
 
 nsresult
@@ -778,17 +781,17 @@ nsPrintEngine::PrintPreview(nsIPrintSett
   // (We can't Print Preview this document if it is still busy)
   nsCOMPtr<nsIDocShell> docShell(do_QueryReferent(mContainer));
   NS_ENSURE_STATE(docShell);
 
   uint32_t busyFlags = nsIDocShell::BUSY_FLAGS_NONE;
   if (NS_FAILED(docShell->GetBusyFlags(&busyFlags)) ||
       busyFlags != nsIDocShell::BUSY_FLAGS_NONE) {
     CloseProgressDialog(aWebProgressListener);
-    ShowPrintErrorDialog(NS_ERROR_GFX_PRINTER_DOC_IS_BUSY, false);
+    FirePrintingErrorEvent(NS_ERROR_GFX_PRINTER_DOC_IS_BUSY);
     return NS_ERROR_FAILURE;
   }
 
   NS_ENSURE_STATE(aChildDOMWin);
   nsCOMPtr<nsIDOMDocument> doc;
   aChildDOMWin->GetDocument(getter_AddRefs(doc));
   NS_ENSURE_STATE(doc);
 
@@ -1500,102 +1503,49 @@ nsresult nsPrintEngine::CleanupOnFailure
 
   /* cleanup done, let's fire-up an error dialog to notify the user
    * what went wrong... 
    * 
    * When rv == NS_ERROR_ABORT, it means we want out of the 
    * print job without displaying any error messages
    */
   if (aResult != NS_ERROR_ABORT) {
-    ShowPrintErrorDialog(aResult, aIsPrinting);
+    FirePrintingErrorEvent(aResult);
   }
 
   FirePrintCompletionEvent();
 
   return aResult;
 
 }
 
 //---------------------------------------------------------------------
 void
-nsPrintEngine::ShowPrintErrorDialog(nsresult aPrintError, bool aIsPrinting)
+nsPrintEngine::FirePrintingErrorEvent(nsresult aPrintError)
 {
-  nsAutoCString stringName;
-  nsXPIDLString msg, title;
-  nsresult rv = NS_OK;
-
-  switch(aPrintError)
-  {
-#define ENTITY_FOR_ERROR(label) \
-    case NS_ERROR_##label: stringName.AssignLiteral("PERR_" #label); break
-
-    ENTITY_FOR_ERROR(GFX_PRINTER_NO_PRINTER_AVAILABLE);
-    ENTITY_FOR_ERROR(GFX_PRINTER_NAME_NOT_FOUND);
-    ENTITY_FOR_ERROR(GFX_PRINTER_COULD_NOT_OPEN_FILE);
-    ENTITY_FOR_ERROR(GFX_PRINTER_STARTDOC);
-    ENTITY_FOR_ERROR(GFX_PRINTER_ENDDOC);
-    ENTITY_FOR_ERROR(GFX_PRINTER_STARTPAGE);
-    ENTITY_FOR_ERROR(GFX_PRINTER_DOC_IS_BUSY);
-
-    ENTITY_FOR_ERROR(ABORT);
-    ENTITY_FOR_ERROR(NOT_AVAILABLE);
-    ENTITY_FOR_ERROR(NOT_IMPLEMENTED);
-    ENTITY_FOR_ERROR(OUT_OF_MEMORY);
-    ENTITY_FOR_ERROR(UNEXPECTED);
-
-    default:
-    ENTITY_FOR_ERROR(FAILURE);
-
-#undef ENTITY_FOR_ERROR
-  }
-
-  if (!aIsPrinting) {
-    // Try first with _PP suffix.
-    stringName.AppendLiteral("_PP");
-    rv = nsContentUtils::GetLocalizedString(
-             nsContentUtils::ePRINTING_PROPERTIES, stringName.get(), msg);
-    if (NS_FAILED(rv)) {
-      stringName.Truncate(stringName.Length() - 3);
-    }
-  }
-  if (aIsPrinting || NS_FAILED(rv)) {
-    rv = nsContentUtils::GetLocalizedString(
-             nsContentUtils::ePRINTING_PROPERTIES, stringName.get(), msg);
-  }
-  if (NS_FAILED(rv)) {
-    return;
-  }
-
-  rv = nsContentUtils::GetLocalizedString(
-           nsContentUtils::ePRINTING_PROPERTIES,
-           aIsPrinting ? "print_error_dialog_title"
-                       : "printpreview_error_dialog_title",
-           title);
-  if (NS_FAILED(rv)) {
-    return;
-  }
-
-  nsCOMPtr<nsIWindowWatcher> wwatch =
-    do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
-  if (NS_FAILED(rv)) {
-    return;
-  }
-
-  nsCOMPtr<nsIDOMWindow> active;
-  wwatch->GetActiveWindow(getter_AddRefs(active));
-
-  nsCOMPtr<nsIPrompt> dialog;
-  /* |GetNewPrompter| allows that |active| is |nullptr|
-   * (see bug 234982 ("nsPrintEngine::ShowPrintErrorDialog() fails in many cases")) */
-  wwatch->GetNewPrompter(active, getter_AddRefs(dialog));
-  if (!dialog) {
-    return;
-  }
-
-  dialog->Alert(title.get(), msg.get());
+  nsCOMPtr<nsIContentViewer> cv = do_QueryInterface(mDocViewerPrint);
+  nsCOMPtr<nsIDocument> doc = cv->GetDocument();
+  nsCOMPtr<nsIDOMCustomEvent> event =
+    NS_NewDOMCustomEvent(doc, nullptr, nullptr);
+
+  MOZ_ASSERT(event);
+  nsCOMPtr<nsIWritableVariant> resultVariant = new nsVariant();
+  // nsresults are Uint32_t's, but XPConnect will interpret it as a double
+  // when any JS attempts to access it, and will therefore interpret it
+  // incorrectly. We preempt this by casting and setting as a double.
+  resultVariant->SetAsDouble(static_cast<double>(aPrintError));
+
+  event->InitCustomEvent(NS_LITERAL_STRING("PrintingError"), false, false,
+                         resultVariant);
+  event->SetTrusted(true);
+
+  nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
+    new AsyncEventDispatcher(doc, event);
+  asyncDispatcher->mOnlyChromeDispatch = true;
+  asyncDispatcher->RunDOMEventWhenSafe();
 }
 
 //-----------------------------------------------------------------
 //-- Section: Reflow Methods
 //-----------------------------------------------------------------
 
 nsresult
 nsPrintEngine::ReconstructAndReflow(bool doSetPixelScale)
@@ -2691,17 +2641,17 @@ nsPrintEngine::PrePrintPage()
   bool done = false;
   nsresult rv = mPageSeqFrame->PrePrintNextPage(mPagePrintTimer, &done);
   if (NS_FAILED(rv)) {
     // ??? ::PrintPage doesn't set |mPrt->mIsAborted = true| if rv != NS_ERROR_ABORT,
     // but I don't really understand why this should be the right thing to do?
     // Shouldn't |mPrt->mIsAborted| set to true all the time if something
     // wents wrong?
     if (rv != NS_ERROR_ABORT) {
-      ShowPrintErrorDialog(rv);
+      FirePrintingErrorEvent(rv);
       mPrt->mIsAborted = true;
     }
     done = true;
   }
   return done;
 }
 
 bool
@@ -2710,17 +2660,17 @@ nsPrintEngine::PrintPage(nsPrintObject* 
 {
   NS_ASSERTION(aPO,            "aPO is null!");
   NS_ASSERTION(mPageSeqFrame,  "mPageSeqFrame is null!");
   NS_ASSERTION(mPrt,           "mPrt is null!");
 
   // Although these should NEVER be nullptr
   // This is added insurance, to make sure we don't crash in optimized builds
   if (!mPrt || !aPO || !mPageSeqFrame) {
-    ShowPrintErrorDialog(NS_ERROR_FAILURE);
+    FirePrintingErrorEvent(NS_ERROR_FAILURE);
     return true; // means we are done printing
   }
 
   PR_PL(("-----------------------------------\n"));
   PR_PL(("------ In DV::PrintPage PO: %p (%s)\n", aPO, gFrameTypesStr[aPO->mFrameType]));
 
   // Check setting to see if someone request it be cancelled
   bool isCancelled = false;
@@ -2772,17 +2722,17 @@ nsPrintEngine::PrintPage(nsPrintObject* 
   // fail and the failure is passed back here.
   // Returning true means we are done printing.
   //
   // When rv == NS_ERROR_ABORT, it means we want out of the
   // print job without displaying any error messages
   nsresult rv = mPageSeqFrame->PrintNextPage();
   if (NS_FAILED(rv)) {
     if (rv != NS_ERROR_ABORT) {
-      ShowPrintErrorDialog(rv);
+      FirePrintingErrorEvent(rv);
       mPrt->mIsAborted = true;
     }
     return true;
   }
 
   mPageSeqFrame->DoPageEnd();
 
   return donePrinting;
--- a/layout/printing/nsPrintEngine.h
+++ b/layout/printing/nsPrintEngine.h
@@ -132,16 +132,17 @@ public:
   void SetDocAndURLIntoProgress(nsPrintObject* aPO,
                                 nsIPrintProgressParams* aParams);
   void EllipseLongString(nsAString& aStr, const uint32_t aLen, bool aDoFront);
   nsresult CheckForPrinters(nsIPrintSettings* aPrintSettings);
   void CleanupDocTitleArray(char16_t**& aArray, int32_t& aCount);
 
   bool IsThereARangeSelection(nsIDOMWindow * aDOMWin);
 
+  void FirePrintingErrorEvent(nsresult aPrintError);
   //---------------------------------------------------------------------
 
 
   // Timer Methods
   nsresult StartPagePrintTimer(nsPrintObject* aPO);
 
   bool IsWindowsInOurSubTree(nsPIDOMWindow * aDOMWindow);
   static bool IsParentAFrameSet(nsIDocShell * aParent);
@@ -160,18 +161,16 @@ public:
   //---------------------------------------------------------------------
   static void GetDocumentTitleAndURL(nsIDocument* aDoc,
                                      nsAString&   aTitle,
                                      nsAString&   aURLStr);
   void GetDisplayTitleAndURL(nsPrintObject*   aPO,
                              nsAString&       aTitle,
                              nsAString&       aURLStr,
                              eDocTitleDefault aDefType);
-  static void ShowPrintErrorDialog(nsresult printerror,
-                                   bool aIsPrinting = true);
 
   static bool HasFramesetChild(nsIContent* aContent);
 
   bool     CheckBeforeDestroy();
   nsresult Cancelled();
 
   nsIPresShell* GetPrintPreviewPresShell() {return mPrtPreview->mPrintObject->mPresShell;}
 
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -1960,17 +1960,17 @@ pref("security.csp.experimentalEnabled",
 // Default Content Security Policy to apply to privileged apps.
 pref("security.apps.privileged.CSP.default", "default-src * data: blob:; script-src 'self'; object-src 'none'; style-src 'self' 'unsafe-inline'");
 
 // Mixed content blocking
 pref("security.mixed_content.block_active_content", false);
 pref("security.mixed_content.block_display_content", false);
 
 // Sub-resource integrity
-pref("security.sri.enable", false);
+pref("security.sri.enable", true);
 
 // Disable pinning checks by default.
 pref("security.cert_pinning.enforcement_level", 0);
 // Do not process hpkp headers rooted by not built in roots by default.
 // This is to prevent accidental pinning from MITM devices and is used
 // for tests.
 pref("security.cert_pinning.process_headers_from_non_builtin_roots", false);
 
@@ -2710,17 +2710,20 @@ pref("font.size.fixed.zh-HK", 16);
 
 pref("font.default.zh-TW", "sans-serif");
 pref("font.minimum-size.zh-TW", 0);
 pref("font.size.variable.zh-TW", 16);
 pref("font.size.fixed.zh-TW", 16);
 
 // mathml.css sets font-size to "inherit" and font-family to "serif" so only
 // font.name.*.x-math and font.minimum-size.x-math are really relevant.
+pref("font.default.x-math", "serif");
 pref("font.minimum-size.x-math", 0);
+pref("font.size.variable.x-math", 16);
+pref("font.size.fixed.x-math", 13);
 
 /*
  * A value greater than zero enables font size inflation for
  * pan-and-zoom UIs, so that the fonts in a block are at least the size
  * that, if a block's width is scaled to match the device's width, the
  * fonts in the block are big enough that at most the pref value ems of
  * text fit in *the width of the device*.
  *
@@ -3160,21 +3163,21 @@ pref("intl.tsf.hack.free_chang_jie.do_no
 pref("intl.tsf.hack.ms_simplified_chinese.do_not_return_no_layout_error", true);
 // For Microsoft ChangJie and Microsoft Quick
 pref("intl.tsf.hack.ms_traditional_chinese.do_not_return_no_layout_error", true);
 // For Easy Changjei
 pref("intl.tsf.hack.easy_changjei.do_not_return_no_layout_error", true);
 // Whether use previous character rect for the result of
 // ITfContextView::GetTextExt() if the specified range is the first character
 // of selected clause of composition string.
-pref("intl.tsf.hack.google_ja_input.do_not_return_no_layout_error_at_first_char", true);
+pref("intl.tsf.hack.ms_japanese_ime.do_not_return_no_layout_error_at_first_char", true);
 // Whether use previous character rect for the result of
 // ITfContextView::GetTextExt() if the specified range is the caret of
 // composition string.
-pref("intl.tsf.hack.google_ja_input.do_not_return_no_layout_error_at_caret", true);
+pref("intl.tsf.hack.ms_japanese_ime.do_not_return_no_layout_error_at_caret", true);
 // Whether hack ITextStoreACP::QueryInsert() or not.  The method should return
 // new selection after specified length text is inserted at specified range.
 // However, Microsoft's some Chinese TIPs expect that the result is same as
 // specified range.  If following prefs are true, ITextStoreACP::QueryInsert()
 // returns specified range only when one of the TIPs is active.
 // For Microsoft Pinyin and Microsoft Wubi
 pref("intl.tsf.hack.ms_simplified_chinese.query_insert_result", true);
 // For Microsoft ChangJie and Microsoft Quick
--- a/netwerk/protocol/http/Http2Session.cpp
+++ b/netwerk/protocol/http/Http2Session.cpp
@@ -90,16 +90,17 @@ Http2Session::Http2Session(nsISocketTran
   , mExpectedPushPromiseID(0)
   , mContinuedPromiseStream(0)
   , mFlatHTTPResponseHeadersOut(0)
   , mShouldGoAway(false)
   , mClosed(false)
   , mCleanShutdown(false)
   , mTLSProfileConfirmed(false)
   , mGoAwayReason(NO_HTTP_ERROR)
+  , mClientGoAwayReason(UNASSIGNED)
   , mPeerGoAwayReason(UNASSIGNED)
   , mGoAwayID(0)
   , mOutgoingGoAwayID(0)
   , mConcurrent(0)
   , mServerPushedResources(0)
   , mServerInitialStreamWindow(kDefaultRwin)
   , mLocalSessionWindow(kDefaultRwin)
   , mServerSessionWindow(kDefaultRwin)
@@ -190,16 +191,17 @@ Http2Session::~Http2Session()
   LOG3(("Http2Session::~Http2Session %p mDownstreamState=%X",
         this, mDownstreamState));
 
   mStreamTransactionHash.Enumerate(ShutdownEnumerator, this);
   Telemetry::Accumulate(Telemetry::SPDY_PARALLEL_STREAMS, mConcurrentHighWater);
   Telemetry::Accumulate(Telemetry::SPDY_REQUEST_PER_CONN, (mNextStreamID - 1) / 2);
   Telemetry::Accumulate(Telemetry::SPDY_SERVER_INITIATED_STREAMS,
                         mServerPushedResources);
+  Telemetry::Accumulate(Telemetry::SPDY_GOAWAY_LOCAL, mClientGoAwayReason);
   Telemetry::Accumulate(Telemetry::SPDY_GOAWAY_PEER, mPeerGoAwayReason);
 }
 
 void
 Http2Session::LogIO(Http2Session *self, Http2Stream *stream,
                     const char *label,
                     const char *data, uint32_t datalen)
 {
@@ -801,16 +803,17 @@ Http2Session::GenerateRstStream(uint32_t
 }
 
 void
 Http2Session::GenerateGoAway(uint32_t aStatusCode)
 {
   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
   LOG3(("Http2Session::GenerateGoAway %p code=%X\n", this, aStatusCode));
 
+  mClientGoAwayReason = aStatusCode;
   uint32_t frameSize = kFrameHeaderBytes + 8;
   char *packet = EnsureOutputBuffer(frameSize);
   mOutputQueueUsed += frameSize;
 
   CreateFrameHeader(packet, 8, FRAME_TYPE_GOAWAY, 0, 0);
 
   // last-good-stream-id are bytes 9-12 reflecting pushes
   NetworkEndian::writeUint32(packet + kFrameHeaderBytes, mOutgoingGoAwayID);
--- a/netwerk/protocol/http/Http2Session.h
+++ b/netwerk/protocol/http/Http2Session.h
@@ -419,18 +419,19 @@ private:
   // The TLS comlpiance checks are not done in the ctor beacuse of bad
   // exception handling - so we do them at IO time and cache the result
   bool                 mTLSProfileConfirmed;
 
   // A specifc reason code for the eventual GoAway frame. If set to NO_HTTP_ERROR
   // only NO_HTTP_ERROR, PROTOCOL_ERROR, or INTERNAL_ERROR will be sent.
   errorType            mGoAwayReason;
 
-  // The error code received from the peer in a goaway frame. UNASSIGNED/31
-  // if not received.
+  // The error code sent/received on the session goaway frame. UNASSIGNED/31
+  // if not transmitted.
+  int32_t             mClientGoAwayReason;
   int32_t             mPeerGoAwayReason;
 
   // If a GoAway message was received this is the ID of the last valid
   // stream. 0 otherwise. (0 is never a valid stream id.)
   uint32_t             mGoAwayID;
 
   // The last stream processed ID we will send in our GoAway frame.
   uint32_t             mOutgoingGoAwayID;
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -2891,18 +2891,19 @@ nsHttpChannel::OpenCacheEntry(bool isHtt
     }
 
     // Don't cache byte range requests which are subranges, only cache 0-
     // byte range requests.
     if (IsSubRangeRequest(mRequestHead))
         return NS_OK;
 
     // Pick up an application cache from the notification
-    // callbacks if available
-    if (!mApplicationCache && mInheritApplicationCache) {
+    // callbacks if available and if we are not an intercepted channel.
+    if (!PossiblyIntercepted() && !mApplicationCache &&
+        mInheritApplicationCache) {
         nsCOMPtr<nsIApplicationCacheContainer> appCacheContainer;
         GetCallback(appCacheContainer);
 
         if (appCacheContainer) {
             appCacheContainer->GetApplicationCache(getter_AddRefs(mApplicationCache));
         }
     }
 
--- a/testing/marionette/client/marionette/tests/unit/test_findelement.py
+++ b/testing/marionette/client/marionette/tests/unit/test_findelement.py
@@ -164,8 +164,16 @@ class TestElements(MarionetteTestCase):
         import re
         test_html = self.marionette.absolute_url("test.html")
         self.marionette.navigate(test_html)
         el = self.marionette.find_element(By.TAG_NAME, "body")
         uuid_regex = re.compile('^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$')
         self.assertIsNotNone(re.search(uuid_regex, el.id),
                              'UUID for the WebElement is not valid. ID is {}'\
                              .format(el.id))
+    def test_should_find_elements_by_link_text(self):
+        test_html = self.marionette.absolute_url("nestedElements.html")
+        self.marionette.navigate(test_html)
+        element = self.marionette.find_element(By.NAME, "div1")
+        children = element.find_elements(By.LINK_TEXT, "hello world")
+        self.assertEqual(len(children), 2)
+        self.assertEqual("link1", children[0].get_attribute("name"))
+        self.assertEqual("link2", children[1].get_attribute("name"))
new file mode 100644
--- /dev/null
+++ b/testing/marionette/client/marionette/www/nestedElements.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<a href="1.html">hello world</a>
+<a href="1.html">hello world</a><a href="1.html">hello world</a>
+<div name="div1">
+  <a href="2.html" name="link1">hello world</a>
+  <a href="2.html" name="link2">hello world</a>
+</div>
+
+<a href="1.html">hello world</a><a href="1.html">hello world</a><a href="1.html">hello world</a>
--- a/testing/marionette/elements.js
+++ b/testing/marionette/elements.js
@@ -676,17 +676,17 @@ ElementManager.prototype = {
       case CLASS_NAME:
         elements = startNode.getElementsByClassName(value);
         break;
       case TAG:
         elements = startNode.getElementsByTagName(value);
         break;
       case LINK_TEXT:
       case PARTIAL_LINK_TEXT:
-        let allLinks = rootNode.getElementsByTagName('A');
+        let allLinks = startNode.getElementsByTagName('A');
         for (let i = 0; i < allLinks.length; i++) {
           let text = allLinks[i].text;
           if (PARTIAL_LINK_TEXT == using) {
             if (text.indexOf(value) != -1) {
               elements.push(allLinks[i]);
             }
           } else if (text == value) {
             elements.push(allLinks[i]);
--- a/testing/web-platform/meta/subresource-integrity/subresource-integrity.html.ini
+++ b/testing/web-platform/meta/subresource-integrity/subresource-integrity.html.ini
@@ -1,86 +1,16 @@
 [subresource-integrity.html]
   type: testharness
-  [Doesn't load scripts with improper integrity URI scheme]
-    expected: FAIL
-
-  [Doesn't load scripts with incorrect content-type]
-    expected: FAIL
-
-  [Doesn't load scripts with non-matching digest]
-    expected: FAIL
-
-  [Doesn't load scripts using weak digest algorithm]
-    expected: FAIL
-
-  [Same-origin script with incorrect hash.]
-    expected: FAIL
-
-  [SHA-512 preferred to SHA-256.]
-    expected: FAIL
-
-  [SHA-512 preferred to SHA-384.]
-    expected: FAIL
-
-  [SHA-384 preferred to SHA-256.]
-    expected: FAIL
-
-  [SHA-256 preferred to MD5.]
-    expected: FAIL
-
-  [getPrioritizedHashFunction('SHA-256', 'SHA-256') returns empty string]
-    expected: FAIL
-
-  [Same-origin script with sha256 match, sha512 mismatch]
-    expected: FAIL
-
-  [<crossorigin='anonymous'> with incorrect hash, ACAO: *]
-    expected: FAIL
-
-  [<crossorigin='use-credentials'> with incorrect hash CORS-eligible]
-    expected: FAIL
-
-  [Resource with Refresh header]
-    expected: FAIL
-
-  [Resource with WWW-Authenticate header]
-    expected: FAIL
-
-  [Script: Same-origin with incorrect hash.]
-    expected: FAIL
-
-  [Script: Same-origin with sha256 match, sha512 mismatch]
-    expected: FAIL
-
-  [Script: <crossorigin='anonymous'> with incorrect hash, ACAO: *]
-    expected: FAIL
-
-  [Script: <crossorigin='use-credentials'> with incorrect hash CORS-eligible]
-    expected: FAIL
-
   [Style: Same-origin with incorrect hash.]
     expected: FAIL
 
   [Style: Same-origin with sha256 match, sha512 mismatch]
     expected: FAIL
 
   [Style: <crossorigin='anonymous'> with incorrect hash, ACAO: *]
     expected: FAIL
 
   [Style: <crossorigin='use-credentials'> with incorrect hash CORS-eligible]
     expected: FAIL
 
   [Style: Same-origin with incorrect sha256 and sha512 hash, rel='alternate stylesheet' enabled]
     expected: FAIL
-
-  [Script: Cross-origin, not CORS request, with correct hash]
-    expected: FAIL
-
-  [Script: Cross-origin, not CORS request, with hash mismatch]
-    expected: FAIL
-
-  [Style: Cross-origin, not CORS request, with correct hash]
-    expected: FAIL
-
-  [Style: Cross-origin, not CORS request, with hash mismatch]
-    expected: FAIL
-
--- a/toolkit/components/printing/content/printUtils.js
+++ b/toolkit/components/printing/content/printUtils.js
@@ -60,16 +60,27 @@
  *
  */
 
 var gPrintSettingsAreGlobal = false;
 var gSavePrintSettings = false;
 var gFocusedElement = null;
 
 var PrintUtils = {
+  init() {
+    window.messageManager.addMessageListener("Printing:Error", this);
+  },
+
+  get bundle() {
+    let stringService = Components.classes["@mozilla.org/intl/stringbundle;1"]
+                                  .getService(Components.interfaces.nsIStringBundleService);
+    delete this.bundle;
+    return this.bundle = stringService.createBundle("chrome://global/locale/printing.properties");
+  },
+
   /**
    * Shows the page setup dialog, and saves any settings changed in
    * that dialog if print.save_print_settings is set to true.
    *
    * @return true on success, false on failure
    */
   showPageSetup: function () {
     try {
@@ -295,17 +306,87 @@ var PrintUtils = {
       window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
             .getInterface(Components.interfaces.nsIWebNavigation)
             .QueryInterface(Components.interfaces.nsILoadContext)
             .useRemoteTabs;
     delete this.usingRemoteTabs;
     return this.usingRemoteTabs = usingRemoteTabs;
   },
 
+  displayPrintingError(nsresult, isPrinting) {
+    // The nsresults from a printing error are mapped to strings that have
+    // similar names to the errors themselves. For example, for error
+    // NS_ERROR_GFX_PRINTER_NO_PRINTER_AVAILABLE, the name of the string
+    // for the error message is: PERR_GFX_PRINTER_NO_PRINTER_AVAILABLE. What's
+    // more, if we're in the process of doing a print preview, it's possible
+    // that there are strings specific for print preview for these errors -
+    // if so, the names of those strings have _PP as a suffix. It's possible
+    // that no print preview specific strings exist, in which case it is fine
+    // to fall back to the original string name.
+    const MSG_CODES = [
+      "GFX_PRINTER_NO_PRINTER_AVAILABLE",
+      "GFX_PRINTER_NAME_NOT_FOUND",
+      "GFX_PRINTER_COULD_NOT_OPEN_FILE",
+      "GFX_PRINTER_STARTDOC",
+      "GFX_PRINTER_ENDDOC",
+      "GFX_PRINTER_STARTPAGE",
+      "GFX_PRINTER_DOC_IS_BUSY",
+      "ABORT",
+      "NOT_AVAILABLE",
+      "NOT_IMPLEMENTED",
+      "OUT_OF_MEMORY",
+      "UNEXPECTED",
+    ];
+
+    // PERR_FAILURE is the catch-all error message if we've gotten one that
+    // we don't recognize.
+    msgName = "PERR_FAILURE";
+
+    for (let code of MSG_CODES) {
+      let nsErrorResult = "NS_ERROR_" + code;
+      if (Components.results[nsErrorResult] == nsresult) {
+        msgName = "PERR_" + code;
+        break;
+      }
+    }
+
+    let msg, title;
+
+    if (!isPrinting) {
+      // Try first with _PP suffix.
+      let ppMsgName = msgName + "_PP";
+      try {
+        msg = this.bundle.GetStringFromName(ppMsgName);
+      } catch(e) {
+        // We allow localizers to not have the print preview error string,
+        // and just fall back to the printing error string.
+      }
+    }
+
+    if (!msg) {
+      msg = this.bundle.GetStringFromName(msgName);
+    }
+
+    title = this.bundle.GetStringFromName(isPrinting ? "print_error_dialog_title"
+                                                     : "printpreview_error_dialog_title");
+
+    let promptSvc = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
+                              .getService(Components.interfaces.nsIPromptService);
+    promptSvc.alert(window, title, msg);
+  },
+
   receiveMessage(aMessage) {
+    if (aMessage.name == "Printing:Error") {
+      this.displayPrintingError(aMessage.data.nsresult,
+                                aMessage.data.isPrinting);
+      return;
+    }
+
+    // If we got here, then the message we've received must involve
+    // updating the print progress UI.
     if (!this._webProgressPP.value) {
       // We somehow didn't get a nsIWebProgressListener to be updated...
       // I guess there's nothing to do.
       return;
     }
 
     let listener = this._webProgressPP.value;
     let mm = aMessage.target.messageManager;
@@ -532,8 +613,10 @@ var PrintUtils = {
     }
     // cancel shortkeys
     if (isModif) {
       aEvent.preventDefault();
       aEvent.stopPropagation();
     }
   }
 }
+
+PrintUtils.init();
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -1519,16 +1519,22 @@
   "SPDY_SETTINGS_IW": {
     "expires_in_version": "never",
     "kind": "exponential",
     "high": "1000",
     "n_buckets": 50,
     "extended_statistics_ok": true,
     "description": "H2: Settings Initial Window (rounded to KB)"
   },
+  "SPDY_GOAWAY_LOCAL": {
+    "expires_in_version": "never",
+    "kind": "enumerated",
+    "n_values": 32,
+    "description": "H2: goaway reason client sent from rfc 7540. 31 is none sent."
+  },
   "SPDY_GOAWAY_PEER": {
     "expires_in_version": "never",
     "kind": "enumerated",
     "n_values": 32,
     "description": "H2: goaway reason from peer from rfc 7540. 31 is none received."
   },
   "DISK_CACHE_CORRUPT_DETAILS": {
     "expires_in_version": "40",
@@ -9448,10 +9454,20 @@
     "description": "Sync storage server authentication errors. Keyed on the Sync record name."
   },
   "WEAVE_HMAC_ERRORS": {
     "alert_emails": ["fx-team@mozilla.com"],
     "expires_in_version": "45",
     "kind": "count",
     "releaseChannelCollection": "opt-out",
     "description": "Sync cryptoKeys collection HMAC mismatches."
+  },
+  "CONTENT_DOCUMENTS_DESTROYED": {
+    "expires_in_version": "never",
+    "kind": "count",
+    "description": "Number of content documents destroyed; used in conjunction with use counter histograms"
+  },
+  "TOP_LEVEL_CONTENT_DOCUMENTS_DESTROYED": {
+    "expires_in_version": "never",
+    "kind": "count",
+    "description": "Number of top-level content documents destroyed; used in conjunction with use counter histograms"
   }
 }
--- a/toolkit/components/telemetry/gen-histogram-enum.py
+++ b/toolkit/components/telemetry/gen-histogram-enum.py
@@ -21,20 +21,20 @@ import sys
 banner = """/* This file is auto-generated, see gen-histogram-enum.py.  */
 """
 
 def main(output, *filenames):
     print(banner, file=output)
     print("enum ID : uint32_t {", file=output)
 
     groups = itertools.groupby(histogram_tools.from_files(filenames),
-                               lambda h: h.name().startswith("USE_COUNTER_"))
+                               lambda h: h.name().startswith("USE_COUNTER2_"))
     seen_use_counters = False
 
-    # Note that histogram_tools.py guarantees that all of the USE_COUNTER_*
+    # Note that histogram_tools.py guarantees that all of the USE_COUNTER2_*
     # histograms are defined in a contiguous block.  We therefore assume
     # that there's at most one group for which use_counter_group is true.
     for (use_counter_group, histograms) in groups:
         if use_counter_group:
             seen_use_counters = True
 
         # The HistogramDUMMY* enum variables are used to make the computation
         # of Histogram{First,Last}UseCounter easier.  Otherwise, we'd have to
--- a/toolkit/components/telemetry/histogram_tools.py
+++ b/toolkit/components/telemetry/histogram_tools.py
@@ -288,17 +288,17 @@ def from_nsDeprecatedOperationList(filen
         for line in f:
             match = operation_regex.search(line)
             if not match:
                 continue
 
             op = match.group(1)
 
             def add_counter(context):
-                name = 'USE_COUNTER_DEPRECATED_%s_%s' % (op, context.upper())
+                name = 'USE_COUNTER2_DEPRECATED_%s_%s' % (op, context.upper())
                 histograms[name] = {
                     'expires_in_version': 'never',
                     'kind': 'boolean',
                     'description': 'Whether a %s used %s' % (context, op)
                 }
             add_counter('document')
             add_counter('page')
 
@@ -334,19 +334,19 @@ the histograms defined in filenames.
         if not isinstance(histograms, OrderedDict):
             raise BaseException, "histogram parser didn't provide an OrderedDict"
 
         for (name, definition) in histograms.iteritems():
             if all_histograms.has_key(name):
                 raise DefinitionException, "duplicate histogram name %s" % name
             all_histograms[name] = definition
 
-    # We require that all USE_COUNTER_* histograms be defined in a contiguous
+    # We require that all USE_COUNTER2_* histograms be defined in a contiguous
     # block.
-    use_counter_indices = filter(lambda x: x[1].startswith("USE_COUNTER_"),
+    use_counter_indices = filter(lambda x: x[1].startswith("USE_COUNTER2_"),
                                  enumerate(all_histograms.iterkeys()));
     if use_counter_indices:
         lower_bound = use_counter_indices[0][0]
         upper_bound = use_counter_indices[-1][0]
         n_counters = upper_bound - lower_bound + 1
         if n_counters != len(use_counter_indices):
             raise DefinitionException, "use counter histograms must be defined in a contiguous block"
 
--- a/toolkit/content/browser-content.js
+++ b/toolkit/content/browser-content.js
@@ -372,23 +372,37 @@ var Printing = {
     "Printing:Preview:Exit",
     "Printing:Preview:Navigate",
     "Printing:Preview:UpdatePageCount",
     "Printing:Print",
   ],
 
   init() {
     this.MESSAGES.forEach(msgName => addMessageListener(msgName, this));
+    addEventListener("PrintingError", this, true);
   },
 
   get shouldSavePrintSettings() {
     return Services.prefs.getBoolPref("print.use_global_printsettings", false) &&
            Services.prefs.getBoolPref("print.save_print_settings", false);
   },
 
+  handleEvent(event) {
+    if (event.type == "PrintingError") {
+      let win = event.target.defaultView;
+      let wbp = win.QueryInterface(Ci.nsIInterfaceRequestor)
+                   .getInterface(Ci.nsIWebBrowserPrint);
+      let nsresult = event.detail;
+      sendAsyncMessage("Printing:Error", {
+        isPrinting: wbp.doingPrint,
+        nsresult: nsresult,
+      });
+    }
+  },
+
   receiveMessage(message) {
     let objects = message.objects;
     let data = message.data;
     switch(message.name) {
       case "Printing:Preview:Enter": {
         this.enterPrintPreview(Services.wm.getOuterWindowWithId(data.windowID));
         break;
       }
@@ -484,16 +498,20 @@ var Printing = {
                                .getInterface(Ci.nsIWebBrowserPrint);
       print.print(printSettings, null);
     } catch(e) {
       // Pressing cancel is expressed as an NS_ERROR_ABORT return value,
       // causing an exception to be thrown which we catch here.
       if (e.result != Cr.NS_ERROR_ABORT) {
         Cu.reportError(`In Printing:Print:Done handler, got unexpected rv
                         ${e.result}.`);
+        sendAsyncMessage("Printing:Error", {
+          isPrinting: true,
+          nsresult: e.result,
+        });
       }
     }
 
     if (this.shouldSavePrintSettings) {
       let PSSVC = Cc["@mozilla.org/gfx/printsettings-service;1"]
                     .getService(Ci.nsIPrintSettingsService);
 
       PSSVC.savePrintSettingsToPrefs(printSettings, true,
new file mode 100644
--- /dev/null
+++ b/tools/power/mach_commands.py
@@ -0,0 +1,135 @@
+# 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/.
+
+from __future__ import print_function
+
+from distutils.version import StrictVersion
+
+from mach.decorators import (
+    Command,
+    CommandArgument,
+    CommandProvider,
+)
+from mozbuild.base import (
+    MachCommandBase,
+    MachCommandConditions as conditions,
+)
+
+
+def is_osx_10_10_or_greater(cls):
+    import platform
+    release = platform.mac_ver()[0]
+    return release and StrictVersion(release) >= StrictVersion('10.10')
+
+
+@CommandProvider
+class MachCommands(MachCommandBase):
+    '''
+    Get system power consumption and related measurements.
+    '''
+    def __init__(self, context):
+        MachCommandBase.__init__(self, context)
+
+    @Command('power', category='misc',
+        conditions=[is_osx_10_10_or_greater],
+        description='Get system power consumption and related measurements for '
+        'all running browsers. Available only on Mac OS X 10.10 and above. '
+        'Requires root access.')
+    @CommandArgument('-i', '--interval', type=int, default=30000,
+        help='The sample period, measured in milliseconds. Defaults to 30000.')
+    def power(self, interval):
+        import os
+        import re
+        import subprocess
+
+        rapl = os.path.join(self.topobjdir, 'dist', 'bin', 'rapl')
+
+        interval = str(interval)
+
+        # Run a trivial command with |sudo| to gain temporary root privileges
+        # before |rapl| and |powermetrics| are called. This ensures that |rapl|
+        # doesn't start measuring while |powermetrics| is waiting for the root
+        # password to be entered.
+        try:
+            subprocess.check_call(['sudo', 'true'])
+        except:
+            print('\nsudo failed; aborting')
+            return 1
+
+        # This runs rapl in the background because nothing in this script
+        # depends on the output. This is good because we want |rapl| and
+        # |powermetrics| to run at the same time.
+        subprocess.Popen([rapl, '-n', '1', '-i', interval])
+
+        lines = subprocess.check_output(['sudo', 'powermetrics',
+                                         '--samplers', 'tasks',
+                                         '--show-process-coalition',
+                                         '--show-process-gpu',
+                                         '-n', '1',
+                                         '-i', interval])
+
+        # When run with --show-process-coalition, |powermetrics| groups outputs
+        # into process coalitions, each of which has a leader.
+        #
+        # For example, when Firefox runs from the dock, its coalition looks
+        # like this:
+        #
+        #   org.mozilla.firefox
+        #     firefox
+        #     plugin-container
+        #
+        # When Safari runs from the dock:
+        #
+        #   com.apple.Safari
+        #     Safari
+        #     com.apple.WebKit.Networking
+        #     com.apple.WebKit.WebContent
+        #     com.apple.WebKit.WebContent
+        #
+        # When Chrome runs from the dock:
+        #
+        #   com.google.Chrome
+        #     Google Chrome
+        #     Google Chrome Helper
+        #     Google Chrome Helper
+        #
+        # In these cases, we want to print the whole coalition.
+        #
+        # Also, when you run any of them from the command line, things are the
+        # same except that the leader is com.apple.Terminal and there may be
+        # non-browser processes in the coalition, e.g.:
+        #
+        #  com.apple.Terminal
+        #    firefox
+        #    plugin-container
+        #    <and possibly other, non-browser processes>
+        #
+        # We want to print all these but omit uninteresting coalitions. We
+        # could do this by properly parsing powermetrics output, but it's
+        # simpler and more robust to just grep for a handful of identifying
+        # strings.
+
+        print()  # blank line between |rapl| output and |powermetrics| output
+
+        for line in lines.splitlines():
+            # Search for the following things.
+            #
+            # - '^Name' is for the columns headings line.
+            #
+            # - 'firefox' and 'plugin-container' are for Firefox
+            #
+            # - 'Safari\b' and 'WebKit' are for Safari. The '\b' excludes
+            #   SafariCloudHistoryPush, which is a process that always
+            #   runs, even when Safari isn't open.
+            #
+            # - 'Chrome' is for Chrome.
+            #
+            # - 'Terminal' is for the terminal. If no browser is running from
+            #   within the terminal, it will show up unnecessarily. This is a
+            #   minor disadvantage of this very simple parsing strategy.
+            #
+            if re.search(r'(^Name|firefox|plugin-container|Safari\b|WebKit|Chrome|Terminal)', line):
+                print(line)
+
+        return 0
--- a/widget/windows/TSFTextStore.cpp
+++ b/widget/windows/TSFTextStore.cpp
@@ -796,21 +796,29 @@ public:
 
   /****************************************************************************
    * Japanese TIP
    ****************************************************************************/
 
   // Note that TIP name may depend on the language of the environment.
   // For example, some TIP may use localized name for its target language
   // environment but English name for the others.
-  bool IsGoogleJapaneseInputActive() const
+
+  bool IsMSJapaneseIMEActive() const
   {
-    return mActiveTIPKeyboardDescription.Equals(
-             NS_LITERAL_STRING("Google \x65E5\x672C\x8A9E\x5165\x529B")) ||
-           mActiveTIPKeyboardDescription.EqualsLiteral("Google Japanese Input");
+    // FYI: Name of MS-IME for Japanese is same as MS-IME for Korean.
+    //      Therefore, we need to check the langid too.
+    return mLangID == 0x411 &&
+      (mActiveTIPKeyboardDescription.EqualsLiteral("Microsoft IME") ||
+       mActiveTIPKeyboardDescription.Equals(
+         NS_LITERAL_STRING("Microsoft \xC785\xB825\xAE30")) ||
+       mActiveTIPKeyboardDescription.Equals(
+         NS_LITERAL_STRING("\x5FAE\x8F6F\x8F93\x5165\x6CD5")) ||
+       mActiveTIPKeyboardDescription.Equals(
+         NS_LITERAL_STRING("\x5FAE\x8EDF\x8F38\x5165\x6CD5")));
   }
 
   bool IsATOKActive() const
   {
     // FYI: Name of ATOK includes the release year like "ATOK 2015".
     return StringBeginsWith(mActiveTIPKeyboardDescription,
                             NS_LITERAL_STRING("ATOK "));
   }
@@ -892,16 +900,18 @@ private:
   bool IsTIPCategoryKeyboard(REFCLSID aTextService, LANGID aLangID,
                              REFGUID aProfile);
 
   // Cookie of installing ITfInputProcessorProfileActivationSink
   DWORD mIPProfileCookie;
   // Cookie of installing ITfActiveLanguageProfileNotifySink
   DWORD mLangProfileCookie;
 
+  LANGID mLangID;
+
   // True if current IME is implemented with IMM.
   bool mIsIMM_IME;
   // True if OnActivated() is already called
   bool mOnActivatedCalled;
 
   nsRefPtr<ITfThreadMgr> mThreadMgr;
   nsRefPtr<ITfInputProcessorProfiles> mInputProcessorProfiles;
 
@@ -912,16 +922,17 @@ private:
   static StaticRefPtr<TSFStaticSink> sInstance;
 };
 
 StaticRefPtr<TSFStaticSink> TSFStaticSink::sInstance;
 
 TSFStaticSink::TSFStaticSink()
   : mIPProfileCookie(TF_INVALID_COOKIE)
   , mLangProfileCookie(TF_INVALID_COOKIE)
+  , mLangID(0)
   , mIsIMM_IME(false)
   , mOnActivatedCalled(false)
 {
 }
 
 bool
 TSFStaticSink::Init(ITfThreadMgr* aThreadMgr,
                     ITfInputProcessorProfiles* aInputProcessorProfiles)
@@ -1030,24 +1041,23 @@ TSFStaticSink::OnActivated(REFCLSID clsi
                            BOOL fActivated)
 {
   // NOTE: This is installed only on XP or Server 2003.
   if (fActivated) {
     // TODO: We should check if the profile's category is keyboard or not.
     mOnActivatedCalled = true;
     mIsIMM_IME = IsIMM_IME(::GetKeyboardLayout(0));
 
-    LANGID langID;
-    HRESULT hr = mInputProcessorProfiles->GetCurrentLanguage(&langID);
+    HRESULT hr = mInputProcessorProfiles->GetCurrentLanguage(&mLangID);
     if (FAILED(hr)) {
       MOZ_LOG(sTextStoreLog, LogLevel::Error,
              ("TSF: TSFStaticSink::OnActivated() FAILED due to "
               "GetCurrentLanguage() failure, hr=0x%08X", hr));
-    } else if (IsTIPCategoryKeyboard(clsid, langID, guidProfile)) {
-      GetTIPDescription(clsid, langID, guidProfile,
+    } else if (IsTIPCategoryKeyboard(clsid, mLangID, guidProfile)) {
+      GetTIPDescription(clsid, mLangID, guidProfile,
                         mActiveTIPKeyboardDescription);
     } else if (clsid == CLSID_NULL || guidProfile == GUID_NULL) {
       // Perhaps, this case is that keyboard layout without TIP is activated.
       mActiveTIPKeyboardDescription.Truncate();
     }
   }
 
   MOZ_LOG(sTextStoreLog, LogLevel::Info,
@@ -1071,18 +1081,19 @@ TSFStaticSink::OnActivated(DWORD dwProfi
 {
   // NOTE: This is installed only on Vista or later.  However, this may be
   //       called by EnsureInitActiveLanguageProfile() even on XP or Server
   //       2003.
   if ((dwFlags & TF_IPSINK_FLAG_ACTIVE) &&
       (dwProfileType == TF_PROFILETYPE_KEYBOARDLAYOUT ||
        catid == GUID_TFCAT_TIP_KEYBOARD)) {
     mOnActivatedCalled = true;
+    mLangID = langid;
     mIsIMM_IME = IsIMM_IME(hkl);
-    GetTIPDescription(rclsid, langid, guidProfile,
+    GetTIPDescription(rclsid, mLangID, guidProfile,
                       mActiveTIPKeyboardDescription);
   }
   MOZ_LOG(sTextStoreLog, LogLevel::Info,
          ("TSF: 0x%p TSFStaticSink::OnActivated(dwProfileType=%s (0x%08X), "
           "langid=0x%08X, rclsid=%s, catid=%s, guidProfile=%s, hkl=0x%08X, "
           "dwFlags=0x%08X (TF_IPSINK_FLAG_ACTIVE: %s)), mIsIMM_IME=%s, "
           "mActiveTIPDescription=\"%s\"",
           this, dwProfileType == TF_PROFILETYPE_INPUTPROCESSOR ?
@@ -1267,18 +1278,18 @@ StaticRefPtr<ITfInputProcessorProfiles> 
 StaticRefPtr<TSFTextStore> TSFTextStore::sEnabledTextStore;
 DWORD TSFTextStore::sClientId  = 0;
 
 bool TSFTextStore::sCreateNativeCaretForATOK = false;
 bool TSFTextStore::sDoNotReturnNoLayoutErrorToMSSimplifiedTIP = false;
 bool TSFTextStore::sDoNotReturnNoLayoutErrorToMSTraditionalTIP = false;
 bool TSFTextStore::sDoNotReturnNoLayoutErrorToFreeChangJie = false;
 bool TSFTextStore::sDoNotReturnNoLayoutErrorToEasyChangjei = false;
-bool TSFTextStore::sDoNotReturnNoLayoutErrorToGoogleJaInputAtFirstChar = false;
-bool TSFTextStore::sDoNotReturnNoLayoutErrorToGoogleJaInputAtCaret = false;
+bool TSFTextStore::sDoNotReturnNoLayoutErrorToMSJapaneseIMEAtFirstChar = false;
+bool TSFTextStore::sDoNotReturnNoLayoutErrorToMSJapaneseIMEAtCaret = false;
 bool TSFTextStore::sHackQueryInsertForMSSimplifiedTIP = false;
 bool TSFTextStore::sHackQueryInsertForMSTraditionalTIP = false;
 
 #define TEXTSTORE_DEFAULT_VIEW (1)
 
 TSFTextStore::TSFTextStore()
   : mEditCookie(0)
   , mSinkMask(0)
@@ -3491,49 +3502,51 @@ TSFTextStore::GetTextExt(TsViewCookie vc
   // NOTE: TSF (at least on Win 8.1) doesn't return TS_E_NOLAYOUT to the
   // caller even if we return it.  It's converted to just E_FAIL.
   // However, this is fixed on Win 10.
 
   const TSFStaticSink* kSink = TSFStaticSink::GetInstance();
   if (mComposition.IsComposing() && mComposition.mStart < acpEnd &&
       mLockedContent.IsLayoutChangedAfter(acpEnd)) {
     const Selection& currentSel = CurrentSelection();
-    if ((sDoNotReturnNoLayoutErrorToGoogleJaInputAtFirstChar ||
-         sDoNotReturnNoLayoutErrorToGoogleJaInputAtCaret) &&
-        kSink->IsGoogleJapaneseInputActive()) {
-      // Google Japanese Input doesn't handle ITfContextView::GetTextExt()
-      // properly due to the same bug of TSF mentioned above.  Google Japanese
-      // Input calls this twice for the first character of changing range of
-      // composition string and the caret which is typically at the end of
-      // composition string.  The formar is used for showing candidate window.
-      // This is typically shown at wrong position.  We should avoid only this
-      // case. This is not necessary on Windows 10.
-      if (sDoNotReturnNoLayoutErrorToGoogleJaInputAtFirstChar &&
-          !mLockedContent.IsLayoutChangedAfter(acpStart) &&
-          acpStart < acpEnd) {
-        acpEnd = acpStart;
-        MOZ_LOG(sTextStoreLog, LogLevel::Debug,
-               ("TSF: 0x%p   TSFTextStore::GetTextExt() hacked the offsets of "
-                "the first character of changing range of the composition "
-                "string for TIP acpStart=%d, acpEnd=%d",
-                this, acpStart, acpEnd));
-      }
-      // Google Japanese Input sometimes uses caret position for deciding its
-      // candidate window position. In such case, we should return the previous
-      // offset of selected clause. However, it's difficult to get where is
-      // selected clause for now.  Instead, we should use the first character
-      // which is modified. This is useful in most cases.
-      else if (sDoNotReturnNoLayoutErrorToGoogleJaInputAtCaret &&
-               acpStart == acpEnd &&
-               currentSel.IsCollapsed() && currentSel.EndOffset() == acpEnd) {
-        acpEnd = acpStart = mLockedContent.MinOffsetOfLayoutChanged();
-        MOZ_LOG(sTextStoreLog, LogLevel::Debug,
-               ("TSF: 0x%p   TSFTextStore::GetTextExt() hacked the offsets of "
-                "the caret of the composition string for TIP acpStart=%d, "
-                "acpEnd=%d", this, acpStart, acpEnd));
+    if ((sDoNotReturnNoLayoutErrorToMSJapaneseIMEAtFirstChar ||
+         sDoNotReturnNoLayoutErrorToMSJapaneseIMEAtCaret) &&
+        kSink->IsMSJapaneseIMEActive()) {
+      // MS IME for Japanese doesn't support asynchronous handling at deciding
+      // its suggest list window position.  The feature was implemented
+      // starting from Windows 8.
+      if (IsWin8OrLater()) {
+        // Basically, MS-IME tries to retrieve whole composition string rect
+        // at deciding suggest window immediately after unlocking the document.
+        // However, in e10s mode, the content hasn't updated yet in most cases.
+        // Therefore, if the first character at the retrieving range rect is
+        // available, we should use it as the result.
+        if (sDoNotReturnNoLayoutErrorToMSJapaneseIMEAtFirstChar &&
+            !mLockedContent.IsLayoutChangedAfter(acpStart) &&
+            acpStart < acpEnd) {
+          acpEnd = acpStart;
+          MOZ_LOG(sTextStoreLog, LogLevel::Debug,
+                 ("TSF: 0x%p   TSFTextStore::GetTextExt() hacked the offsets "
+                  "of the first character of changing range of the composition "
+                  "string for TIP acpStart=%d, acpEnd=%d",
+                  this, acpStart, acpEnd));
+        }
+        // Although, the condition is not clear, MS-IME sometimes retrieves the
+        // caret rect immediately after modifying the composition string but
+        // before unlocking the document.  In such case, we should return the
+        // nearest character rect.
+        else if (sDoNotReturnNoLayoutErrorToMSJapaneseIMEAtCaret &&
+                 acpStart == acpEnd &&
+                 currentSel.IsCollapsed() && currentSel.EndOffset() == acpEnd) {
+          acpEnd = acpStart = mLockedContent.MinOffsetOfLayoutChanged();
+          MOZ_LOG(sTextStoreLog, LogLevel::Debug,
+                 ("TSF: 0x%p   TSFTextStore::GetTextExt() hacked the offsets "
+                  "of the caret of the composition string for TIP acpStart=%d, "
+                  "acpEnd=%d", this, acpStart, acpEnd));
+        }
       }
     }
     // Free ChangJie 2010 and Easy Changjei 1.0.12.0 doesn't handle
     // ITfContextView::GetTextExt() properly.  Prehaps, it's due to the bug of
     // TSF.  We need to check if this is necessary on Windows 10 before
     // disabling this on Windows 10.
     else if ((sDoNotReturnNoLayoutErrorToFreeChangJie &&
               kSink->IsFreeChangJieActive()) ||
@@ -5221,47 +5234,47 @@ TSFTextStore::Initialize()
       "intl.tsf.hack.ms_traditional_chinese.do_not_return_no_layout_error",
       true);
   sDoNotReturnNoLayoutErrorToFreeChangJie =
     Preferences::GetBool(
       "intl.tsf.hack.free_chang_jie.do_not_return_no_layout_error", true);
   sDoNotReturnNoLayoutErrorToEasyChangjei =
     Preferences::GetBool(
       "intl.tsf.hack.easy_changjei.do_not_return_no_layout_error", true);
-  sDoNotReturnNoLayoutErrorToGoogleJaInputAtFirstChar =
+  sDoNotReturnNoLayoutErrorToMSJapaneseIMEAtFirstChar =
     Preferences::GetBool(
-      "intl.tsf.hack.google_ja_input."
-        "do_not_return_no_layout_error_at_first_char", true);
-  sDoNotReturnNoLayoutErrorToGoogleJaInputAtCaret =
+      "intl.tsf.hack.ms_japanese_ime."
+      "do_not_return_no_layout_error_at_first_char", true);
+  sDoNotReturnNoLayoutErrorToMSJapaneseIMEAtCaret =
     Preferences::GetBool(
-      "intl.tsf.hack.google_ja_input.do_not_return_no_layout_error_at_caret",
+      "intl.tsf.hack.ms_japanese_ime.do_not_return_no_layout_error_at_caret",
       true);
   sHackQueryInsertForMSSimplifiedTIP =
     Preferences::GetBool(
       "intl.tsf.hack.ms_simplified_chinese.query_insert_result", true);
   sHackQueryInsertForMSTraditionalTIP =
     Preferences::GetBool(
       "intl.tsf.hack.ms_traditional_chinese.query_insert_result", true);
 
   MOZ_LOG(sTextStoreLog, LogLevel::Info,
     ("TSF:   TSFTextStore::Initialize(), sThreadMgr=0x%p, "
      "sClientId=0x%08X, sDisplayAttrMgr=0x%p, "
      "sCategoryMgr=0x%p, sDisabledDocumentMgr=0x%p, sDisabledContext=%p, "
      "sCreateNativeCaretForATOK=%s, "
      "sDoNotReturnNoLayoutErrorToFreeChangJie=%s, "
      "sDoNotReturnNoLayoutErrorToEasyChangjei=%s, "
-     "sDoNotReturnNoLayoutErrorToGoogleJaInputAtFirstChar=%s, "
-     "sDoNotReturnNoLayoutErrorToGoogleJaInputAtCaret=%s",
+     "sDoNotReturnNoLayoutErrorToMSJapaneseIMEAtFirstChar=%s, "
+     "sDoNotReturnNoLayoutErrorToMSJapaneseIMEAtCaret=%s",
      sThreadMgr.get(), sClientId, sDisplayAttrMgr.get(),
      sCategoryMgr.get(), sDisabledDocumentMgr.get(), sDisabledContext.get(),
      GetBoolName(sCreateNativeCaretForATOK),
      GetBoolName(sDoNotReturnNoLayoutErrorToFreeChangJie),
      GetBoolName(sDoNotReturnNoLayoutErrorToEasyChangjei),
-     GetBoolName(sDoNotReturnNoLayoutErrorToGoogleJaInputAtFirstChar),
-     GetBoolName(sDoNotReturnNoLayoutErrorToGoogleJaInputAtCaret)));
+     GetBoolName(sDoNotReturnNoLayoutErrorToMSJapaneseIMEAtFirstChar),
+     GetBoolName(sDoNotReturnNoLayoutErrorToMSJapaneseIMEAtCaret)));
 }
 
 // static
 void
 TSFTextStore::Terminate()
 {
   MOZ_LOG(sTextStoreLog, LogLevel::Info, ("TSF: TSFTextStore::Terminate()"));
 
--- a/widget/windows/TSFTextStore.h
+++ b/widget/windows/TSFTextStore.h
@@ -834,18 +834,18 @@ protected:
   static DWORD sClientId;
 
   // Enables/Disables hack for specific TIP.
   static bool sCreateNativeCaretForATOK;
   static bool sDoNotReturnNoLayoutErrorToMSSimplifiedTIP;
   static bool sDoNotReturnNoLayoutErrorToMSTraditionalTIP;
   static bool sDoNotReturnNoLayoutErrorToFreeChangJie;
   static bool sDoNotReturnNoLayoutErrorToEasyChangjei;
-  static bool sDoNotReturnNoLayoutErrorToGoogleJaInputAtFirstChar;
-  static bool sDoNotReturnNoLayoutErrorToGoogleJaInputAtCaret;
+  static bool sDoNotReturnNoLayoutErrorToMSJapaneseIMEAtFirstChar;
+  static bool sDoNotReturnNoLayoutErrorToMSJapaneseIMEAtCaret;
   static bool sHackQueryInsertForMSSimplifiedTIP;
   static bool sHackQueryInsertForMSTraditionalTIP;
 };
 
 } // namespace widget
 } // namespace mozilla
 
 #endif // #ifndef TSFTextStore_h_