merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Mon, 30 Mar 2015 11:59:17 +0200
changeset 266773 6082a98d38613b236758b852ad751b7b454c79c0
parent 266750 5dfc78816549039788ff4d10070c1aa2f9164a74 (current diff)
parent 266772 30bf6cb70f615d9ffb9a0e55220f8c15075b700e (diff)
child 266774 3eca9f579de60f875213bd38c1f457163166016b
child 267853 8dda027310f53d000e2761f9116fb1920e22b386
child 267951 0c57e40f0b98a1b757006fdf9e47f3cfbd2be448
push id830
push userraliiev@mozilla.com
push dateFri, 19 Jun 2015 19:24:37 +0000
treeherdermozilla-release@932614382a68 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone39.0a1
first release with
nightly linux32
6082a98d3861 / 39.0a1 / 20150330030203 / files
nightly linux64
6082a98d3861 / 39.0a1 / 20150330030203 / files
nightly mac
6082a98d3861 / 39.0a1 / 20150330030203 / files
nightly win32
6082a98d3861 / 39.0a1 / 20150330030203 / files
nightly win64
6082a98d3861 / 39.0a1 / 20150330030203 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central a=merge
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -318,17 +318,17 @@ pref("media.cache_readahead_limit", 30);
 pref("media.fragmented-mp4.gonk.enabled", true);
 #endif
 // The default number of decoded video frames that are enqueued in
 // MediaDecoderReader's mVideoQueue.
 pref("media.video-queue.default-size", 3);
 
 // optimize images' memory usage
 pref("image.decode-only-on-draw.enabled", true);
-pref("image.mem.allow_locking_in_content_processes", false); /* don't allow image locking */
+pref("image.mem.allow_locking_in_content_processes", true);
 // Limit the surface cache to 1/8 of main memory or 128MB, whichever is smaller.
 // Almost everything that was factored into 'max_decoded_image_kb' is now stored
 // in the surface cache.  1/8 of main memory is 32MB on a 256MB device, which is
 // about the same as the old 'max_decoded_image_kb'.
 pref("image.mem.surfacecache.max_size_kb", 131072);  // 128MB
 pref("image.mem.surfacecache.size_factor", 8);  // 1/8 of main memory
 pref("image.mem.surfacecache.discard_factor", 2);  // Discard 1/2 of the surface cache at a time.
 pref("image.mem.surfacecache.min_expiration_ms", 86400000); // 24h, we rely on the out of memory hook
--- a/browser/base/content/sanitize.js
+++ b/browser/base/content/sanitize.js
@@ -194,16 +194,21 @@ Sanitizer.prototype = {
               cookieMgr.remove(cookie.host, cookie.name, cookie.path, false);
           }
         }
         else {
           // Remove everything
           cookieMgr.removeAll();
         }
 
+        // Clear deviceIds. Done asynchronously (returns before complete).
+        let mediaMgr = Components.classes["@mozilla.org/mediaManagerService;1"]
+                                 .getService(Ci.nsIMediaManagerService);
+        mediaMgr.sanitizeDeviceIds(this.range && this.range[0]);
+
         // Clear plugin data.
         const phInterface = Ci.nsIPluginHost;
         const FLAG_CLEAR_ALL = phInterface.FLAG_CLEAR_ALL;
         let ph = Cc["@mozilla.org/plugin/host;1"].getService(phInterface);
 
         // Determine age range in seconds. (-1 means clear all.) We don't know
         // that this.range[1] is actually now, so we compute age range based
         // on the lower bound. If this.range results in a negative age, do
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -2843,17 +2843,16 @@ let E10SUINotification = {
         let win = RecentWindow.getMostRecentBrowserWindow();
         let winutils = win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
         isHardwareAccelerated = winutils.layerManagerType != "Basic";
       } catch (e) {}
 #endif
 
       if (!Services.appinfo.inSafeMode &&
           !Services.appinfo.accessibilityEnabled &&
-          !Services.appinfo.keyboardMayHaveIME &&
           isHardwareAccelerated &&
           e10sPromptShownCount < 5) {
         Services.tm.mainThread.dispatch(() => {
           try {
             this._showE10SPrompt();
             Services.prefs.setIntPref(this.CURRENT_PROMPT_PREF, e10sPromptShownCount + 1);
             Services.prefs.clearUserPref(this.PREVIOUS_PROMPT_PREF);
           } catch (ex) {
new file mode 100644
--- /dev/null
+++ b/dom/media/MediaDeviceInfo.cpp
@@ -0,0 +1,67 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/MediaDeviceInfo.h"
+#include "mozilla/dom/MediaStreamBinding.h"
+#include "mozilla/MediaManager.h"
+#include "nsIScriptGlobalObject.h"
+
+namespace mozilla {
+namespace dom {
+
+MediaDeviceInfo::MediaDeviceInfo(const nsAString& aDeviceId,
+                                 MediaDeviceKind aKind,
+                                 const nsAString& aLabel,
+                                 const nsAString& aGroupId)
+  : mKind(aKind)
+  , mDeviceId(aDeviceId)
+  , mLabel(aLabel)
+  , mGroupId(aGroupId) {}
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(MediaDeviceInfo)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(MediaDeviceInfo)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(MediaDeviceInfo)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MediaDeviceInfo)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+JSObject*
+MediaDeviceInfo::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
+{
+  return MediaDeviceInfoBinding::Wrap(aCx, this, aGivenProto);
+}
+
+nsISupports* MediaDeviceInfo::GetParentObject()
+{
+  return nullptr;
+}
+
+void MediaDeviceInfo::GetDeviceId(nsString& retval)
+{
+  retval = mDeviceId;
+}
+
+MediaDeviceKind
+MediaDeviceInfo::Kind()
+{
+  return mKind;
+}
+
+void MediaDeviceInfo::GetGroupId(nsString& retval)
+{
+  retval = mGroupId;
+}
+
+void MediaDeviceInfo::GetLabel(nsString& retval)
+{
+  retval = mLabel;
+}
+
+MediaDeviceKind Kind();
+void GetLabel(nsString& retval);
+void GetGroupId(nsString& retval);
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/media/MediaDeviceInfo.h
@@ -0,0 +1,60 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_MediaDeviceInfo_h
+#define mozilla_dom_MediaDeviceInfo_h
+
+#include "mozilla/ErrorResult.h"
+#include "nsISupportsImpl.h"
+#include "mozilla/dom/BindingUtils.h"
+#include "MediaDeviceInfoBinding.h"
+#include "nsPIDOMWindow.h"
+
+namespace mozilla {
+namespace dom {
+
+class Promise;
+struct MediaStreamConstraints;
+
+#define MOZILLA_DOM_MEDIADEVICEINFO_IMPLEMENTATION_IID \
+{0x25091870, 0x84d6, 0x4acf, {0xaf, 0x97, 0x6e, 0xd5, 0x5b, 0xe0, 0x47, 0xb2}}
+
+class MediaDeviceInfo final : public nsISupports, public nsWrapperCache
+{
+public:
+  explicit MediaDeviceInfo(const nsAString& aDeviceId,
+                           MediaDeviceKind aKind,
+                           const nsAString& aLabel,
+                           const nsAString& aGroupId = nsString());
+
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(MediaDeviceInfo)
+  NS_DECLARE_STATIC_IID_ACCESSOR(MOZILLA_DOM_MEDIADEVICEINFO_IMPLEMENTATION_IID)
+
+  JSObject*
+  WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto) override;
+
+  nsISupports* GetParentObject();
+
+  void GetDeviceId(nsString& retval);
+  MediaDeviceKind Kind();
+  void GetLabel(nsString& retval);
+  void GetGroupId(nsString& retval);
+
+private:
+  MediaDeviceKind mKind;
+  nsString mDeviceId;
+  nsString mLabel;
+  nsString mGroupId;
+
+  virtual ~MediaDeviceInfo() {}
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(MediaDeviceInfo,
+                              MOZILLA_DOM_MEDIADEVICEINFO_IMPLEMENTATION_IID)
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_MediaDeviceInfo_h
--- a/dom/media/MediaDevices.cpp
+++ b/dom/media/MediaDevices.cpp
@@ -4,16 +4,17 @@
 
 #include "mozilla/dom/MediaDevices.h"
 #include "mozilla/dom/MediaStreamBinding.h"
 #include "mozilla/dom/MediaDevicesBinding.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/MediaManager.h"
 #include "nsIEventTarget.h"
 #include "nsIScriptGlobalObject.h"
+#include "nsIPermissionManager.h"
 #include "nsPIDOMWindow.h"
 
 namespace mozilla {
 namespace dom {
 
 class MediaDevices::GumResolver : public nsIDOMGetUserMediaSuccessCallback
 {
 public:
@@ -32,16 +33,119 @@ public:
     return NS_OK;
   }
 
 private:
   virtual ~GumResolver() {}
   nsRefPtr<Promise> mPromise;
 };
 
+class MediaDevices::EnumDevResolver : public nsIGetUserMediaDevicesSuccessCallback
+{
+  static bool HasAPersistentPermission(uint64_t aWindowId)
+  {
+    nsPIDOMWindow *window = static_cast<nsPIDOMWindow*>
+        (nsGlobalWindow::GetInnerWindowWithId(aWindowId));
+    if (NS_WARN_IF(!window)) {
+      return false;
+    }
+    // Check if this site has persistent permissions.
+    nsresult rv;
+    nsCOMPtr<nsIPermissionManager> mgr =
+      do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return false; // no permission manager no permissions!
+    }
+
+    uint32_t audio = nsIPermissionManager::UNKNOWN_ACTION;
+    uint32_t video = nsIPermissionManager::UNKNOWN_ACTION;
+    {
+      auto* principal = window->GetExtantDoc()->NodePrincipal();
+      rv = mgr->TestExactPermissionFromPrincipal(principal, "microphone", &audio);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return false;
+      }
+      rv = mgr->TestExactPermissionFromPrincipal(principal, "camera", &video);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return false;
+      }
+    }
+    return audio == nsIPermissionManager::ALLOW_ACTION ||
+           video == nsIPermissionManager::ALLOW_ACTION;
+  }
+
+public:
+  NS_DECL_ISUPPORTS
+
+  EnumDevResolver(Promise* aPromise, uint64_t aWindowId)
+  : mPromise(aPromise), mWindowId(aWindowId) {}
+
+  NS_IMETHOD
+  OnSuccess(nsIVariant* aDevices) override
+  {
+    // Cribbed from MediaPermissionGonk.cpp
+    nsIID elementIID;
+    uint16_t elementType;
+
+    // Create array for nsIMediaDevice
+    nsTArray<nsCOMPtr<nsIMediaDevice>> devices;
+    // Contain the fumes
+    {
+      void* rawArray;
+      uint32_t arrayLen;
+      nsresult rv;
+      rv = aDevices->GetAsArray(&elementType, &elementIID, &arrayLen, &rawArray);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      if (elementType != nsIDataType::VTYPE_INTERFACE) {
+        NS_Free(rawArray);
+        return NS_ERROR_FAILURE;
+      }
+
+      nsISupports **supportsArray = reinterpret_cast<nsISupports **>(rawArray);
+      for (uint32_t i = 0; i < arrayLen; ++i) {
+        nsCOMPtr<nsIMediaDevice> device(do_QueryInterface(supportsArray[i]));
+        devices.AppendElement(device);
+        NS_IF_RELEASE(supportsArray[i]); // explicitly decrease refcount for rawptr
+      }
+      NS_Free(rawArray); // explicitly free memory from nsIVariant::GetAsArray
+    }
+    nsTArray<nsRefPtr<MediaDeviceInfo>> infos;
+    for (auto& device : devices) {
+      nsString type;
+      device->GetType(type);
+      bool isVideo = type.EqualsLiteral("video");
+      bool isAudio = type.EqualsLiteral("audio");
+      if (isVideo || isAudio) {
+        MediaDeviceKind kind = isVideo ?
+            MediaDeviceKind::Videoinput : MediaDeviceKind::Audioinput;
+        nsString id;
+        nsString name;
+        device->GetId(id);
+        // Include name only if page currently has a gUM stream active or
+        // persistent permissions (audio or video) have been granted
+        if (MediaManager::Get()->IsWindowActivelyCapturing(mWindowId) ||
+            HasAPersistentPermission(mWindowId) ||
+            Preferences::GetBool("media.navigator.permission.disabled", false)) {
+          device->GetName(name);
+        }
+        nsRefPtr<MediaDeviceInfo> info = new MediaDeviceInfo(id, kind, name);
+        infos.AppendElement(info);
+      }
+    }
+    mPromise->MaybeResolve(infos);
+    return NS_OK;
+  }
+
+private:
+  virtual ~EnumDevResolver() {}
+  nsRefPtr<Promise> mPromise;
+  uint64_t mWindowId;
+};
+
 class MediaDevices::GumRejecter : public nsIDOMGetUserMediaErrorCallback
 {
 public:
   NS_DECL_ISUPPORTS
 
   explicit GumRejecter(Promise* aPromise) : mPromise(aPromise) {}
 
   NS_IMETHOD
@@ -56,36 +160,51 @@ public:
   }
 
 private:
   virtual ~GumRejecter() {}
   nsRefPtr<Promise> mPromise;
 };
 
 NS_IMPL_ISUPPORTS(MediaDevices::GumResolver, nsIDOMGetUserMediaSuccessCallback)
+NS_IMPL_ISUPPORTS(MediaDevices::EnumDevResolver, nsIGetUserMediaDevicesSuccessCallback)
 NS_IMPL_ISUPPORTS(MediaDevices::GumRejecter, nsIDOMGetUserMediaErrorCallback)
 
 already_AddRefed<Promise>
 MediaDevices::GetUserMedia(const MediaStreamConstraints& aConstraints,
                            ErrorResult &aRv)
 {
-  ErrorResult rv;
   nsPIDOMWindow* window = GetOwner();
   nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(window);
   nsRefPtr<Promise> p = Promise::Create(go, aRv);
-  NS_ENSURE_TRUE(!rv.Failed(), nullptr);
+  NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
 
   nsRefPtr<GumResolver> resolver = new GumResolver(p);
   nsRefPtr<GumRejecter> rejecter = new GumRejecter(p);
 
   aRv = MediaManager::Get()->GetUserMedia(window, aConstraints,
                                           resolver, rejecter);
   return p.forget();
 }
 
+already_AddRefed<Promise>
+MediaDevices::EnumerateDevices(ErrorResult &aRv)
+{
+  nsPIDOMWindow* window = GetOwner();
+  nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(window);
+  nsRefPtr<Promise> p = Promise::Create(go, aRv);
+  NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
+
+  nsRefPtr<EnumDevResolver> resolver = new EnumDevResolver(p, window->WindowID());
+  nsRefPtr<GumRejecter> rejecter = new GumRejecter(p);
+
+  aRv = MediaManager::Get()->EnumerateDevices(window, resolver, rejecter);
+  return p.forget();
+}
+
 NS_IMPL_ADDREF_INHERITED(MediaDevices, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(MediaDevices, DOMEventTargetHelper)
 NS_INTERFACE_MAP_BEGIN(MediaDevices)
   NS_INTERFACE_MAP_ENTRY(MediaDevices)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
 JSObject*
 MediaDevices::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
--- a/dom/media/MediaDevices.h
+++ b/dom/media/MediaDevices.h
@@ -1,14 +1,14 @@
 /* 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 MediaDevices_h__
-#define MediaDevices_h__
+#ifndef mozilla_dom_MediaDevices_h
+#define mozilla_dom_MediaDevices_h
 
 #include "mozilla/ErrorResult.h"
 #include "nsISupportsImpl.h"
 #include "mozilla/DOMEventTargetHelper.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "nsPIDOMWindow.h"
 
 namespace mozilla {
@@ -30,22 +30,26 @@ public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECLARE_STATIC_IID_ACCESSOR(MOZILLA_DOM_MEDIADEVICES_IMPLEMENTATION_IID)
 
   JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto) override;
 
   already_AddRefed<Promise>
   GetUserMedia(const MediaStreamConstraints& aConstraints, ErrorResult &aRv);
 
+  already_AddRefed<Promise>
+  EnumerateDevices(ErrorResult &aRv);
+
 private:
   class GumResolver;
+  class EnumDevResolver;
   class GumRejecter;
 
   virtual ~MediaDevices() {}
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(MediaDevices,
                               MOZILLA_DOM_MEDIADEVICES_IMPLEMENTATION_IID)
 
 } // namespace dom
 } // namespace mozilla
 
-#endif // MediaDevices_h__
+#endif // mozilla_dom_MediaDevices_h
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -20,33 +20,47 @@
 #include "nsIPopupWindowManager.h"
 #include "nsISupportsArray.h"
 #include "nsIDocShell.h"
 #include "nsIDocument.h"
 #include "nsISupportsPrimitives.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIIDNService.h"
 #include "nsNetUtil.h"
+#include "nsPrincipal.h"
+#include "nsICryptoHash.h"
+#include "nsICryptoHMAC.h"
+#include "nsIKeyModule.h"
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsIInputStream.h"
+#include "nsILineInputStream.h"
 #include "mozilla/Types.h"
 #include "mozilla/PeerIdentity.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/MediaStreamBinding.h"
 #include "mozilla/dom/MediaStreamTrackBinding.h"
 #include "mozilla/dom/GetUserMediaRequestBinding.h"
 #include "mozilla/Preferences.h"
+#include "mozilla/Base64.h"
+#include "mozilla/ipc/BackgroundChild.h"
+#include "mozilla/media/MediaChild.h"
 #include "MediaTrackConstraints.h"
-
+#include "VideoUtils.h"
 #include "Latency.h"
 
 // For PR_snprintf
 #include "prprf.h"
 
 #include "nsJSUtils.h"
 #include "nsGlobalWindow.h"
+#include "nsIUUIDGenerator.h"
+#include "nspr.h"
+#include "nss.h"
+#include "pk11pub.h"
 
 /* Using WebRTC backend on Desktops (Mac, Windows, Linux), otherwise default */
 #include "MediaEngineDefault.h"
 #if defined(MOZ_WEBRTC)
 #include "MediaEngineWebRTC.h"
 #include "browser_logging/WebRtcLog.h"
 #endif
 
@@ -189,55 +203,71 @@ HostHasPermission(nsIURI &docURI)
     }
 
     begin = end + 1;
   } while (end < domainWhiteList.Length());
 
   return false;
 }
 
-ErrorCallbackRunnable::ErrorCallbackRunnable(
-  nsCOMPtr<nsIDOMGetUserMediaSuccessCallback>& aOnSuccess,
-  nsCOMPtr<nsIDOMGetUserMediaErrorCallback>& aOnFailure,
-  MediaMgrError& aError,
-  uint64_t aWindowID)
-  : mError(&aError)
-  , mWindowID(aWindowID)
-  , mManager(MediaManager::GetInstance())
+/**
+ * Send an error back to content.
+ * Do this only on the main thread. The onSuccess callback is also passed here
+ * so it can be released correctly.
+ */
+template<class SuccessCallbackType>
+class ErrorCallbackRunnable : public nsRunnable
 {
-  mOnSuccess.swap(aOnSuccess);
-  mOnFailure.swap(aOnFailure);
-}
+public:
+  ErrorCallbackRunnable(
+    nsCOMPtr<SuccessCallbackType>& aOnSuccess,
+    nsCOMPtr<nsIDOMGetUserMediaErrorCallback>& aOnFailure,
+    MediaMgrError& aError,
+    uint64_t aWindowID)
+    : mError(&aError)
+    , mWindowID(aWindowID)
+    , mManager(MediaManager::GetInstance())
+  {
+    mOnSuccess.swap(aOnSuccess);
+    mOnFailure.swap(aOnFailure);
+  }
 
-ErrorCallbackRunnable::~ErrorCallbackRunnable()
-{
-  MOZ_ASSERT(!mOnSuccess && !mOnFailure);
-}
+  NS_IMETHODIMP
+  Run()
+  {
+    NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
+
+    nsCOMPtr<SuccessCallbackType> onSuccess = mOnSuccess.forget();
+    nsCOMPtr<nsIDOMGetUserMediaErrorCallback> onFailure = mOnFailure.forget();
 
-NS_IMETHODIMP
-ErrorCallbackRunnable::Run()
-{
-  // Only run if the window is still active.
-  NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
-
-  nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> onSuccess = mOnSuccess.forget();
-  nsCOMPtr<nsIDOMGetUserMediaErrorCallback> onFailure = mOnFailure.forget();
-
-  if (!(mManager->IsWindowStillActive(mWindowID))) {
+    // Only run if the window is still active.
+    if (!(mManager->IsWindowStillActive(mWindowID))) {
+      return NS_OK;
+    }
+    // This is safe since we're on main-thread, and the windowlist can only
+    // be invalidated from the main-thread (see OnNavigation)
+    nsGlobalWindow* window = nsGlobalWindow::GetInnerWindowWithId(mWindowID);
+    if (window) {
+      nsRefPtr<MediaStreamError> error = new MediaStreamError(window, *mError);
+      onFailure->OnError(error);
+    }
     return NS_OK;
   }
-  // This is safe since we're on main-thread, and the windowlist can only
-  // be invalidated from the main-thread (see OnNavigation)
-  nsGlobalWindow* window = nsGlobalWindow::GetInnerWindowWithId(mWindowID);
-  if (window) {
-    nsRefPtr<MediaStreamError> error = new MediaStreamError(window, *mError);
-    onFailure->OnError(error);
+private:
+  ~ErrorCallbackRunnable()
+  {
+    MOZ_ASSERT(!mOnSuccess && !mOnFailure);
   }
-  return NS_OK;
-}
+
+  nsCOMPtr<SuccessCallbackType> mOnSuccess;
+  nsCOMPtr<nsIDOMGetUserMediaErrorCallback> mOnFailure;
+  nsRefPtr<MediaMgrError> mError;
+  uint64_t mWindowID;
+  nsRefPtr<MediaManager> mManager; // get ref to this when creating the runnable
+};
 
 /**
  * Invoke the "onSuccess" callback in content. The callback will take a
  * DOMBlob in the case of {picture:true}, and a MediaStream in the case of
  * {audio:true} or {video:true}. There is a constructor available for each
  * form. Do this only on the main thread.
  */
 class SuccessCallbackRunnable : public nsRunnable
@@ -297,60 +327,111 @@ public:
     : mDevices(aDevices)
     , mWindowID(aWindowID)
     , mManager(MediaManager::GetInstance())
   {
     mOnSuccess.swap(aOnSuccess);
     mOnFailure.swap(aOnFailure);
   }
 
+  nsresult
+  AnonymizeId(nsAString& aId, const nsACString& aOriginKey)
+  {
+    nsresult rv;
+    nsCOMPtr<nsIKeyObjectFactory> factory =
+      do_GetService("@mozilla.org/security/keyobjectfactory;1", &rv);
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
+    nsCString rawKey;
+    rv = Base64Decode(aOriginKey, rawKey);
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
+    nsCOMPtr<nsIKeyObject> key;
+    rv = factory->KeyFromString(nsIKeyObject::HMAC, rawKey, getter_AddRefs(key));
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
+
+    nsCOMPtr<nsICryptoHMAC> hasher =
+      do_CreateInstance(NS_CRYPTO_HMAC_CONTRACTID, &rv);
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
+    rv = hasher->Init(nsICryptoHMAC::SHA256, key);
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
+    NS_ConvertUTF16toUTF8 id(aId);
+    rv = hasher->Update(reinterpret_cast<const uint8_t*> (id.get()), id.Length());
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
+    nsCString mac;
+    rv = hasher->Finish(true, mac);
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
+
+    aId = NS_ConvertUTF8toUTF16(mac);
+    return NS_OK;
+  }
+
   NS_IMETHOD
   Run()
   {
     NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
 
     // Only run if window is still on our active list.
     if (!mManager->IsWindowStillActive(mWindowID)) {
       return NS_OK;
     }
 
     nsCOMPtr<nsIWritableVariant> devices =
       do_CreateInstance("@mozilla.org/variant;1");
 
-    int32_t len = mDevices->Length();
+    size_t len = mDevices->Length();
     if (len == 0) {
       // XXX
       // We should in the future return an empty array, and dynamically add
       // devices to the dropdowns if things are hotplugged while the
       // requester is up.
       nsGlobalWindow* window = nsGlobalWindow::GetInnerWindowWithId(mWindowID);
       if (window) {
         nsRefPtr<MediaStreamError> error = new MediaStreamError(window,
             NS_LITERAL_STRING("NotFoundError"));
         mOnFailure->OnError(error);
       }
       return NS_OK;
     }
 
     nsTArray<nsIMediaDevice*> tmp(len);
-    for (int32_t i = 0; i < len; i++) {
-      tmp.AppendElement(mDevices->ElementAt(i));
+    for (auto& device : *mDevices) {
+      if (!mOriginKey.IsEmpty()) {
+        nsString id;
+        device->GetId(id);
+        AnonymizeId(id, mOriginKey);
+        device->SetId(id);
+      }
+      tmp.AppendElement(device);
     }
 
     devices->SetAsArray(nsIDataType::VTYPE_INTERFACE,
                         &NS_GET_IID(nsIMediaDevice),
                         mDevices->Length(),
                         const_cast<void*>(
                           static_cast<const void*>(tmp.Elements())
                         ));
 
     mOnSuccess->OnSuccess(devices);
     return NS_OK;
   }
 
+  nsCString mOriginKey;
 private:
   nsCOMPtr<nsIGetUserMediaDevicesSuccessCallback> mOnSuccess;
   nsCOMPtr<nsIDOMGetUserMediaErrorCallback> mOnFailure;
   nsAutoPtr<nsTArray<nsRefPtr<MediaDevice>>> mDevices;
   uint64_t mWindowID;
   nsRefPtr<MediaManager> mManager;
 };
 
@@ -511,16 +592,22 @@ AudioDevice::GetType(nsAString& aType)
 
 NS_IMETHODIMP
 MediaDevice::GetId(nsAString& aID)
 {
   aID.Assign(mID);
   return NS_OK;
 }
 
+void
+MediaDevice::SetId(const nsAString& aID)
+{
+  mID.Assign(aID);
+}
+
 NS_IMETHODIMP
 MediaDevice::GetFacingMode(nsAString& aFacingMode)
 {
   if (mHasFacingMode) {
     aFacingMode.Assign(NS_ConvertUTF8toUTF16(
         dom::VideoFacingModeEnumValues::strings[uint32_t(mFacingMode)].value));
   } else {
     aFacingMode.Truncate(0);
@@ -721,16 +808,36 @@ public:
   bool mAgcOn;
   bool mNoiseOn;
   uint32_t mEcho;
   uint32_t mAgc;
   uint32_t mNoise;
   uint32_t mPlayoutDelay;
 };
 
+
+void
+MediaOperationTask::ReturnCallbackError(nsresult rv, const char* errorLog)
+{
+  MM_LOG(("%s , rv=%d", errorLog, rv));
+  NS_DispatchToMainThread(new ReleaseMediaOperationResource(mStream.forget(),
+      mOnTracksAvailableCallback.forget()));
+  nsString log;
+
+  log.AssignASCII(errorLog);
+  nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> onSuccess;
+  nsRefPtr<MediaMgrError> error = new MediaMgrError(
+    NS_LITERAL_STRING("InternalError"), log);
+  NS_DispatchToMainThread(
+    new ErrorCallbackRunnable<nsIDOMGetUserMediaSuccessCallback>(onSuccess,
+                                                                 mOnFailure,
+                                                                 *error,
+                                                                 mWindowID));
+}
+
 /**
  * Creates a MediaStream, attaches a listener and fires off a success callback
  * to the DOM with the stream. We also pass in the error callback so it can
  * be released correctly.
  *
  * All of this must be done on the main thread!
  *
  * Note that the various GetUserMedia Runnable classes currently allow for
@@ -1086,31 +1193,34 @@ public:
 
   ~GetUserMediaTask() {
   }
 
   void
   Fail(const nsAString& aName,
        const nsAString& aMessage = EmptyString()) {
     nsRefPtr<MediaMgrError> error = new MediaMgrError(aName, aMessage);
-    nsRefPtr<ErrorCallbackRunnable> runnable =
-      new ErrorCallbackRunnable(mOnSuccess, mOnFailure, *error, mWindowID);
+    nsRefPtr<ErrorCallbackRunnable<nsIDOMGetUserMediaSuccessCallback>> runnable =
+      new ErrorCallbackRunnable<nsIDOMGetUserMediaSuccessCallback>(mOnSuccess,
+                                                                   mOnFailure,
+                                                                   *error,
+                                                                   mWindowID);
     // These should be empty now
     MOZ_ASSERT(!mOnSuccess);
     MOZ_ASSERT(!mOnFailure);
 
     NS_DispatchToMainThread(runnable);
     // Do after ErrorCallbackRunnable Run()s, as it checks active window list
     NS_DispatchToMainThread(new GetUserMediaListenerRemove(mWindowID, mListener));
   }
 
   void
   Run()
   {
-    NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
+    MOZ_ASSERT(!NS_IsMainThread());
     MOZ_ASSERT(mOnSuccess);
     MOZ_ASSERT(mOnFailure);
 
     MediaEngine* backend = mBackend;
     // Was a backend provided?
     if (!backend) {
       backend = mManager->GetBackend(mWindowID);
     }
@@ -1304,90 +1414,136 @@ public:
     return NS_OK;
   }
 
 private:
   nsAutoPtr<GetUserMediaTask> mTask;
 };
 #endif
 
+class SanitizeDeviceIdsTask : public Task
+{
+public:
+  explicit SanitizeDeviceIdsTask(int64_t aSinceWhen)
+  : mSinceWhen(aSinceWhen) {}
+
+  void // NS_IMETHOD
+  Run()
+  {
+    MOZ_ASSERT(!NS_IsMainThread());
+    nsRefPtr<media::ChildPledge<bool>> p =
+        mozilla::media::SanitizeOriginKeys(mSinceWhen); // we fire and forget
+  }
+private:
+  int64_t mSinceWhen;
+};
+
 /**
  * Similar to GetUserMediaTask, but used for the chrome-only
  * GetUserMediaDevices function. Enumerates a list of audio & video devices,
  * wraps them up in nsIMediaDevice objects and returns it to the success
  * callback.
+ *
+ * All code in this class runs on the MediaManager thread.
  */
 class GetUserMediaDevicesTask : public Task
 {
 public:
   GetUserMediaDevicesTask(
     const MediaStreamConstraints& aConstraints,
     already_AddRefed<nsIGetUserMediaDevicesSuccessCallback> aOnSuccess,
     already_AddRefed<nsIDOMGetUserMediaErrorCallback> aOnFailure,
     uint64_t aWindowId, nsACString& aAudioLoopbackDev,
-    nsACString& aVideoLoopbackDev, bool aUseFakeDevices)
+    nsACString& aVideoLoopbackDev, bool aPrivileged, const nsACString& aOrigin,
+    bool aInPrivateBrowsing, bool aUseFakeDevices)
     : mConstraints(aConstraints)
     , mOnSuccess(aOnSuccess)
     , mOnFailure(aOnFailure)
     , mManager(MediaManager::GetInstance())
     , mWindowId(aWindowId)
     , mLoopbackAudioDevice(aAudioLoopbackDev)
     , mLoopbackVideoDevice(aVideoLoopbackDev)
+    , mPrivileged(aPrivileged)
+    , mOrigin(aOrigin)
+    , mInPrivateBrowsing(aInPrivateBrowsing)
     , mUseFakeDevices(aUseFakeDevices) {}
 
   void // NS_IMETHOD
   Run()
   {
-    NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
+    MOZ_ASSERT(!NS_IsMainThread());
 
     nsRefPtr<MediaEngine> backend;
     if (mConstraints.mFake || mUseFakeDevices)
       backend = new MediaEngineDefault(mConstraints.mFakeTracks);
     else
       backend = mManager->GetBackend(mWindowId);
 
     typedef nsTArray<nsRefPtr<MediaDevice>> SourceSet;
 
-    ScopedDeletePtr<SourceSet> final(new SourceSet);
+    ScopedDeletePtr<SourceSet> result(new SourceSet);
     if (IsOn(mConstraints.mVideo)) {
-      nsTArray<nsRefPtr<VideoDevice>> s;
+      nsTArray<nsRefPtr<VideoDevice>> sources;
       GetSources(backend, GetInvariant(mConstraints.mVideo),
-                 &MediaEngine::EnumerateVideoDevices, s, mLoopbackVideoDevice.get());
-      for (uint32_t i = 0; i < s.Length(); i++) {
-        final->AppendElement(s[i]);
+                 &MediaEngine::EnumerateVideoDevices, sources,
+                 mLoopbackVideoDevice.get());
+      for (auto& source : sources) {
+        result->AppendElement(source);
       }
     }
     if (IsOn(mConstraints.mAudio)) {
-      nsTArray<nsRefPtr<AudioDevice>> s;
+      nsTArray<nsRefPtr<AudioDevice>> sources;
       GetSources(backend, GetInvariant(mConstraints.mAudio),
-                 &MediaEngine::EnumerateAudioDevices, s, mLoopbackAudioDevice.get());
-      for (uint32_t i = 0; i < s.Length(); i++) {
-        final->AppendElement(s[i]);
+                 &MediaEngine::EnumerateAudioDevices, sources,
+                 mLoopbackAudioDevice.get());
+      for (auto& source : sources) {
+        result->AppendElement(source);
       }
     }
-
-    NS_DispatchToMainThread(new DeviceSuccessCallbackRunnable(mWindowId,
-                                                              mOnSuccess, mOnFailure,
-                                                              final.forget()));
-    // DeviceSuccessCallbackRunnable should have taken these.
+    // In the case of failure with this newly allocated runnable, we
+    // intentionally leak the runnable, because we've pawned mOnSuccess and
+    // mOnFailure onto it which are main thread objects unsafe to release here.
+    DeviceSuccessCallbackRunnable* runnable =
+        new DeviceSuccessCallbackRunnable(mWindowId, mOnSuccess, mOnFailure,
+                                          result.forget());
+    if (mPrivileged) {
+      NS_DispatchToMainThread(runnable);
+    } else {
+      // Get persistent origin-unique uuid to anonymize deviceIds back on main.
+      //
+      // GetOriginKey is an async API that returns a pledge (as promise-like
+      // pattern). We use .Then() to pass in a lambda to run back on this
+      // thread once GetOriginKey resolves asynchronously . The "runnable"
+      // pointer is "captured" (passed by value) into the lambda.
+      nsRefPtr<media::ChildPledge<nsCString>> p =
+          media::GetOriginKey(mOrigin, mInPrivateBrowsing);
+      p->Then([runnable](nsCString result) mutable {
+        runnable->mOriginKey = result;
+        NS_DispatchToMainThread(runnable);
+      });
+    }
+    // One of the Runnables have taken these.
     MOZ_ASSERT(!mOnSuccess && !mOnFailure);
   }
 
 private:
   MediaStreamConstraints mConstraints;
   nsCOMPtr<nsIGetUserMediaDevicesSuccessCallback> mOnSuccess;
   nsCOMPtr<nsIDOMGetUserMediaErrorCallback> mOnFailure;
   nsRefPtr<MediaManager> mManager;
   uint64_t mWindowId;
   const nsString mCallId;
   // Audio & Video loopback devices to be used based on
   // the preference settings. This is currently used for
   // automated media tests only.
   nsCString mLoopbackAudioDevice;
   nsCString mLoopbackVideoDevice;
+  bool mPrivileged;
+  nsCString mOrigin;
+  bool mInPrivateBrowsing;
   bool mUseFakeDevices;
 };
 
 MediaManager::MediaManager()
   : mMediaThread(nullptr)
   , mMutex("mozilla::MediaManager")
   , mBackend(nullptr) {
   mPrefs.mWidth  = 0; // adaptive default
@@ -1406,16 +1562,26 @@ MediaManager::MediaManager()
   LOG(("%s: default prefs: %dx%d @%dfps (min %d)", __FUNCTION__,
        mPrefs.mWidth, mPrefs.mHeight, mPrefs.mFPS, mPrefs.mMinFPS));
 }
 
 NS_IMPL_ISUPPORTS(MediaManager, nsIMediaManagerService, nsIObserver)
 
 /* static */ StaticRefPtr<MediaManager> MediaManager::sSingleton;
 
+#ifdef DEBUG
+/* static */ bool
+MediaManager::IsInMediaThread()
+{
+  return sSingleton?
+      (sSingleton->mMediaThread->thread_id() == PlatformThread::CurrentId()) :
+      false;
+}
+#endif
+
 // NOTE: never Dispatch(....,NS_DISPATCH_SYNC) to the MediaManager
 // thread from the MainThread, as we NS_DISPATCH_SYNC to MainThread
 // from MediaManager thread.
 /* static */  MediaManager*
 MediaManager::Get() {
   if (!sSingleton) {
     NS_ASSERTION(NS_IsMainThread(), "Only create MediaManager on main thread");
 #ifdef DEBUG
@@ -1572,26 +1738,18 @@ MediaManager::GetUserMedia(
     // Hack: should init singleton earlier unless it's expensive (mem or CPU)
     (void) MediaManager::Get();
 #ifdef MOZ_B2G
     // Initialize MediaPermissionManager before send out any permission request.
     (void) MediaPermissionManager::GetInstance();
 #endif //MOZ_B2G
   }
 
-  // Store the WindowID in a hash table and mark as active. The entry is removed
-  // when this window is closed or navigated away from.
   uint64_t windowID = aWindow->WindowID();
-  // This is safe since we're on main-thread, and the windowlist can only
-  // be invalidated from the main-thread (see OnNavigation)
-  StreamListeners* listeners = GetActiveWindows()->Get(windowID);
-  if (!listeners) {
-    listeners = new StreamListeners;
-    GetActiveWindows()->Put(windowID, listeners);
-  }
+  StreamListeners* listeners = AddWindowID(windowID);
 
   // Create a disabled listener to act as a placeholder
   GetUserMediaCallbackMediaStreamListener* listener =
     new GetUserMediaCallbackMediaStreamListener(mMediaThread, windowID);
 
   // No need for locking because we always do this in the main thread.
   listeners->AppendElement(listener);
 
@@ -1796,17 +1954,18 @@ MediaManager::GetUserMedia(
   return NS_OK;
 }
 
 nsresult
 MediaManager::GetUserMediaDevices(nsPIDOMWindow* aWindow,
   const MediaStreamConstraints& aConstraints,
   nsIGetUserMediaDevicesSuccessCallback* aOnSuccess,
   nsIDOMGetUserMediaErrorCallback* aOnFailure,
-  uint64_t aInnerWindowID)
+  uint64_t aInnerWindowID,
+  bool aPrivileged)
 {
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
 
   NS_ENSURE_TRUE(aOnFailure, NS_ERROR_NULL_POINTER);
   NS_ENSURE_TRUE(aOnSuccess, NS_ERROR_NULL_POINTER);
 
   nsCOMPtr<nsIGetUserMediaDevicesSuccessCallback> onSuccess(aOnSuccess);
   nsCOMPtr<nsIDOMGetUserMediaErrorCallback> onFailure(aOnFailure);
@@ -1814,25 +1973,50 @@ MediaManager::GetUserMediaDevices(nsPIDO
   // Check if the preference for using loopback devices is enabled.
   nsAdoptingCString loopbackAudioDevice =
     Preferences::GetCString("media.audio_loopback_dev");
   nsAdoptingCString loopbackVideoDevice =
     Preferences::GetCString("media.video_loopback_dev");
   bool useFakeStreams =
     Preferences::GetBool("media.navigator.streams.fake", false);
 
+  nsCString origin;
+  nsPrincipal::GetOriginForURI(aWindow->GetDocumentURI(),
+                               getter_Copies(origin));
+  bool inPrivateBrowsing;
+  {
+    nsCOMPtr<nsIDocument> doc = aWindow->GetDoc();
+    nsCOMPtr<nsILoadContext> loadContext = doc->GetLoadContext();
+    inPrivateBrowsing = loadContext && loadContext->UsePrivateBrowsing();
+  }
   MediaManager::GetMessageLoop()->PostTask(FROM_HERE,
     new GetUserMediaDevicesTask(
       aConstraints, onSuccess.forget(), onFailure.forget(),
       (aInnerWindowID ? aInnerWindowID : aWindow->WindowID()),
-      loopbackAudioDevice, loopbackVideoDevice, useFakeStreams));
+      loopbackAudioDevice, loopbackVideoDevice, aPrivileged, origin,
+      inPrivateBrowsing, useFakeStreams));
 
   return NS_OK;
 }
 
+nsresult
+MediaManager::EnumerateDevices(nsPIDOMWindow* aWindow,
+                               nsIGetUserMediaDevicesSuccessCallback* aOnSuccess,
+                               nsIDOMGetUserMediaErrorCallback* aOnFailure)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
+
+  MediaStreamConstraints c;
+  c.mVideo.SetAsBoolean() = true;
+  c.mAudio.SetAsBoolean() = true;
+
+  AddWindowID(aWindow->WindowID());
+  return GetUserMediaDevices(aWindow, c, aOnSuccess, aOnFailure, 0, false);
+}
+
 MediaEngine*
 MediaManager::GetBackend(uint64_t aWindowId)
 {
   // Plugin backends as appropriate. The default engine also currently
   // includes picture support for Android.
   // This IS called off main-thread.
   MutexAutoLock lock(mMutex);
   if (!mBackend) {
@@ -1891,16 +2075,32 @@ MediaManager::OnNavigation(uint64_t aWin
       (nsGlobalWindow::GetInnerWindowWithId(aWindowID));
   if (window) {
     IterateWindowListeners(window, StopSharingCallback, nullptr);
   } else {
     RemoveWindowID(aWindowID);
   }
 }
 
+StreamListeners*
+MediaManager::AddWindowID(uint64_t aWindowId)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
+  // Store the WindowID in a hash table and mark as active. The entry is removed
+  // when this window is closed or navigated away from.
+  // This is safe since we're on main-thread, and the windowlist can only
+  // be invalidated from the main-thread (see OnNavigation)
+  StreamListeners* listeners = GetActiveWindows()->Get(aWindowId);
+  if (!listeners) {
+    listeners = new StreamListeners;
+    GetActiveWindows()->Put(aWindowId, listeners);
+  }
+  return listeners;
+}
+
 void
 MediaManager::RemoveWindowID(uint64_t aWindowId)
 {
   mActiveWindows.Remove(aWindowId);
 
   // get outer windowID
   nsPIDOMWindow *window = static_cast<nsPIDOMWindow*>
     (nsGlobalWindow::GetInnerWindowWithId(aWindowId));
@@ -2004,31 +2204,57 @@ MediaManager::Observe(nsISupports* aSubj
     nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
     if (prefs) {
       prefs->RemoveObserver("media.navigator.video.default_width", this);
       prefs->RemoveObserver("media.navigator.video.default_height", this);
       prefs->RemoveObserver("media.navigator.video.default_fps", this);
       prefs->RemoveObserver("media.navigator.video.default_minfps", this);
     }
 
-    // Close off any remaining active windows.
+    // Because mMediaThread is not an nsThread, we must dispatch to it so it can
+    // clean up BackgroundChild. Continue stopping thread once this is done.
+
+    class ShutdownTask : public Task
     {
+    public:
+      explicit ShutdownTask(nsRunnable* aReply) : mReply(aReply) {}
+    private:
+      virtual void
+      Run()
+      {
+        MOZ_ASSERT(MediaManager::IsInMediaThread());
+        mozilla::ipc::BackgroundChild::CloseForCurrentThread();
+        NS_DispatchToMainThread(mReply);
+      }
+      nsRefPtr<nsRunnable> mReply;
+    };
+
+    // Post ShutdownTask to execute on mMediaThread and pass in a lambda
+    // callback to be executed back on this thread once it is done.
+    //
+    // The lambda callback "captures" the 'this' pointer for member access.
+    // This is safe since this is guaranteed to be here since sSingleton isn't
+    // cleared until the lambda function clears it.
+
+    MediaManager::GetMessageLoop()->PostTask(FROM_HERE, new ShutdownTask(
+        media::CallbackRunnable::New([this]() mutable {
+      // Close off any remaining active windows.
       MutexAutoLock lock(mMutex);
       GetActiveWindows()->Clear();
       mActiveCallbacks.Clear();
       mCallIds.Clear();
       LOG(("Releasing MediaManager singleton and thread"));
       // Note: won't be released immediately as the Observer has a ref to us
       sSingleton = nullptr;
       if (mMediaThread) {
         mMediaThread->Stop();
       }
       mBackend = nullptr;
-    }
-
+      return NS_OK;
+    })));
     return NS_OK;
 
   } else if (!strcmp(aTopic, "getUserMedia:response:allow")) {
     nsString key(aData);
     nsAutoPtr<GetUserMediaTask> task;
     mActiveCallbacks.RemoveAndForget(key, task);
     if (!task) {
       return NS_OK;
@@ -2254,16 +2480,27 @@ MediaManager::MediaCaptureWindowState(ns
   LOG(("%s: window %lld capturing %s %s %s %s %s %s", __FUNCTION__, piWin ? piWin->WindowID() : -1,
        *aVideo ? "video" : "", *aAudio ? "audio" : "",
        *aScreenShare ? "screenshare" : "",  *aWindowShare ? "windowshare" : "",
        *aAppShare ? "appshare" : "", *aBrowserShare ? "browsershare" : ""));
 #endif
   return NS_OK;
 }
 
+NS_IMETHODIMP
+MediaManager::SanitizeDeviceIds(int64_t aSinceWhen)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
+  LOG(("%s: sinceWhen = %llu", __FUNCTION__, aSinceWhen));
+
+  MediaManager::GetMessageLoop()->PostTask(FROM_HERE,
+    new SanitizeDeviceIdsTask(aSinceWhen));
+  return NS_OK;
+}
+
 static void
 StopScreensharingCallback(MediaManager *aThis,
                           uint64_t aWindowID,
                           StreamListeners *aListeners,
                           void *aData)
 {
   if (aListeners) {
     auto length = aListeners->Length();
@@ -2340,16 +2577,34 @@ MediaManager::StopMediaStreams()
     array->GetElementAt(i, getter_AddRefs(window));
     nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(window));
     if (win) {
       OnNavigation(win->WindowID());
     }
   }
 }
 
+bool
+MediaManager::IsWindowActivelyCapturing(uint64_t aWindowId)
+{
+  nsCOMPtr<nsISupportsArray> array;
+  GetActiveMediaCaptureWindows(getter_AddRefs(array));
+  uint32_t len;
+  array->Count(&len);
+  for (uint32_t i = 0; i < len; i++) {
+    nsCOMPtr<nsISupports> window;
+    array->GetElementAt(i, getter_AddRefs(window));
+    nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(window));
+    if (win && win->WindowID() == aWindowId) {
+      return true;
+    }
+  }
+  return false;
+}
+
 void
 GetUserMediaCallbackMediaStreamListener::AudioConfig(bool aEchoOn,
               uint32_t aEcho,
               bool aAgcOn, uint32_t aAGC,
               bool aNoiseOn, uint32_t aNoise,
               int32_t aPlayoutDelay)
 {
   if (mAudioSource) {
--- a/dom/media/MediaManager.h
+++ b/dom/media/MediaManager.h
@@ -295,39 +295,16 @@ typedef enum {
   MEDIA_STOP,
   MEDIA_STOP_TRACK,
   MEDIA_DIRECT_LISTENERS
 } MediaOperation;
 
 class MediaManager;
 class GetUserMediaTask;
 
-/**
- * Send an error back to content.
- * Do this only on the main thread. The onSuccess callback is also passed here
- * so it can be released correctly.
- */
-class ErrorCallbackRunnable : public nsRunnable
-{
-public:
-  ErrorCallbackRunnable(
-    nsCOMPtr<nsIDOMGetUserMediaSuccessCallback>& aOnSuccess,
-    nsCOMPtr<nsIDOMGetUserMediaErrorCallback>& aOnFailure,
-    MediaMgrError& aError, uint64_t aWindowID);
-  NS_IMETHOD Run();
-private:
-  ~ErrorCallbackRunnable();
-
-  nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> mOnSuccess;
-  nsCOMPtr<nsIDOMGetUserMediaErrorCallback> mOnFailure;
-  nsRefPtr<MediaMgrError> mError;
-  uint64_t mWindowID;
-  nsRefPtr<MediaManager> mManager; // get ref to this when creating the runnable
-};
-
 class ReleaseMediaOperationResource : public nsRunnable
 {
 public:
   ReleaseMediaOperationResource(already_AddRefed<DOMMediaStream> aStream,
     DOMMediaStream::OnTracksAvailableCallback* aOnTracksAvailableCallback):
     mStream(aStream),
     mOnTracksAvailableCallback(aOnTracksAvailableCallback) {}
   NS_IMETHOD Run() override {return NS_OK;}
@@ -364,30 +341,17 @@ public:
   {}
 
   ~MediaOperationTask()
   {
     // MediaStreams can be released on any thread.
   }
 
   void
-  ReturnCallbackError(nsresult rv, const char* errorLog)
-  {
-    MM_LOG(("%s , rv=%d", errorLog, rv));
-    NS_DispatchToMainThread(new ReleaseMediaOperationResource(mStream.forget(),
-          mOnTracksAvailableCallback.forget()));
-    nsString log;
-
-    log.AssignASCII(errorLog);
-    nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> onSuccess;
-    nsRefPtr<MediaMgrError> error = new MediaMgrError(
-        NS_LITERAL_STRING("InternalError"), log);
-    NS_DispatchToMainThread(new ErrorCallbackRunnable(onSuccess, mOnFailure,
-        *error, mWindowID));
-  }
+  ReturnCallbackError(nsresult rv, const char* errorLog);
 
   void
   Run()
   {
     SourceMediaStream *source = mListener->GetSourceStream();
     // No locking between these is required as all the callbacks for the
     // same MediaStream will occur on the same thread.
     if (!source) // means the stream was never Activated()
@@ -498,16 +462,17 @@ typedef nsTArray<nsRefPtr<GetUserMediaCa
 typedef nsClassHashtable<nsUint64HashKey, StreamListeners> WindowTable;
 
 class MediaDevice : public nsIMediaDevice
 {
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIMEDIADEVICE
 
+  void SetId(const nsAString& aID);
 protected:
   virtual ~MediaDevice() {}
   explicit MediaDevice(MediaEngineSource* aSource);
   nsString mName;
   nsString mID;
   bool mHasFacingMode;
   dom::VideoFacingModeEnum mFacingMode;
   dom::MediaSourceEnum mMediaSource;
@@ -551,16 +516,19 @@ public:
   static already_AddRefed<MediaManager> GetInstance();
 
   // NOTE: never Dispatch(....,NS_DISPATCH_SYNC) to the MediaManager
   // thread from the MainThread, as we NS_DISPATCH_SYNC to MainThread
   // from MediaManager thread.
   static MediaManager* Get();
   static MediaManager* GetIfExists();
   static MessageLoop* GetMessageLoop();
+#ifdef DEBUG
+  static bool IsInMediaThread();
+#endif
 
   static bool Exists()
   {
     return !!sSingleton;
   }
 
   static nsresult NotifyRecordingStatusChange(nsPIDOMWindow* aWindow,
                                               const nsString& aMsg,
@@ -590,22 +558,31 @@ public:
     const dom::MediaStreamConstraints& aConstraints,
     nsIDOMGetUserMediaSuccessCallback* onSuccess,
     nsIDOMGetUserMediaErrorCallback* onError);
 
   nsresult GetUserMediaDevices(nsPIDOMWindow* aWindow,
     const dom::MediaStreamConstraints& aConstraints,
     nsIGetUserMediaDevicesSuccessCallback* onSuccess,
     nsIDOMGetUserMediaErrorCallback* onError,
-    uint64_t aInnerWindowID = 0);
+    uint64_t aInnerWindowID = 0,
+    bool aPrivileged = true);
+
+  nsresult EnumerateDevices(nsPIDOMWindow* aWindow,
+                            nsIGetUserMediaDevicesSuccessCallback* aOnSuccess,
+                            nsIDOMGetUserMediaErrorCallback* aOnFailure);
+
+  nsresult EnumerateDevices(nsPIDOMWindow* aWindow, dom::Promise& aPromise);
   void OnNavigation(uint64_t aWindowID);
+  bool IsWindowActivelyCapturing(uint64_t aWindowId);
 
   MediaEnginePrefs mPrefs;
 
 private:
+  StreamListeners* AddWindowID(uint64_t aWindowId);
   WindowTable *GetActiveWindows() {
     NS_ASSERTION(NS_IsMainThread(), "Only access windowlist on main thread");
     return &mActiveWindows;
   }
 
   void GetPref(nsIPrefBranch *aBranch, const char *aPref,
                const char *aData, int32_t *aVal);
   void GetPrefBool(nsIPrefBranch *aBranch, const char *aPref,
@@ -623,16 +600,17 @@ private:
                               void *aData);
 
   void StopMediaStreams();
 
   // ONLY access from MainThread so we don't need to lock
   WindowTable mActiveWindows;
   nsClassHashtable<nsStringHashKey, GetUserMediaTask> mActiveCallbacks;
   nsClassHashtable<nsUint64HashKey, nsTArray<nsString>> mCallIds;
+
   // Always exists
   nsAutoPtr<base::Thread> mMediaThread;
 
   Mutex mMutex;
   // protected with mMutex:
   RefPtr<MediaEngine> mBackend;
 
   static StaticRefPtr<MediaManager> sSingleton;
--- a/dom/media/VideoUtils.cpp
+++ b/dom/media/VideoUtils.cpp
@@ -256,49 +256,52 @@ ExtractH264CodecDetails(const nsAString&
   // otherwise collect 0 for unknown.
   Telemetry::Accumulate(Telemetry::VIDEO_CANPLAYTYPE_H264_LEVEL,
                         (aLevel >= 10 && aLevel <= 52) ? aLevel : 0);
 
   return true;
 }
 
 nsresult
-GenerateRandomPathName(nsCString& aOutSalt, uint32_t aLength)
+GenerateRandomName(nsCString& aOutSalt, uint32_t aLength)
 {
   nsresult rv;
   nsCOMPtr<nsIRandomGenerator> rg =
     do_GetService("@mozilla.org/security/random-generator;1", &rv);
   if (NS_FAILED(rv)) return rv;
 
-  // For each three bytes of random data we will get four bytes of
-  // ASCII. Request a bit more to be safe and truncate to the length
-  // we want at the end.
+  // For each three bytes of random data we will get four bytes of ASCII.
   const uint32_t requiredBytesLength =
-    static_cast<uint32_t>((aLength + 1) / 4 * 3);
+    static_cast<uint32_t>((aLength + 3) / 4 * 3);
 
   uint8_t* buffer;
   rv = rg->GenerateRandomBytes(requiredBytesLength, &buffer);
   if (NS_FAILED(rv)) return rv;
 
   nsAutoCString temp;
   nsDependentCSubstring randomData(reinterpret_cast<const char*>(buffer),
                                    requiredBytesLength);
   rv = Base64Encode(randomData, temp);
   NS_Free(buffer);
   buffer = nullptr;
   if (NS_FAILED (rv)) return rv;
 
-  temp.Truncate(aLength);
+  aOutSalt = temp;
+  return NS_OK;
+}
+
+nsresult
+GenerateRandomPathName(nsCString& aOutSalt, uint32_t aLength)
+{
+  nsresult rv = GenerateRandomName(aOutSalt, aLength);
+  if (NS_FAILED(rv)) return rv;
 
   // Base64 characters are alphanumeric (a-zA-Z0-9) and '+' and '/', so we need
   // to replace illegal characters -- notably '/'
-  temp.ReplaceChar(FILE_PATH_SEPARATOR FILE_ILLEGAL_CHARACTERS, '_');
-
-  aOutSalt = temp;
-
+  aOutSalt.ReplaceChar(FILE_PATH_SEPARATOR FILE_ILLEGAL_CHARACTERS, '_');
   return NS_OK;
 }
 
 class CreateTaskQueueTask : public nsRunnable {
 public:
   NS_IMETHOD Run() {
     MOZ_ASSERT(NS_IsMainThread());
     mTaskQueue = new MediaTaskQueue(GetMediaThreadPool());
--- a/dom/media/VideoUtils.h
+++ b/dom/media/VideoUtils.h
@@ -256,17 +256,21 @@ enum H264_LEVEL {
 // for more details.
 // Returns false on failure.
 bool
 ExtractH264CodecDetails(const nsAString& aCodecs,
                         int16_t& aProfile,
                         int16_t& aLevel);
 
 // Use a cryptographic quality PRNG to generate raw random bytes
-// and convert that to a base64 string suitable for use as a file or URL
+// and convert that to a base64 string.
+nsresult
+GenerateRandomName(nsCString& aOutSalt, uint32_t aLength);
+
+// This version returns a string suitable for use as a file or URL
 // path. This is based on code from nsExternalAppHandler::SetUpTempFile.
 nsresult
 GenerateRandomPathName(nsCString& aOutSalt, uint32_t aLength);
 
 class MediaTaskQueue;
 class FlushableMediaTaskQueue;
 
 already_AddRefed<MediaTaskQueue>
--- a/dom/media/gmp/GMPAudioDecoderParent.cpp
+++ b/dom/media/gmp/GMPAudioDecoderParent.cpp
@@ -135,17 +135,17 @@ GMPAudioDecoderParent::Drain()
   return NS_OK;
 }
 
 // Note: Consider keeping ActorDestroy sync'd up when making changes here.
 nsresult
 GMPAudioDecoderParent::Close()
 {
   LOGD(("%s: %p", __FUNCTION__, this));
-  MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread());
+  MOZ_ASSERT(!mPlugin || mPlugin->GMPThread() == NS_GetCurrentThread());
 
   // Consumer is done with us; we can shut down.  No more callbacks should
   // be made to mCallback.  Note: do this before Shutdown()!
   mCallback = nullptr;
   // Let Shutdown mark us as dead so it knows if we had been alive
 
   // In case this is the last reference
   nsRefPtr<GMPAudioDecoderParent> kungfudeathgrip(this);
@@ -155,17 +155,17 @@ GMPAudioDecoderParent::Close()
   return NS_OK;
 }
 
 // Note: Consider keeping ActorDestroy sync'd up when making changes here.
 nsresult
 GMPAudioDecoderParent::Shutdown()
 {
   LOGD(("%s: %p", __FUNCTION__, this));
-  MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread());
+  MOZ_ASSERT(!mPlugin || mPlugin->GMPThread() == NS_GetCurrentThread());
 
   if (mShuttingDown) {
     return NS_OK;
   }
   mShuttingDown = true;
 
   // Notify client we're gone!  Won't occur after Close()
   if (mCallback) {
--- a/dom/media/moz.build
+++ b/dom/media/moz.build
@@ -141,16 +141,17 @@ if CONFIG['MOZ_B2G']:
         'MediaPermissionGonk.h',
     ]
 
 EXPORTS.mozilla.dom += [
     'AudioStreamTrack.h',
     'AudioTrack.h',
     'AudioTrackList.h',
     'GetUserMediaRequest.h',
+    'MediaDeviceInfo.h',
     'MediaDevices.h',
     'MediaStreamError.h',
     'MediaStreamTrack.h',
     'RTCIdentityProviderRegistrar.h',
     'TextTrack.h',
     'TextTrackCue.h',
     'TextTrackCueList.h',
     'TextTrackList.h',
@@ -178,16 +179,17 @@ UNIFIED_SOURCES += [
     'GetUserMediaRequest.cpp',
     'GraphDriver.cpp',
     'Latency.cpp',
     'MediaCache.cpp',
     'MediaData.cpp',
     'MediaDecoder.cpp',
     'MediaDecoderReader.cpp',
     'MediaDecoderStateMachine.cpp',
+    'MediaDeviceInfo.cpp',
     'MediaDevices.cpp',
     'MediaManager.cpp',
     'MediaRecorder.cpp',
     'MediaResource.cpp',
     'MediaShutdownManager.cpp',
     'MediaStreamError.cpp',
     'MediaStreamGraph.cpp',
     'MediaStreamTrack.cpp',
--- a/dom/media/nsIMediaManager.idl
+++ b/dom/media/nsIMediaManager.idl
@@ -7,19 +7,23 @@
 interface nsISupportsArray;
 interface nsIDOMWindow;
 
 %{C++
 #define NS_MEDIAMANAGERSERVICE_CID {0xabc622ea, 0x9655, 0x4123, {0x80, 0xd9, 0x22, 0x62, 0x1b, 0xdd, 0x54, 0x65}}
 #define MEDIAMANAGERSERVICE_CONTRACTID "@mozilla.org/mediaManagerService;1"
 %}
 
-[scriptable, builtinclass, uuid(9b10661f-77b3-47f7-a8de-ee58daaff5d2)]
+[scriptable, builtinclass, uuid(24b23e01-33fd-401f-ba25-6e52658750b0)]
 interface nsIMediaManagerService : nsISupports
 {
   /* return a array of inner windows that have active captures */
   readonly attribute nsISupportsArray activeMediaCaptureWindows;
 
   /* Get the capture state for the given window and all descendant windows (iframes, etc) */
   void mediaCaptureWindowState(in nsIDOMWindow aWindow, out boolean aVideo, out boolean aAudio,
                                [optional] out boolean aScreenShare, [optional] out boolean aWindowShare,
                                [optional] out boolean aAppShare, [optional] out boolean aBrowserShare);
+
+  /* Clear per-orgin list of persistent DeviceIds stored for enumerateDevices
+     sinceTime is milliseconds since 1 January 1970 00:00:00 UTC. 0 = clear all */
+  void sanitizeDeviceIds(in long long sinceWhen);
 };
new file mode 100644
--- /dev/null
+++ b/dom/media/systemservices/MediaChild.cpp
@@ -0,0 +1,174 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et ft=cpp : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "MediaChild.h"
+
+#include "mozilla/ipc/BackgroundChild.h"
+#include "mozilla/ipc/PBackgroundChild.h"
+#include "nsGlobalWindow.h"
+#include "mozilla/MediaManager.h"
+#include "prlog.h"
+
+#undef LOG
+#if defined(PR_LOGGING)
+PRLogModuleInfo *gMediaChildLog;
+#define LOG(args) PR_LOG(gMediaChildLog, PR_LOG_DEBUG, args)
+#else
+#define LOG(args)
+#endif
+
+
+namespace mozilla {
+namespace media {
+
+static Child* sChild;
+
+template<typename ValueType> void
+ChildPledge<ValueType>::ActorCreated(PBackgroundChild* aActor)
+{
+  if (!sChild) {
+    // Create PMedia by sending a message to the parent
+    sChild = static_cast<Child*>(aActor->SendPMediaConstructor());
+  }
+  Run(sChild);
+}
+
+template<typename ValueType> void
+ChildPledge<ValueType>::ActorFailed()
+{
+  Pledge<ValueType>::Reject(NS_ERROR_UNEXPECTED);
+}
+
+template<typename ValueType> NS_IMPL_ADDREF(ChildPledge<ValueType>)
+template<typename ValueType> NS_IMPL_RELEASE(ChildPledge<ValueType>)
+template<typename ValueType> NS_INTERFACE_MAP_BEGIN(ChildPledge<ValueType>)
+NS_INTERFACE_MAP_ENTRY(nsIIPCBackgroundChildCreateCallback)
+NS_INTERFACE_MAP_END
+
+already_AddRefed<ChildPledge<nsCString>>
+GetOriginKey(const nsCString& aOrigin, bool aPrivateBrowsing)
+{
+  class Pledge : public ChildPledge<nsCString>
+  {
+  public:
+    explicit Pledge(const nsCString& aOrigin, bool aPrivateBrowsing)
+    : mOrigin(aOrigin), mPrivateBrowsing(aPrivateBrowsing) {}
+  private:
+    ~Pledge() {}
+    void Run(PMediaChild* aChild)
+    {
+      Child* child = static_cast<Child*>(aChild);
+
+      uint32_t id = child->AddRequestPledge(*this);
+      child->SendGetOriginKey(id, mOrigin, mPrivateBrowsing);
+    }
+    const nsCString mOrigin;
+    const bool mPrivateBrowsing;
+  };
+
+  nsRefPtr<ChildPledge<nsCString>> p = new Pledge(aOrigin, aPrivateBrowsing);
+  nsCOMPtr<nsIIPCBackgroundChildCreateCallback> cb = do_QueryObject(p);
+  bool ok = ipc::BackgroundChild::GetOrCreateForCurrentThread(cb);
+  MOZ_RELEASE_ASSERT(ok);
+  return p.forget();
+}
+
+already_AddRefed<ChildPledge<bool>>
+SanitizeOriginKeys(const uint64_t& aSinceWhen)
+{
+  class Pledge : public ChildPledge<bool>
+  {
+  public:
+    explicit Pledge(const uint64_t& aSinceWhen) : mSinceWhen(aSinceWhen) {}
+  private:
+    void Run(PMediaChild* aMedia)
+    {
+      aMedia->SendSanitizeOriginKeys(mSinceWhen);
+      mValue = true;
+      LOG(("SanitizeOriginKeys since %llu", mSinceWhen));
+      Resolve();
+    }
+    const uint64_t mSinceWhen;
+  };
+
+  nsRefPtr<ChildPledge<bool>> p = new Pledge(aSinceWhen);
+  nsCOMPtr<nsIIPCBackgroundChildCreateCallback> cb = do_QueryObject(p);
+  bool ok = ipc::BackgroundChild::GetOrCreateForCurrentThread(cb);
+  MOZ_RELEASE_ASSERT(ok);
+  return p.forget();
+}
+
+Child::Child()
+{
+#if defined(PR_LOGGING)
+  if (!gMediaChildLog) {
+    gMediaChildLog = PR_NewLogModule("MediaChild");
+  }
+#endif
+  LOG(("media::Child: %p", this));
+  MOZ_COUNT_CTOR(Child);
+}
+
+Child::~Child()
+{
+  LOG(("~media::Child: %p", this));
+  sChild = nullptr;
+  MOZ_COUNT_DTOR(Child);
+}
+
+uint32_t Child::sRequestCounter = 0;
+
+uint32_t
+Child::AddRequestPledge(ChildPledge<nsCString>& aPledge)
+{
+  uint32_t id = ++sRequestCounter;
+  nsRefPtr<ChildPledge<nsCString>> ptr(&aPledge);
+  mRequestPledges.AppendElement(PledgeEntry(id, ptr));
+  return id;
+}
+
+already_AddRefed<ChildPledge<nsCString>>
+Child::RemoveRequestPledge(uint32_t aRequestId)
+{
+  for (PledgeEntry& entry : mRequestPledges) {
+    if (entry.first == aRequestId) {
+      nsRefPtr<ChildPledge<nsCString>> ref;
+      ref.swap(entry.second);
+      mRequestPledges.RemoveElement(entry);
+      return ref.forget();
+    }
+  }
+  MOZ_ASSERT_UNREACHABLE("Received response with no matching media::ChildPledge!");
+  return nullptr;
+}
+
+bool
+Child::RecvGetOriginKeyResponse(const uint32_t& aRequestId, const nsCString& aKey)
+{
+  nsRefPtr<ChildPledge<nsCString>> pledge = RemoveRequestPledge(aRequestId);
+  if (pledge) {
+    pledge->Resolve(aKey);
+  }
+  return true;
+}
+
+PMediaChild*
+AllocPMediaChild()
+{
+  Child* obj = new Child();
+  obj->AddRef();
+  return obj;
+}
+
+bool
+DeallocPMediaChild(media::PMediaChild *aActor)
+{
+  static_cast<Child*>(aActor)->Release();
+  return true;
+}
+
+}
+}
new file mode 100644
--- /dev/null
+++ b/dom/media/systemservices/MediaChild.h
@@ -0,0 +1,74 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et ft=cpp : */
+/* 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_MediaChild_h
+#define mozilla_MediaChild_h
+
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/media/PMediaChild.h"
+#include "mozilla/media/PMediaParent.h"
+#include "nsIIPCBackgroundChildCreateCallback.h"
+#include "MediaUtils.h"
+
+namespace mozilla {
+namespace media {
+
+// media::Child implements proxying to the chrome process for some media-related
+// functions, for the moment just:
+//
+// GetOriginKey() - get a cookie-like persisted unique key for a given origin.
+// SanitizeOriginKeys() - reset persisted unique keys.
+
+// GetOriginKey and SanitizeOriginKeys are asynchronous APIs that return pledges
+// (promise-like objects) with the future value. Use pledge.Then(func) to access.
+
+class Child;
+
+template<typename ValueType>
+class ChildPledge : public Pledge<ValueType>,
+                    public nsIIPCBackgroundChildCreateCallback
+{
+  friend Child;
+  NS_DECL_NSIIPCBACKGROUNDCHILDCREATECALLBACK
+  NS_DECL_ISUPPORTS
+public:
+  explicit ChildPledge() {};
+protected:
+  virtual ~ChildPledge() {}
+  virtual void Run(PMediaChild* aMedia) = 0;
+};
+
+already_AddRefed<ChildPledge<nsCString>>
+GetOriginKey(const nsCString& aOrigin, bool aPrivateBrowsing);
+
+already_AddRefed<ChildPledge<bool>>
+SanitizeOriginKeys(const uint64_t& aSinceWhen);
+
+class Child : public PMediaChild
+{
+  NS_INLINE_DECL_REFCOUNTING(Child)
+public:
+  Child();
+
+  bool RecvGetOriginKeyResponse(const uint32_t& aRequestId, const nsCString& aKey);
+
+  uint32_t AddRequestPledge(ChildPledge<nsCString>& aPledge);
+  already_AddRefed<ChildPledge<nsCString>> RemoveRequestPledge(uint32_t aRequestId);
+private:
+  virtual ~Child();
+
+  typedef std::pair<uint32_t,nsRefPtr<ChildPledge<nsCString>>> PledgeEntry;
+  static uint32_t sRequestCounter;
+  nsTArray<PledgeEntry> mRequestPledges;
+};
+
+PMediaChild* AllocPMediaChild();
+bool DeallocPMediaChild(PMediaChild *aActor);
+
+} // namespace media
+} // namespace mozilla
+
+#endif  // mozilla_MediaChild_h
new file mode 100644
--- /dev/null
+++ b/dom/media/systemservices/MediaParent.cpp
@@ -0,0 +1,456 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et ft=cpp : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "MediaParent.h"
+
+#include "mozilla/Base64.h"
+#include <mozilla/StaticMutex.h>
+
+#include "MediaUtils.h"
+#include "MediaEngine.h"
+#include "VideoUtils.h"
+#include "nsThreadUtils.h"
+#include "nsNetUtil.h"
+#include "nsILineInputStream.h"
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsISupportsImpl.h"
+#include "prlog.h"
+
+#undef LOG
+#if defined(PR_LOGGING)
+PRLogModuleInfo *gMediaParentLog;
+#define LOG(args) PR_LOG(gMediaParentLog, PR_LOG_DEBUG, args)
+#else
+#define LOG(args)
+#endif
+
+// A file in the profile dir is used to persist mOriginKeys used to anonymize
+// deviceIds to be unique per origin, to avoid them being supercookies.
+
+#define ORIGINKEYS_FILE "enumerate_devices.txt"
+#define ORIGINKEYS_VERSION "1"
+
+namespace mozilla {
+namespace media {
+
+static StaticMutex gMutex;
+static ParentSingleton* sParentSingleton = nullptr;
+
+class ParentSingleton : public nsISupports
+{
+  NS_DECL_THREADSAFE_ISUPPORTS
+
+  class OriginKey
+  {
+  public:
+    static const size_t DecodedLength = 18;
+
+    OriginKey(const nsACString& aKey, int64_t aSecondsStamp)
+    : mKey(aKey)
+    , mSecondsStamp(aSecondsStamp) {}
+
+    nsCString mKey; // Base64 encoded.
+    int64_t mSecondsStamp;
+  };
+
+  class OriginKeysTable
+  {
+  public:
+    OriginKeysTable() {}
+
+    nsresult
+    GetOriginKey(const nsACString& aOrigin, nsCString& result)
+    {
+      OriginKey* key;
+      if (!mKeys.Get(aOrigin, &key)) {
+        nsCString salt; // Make a new one
+        nsresult rv = GenerateRandomName(salt, key->DecodedLength * 4 / 3);
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          return rv;
+        }
+        key = new OriginKey(salt, PR_Now() / PR_USEC_PER_SEC);
+        mKeys.Put(aOrigin, key);
+      }
+      result = key->mKey;
+      return NS_OK;
+    }
+
+    static PLDHashOperator
+    HashCleaner(const nsACString& aOrigin, nsAutoPtr<OriginKey>& aOriginKey,
+                void *aUserArg)
+    {
+      OriginKey* since = static_cast<OriginKey*>(aUserArg);
+
+      LOG((((aOriginKey->mSecondsStamp >= since->mSecondsStamp)?
+            "%s: REMOVE %lld >= %lld" :
+            "%s: KEEP   %lld < %lld"),
+            __FUNCTION__, aOriginKey->mSecondsStamp, since->mSecondsStamp));
+
+      return (aOriginKey->mSecondsStamp >= since->mSecondsStamp)?
+          PL_DHASH_REMOVE : PL_DHASH_NEXT;
+    }
+
+    void Clear(int64_t aSinceWhen)
+    {
+      // Avoid int64_t* <-> void* casting offset
+      OriginKey since(nsCString(), aSinceWhen  / PR_USEC_PER_SEC);
+      mKeys.Enumerate(HashCleaner, &since);
+    }
+
+  protected:
+    nsClassHashtable<nsCStringHashKey, OriginKey> mKeys;
+  };
+
+  class OriginKeysLoader : public OriginKeysTable
+  {
+  public:
+    OriginKeysLoader()
+    {
+      Load();
+    }
+
+    nsresult
+    GetOriginKey(const nsACString& aOrigin, nsCString& result)
+    {
+      auto before = mKeys.Count();
+      OriginKeysTable::GetOriginKey(aOrigin, result);
+      if (mKeys.Count() != before) {
+        Save();
+      }
+      return NS_OK;
+    }
+
+    already_AddRefed<nsIFile>
+    GetFile()
+    {
+      if (!mProfileDir) {
+        nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
+                                             getter_AddRefs(mProfileDir));
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          return nullptr;
+        }
+      }
+      nsCOMPtr<nsIFile> file;
+      nsresult rv = mProfileDir->Clone(getter_AddRefs(file));
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return nullptr;
+      }
+      file->Append(NS_LITERAL_STRING(ORIGINKEYS_FILE));
+      return file.forget();
+    }
+
+    // Format of file is key secondsstamp origin (first line is version #):
+    //
+    // 1
+    // rOMAAbFujNwKyIpj4RJ3Wt5Q 1424733961 http://fiddle.jshell.net
+    // rOMAAbFujNwKyIpj4RJ3Wt5Q 1424734841 http://mozilla.github.io
+    // etc.
+
+    nsresult Read()
+    {
+      nsCOMPtr<nsIFile> file = GetFile();
+      if (NS_WARN_IF(!file)) {
+        return NS_ERROR_UNEXPECTED;
+      }
+      bool exists;
+      nsresult rv = file->Exists(&exists);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+      if (!exists) {
+        return NS_OK;
+      }
+
+      nsCOMPtr<nsIInputStream> stream;
+      rv = NS_NewLocalFileInputStream(getter_AddRefs(stream), file);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+      nsCOMPtr<nsILineInputStream> i = do_QueryInterface(stream);
+      MOZ_ASSERT(i);
+
+      nsCString line;
+      bool hasMoreLines;
+      rv = i->ReadLine(line, &hasMoreLines);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+      if (!line.EqualsLiteral(ORIGINKEYS_VERSION)) {
+        // If version on disk is newer than we can understand then ignore it.
+        return NS_OK;
+      }
+
+      while (hasMoreLines) {
+        rv = i->ReadLine(line, &hasMoreLines);
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          return rv;
+        }
+        // Read key secondsstamp origin.
+        // Ignore any lines that don't fit format in the comment above exactly.
+        int32_t f = line.FindChar(' ');
+        if (f < 0) {
+          continue;
+        }
+        const nsACString& key = Substring(line, 0, f);
+        const nsACString& s = Substring(line, f+1);
+        f = s.FindChar(' ');
+        if (f < 0) {
+          continue;
+        }
+        int64_t secondsstamp = nsCString(Substring(s, 0, f)).ToInteger64(&rv);
+        if (NS_FAILED(rv)) {
+          continue;
+        }
+        const nsACString& origin = Substring(s, f+1);
+
+        // Validate key
+        if (key.Length() != OriginKey::DecodedLength) {
+          continue;
+        }
+        nsCString dummy;
+        rv = Base64Decode(key, dummy);
+        if (NS_FAILED(rv)) {
+          continue;
+        }
+        mKeys.Put(origin, new OriginKey(key, secondsstamp));
+      }
+      return NS_OK;
+    }
+
+    static PLDHashOperator
+    HashWriter(const nsACString& aOrigin, OriginKey* aOriginKey, void *aUserArg)
+    {
+      auto* stream = static_cast<nsIOutputStream *>(aUserArg);
+
+      nsCString buffer;
+      buffer.Append(aOriginKey->mKey);
+      buffer.Append(' ');
+      buffer.AppendInt(aOriginKey->mSecondsStamp);
+      buffer.Append(' ');
+      buffer.Append(aOrigin);
+      buffer.Append('\n');
+
+      uint32_t count;
+      nsresult rv = stream->Write(buffer.Data(), buffer.Length(), &count);
+      if (NS_WARN_IF(NS_FAILED(rv)) || count != buffer.Length()) {
+        return PL_DHASH_STOP;
+      }
+      return PL_DHASH_NEXT;
+    }
+
+    nsresult
+    Write()
+    {
+      nsCOMPtr<nsIFile> file = GetFile();
+      if (NS_WARN_IF(!file)) {
+        return NS_ERROR_UNEXPECTED;
+      }
+
+      nsCOMPtr<nsIOutputStream> stream;
+      nsresult rv = NS_NewSafeLocalFileOutputStream(getter_AddRefs(stream), file);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+      nsAutoCString buffer;
+      buffer.AppendLiteral(ORIGINKEYS_VERSION);
+      buffer.Append('\n');
+
+      uint32_t count;
+      rv = stream->Write(buffer.Data(), buffer.Length(), &count);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+      if (count != buffer.Length()) {
+        return NS_ERROR_UNEXPECTED;
+      }
+      mKeys.EnumerateRead(HashWriter, stream.get());
+
+      nsCOMPtr<nsISafeOutputStream> safeStream = do_QueryInterface(stream);
+      MOZ_ASSERT(safeStream);
+
+      rv = safeStream->Finish();
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+      return NS_OK;
+    }
+
+    nsresult Load()
+    {
+      nsresult rv = Read();
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        Delete();
+      }
+      return rv;
+    }
+
+    nsresult Save()
+    {
+      nsresult rv = Write();
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        NS_WARNING("Failed to write data for EnumerateDevices id-persistence.");
+        Delete();
+      }
+      return rv;
+    }
+
+    void Clear(int64_t aSinceWhen)
+    {
+      OriginKeysTable::Clear(aSinceWhen);
+      Delete();
+      Save();
+    }
+
+    nsresult Delete()
+    {
+      nsCOMPtr<nsIFile> file = GetFile();
+      if (NS_WARN_IF(!file)) {
+        return NS_ERROR_UNEXPECTED;
+      }
+      nsresult rv = file->Remove(false);
+      if (rv == NS_ERROR_FILE_NOT_FOUND) {
+        return NS_OK;
+      }
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+      return NS_OK;
+    }
+
+  private:
+    nsCOMPtr<nsIFile> mProfileDir;
+  };
+
+private:
+  virtual ~ParentSingleton()
+  {
+    sParentSingleton = nullptr;
+    LOG((__FUNCTION__));
+  }
+
+public:
+  static ParentSingleton* Get()
+  {
+    // Protect creation of singleton and access from multiple Background threads.
+    //
+    // Multiple Background threads happen because sanitize.js calls us from the
+    // chrome process and gets a thread separate from the one servicing ipc from
+    // the content process.
+
+    StaticMutexAutoLock lock(gMutex);
+    if (!sParentSingleton) {
+      sParentSingleton = new ParentSingleton();
+    }
+    return sParentSingleton;
+  }
+
+  OriginKeysLoader mOriginKeys;
+  OriginKeysTable mPrivateBrowsingOriginKeys;
+};
+
+NS_IMPL_ISUPPORTS0(ParentSingleton)
+
+bool
+Parent::RecvGetOriginKey(const uint32_t& aRequestId,
+                         const nsCString& aOrigin,
+                         const bool& aPrivateBrowsing)
+{
+  // Hand over to stream-transport thread.
+
+  nsCOMPtr<nsIEventTarget> sts = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
+  MOZ_ASSERT(sts);
+  nsRefPtr<ParentSingleton> singleton(mSingleton);
+
+  nsRefPtr<PledgeRunnable<nsCString>> p = PledgeRunnable<nsCString>::New(
+      [singleton, aOrigin, aPrivateBrowsing](nsCString& aResult) {
+    if (aPrivateBrowsing) {
+      singleton->mPrivateBrowsingOriginKeys.GetOriginKey(aOrigin, aResult);
+    } else {
+      singleton->mOriginKeys.GetOriginKey(aOrigin, aResult);
+    }
+    return NS_OK;
+  });
+  nsresult rv = sts->Dispatch(p, NS_DISPATCH_NORMAL);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return false;
+  }
+  nsRefPtr<media::Parent> keepAlive(this);
+  p->Then([this, keepAlive, aRequestId](const nsCString& aKey) mutable {
+    if (!mDestroyed) {
+      unused << SendGetOriginKeyResponse(aRequestId, aKey);
+    }
+    return NS_OK;
+  });
+  return true;
+}
+
+bool
+Parent::RecvSanitizeOriginKeys(const uint64_t& aSinceWhen)
+{
+  // Hand over to stream-transport thread.
+
+  nsCOMPtr<nsIEventTarget> sts = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
+  MOZ_ASSERT(sts);
+  nsRefPtr<ParentSingleton> singleton(mSingleton);
+
+  nsRefPtr<PledgeRunnable<bool>> p = PledgeRunnable<bool>::New(
+      [singleton, aSinceWhen](bool) {
+    singleton->mPrivateBrowsingOriginKeys.Clear(aSinceWhen);
+    singleton->mOriginKeys.Clear(aSinceWhen);
+    return NS_OK;
+  });
+  nsresult rv = sts->Dispatch(p, NS_DISPATCH_NORMAL);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return false;
+  }
+  return true;
+}
+
+void
+Parent::ActorDestroy(ActorDestroyReason aWhy)
+{
+  // No more IPC from here
+  mDestroyed = true;
+  LOG((__FUNCTION__));
+}
+
+Parent::Parent()
+  : mSingleton(ParentSingleton::Get())
+  , mDestroyed(false)
+{
+#if defined(PR_LOGGING)
+  if (!gMediaParentLog)
+    gMediaParentLog = PR_NewLogModule("MediaParent");
+#endif
+  LOG(("media::Parent: %p", this));
+
+  MOZ_COUNT_CTOR(Parent);
+}
+
+Parent::~Parent()
+{
+  LOG(("~media::Parent: %p", this));
+
+  MOZ_COUNT_DTOR(Parent);
+}
+
+PMediaParent*
+AllocPMediaParent()
+{
+  Parent* obj = new Parent();
+  obj->AddRef();
+  return obj;
+}
+
+bool
+DeallocPMediaParent(media::PMediaParent *aActor)
+{
+  static_cast<Parent*>(aActor)->Release();
+  return true;
+}
+
+}
+}
new file mode 100644
--- /dev/null
+++ b/dom/media/systemservices/MediaParent.h
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et ft=cpp : */
+/* 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_MediaParent_h
+#define mozilla_MediaParent_h
+
+#include "MediaChild.h"
+
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/media/PMediaParent.h"
+
+namespace mozilla {
+namespace media {
+
+// media::Parent implements the chrome-process side of ipc for media::Child APIs
+
+class ParentSingleton;
+
+class Parent : public PMediaParent
+{
+  NS_INLINE_DECL_REFCOUNTING(Parent)
+public:
+  virtual bool RecvGetOriginKey(const uint32_t& aRequestId,
+                                const nsCString& aOrigin,
+                                const bool& aPrivateBrowsing) override;
+  virtual bool RecvSanitizeOriginKeys(const uint64_t& aSinceWhen) override;
+  virtual void ActorDestroy(ActorDestroyReason aWhy) override;
+
+  Parent();
+private:
+  virtual ~Parent();
+
+  nsRefPtr<ParentSingleton> mSingleton;
+  bool mDestroyed;
+};
+
+PMediaParent* AllocPMediaParent();
+bool DeallocPMediaParent(PMediaParent *aActor);
+
+} // namespace media
+} // namespace mozilla
+
+#endif  // mozilla_MediaParent_h
new file mode 100644
--- /dev/null
+++ b/dom/media/systemservices/MediaUtils.cpp
@@ -0,0 +1,13 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et ft=cpp : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "MediaUtils.h"
+
+namespace mozilla {
+namespace media {
+
+}
+}
new file mode 100644
--- /dev/null
+++ b/dom/media/systemservices/MediaUtils.h
@@ -0,0 +1,197 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et ft=cpp : */
+/* 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_MediaUtils_h
+#define mozilla_MediaUtils_h
+
+#include "nsAutoPtr.h"
+#include "nsThreadUtils.h"
+
+namespace mozilla {
+namespace media {
+
+// A media::Pledge is a promise-like pattern for c++ that takes lambda functions.
+//
+// Asynchronous APIs that proxy to the chrome process and back, may return a
+// pledge to allow callers to use pledge.Then() to specify a lambda function to
+// invoke with the result once the asynchronous API resolves later back on the
+// same thread.
+//
+// An advantage of this pattern is that lambdas allow "capturing" of local
+// variables, much like closures in JavaScript.
+
+template<typename ValueType>
+class Pledge
+{
+  // TODO: Remove workaround once mozilla allows std::function from <functional>
+  class FunctorsBase
+  {
+  public:
+    FunctorsBase() {}
+    virtual void Succeed(const ValueType& result) = 0;
+    virtual void Fail(nsresult rv) = 0;
+    virtual ~FunctorsBase() {};
+  };
+
+public:
+  explicit Pledge() : mDone(false), mResult(NS_OK) {}
+
+  template<typename OnSuccessType>
+  void Then(OnSuccessType aOnSuccess)
+  {
+    Then(aOnSuccess, [](nsresult){});
+  }
+
+  template<typename OnSuccessType, typename OnFailureType>
+  void Then(OnSuccessType aOnSuccess, OnFailureType aOnFailure)
+  {
+    class F : public FunctorsBase
+    {
+    public:
+      F(OnSuccessType& aOnSuccess, OnFailureType& aOnFailure)
+        : mOnSuccess(aOnSuccess), mOnFailure(aOnFailure) {}
+
+      void Succeed(const ValueType& result)
+      {
+        mOnSuccess(result);
+      }
+      void Fail(nsresult rv)
+      {
+        mOnFailure(rv);
+      };
+
+      OnSuccessType mOnSuccess;
+      OnFailureType mOnFailure;
+    };
+    mFunctors = new F(aOnSuccess, aOnFailure);
+
+    if (mDone) {
+      if (mResult == NS_OK) {
+        mFunctors->Succeed(mValue);
+      } else {
+        mFunctors->Fail(mResult);
+      }
+    }
+  }
+
+protected:
+  void Resolve(const ValueType& aValue)
+  {
+    mValue = aValue;
+    Resolve();
+  }
+
+  void Resolve()
+  {
+    if (!mDone) {
+      mDone = true;
+      MOZ_ASSERT(mResult == NS_OK);
+      if (mFunctors) {
+        mFunctors->Succeed(mValue);
+      }
+    }
+  }
+
+  void Reject(nsresult rv)
+  {
+    if (!mDone) {
+      mDone = true;
+      mResult = rv;
+      if (mFunctors) {
+        mFunctors->Fail(mResult);
+      }
+    }
+  }
+
+  ValueType mValue;
+protected:
+  bool mDone;
+  nsresult mResult;
+private:
+  nsAutoPtr<FunctorsBase> mFunctors;
+};
+
+// General purpose runnable that also acts as a Pledge for the resulting value.
+// Use PledgeRunnable<>::New() factory function to use with lambdas.
+
+template<typename ValueType>
+class PledgeRunnable : public Pledge<ValueType>, public nsRunnable
+{
+public:
+  template<typename OnRunType>
+  static PledgeRunnable<ValueType>*
+  New(OnRunType aOnRun)
+  {
+    class P : public PledgeRunnable<ValueType>
+    {
+    public:
+      explicit P(OnRunType& aOnRun)
+      : mOriginThread(NS_GetCurrentThread())
+      , mOnRun(aOnRun)
+      , mHasRun(false) {}
+    private:
+      virtual ~P() {}
+      NS_IMETHODIMP
+      Run()
+      {
+        if (!mHasRun) {
+          P::mResult = mOnRun(P::mValue);
+          mHasRun = true;
+          return mOriginThread->Dispatch(this, NS_DISPATCH_NORMAL);
+        }
+        bool on;
+        MOZ_RELEASE_ASSERT(NS_SUCCEEDED(mOriginThread->IsOnCurrentThread(&on)));
+        MOZ_RELEASE_ASSERT(on);
+
+        if (NS_SUCCEEDED(P::mResult)) {
+          P::Resolve();
+        } else {
+          P::Reject(P::mResult);
+        }
+        return NS_OK;
+      }
+      nsCOMPtr<nsIThread> mOriginThread;
+      OnRunType mOnRun;
+      bool mHasRun;
+    };
+
+    return new P(aOnRun);
+  }
+
+protected:
+  virtual ~PledgeRunnable() {}
+};
+
+// General purpose runnable with an eye toward lambdas
+
+namespace CallbackRunnable
+{
+template<typename OnRunType>
+class Impl : public nsRunnable
+{
+public:
+  explicit Impl(OnRunType& aOnRun) : mOnRun(aOnRun) {}
+private:
+  NS_IMETHODIMP
+  Run()
+  {
+    return mOnRun();
+  }
+  OnRunType mOnRun;
+};
+
+template<typename OnRunType>
+Impl<OnRunType>*
+New(OnRunType aOnRun)
+{
+  return new Impl<OnRunType>(aOnRun);
+}
+}
+
+}
+}
+
+#endif // mozilla_MediaUtils_h
new file mode 100644
--- /dev/null
+++ b/dom/media/systemservices/PMedia.ipdl
@@ -0,0 +1,35 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include protocol PContent;
+include protocol PBackground;
+
+namespace mozilla {
+namespace media {
+
+protocol PMedia
+{
+  manager PBackground;
+
+parent:
+  /**
+   * Requests a persistent unique secret key for each origin.
+   * Has no expiry, but is cleared by age along with cookies.
+   * This is needed by mediaDevices.enumerateDevices() to produce persistent
+   * deviceIds that wont work cross-origin.
+   */
+  GetOriginKey(uint32_t aRequestId, nsCString aOrigin, bool aPrivateBrowsing);
+
+  /**
+   * On clear cookies. Fire and forget.
+   */
+  SanitizeOriginKeys(uint64_t aSinceWhen);
+
+child:
+  GetOriginKeyResponse(uint32_t aRequestId, nsCString key);
+  __delete__();
+};
+
+} // namespace media
+} // namespace mozilla
--- a/dom/media/systemservices/moz.build
+++ b/dom/media/systemservices/moz.build
@@ -32,13 +32,29 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'coco
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
     CXXFLAGS += [
         '-I%s/%s' % (CONFIG['ANDROID_SOURCE'], d) for d in [
             'frameworks/wilhelm/include',
             'system/media/wilhelm/include',
         ]
     ]
 
+EXPORTS.mozilla.media += ['MediaChild.h',
+            'MediaParent.h',
+            'MediaUtils.h'
+]
+UNIFIED_SOURCES += ['MediaChild.cpp',
+                    'MediaParent.cpp',
+                    'MediaUtils.cpp'
+]
+IPDL_SOURCES += [
+    'PMedia.ipdl',
+]
+# /dom/base needed for nsGlobalWindow.h in MediaChild.cpp
+LOCAL_INCLUDES += [
+    '/dom/base',
+]
+
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 
 FAIL_ON_WARNINGS = True
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -640,16 +640,18 @@ var interfaceNamesInGlobalScope =
     "KeyEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "KeyboardEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "LocalMediaStream",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "Location",
 // IMPORTANT: Do not change this list without review from a DOM peer!
+    "MediaDeviceInfo",
+// IMPORTANT: Do not change this list without review from a DOM peer!
     "MediaDevices",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "MediaElementAudioSourceNode",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "MediaError",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "MediaKeyError", pref: "media.eme.apiVisible"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
new file mode 100644
--- /dev/null
+++ b/dom/webidl/MediaDeviceInfo.webidl
@@ -0,0 +1,22 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * The origin of this IDL file is
+ * http://dev.w3.org/2011/webrtc/editor/getusermedia.html
+ */
+
+enum MediaDeviceKind {
+  "audioinput",
+  "audiooutput",
+  "videoinput"
+};
+
+[Func="Navigator::HasUserMediaSupport"]
+interface MediaDeviceInfo {
+  readonly attribute DOMString       deviceId;
+  readonly attribute MediaDeviceKind kind;
+  readonly attribute DOMString       label;
+  readonly attribute DOMString       groupId;
+};
--- a/dom/webidl/MediaDevices.webidl
+++ b/dom/webidl/MediaDevices.webidl
@@ -7,17 +7,17 @@
  * http://dev.w3.org/2011/webrtc/editor/getusermedia.html
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
 [Func="Navigator::HasUserMediaSupport"]
 interface MediaDevices : EventTarget {
-//    attribute EventHandler ondevicechange;
-//
-//    void enumerateDevices (MediaDeviceInfoCallback resultCallback);
-//
+//  attribute EventHandler ondevicechange;
 //  static Dictionary getSupportedConstraints (DOMString kind);
 
-  [Throws, Func="Navigator::HasUserMediaSupport"]
+  [Throws]
+  Promise<sequence<MediaDeviceInfo>> enumerateDevices();
+
+  [Throws]
   Promise<MediaStream> getUserMedia(optional MediaStreamConstraints constraints);
 };
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -257,16 +257,17 @@ WEBIDL_FILES = [
     'KeyAlgorithm.webidl',
     'KeyboardEvent.webidl',
     'KeyEvent.webidl',
     'LegacyQueryInterface.webidl',
     'LinkStyle.webidl',
     'ListBoxObject.webidl',
     'LocalMediaStream.webidl',
     'Location.webidl',
+    'MediaDeviceInfo.webidl',
     'MediaDevices.webidl',
     'MediaElementAudioSourceNode.webidl',
     'MediaError.webidl',
     'MediaList.webidl',
     'MediaQueryList.webidl',
     'MediaRecorder.webidl',
     'MediaSource.webidl',
     'MediaStream.webidl',
--- a/image/decoders/icon/mac/nsIconChannel.h
+++ b/image/decoders/icon/mac/nsIconChannel.h
@@ -35,17 +35,16 @@ public:
 
   nsresult Init(nsIURI* uri);
 
 protected:
   virtual ~nsIconChannel();
 
   nsCOMPtr<nsIURI> mUrl;
   nsCOMPtr<nsIURI> mOriginalURI;
-  int64_t mContentLength;
   nsCOMPtr<nsILoadGroup> mLoadGroup;
   nsCOMPtr<nsIInterfaceRequestor> mCallbacks;
   nsCOMPtr<nsISupports>  mOwner;
   nsCOMPtr<nsILoadInfo>  mLoadInfo;
 
   nsCOMPtr<nsIInputStreamPump> mPump;
   nsCOMPtr<nsIStreamListener>  mListener;
 
--- a/image/decoders/icon/mac/nsIconChannelCocoa.mm
+++ b/image/decoders/icon/mac/nsIconChannelCocoa.mm
@@ -455,18 +455,18 @@ nsIconChannel::
   GetContentDispositionHeader(nsACString& aContentDispositionHeader)
 {
   return NS_ERROR_NOT_AVAILABLE;
 }
 
 NS_IMETHODIMP
 nsIconChannel::GetContentLength(int64_t* aContentLength)
 {
-  *aContentLength = mContentLength;
-  return NS_OK;
+  *aContentLength = 0;
+  return NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
 nsIconChannel::SetContentLength(int64_t aContentLength)
 {
   NS_NOTREACHED("nsIconChannel::SetContentLength");
   return NS_ERROR_NOT_IMPLEMENTED;
 }
--- a/image/decoders/icon/win/nsIconChannel.cpp
+++ b/image/decoders/icon/win/nsIconChannel.cpp
@@ -703,18 +703,18 @@ nsIconChannel::
   GetContentDispositionHeader(nsACString& aContentDispositionHeader)
 {
   return NS_ERROR_NOT_AVAILABLE;
 }
 
 NS_IMETHODIMP
 nsIconChannel::GetContentLength(int64_t* aContentLength)
 {
-  *aContentLength = mContentLength;
-  return NS_OK;
+  *aContentLength = 0;
+  return NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
 nsIconChannel::SetContentLength(int64_t aContentLength)
 {
   NS_NOTREACHED("nsIconChannel::SetContentLength");
   return NS_ERROR_NOT_IMPLEMENTED;
 }
--- a/image/decoders/icon/win/nsIconChannel.h
+++ b/image/decoders/icon/win/nsIconChannel.h
@@ -38,17 +38,16 @@ public:
 
   nsIconChannel();
 
   nsresult Init(nsIURI* uri);
 
 protected:
   nsCOMPtr<nsIURI> mUrl;
   nsCOMPtr<nsIURI> mOriginalURI;
-  int64_t          mContentLength;
   nsCOMPtr<nsILoadGroup> mLoadGroup;
   nsCOMPtr<nsIInterfaceRequestor> mCallbacks;
   nsCOMPtr<nsISupports>  mOwner;
   nsCOMPtr<nsILoadInfo>  mLoadInfo;
 
   nsCOMPtr<nsIInputStreamPump> mPump;
   nsCOMPtr<nsIStreamListener>  mListener;
 
--- a/image/src/SourceBuffer.cpp
+++ b/image/src/SourceBuffer.cpp
@@ -116,18 +116,23 @@ SourceBuffer::Compact()
   MOZ_ASSERT(mConsumerCount == 0, "Should have no consumers here");
   MOZ_ASSERT(mWaitingConsumers.Length() == 0, "Shouldn't have waiters");
   MOZ_ASSERT(mStatus, "Should be complete here");
 
   // Compact our waiting consumers list, since we're complete and no future
   // consumer will ever have to wait.
   mWaitingConsumers.Compact();
 
-  // If we have no more than one chunk, then we can't compact further.
-  if (mChunks.Length() < 2) {
+  // If we have no chunks, then there's nothing to compact.
+  if (mChunks.Length() < 1) {
+    return NS_OK;
+  }
+
+  // If we have one chunk, then we can compact if it has excess capacity.
+  if (mChunks.Length() == 1 && mChunks[0].Length() == mChunks[0].Capacity()) {
     return NS_OK;
   }
 
   // We can compact our buffer. Determine the total length.
   size_t length = 0;
   for (uint32_t i = 0 ; i < mChunks.Length() ; ++i) {
     length += mChunks[i].Length();
   }
--- a/image/src/imgRequest.cpp
+++ b/image/src/imgRequest.cpp
@@ -657,16 +657,23 @@ imgRequest::CacheChanged(nsIRequest* aNe
 
 bool
 imgRequest::GetMultipart() const
 {
   MutexAutoLock lock(mMutex);
   return mIsMultiPartChannel;
 }
 
+bool
+imgRequest::HadInsecureRedirect() const
+{
+  MutexAutoLock lock(mMutex);
+  return mHadInsecureRedirect;
+}
+
 /** nsIRequestObserver methods **/
 
 /* void onStartRequest (in nsIRequest request, in nsISupports ctxt); */
 NS_IMETHODIMP imgRequest::OnStartRequest(nsIRequest *aRequest, nsISupports *ctxt)
 {
   LOG_SCOPE(GetImgLog(), "imgRequest::OnStartRequest");
 
   nsRefPtr<Image> image;
@@ -1179,16 +1186,17 @@ imgRequest::OnRedirectVerifyCallback(nsr
   // point in the redirect chain.
   bool isHttps = false;
   bool isChrome = false;
   bool schemeLocal = false;
   if (NS_FAILED(mCurrentURI->SchemeIs("https", &isHttps)) ||
       NS_FAILED(mCurrentURI->SchemeIs("chrome", &isChrome)) ||
       NS_FAILED(NS_URIChainHasFlags(mCurrentURI, nsIProtocolHandler::URI_IS_LOCAL_RESOURCE , &schemeLocal))  ||
       (!isHttps && !isChrome && !schemeLocal)) {
+    MutexAutoLock lock(mMutex);
     mHadInsecureRedirect = true;
   }
 
   // Update the current URI.
   mChannel->GetURI(getter_AddRefs(mCurrentURI));
 
   if (LOG_TEST(PR_LOG_DEBUG)) {
     nsAutoCString spec;
--- a/image/src/imgRequest.h
+++ b/image/src/imgRequest.h
@@ -111,17 +111,17 @@ public:
   // on one of the loads is considered a change of a loading cache since
   // HTTP cache may contain a different data then app cache.
   bool CacheChanged(nsIRequest* aNewRequest);
 
   bool GetMultipart() const;
 
   // Returns whether we went through an insecure (non-HTTPS) redirect at some
   // point during loading. This does not consider the current URI.
-  bool HadInsecureRedirect() const { return mHadInsecureRedirect; }
+  bool HadInsecureRedirect() const;
 
   // The CORS mode for which we loaded this image.
   int32_t GetCORSMode() const { return mCORSMode; }
 
   // The Referrer Policy in effect when loading this image.
   ReferrerPolicy GetReferrerPolicy() const { return mReferrerPolicy; }
 
   // The principal for the document that loaded this image. Used when trying to
--- a/ipc/glue/BackgroundChildImpl.cpp
+++ b/ipc/glue/BackgroundChildImpl.cpp
@@ -2,16 +2,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "BackgroundChildImpl.h"
 
 #include "ActorsChild.h" // IndexedDB
 #include "BroadcastChannelChild.h"
 #include "FileDescriptorSetChild.h"
+#include "mozilla/media/MediaChild.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/dom/PBlobChild.h"
 #include "mozilla/dom/cache/ActorUtils.h"
 #include "mozilla/dom/indexedDB/PBackgroundIDBFactoryChild.h"
 #include "mozilla/dom/ipc/BlobChild.h"
 #include "mozilla/ipc/PBackgroundTestChild.h"
 #include "mozilla/layout/VsyncChild.h"
 #include "nsID.h"
@@ -276,16 +277,28 @@ BackgroundChildImpl::AllocPCacheStreamCo
 
 bool
 BackgroundChildImpl::DeallocPCacheStreamControlChild(PCacheStreamControlChild* aActor)
 {
   dom::cache::DeallocPCacheStreamControlChild(aActor);
   return true;
 }
 
+media::PMediaChild*
+BackgroundChildImpl::AllocPMediaChild()
+{
+  return media::AllocPMediaChild();
+}
+
+bool
+BackgroundChildImpl::DeallocPMediaChild(media::PMediaChild *aActor)
+{
+  return media::DeallocPMediaChild(aActor);
+}
+
 } // namespace ipc
 } // namespace mozilla
 
 bool
 TestChild::Recv__delete__(const nsCString& aTestArg)
 {
   MOZ_RELEASE_ASSERT(aTestArg == mTestArg,
                      "BackgroundTest message was corrupted!");
--- a/ipc/glue/BackgroundChildImpl.h
+++ b/ipc/glue/BackgroundChildImpl.h
@@ -66,16 +66,22 @@ protected:
 
   virtual PFileDescriptorSetChild*
   AllocPFileDescriptorSetChild(const FileDescriptor& aFileDescriptor)
                                override;
 
   virtual bool
   DeallocPFileDescriptorSetChild(PFileDescriptorSetChild* aActor) override;
 
+  virtual PMediaChild*
+  AllocPMediaChild() override;
+
+  virtual bool
+  DeallocPMediaChild(PMediaChild* aActor) override;
+
   virtual PVsyncChild*
   AllocPVsyncChild() override;
 
   virtual bool
   DeallocPVsyncChild(PVsyncChild* aActor) override;
 
   virtual PBroadcastChannelChild*
   AllocPBroadcastChannelChild(const PrincipalInfo& aPrincipalInfo,
--- a/ipc/glue/BackgroundParentImpl.cpp
+++ b/ipc/glue/BackgroundParentImpl.cpp
@@ -1,16 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "BackgroundParentImpl.h"
 
 #include "BroadcastChannelParent.h"
 #include "FileDescriptorSetParent.h"
+#include "mozilla/media/MediaParent.h"
 #include "mozilla/AppProcessChecker.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/PBlobParent.h"
 #include "mozilla/dom/ServiceWorkerRegistrar.h"
 #include "mozilla/dom/cache/ActorUtils.h"
 #include "mozilla/dom/indexedDB/ActorsParent.h"
 #include "mozilla/dom/ipc/BlobParent.h"
@@ -355,16 +356,28 @@ BackgroundParentImpl::DeallocPBroadcastC
   AssertIsInMainProcess();
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aActor);
 
   delete static_cast<BroadcastChannelParent*>(aActor);
   return true;
 }
 
+media::PMediaParent*
+BackgroundParentImpl::AllocPMediaParent()
+{
+  return media::AllocPMediaParent();
+}
+
+bool
+BackgroundParentImpl::DeallocPMediaParent(media::PMediaParent *aActor)
+{
+  return media::DeallocPMediaParent(aActor);
+}
+
 namespace {
 
 class RegisterServiceWorkerCallback final : public nsRunnable
 {
 public:
   explicit RegisterServiceWorkerCallback(
                                      const ServiceWorkerRegistrationData& aData)
     : mData(aData)
--- a/ipc/glue/BackgroundParentImpl.h
+++ b/ipc/glue/BackgroundParentImpl.h
@@ -79,16 +79,22 @@ protected:
   RecvPBroadcastChannelConstructor(PBroadcastChannelParent* actor,
                                    const PrincipalInfo& aPrincipalInfo,
                                    const nsString& origin,
                                    const nsString& channel) override;
 
   virtual bool
   DeallocPBroadcastChannelParent(PBroadcastChannelParent* aActor) override;
 
+  virtual PMediaParent*
+  AllocPMediaParent() override;
+
+  virtual bool
+  DeallocPMediaParent(PMediaParent* aActor) override;
+
   virtual bool
   RecvRegisterServiceWorker(const ServiceWorkerRegistrationData& aData)
                             override;
 
   virtual bool
   RecvUnregisterServiceWorker(const PrincipalInfo& aPrincipalInfo,
                               const nsString& aScope) override;
 
--- a/ipc/glue/PBackground.ipdl
+++ b/ipc/glue/PBackground.ipdl
@@ -6,16 +6,17 @@ include protocol PBackgroundIDBFactory;
 include protocol PBackgroundTest;
 include protocol PBlob;
 include protocol PBroadcastChannel;
 include protocol PCache;
 include protocol PCacheStorage;
 include protocol PCacheStreamControl;
 include protocol PFileDescriptorSet;
 include protocol PVsync;
+include protocol PMedia;
 
 include DOMTypes;
 include PBackgroundSharedTypes;
 include PBackgroundIDBSharedTypes;
 include ServiceWorkerRegistrarTypes;
 
 using mozilla::dom::cache::Namespace from "mozilla/dom/cache/Types.h";
 include "mozilla/dom/cache/IPCUtils.h";
@@ -29,24 +30,26 @@ sync protocol PBackground
   manages PBackgroundTest;
   manages PBlob;
   manages PBroadcastChannel;
   manages PCache;
   manages PCacheStorage;
   manages PCacheStreamControl;
   manages PFileDescriptorSet;
   manages PVsync;
+  manages PMedia;
 
 parent:
   // Only called at startup during mochitests to check the basic infrastructure.
   PBackgroundTest(nsCString testArg);
 
   PBackgroundIDBFactory(LoggingInfo loggingInfo);
 
   PVsync();
+  PMedia();
 
   PBroadcastChannel(PrincipalInfo pInfo, nsString origin, nsString channel);
 
   RegisterServiceWorker(ServiceWorkerRegistrationData data);
   UnregisterServiceWorker(PrincipalInfo principalInfo,
                           nsString scope);
   ShutdownServiceWorkerRegistrar();
 
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -9083,18 +9083,16 @@ PresShell::DoReflow(nsIFrame* target, bo
   nsIFrame* rootFrame = mFrameConstructor->GetRootFrame();
 
   nsRenderingContext rcx(CreateReferenceRenderingContext());
 
 #ifdef DEBUG
   mCurrentReflowRoot = target;
 #endif
 
-  target->WillReflow(mPresContext);
-
   // If the target frame is the root of the frame hierarchy, then
   // use all the available space. If it's simply a `reflow root',
   // then use the target frame's size as the available space.
   WritingMode wm = target->GetWritingMode();
   LogicalSize size(wm);
   if (target == rootFrame) {
     size = LogicalSize(wm, mPresContext->GetVisibleArea().Size());
   } else {
--- a/layout/doc/obsolete/layout-internals.html
+++ b/layout/doc/obsolete/layout-internals.html
@@ -41,19 +41,19 @@ with a rectangular region on the present
 etc.) and contains the formatting information needed to render that rectangle.&nbsp;
 The block and inline frame classes implement the nsIFrame and nsIHTMLReflow
 interfaces.&nbsp; The nsIFrame interface contains methods for managing
 child frames and linkage with sibling frames, accessing the style context
 associated with the frame, painting the frame, and handling events that
 are passed in from the widget hierarchy.&nbsp; The nsIHTMLReflow interface
 inherits from the nsIReflow interface and adds methods related to word
 breaking and whitespace querying.&nbsp; The nsIReflow interface defines
-the Reflow() method that initiates the reflow process along with the WillReflow()
-and DidReflow() methods that get called before and after the reflow process
-respectively.&nbsp; nsReflowState and nsReflowMetrics are parameters to
+the Reflow() method that initiates the reflow process along with the
+DidReflow() method that get calledafter the reflow process.&nbsp;
+nsReflowState and nsReflowMetrics are parameters to
 the templatized nsIReflow interface: the former is used to hold state during
 reflow of a frame and the latter is used to return the frame's desired
 size and alignment to the parent frame during the reflow process.
 <p>nsBlockReflowContext and nsBlockReflowState both hold state information
 during the reflow process.&nbsp; nsBlockReflowContext encapsulates the
 state and algorithm for reflowing child block frames.&nbsp; nsBlockReflowState
 contains state and methods used by a block frame to reflow itself.&nbsp;
 Both these classes are instantiated once per block frame.
--- a/layout/forms/nsComboboxControlFrame.cpp
+++ b/layout/forms/nsComboboxControlFrame.cpp
@@ -770,16 +770,17 @@ nsComboboxControlFrame::GetPrefISize(nsR
 }
 
 void
 nsComboboxControlFrame::Reflow(nsPresContext*          aPresContext,
                                nsHTMLReflowMetrics&     aDesiredSize,
                                const nsHTMLReflowState& aReflowState,
                                nsReflowStatus&          aStatus)
 {
+  MarkInReflow();
   // Constraints we try to satisfy:
 
   // 1) Default width of button is the vertical scrollbar size
   // 2) If the width of button is bigger than our width, set width of
   //    button to 0.
   // 3) Default height of button is height of display area
   // 4) Width of display area is whatever is left over from our width after
   //    allocating width for the button.
--- a/layout/forms/nsFieldSetFrame.cpp
+++ b/layout/forms/nsFieldSetFrame.cpp
@@ -374,16 +374,17 @@ nsFieldSetFrame::ComputeSize(nsRendering
 }
 
 void
 nsFieldSetFrame::Reflow(nsPresContext*           aPresContext,
                         nsHTMLReflowMetrics&     aDesiredSize,
                         const nsHTMLReflowState& aReflowState,
                         nsReflowStatus&          aStatus)
 {
+  MarkInReflow();
   DO_GLOBAL_REFLOW_COUNT("nsFieldSetFrame");
   DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
 
   NS_PRECONDITION(aReflowState.ComputedISize() != NS_INTRINSICSIZE,
                   "Should have a precomputed inline-size!");
 
   // Initialize OUT parameter
   aStatus = NS_FRAME_COMPLETE;
--- a/layout/forms/nsHTMLButtonControlFrame.cpp
+++ b/layout/forms/nsHTMLButtonControlFrame.cpp
@@ -175,20 +175,21 @@ nsHTMLButtonControlFrame::GetPrefISize(n
     ? mRenderer.GetAddedButtonBorderAndPadding().TopBottom()
     : mRenderer.GetAddedButtonBorderAndPadding().LeftRight();
 
   return result;
 }
 
 void
 nsHTMLButtonControlFrame::Reflow(nsPresContext* aPresContext,
-                               nsHTMLReflowMetrics& aDesiredSize,
-                               const nsHTMLReflowState& aReflowState,
-                               nsReflowStatus& aStatus)
+                                 nsHTMLReflowMetrics& aDesiredSize,
+                                 const nsHTMLReflowState& aReflowState,
+                                 nsReflowStatus& aStatus)
 {
+  MarkInReflow();
   DO_GLOBAL_REFLOW_COUNT("nsHTMLButtonControlFrame");
   DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
 
   NS_PRECONDITION(aReflowState.ComputedISize() != NS_INTRINSICSIZE,
                   "Should have real computed inline-size by now");
 
   if (mState & NS_FRAME_FIRST_REFLOW) {
     nsFormControlFrame::RegUnRegAccessKey(static_cast<nsIFrame*>(this), true);
--- a/layout/forms/nsImageControlFrame.cpp
+++ b/layout/forms/nsImageControlFrame.cpp
@@ -121,20 +121,20 @@ nsImageControlFrame::AccessibleType()
 
 nsIAtom*
 nsImageControlFrame::GetType() const
 {
   return nsGkAtoms::imageControlFrame; 
 }
 
 void
-nsImageControlFrame::Reflow(nsPresContext*         aPresContext,
-                           nsHTMLReflowMetrics&     aDesiredSize,
-                           const nsHTMLReflowState& aReflowState,
-                           nsReflowStatus&          aStatus)
+nsImageControlFrame::Reflow(nsPresContext*           aPresContext,
+                            nsHTMLReflowMetrics&     aDesiredSize,
+                            const nsHTMLReflowState& aReflowState,
+                            nsReflowStatus&          aStatus)
 {
   DO_GLOBAL_REFLOW_COUNT("nsImageControlFrame");
   DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
   if (!GetPrevInFlow() && (mState & NS_FRAME_FIRST_REFLOW)) {
     nsFormControlFrame::RegUnRegAccessKey(this, true);
   }
   return nsImageControlFrameSuper::Reflow(aPresContext, aDesiredSize, aReflowState, aStatus);
 }
--- a/layout/forms/nsListControlFrame.cpp
+++ b/layout/forms/nsListControlFrame.cpp
@@ -359,16 +359,17 @@ nsListControlFrame::Reflow(nsPresContext
     nsFormControlFrame::RegUnRegAccessKey(this, true);
   }
 
   if (IsInDropDownMode()) {
     ReflowAsDropdown(aPresContext, aDesiredSize, aReflowState, aStatus);
     return;
   }
 
+  MarkInReflow();
   /*
    * Due to the fact that our intrinsic height depends on the heights of our
    * kids, we end up having to do two-pass reflow, in general -- the first pass
    * to find the intrinsic height and a second pass to reflow the scrollframe
    * at that height (which will size the scrollbars correctly, etc).
    *
    * Naturaly, we want to avoid doing the second reflow as much as possible.
    * We can skip it in the following cases (in all of which the first reflow is
@@ -452,18 +453,16 @@ nsListControlFrame::Reflow(nsPresContext
   nsHTMLScrollFrame::DidReflow(aPresContext, &state,
                                nsDidReflowStatus::FINISHED);
 
   // Now compute the height we want to have
   nscoord computedHeight = CalcIntrinsicBSize(HeightOfARow(), length); 
   computedHeight = state.ApplyMinMaxHeight(computedHeight);
   state.SetComputedHeight(computedHeight);
 
-  nsHTMLScrollFrame::WillReflow(aPresContext);
-
   // XXXbz to make the ascent really correct, we should add our
   // mComputedPadding.top to it (and subtract it from descent).  Need that
   // because nsGfxScrollFrame just adds in the border....
   nsHTMLScrollFrame::Reflow(aPresContext, aDesiredSize, state, aStatus);
 }
 
 void
 nsListControlFrame::ReflowAsDropdown(nsPresContext*           aPresContext, 
@@ -572,17 +571,16 @@ nsListControlFrame::ReflowAsDropdown(nsP
       state.SetComputedHeight(newHeight);
       mDropdownCanGrow = visibleHeight - newHeight >= heightOfARow &&
                          mNumDisplayRows != kMaxDropDownRows;
     }
   }
 
   mLastDropdownComputedHeight = state.ComputedHeight();
 
-  nsHTMLScrollFrame::WillReflow(aPresContext);
   nsHTMLScrollFrame::Reflow(aPresContext, aDesiredSize, state, aStatus);
 }
 
 ScrollbarStyles
 nsListControlFrame::GetScrollbarStyles() const
 {
   // We can't express this in the style system yet; when we can, this can go away
   // and GetScrollbarStyles can be devirtualized
--- a/layout/forms/nsMeterFrame.cpp
+++ b/layout/forms/nsMeterFrame.cpp
@@ -90,20 +90,21 @@ nsMeterFrame::AppendAnonymousContentTo(n
 NS_QUERYFRAME_HEAD(nsMeterFrame)
   NS_QUERYFRAME_ENTRY(nsMeterFrame)
   NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator)
 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
 
 
 void
 nsMeterFrame::Reflow(nsPresContext*           aPresContext,
-                                   nsHTMLReflowMetrics&     aDesiredSize,
-                                   const nsHTMLReflowState& aReflowState,
-                                   nsReflowStatus&          aStatus)
+                     nsHTMLReflowMetrics&     aDesiredSize,
+                     const nsHTMLReflowState& aReflowState,
+                     nsReflowStatus&          aStatus)
 {
+  MarkInReflow();
   DO_GLOBAL_REFLOW_COUNT("nsMeterFrame");
   DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
 
   NS_ASSERTION(mBarDiv, "Meter bar div must exist!");
   NS_ASSERTION(!GetPrevContinuation(),
                "nsMeterFrame should not have continuations; if it does we "
                "need to call RegUnregAccessKey only for the first.");
 
--- a/layout/forms/nsNumberControlFrame.cpp
+++ b/layout/forms/nsNumberControlFrame.cpp
@@ -101,16 +101,17 @@ nsNumberControlFrame::GetPrefISize(nsRen
 }
 
 void
 nsNumberControlFrame::Reflow(nsPresContext* aPresContext,
                              nsHTMLReflowMetrics& aDesiredSize,
                              const nsHTMLReflowState& aReflowState,
                              nsReflowStatus& aStatus)
 {
+  MarkInReflow();
   DO_GLOBAL_REFLOW_COUNT("nsNumberControlFrame");
   DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
 
   NS_ASSERTION(mOuterWrapper, "Outer wrapper div must exist!");
 
   NS_ASSERTION(!GetPrevContinuation() && !GetNextContinuation(),
                "nsNumberControlFrame should not have continuations; if it does we "
                "need to call RegUnregAccessKey only for the first");
--- a/layout/forms/nsProgressFrame.cpp
+++ b/layout/forms/nsProgressFrame.cpp
@@ -95,20 +95,21 @@ nsProgressFrame::BuildDisplayList(nsDisp
                                   const nsRect&           aDirtyRect,
                                   const nsDisplayListSet& aLists)
 {
   BuildDisplayListForInline(aBuilder, aDirtyRect, aLists);
 }
 
 void
 nsProgressFrame::Reflow(nsPresContext*           aPresContext,
-                                      nsHTMLReflowMetrics&     aDesiredSize,
-                                      const nsHTMLReflowState& aReflowState,
-                                      nsReflowStatus&          aStatus)
+                        nsHTMLReflowMetrics&     aDesiredSize,
+                        const nsHTMLReflowState& aReflowState,
+                        nsReflowStatus&          aStatus)
 {
+  MarkInReflow();
   DO_GLOBAL_REFLOW_COUNT("nsProgressFrame");
   DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
 
   NS_ASSERTION(mBarDiv, "Progress bar div must exist!");
   NS_ASSERTION(!GetPrevContinuation(),
                "nsProgressFrame should not have continuations; if it does we "
                "need to call RegUnregAccessKey only for the first.");
 
--- a/layout/forms/nsRangeFrame.cpp
+++ b/layout/forms/nsRangeFrame.cpp
@@ -274,16 +274,17 @@ nsRangeFrame::BuildDisplayList(nsDisplay
 }
 
 void
 nsRangeFrame::Reflow(nsPresContext*           aPresContext,
                      nsHTMLReflowMetrics&     aDesiredSize,
                      const nsHTMLReflowState& aReflowState,
                      nsReflowStatus&          aStatus)
 {
+  MarkInReflow();
   DO_GLOBAL_REFLOW_COUNT("nsRangeFrame");
   DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
 
   NS_ASSERTION(mTrackDiv, "::-moz-range-track div must exist!");
   NS_ASSERTION(mProgressDiv, "::-moz-range-progress div must exist!");
   NS_ASSERTION(mThumbDiv, "::-moz-range-thumb div must exist!");
   NS_ASSERTION(!GetPrevContinuation() && !GetNextContinuation(),
                "nsRangeFrame should not have continuations; if it does we "
--- a/layout/forms/nsTextControlFrame.cpp
+++ b/layout/forms/nsTextControlFrame.cpp
@@ -484,16 +484,17 @@ nsTextControlFrame::ComputeAutoSize(nsRe
 }
 
 void
 nsTextControlFrame::Reflow(nsPresContext*   aPresContext,
                            nsHTMLReflowMetrics&     aDesiredSize,
                            const nsHTMLReflowState& aReflowState,
                            nsReflowStatus&          aStatus)
 {
+  MarkInReflow();
   DO_GLOBAL_REFLOW_COUNT("nsTextControlFrame");
   DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
 
   // make sure that the form registers itself on the initial/first reflow
   if (mState & NS_FRAME_FIRST_REFLOW) {
     nsFormControlFrame::RegUnRegAccessKey(this, true);
   }
 
--- a/layout/generic/nsAbsoluteContainingBlock.cpp
+++ b/layout/generic/nsAbsoluteContainingBlock.cpp
@@ -392,19 +392,16 @@ nsAbsoluteContainingBlock::ReflowAbsolut
 
   nsHTMLReflowMetrics kidDesiredSize(aReflowState);
   nsHTMLReflowState kidReflowState(aPresContext, aReflowState, aKidFrame,
                                    LogicalSize(wm, availISize,
                                                NS_UNCONSTRAINEDSIZE),
                                    aContainingBlock.width,
                                    aContainingBlock.height);
 
-  // Send the WillReflow() notification and position the frame
-  aKidFrame->WillReflow(aPresContext);
-
   // Get the border values
   const nsMargin& border = aReflowState.mStyleBorder->GetComputedBorder();
 
   bool constrainHeight = (aReflowState.AvailableHeight() != NS_UNCONSTRAINEDSIZE)
     && aConstrainHeight
        // Don't split if told not to (e.g. for fixed frames)
     && (aDelegatingFrame->GetType() != nsGkAtoms::inlineFrame)
        //XXX we don't handle splitting frames for inline absolute containing blocks yet
--- a/layout/generic/nsBRFrame.cpp
+++ b/layout/generic/nsBRFrame.cpp
@@ -79,16 +79,17 @@ BRFrame::~BRFrame()
 }
 
 void
 BRFrame::Reflow(nsPresContext* aPresContext,
                 nsHTMLReflowMetrics& aMetrics,
                 const nsHTMLReflowState& aReflowState,
                 nsReflowStatus& aStatus)
 {
+  MarkInReflow();
   DO_GLOBAL_REFLOW_COUNT("BRFrame");
   DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus);
   WritingMode wm = aReflowState.GetWritingMode();
   LogicalSize finalSize(wm);
   finalSize.BSize(wm) = 0; // BR frames with block size 0 are ignored in quirks
                            // mode by nsLineLayout::VerticalAlignFrames .
                            // However, it's not always 0.  See below.
   finalSize.ISize(wm) = 0;
--- a/layout/generic/nsBlockFrame.cpp
+++ b/layout/generic/nsBlockFrame.cpp
@@ -1007,16 +1007,17 @@ CalculateContainingBlockSizeForAbsolutes
 }
 
 void
 nsBlockFrame::Reflow(nsPresContext*           aPresContext,
                      nsHTMLReflowMetrics&     aMetrics,
                      const nsHTMLReflowState& aReflowState,
                      nsReflowStatus&          aStatus)
 {
+  MarkInReflow();
   DO_GLOBAL_REFLOW_COUNT("nsBlockFrame");
   DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus);
 #ifdef DEBUG
   if (gNoisyReflow) {
     IndentBy(stdout, gNoiseIndent);
     ListTag(stdout);
     printf(": begin reflow availSize=%d,%d computedSize=%d,%d\n",
            aReflowState.AvailableISize(), aReflowState.AvailableBSize(),
@@ -6998,17 +6999,16 @@ nsBlockFrame::ReflowBullet(nsIFrame* aBu
   availSize.BSize(bulletWM) = NS_UNCONSTRAINEDSIZE;
 
   // Get the reason right.
   // XXXwaterson Should this look just like the logic in
   // nsBlockReflowContext::ReflowBlock and nsLineLayout::ReflowFrame?
   nsHTMLReflowState reflowState(aState.mPresContext, rs,
                                 aBulletFrame, availSize);
   nsReflowStatus  status;
-  aBulletFrame->WillReflow(aState.mPresContext);
   aBulletFrame->Reflow(aState.mPresContext, aMetrics, reflowState, status);
 
   // Get the float available space using our saved state from before we
   // started reflowing the block, so that we ignore any floats inside
   // the block.
   // FIXME: aLineTop isn't actually set correctly by some callers, since
   // they reposition the line.
   LogicalRect floatAvailSpace =
--- a/layout/generic/nsBlockReflowContext.cpp
+++ b/layout/generic/nsBlockReflowContext.cpp
@@ -277,19 +277,16 @@ nsBlockReflowContext::ReflowBlock(const 
     tI = space.LineLeft(mWritingMode, mContainerWidth);
     tB = mBCoord;
 
     if ((mFrame->GetStateBits() & NS_BLOCK_FLOAT_MGR) == 0)
       aFrameRS.mBlockDelta =
         mOuterReflowState.mBlockDelta + mBCoord - aLine->BStart();
   }
 
-  // Let frame know that we are reflowing it
-  mFrame->WillReflow(mPresContext);
-
 #ifdef DEBUG
   mMetrics.ISize(mWritingMode) = nscoord(0xdeadbeef);
   mMetrics.BSize(mWritingMode) = nscoord(0xdeadbeef);
 #endif
 
   mOuterReflowState.mFloatManager->Translate(tI, tB);
   mFrame->Reflow(mPresContext, mMetrics, aFrameRS, aFrameReflowStatus);
   mOuterReflowState.mFloatManager->Translate(-tI, -tB);
--- a/layout/generic/nsBulletFrame.cpp
+++ b/layout/generic/nsBulletFrame.cpp
@@ -618,16 +618,17 @@ nsBulletFrame::GetDesiredSize(nsPresCont
 }
 
 void
 nsBulletFrame::Reflow(nsPresContext* aPresContext,
                       nsHTMLReflowMetrics& aMetrics,
                       const nsHTMLReflowState& aReflowState,
                       nsReflowStatus& aStatus)
 {
+  MarkInReflow();
   DO_GLOBAL_REFLOW_COUNT("nsBulletFrame");
   DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus);
 
   float inflation = nsLayoutUtils::FontSizeInflationFor(this);
   SetFontSizeInflation(inflation);
 
   // Get the base size
   GetDesiredSize(aPresContext, aReflowState.rendContext,
--- a/layout/generic/nsCanvasFrame.cpp
+++ b/layout/generic/nsCanvasFrame.cpp
@@ -599,16 +599,17 @@ nsCanvasFrame::GetPrefISize(nsRenderingC
 }
 
 void
 nsCanvasFrame::Reflow(nsPresContext*           aPresContext,
                       nsHTMLReflowMetrics&     aDesiredSize,
                       const nsHTMLReflowState& aReflowState,
                       nsReflowStatus&          aStatus)
 {
+  MarkInReflow();
   DO_GLOBAL_REFLOW_COUNT("nsCanvasFrame");
   DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
   NS_FRAME_TRACE_REFLOW_IN("nsCanvasFrame::Reflow");
 
   // Initialize OUT parameter
   aStatus = NS_FRAME_COMPLETE;
 
   nsCanvasFrame* prevCanvasFrame = static_cast<nsCanvasFrame*>
--- a/layout/generic/nsColumnSetFrame.cpp
+++ b/layout/generic/nsColumnSetFrame.cpp
@@ -1009,16 +1009,17 @@ nsColumnSetFrame::FindBestBalanceBSize(c
 }
 
 void
 nsColumnSetFrame::Reflow(nsPresContext*           aPresContext,
                          nsHTMLReflowMetrics&     aDesiredSize,
                          const nsHTMLReflowState& aReflowState,
                          nsReflowStatus&          aStatus)
 {
+  MarkInReflow();
   // Don't support interruption in columns
   nsPresContext::InterruptPreventer noInterrupts(aPresContext);
 
   DO_GLOBAL_REFLOW_COUNT("nsColumnSetFrame");
   DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
 
   // Initialize OUT parameter
   aStatus = NS_FRAME_COMPLETE;
--- a/layout/generic/nsContainerFrame.cpp
+++ b/layout/generic/nsContainerFrame.cpp
@@ -929,21 +929,16 @@ nsContainerFrame::ComputeAutoSize(nsRend
       if (min > result.ISize(aWM)) {
         result.ISize(aWM) = min;
       }
     }
   }
   return result;
 }
 
-/**
- * Invokes the WillReflow() function, positions the frame and its view (if
- * requested), and then calls Reflow(). If the reflow succeeds and the child
- * frame is complete, deletes any next-in-flows using DeleteNextInFlowChild()
- */
 void
 nsContainerFrame::ReflowChild(nsIFrame*                aKidFrame,
                               nsPresContext*           aPresContext,
                               nsHTMLReflowMetrics&     aDesiredSize,
                               const nsHTMLReflowState& aReflowState,
                               const WritingMode&       aWM,
                               const LogicalPoint&      aPos,
                               nscoord                  aContainerWidth,
@@ -952,20 +947,17 @@ nsContainerFrame::ReflowChild(nsIFrame* 
                               nsOverflowContinuationTracker* aTracker)
 {
   NS_PRECONDITION(aReflowState.frame == aKidFrame, "bad reflow state");
   if (aWM.IsVerticalRL() || (!aWM.IsVertical() && !aWM.IsBidiLTR())) {
     NS_ASSERTION(aContainerWidth != NS_UNCONSTRAINEDSIZE,
                  "FinishReflowChild with unconstrained container width!");
   }
 
-  // Send the WillReflow() notification, and position the child frame
-  // and its view if requested
-  aKidFrame->WillReflow(aPresContext);
-
+  // Position the child frame and its view if requested.
   if (NS_FRAME_NO_MOVE_FRAME != (aFlags & NS_FRAME_NO_MOVE_FRAME)) {
     aKidFrame->SetPosition(aWM, aPos, aContainerWidth);
   }
 
   if (0 == (aFlags & NS_FRAME_NO_MOVE_VIEW)) {
     PositionFrameView(aKidFrame);
   }
 
@@ -997,20 +989,17 @@ nsContainerFrame::ReflowChild(nsIFrame* 
                               nscoord                  aX,
                               nscoord                  aY,
                               uint32_t                 aFlags,
                               nsReflowStatus&          aStatus,
                               nsOverflowContinuationTracker* aTracker)
 {
   NS_PRECONDITION(aReflowState.frame == aKidFrame, "bad reflow state");
 
-  // Send the WillReflow() notification, and position the child frame
-  // and its view if requested
-  aKidFrame->WillReflow(aPresContext);
-
+  // Position the child frame and its view if requested.
   if (NS_FRAME_NO_MOVE_FRAME != (aFlags & NS_FRAME_NO_MOVE_FRAME)) {
     aKidFrame->SetPosition(nsPoint(aX, aY));
   }
 
   if (0 == (aFlags & NS_FRAME_NO_MOVE_VIEW)) {
     PositionFrameView(aKidFrame);
   }
 
--- a/layout/generic/nsContainerFrame.h
+++ b/layout/generic/nsContainerFrame.h
@@ -226,27 +226,27 @@ public:
                   const mozilla::LogicalSize& aCBSize,
                   nscoord aAvailableISize,
                   const mozilla::LogicalSize& aMargin,
                   const mozilla::LogicalSize& aBorder,
                   const mozilla::LogicalSize& aPadding,
                   bool aShrinkWrap) override;
 
   /**
-   * Invokes the WillReflow() function, positions the frame and its view (if
-   * requested), and then calls Reflow(). If the reflow succeeds and the child
-   * frame is complete, deletes any next-in-flows using DeleteNextInFlowChild()
+   * Positions aChildFrame and its view (if requested), and then calls Reflow().
+   * If the reflow status after reflowing the child is FULLY_COMPLETE then any
+   * next-in-flows are deleted using DeleteNextInFlowChild().
    *
    * Flags:
    * NS_FRAME_NO_MOVE_VIEW - don't position the frame's view. Set this if you
    *    don't want to automatically sync the frame and view
    * NS_FRAME_NO_MOVE_FRAME - don't move the frame. aX and aY are ignored in this
    *    case. Also implies NS_FRAME_NO_MOVE_VIEW
    */
-  void ReflowChild(nsIFrame*                      aKidFrame,
+  void ReflowChild(nsIFrame*                      aChildFrame,
                    nsPresContext*                 aPresContext,
                    nsHTMLReflowMetrics&           aDesiredSize,
                    const nsHTMLReflowState&       aReflowState,
                    const mozilla::WritingMode&    aWM,
                    const mozilla::LogicalPoint&   aPos,
                    nscoord                        aContainerWidth,
                    uint32_t                       aFlags,
                    nsReflowStatus&                aStatus,
--- a/layout/generic/nsFirstLetterFrame.cpp
+++ b/layout/generic/nsFirstLetterFrame.cpp
@@ -159,16 +159,17 @@ nsFirstLetterFrame::ComputeSize(nsRender
 }
 
 void
 nsFirstLetterFrame::Reflow(nsPresContext*          aPresContext,
                            nsHTMLReflowMetrics&     aMetrics,
                            const nsHTMLReflowState& aReflowState,
                            nsReflowStatus&          aReflowStatus)
 {
+  MarkInReflow();
   DO_GLOBAL_REFLOW_COUNT("nsFirstLetterFrame");
   DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aReflowStatus);
 
   // Grab overflow list
   DrainOverflowFrames(aPresContext);
 
   nsIFrame* kid = mFrames.FirstChild();
 
@@ -200,17 +201,16 @@ nsFirstLetterFrame::Reflow(nsPresContext
                        availSize.ISize(wm), NS_UNCONSTRAINEDSIZE,
                        false, true, kidWritingMode,
                        nsSize(aReflowState.AvailableWidth(),
                               aReflowState.AvailableHeight()));
     rs.mLineLayout = &ll;
     ll.SetInFirstLetter(true);
     ll.SetFirstLetterStyleOK(true);
 
-    kid->WillReflow(aPresContext);
     kid->Reflow(aPresContext, kidMetrics, rs, aReflowStatus);
 
     ll.EndLineReflow();
     ll.SetInFirstLetter(false);
 
     // In the floating first-letter case, we need to set this ourselves;
     // nsLineLayout::BeginSpan will set it in the other case
     mBaseline = kidMetrics.BlockStartAscent();
--- a/layout/generic/nsFlexContainerFrame.cpp
+++ b/layout/generic/nsFlexContainerFrame.cpp
@@ -3424,16 +3424,17 @@ FlexLine::PositionItemsInCrossAxis(nscoo
 }
 
 void
 nsFlexContainerFrame::Reflow(nsPresContext*           aPresContext,
                              nsHTMLReflowMetrics&     aDesiredSize,
                              const nsHTMLReflowState& aReflowState,
                              nsReflowStatus&          aStatus)
 {
+  MarkInReflow();
   DO_GLOBAL_REFLOW_COUNT("nsFlexContainerFrame");
   DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
   PR_LOG(GetFlexContainerLog(), PR_LOG_DEBUG,
          ("Reflow() for nsFlexContainerFrame %p\n", this));
 
   if (IsFrameTreeTooDeep(aReflowState, aDesiredSize, aStatus)) {
     return;
   }
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -4406,30 +4406,16 @@ nsFrame::ShrinkWidthToFit(nsRenderingCon
     } else {
       result = prefWidth;
     }
   }
   return result;
 }
 
 void
-nsFrame::WillReflow(nsPresContext* aPresContext)
-{
-#ifdef DEBUG_dbaron_off
-  // bug 81268
-  NS_ASSERTION(!(mState & NS_FRAME_IN_REFLOW),
-               "nsFrame::WillReflow: frame is already in reflow");
-#endif
-
-  NS_FRAME_TRACE_MSG(NS_FRAME_TRACE_CALLS,
-                     ("WillReflow: oldState=%x", mState));
-  mState |= NS_FRAME_IN_REFLOW;
-}
-
-void
 nsFrame::DidReflow(nsPresContext*           aPresContext,
                    const nsHTMLReflowState*  aReflowState,
                    nsDidReflowStatus         aStatus)
 {
   NS_FRAME_TRACE_MSG(NS_FRAME_TRACE_CALLS,
                      ("nsFrame::DidReflow: aStatus=%d", static_cast<uint32_t>(aStatus)));
 
   nsSVGEffects::InvalidateDirectRenderingObservers(this, nsSVGEffects::INVALIDATE_REFLOW);
@@ -4519,16 +4505,17 @@ nsFrame::CanContinueTextRun() const
 }
 
 void
 nsFrame::Reflow(nsPresContext*          aPresContext,
                 nsHTMLReflowMetrics&     aDesiredSize,
                 const nsHTMLReflowState& aReflowState,
                 nsReflowStatus&          aStatus)
 {
+  MarkInReflow();
   DO_GLOBAL_REFLOW_COUNT("nsFrame");
   aDesiredSize.ClearSize();
   aStatus = NS_FRAME_COMPLETE;
   NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
 }
 
 nsresult
 nsFrame::CharacterDataChanged(CharacterDataChangeInfo* aInfo)
@@ -8639,17 +8626,16 @@ nsFrame::BoxReflow(nsBoxLayoutState&    
       printf("Size=(%d,%d)\n",reflowState.ComputedWidth(),
              reflowState.ComputedHeight());
       nsAdaptorAddIndents();
       nsAdaptorPrintReason(reflowState);
       printf("\n");
     #endif
 
        // place the child and reflow
-    WillReflow(aPresContext);
 
     Reflow(aPresContext, aDesiredSize, reflowState, status);
 
     NS_ASSERTION(NS_FRAME_IS_COMPLETE(status), "bad status");
 
     uint32_t layoutFlags = aState.LayoutFlags();
     nsContainerFrame::FinishReflowChild(this, aPresContext, aDesiredSize,
                                         &reflowState, aX, aY, layoutFlags | NS_FRAME_NO_MOVE_FRAME);
@@ -8956,40 +8942,40 @@ GetTagName(nsFrame* aFrame, nsIContent* 
   else {
     PR_snprintf(aResult, aResultSize, "@%p", aFrame);
   }
 }
 
 void
 nsFrame::Trace(const char* aMethod, bool aEnter)
 {
-  if (NS_FRAME_LOG_TEST(gLogModule, NS_FRAME_TRACE_CALLS)) {
+  if (NS_FRAME_LOG_TEST(GetLogModuleInfo(), NS_FRAME_TRACE_CALLS)) {
     char tagbuf[40];
     GetTagName(this, mContent, sizeof(tagbuf), tagbuf);
     PR_LogPrint("%s: %s %s", tagbuf, aEnter ? "enter" : "exit", aMethod);
   }
 }
 
 void
 nsFrame::Trace(const char* aMethod, bool aEnter, nsReflowStatus aStatus)
 {
-  if (NS_FRAME_LOG_TEST(gLogModule, NS_FRAME_TRACE_CALLS)) {
+  if (NS_FRAME_LOG_TEST(GetLogModuleInfo(), NS_FRAME_TRACE_CALLS)) {
     char tagbuf[40];
     GetTagName(this, mContent, sizeof(tagbuf), tagbuf);
     PR_LogPrint("%s: %s %s, status=%scomplete%s",
                 tagbuf, aEnter ? "enter" : "exit", aMethod,
                 NS_FRAME_IS_NOT_COMPLETE(aStatus) ? "not" : "",
                 (NS_FRAME_REFLOW_NEXTINFLOW & aStatus) ? "+reflow" : "");
   }
 }
 
 void
 nsFrame::TraceMsg(const char* aFormatString, ...)
 {
-  if (NS_FRAME_LOG_TEST(gLogModule, NS_FRAME_TRACE_CALLS)) {
+  if (NS_FRAME_LOG_TEST(GetLogModuleInfo(), NS_FRAME_TRACE_CALLS)) {
     // Format arguments into a buffer
     char argbuf[200];
     va_list ap;
     va_start(ap, aFormatString);
     PR_vsnprintf(argbuf, sizeof(argbuf), aFormatString, ap);
     va_end(ap);
 
     char tagbuf[40];
--- a/layout/generic/nsFrame.h
+++ b/layout/generic/nsFrame.h
@@ -297,17 +297,16 @@ public:
 
   /**
    * Utility function for ComputeAutoSize implementations.  Return
    * max(GetMinISize(), min(aWidthInCB, GetPrefISize()))
    */
   nscoord ShrinkWidthToFit(nsRenderingContext *aRenderingContext,
                            nscoord aWidthInCB);
 
-  virtual void WillReflow(nsPresContext* aPresContext) override;
   /**
    * Calculates the size of this frame after reflowing (calling Reflow on, and
    * updating the size and position of) its children, as necessary.  The
    * calculated size is returned to the caller via the nsHTMLReflowMetrics
    * outparam.  (The caller is responsible for setting the actual size and
    * position of this frame.)
    *
    * A frame's children must _all_ be reflowed if the frame is dirty (the
--- a/layout/generic/nsFrameSetFrame.cpp
+++ b/layout/generic/nsFrameSetFrame.cpp
@@ -850,16 +850,17 @@ nscolor nsHTMLFramesetFrame::GetBorderCo
 }
 
 void
 nsHTMLFramesetFrame::Reflow(nsPresContext*           aPresContext,
                             nsHTMLReflowMetrics&     aDesiredSize,
                             const nsHTMLReflowState& aReflowState,
                             nsReflowStatus&          aStatus)
 {
+  MarkInReflow();
   DO_GLOBAL_REFLOW_COUNT("nsHTMLFramesetFrame");
   DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
   nsIPresShell *shell = aPresContext->PresShell();
   nsStyleSet *styleSet = shell->StyleSet();
 
   GetParent()->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT);
 
   //printf("FramesetFrame2::Reflow %X (%d,%d) \n", this, aReflowState.AvailableWidth(), aReflowState.AvailableHeight());
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -797,16 +797,17 @@ GetBrowserRoot(nsIContent* aContent)
 }
 
 void
 nsHTMLScrollFrame::Reflow(nsPresContext*           aPresContext,
                           nsHTMLReflowMetrics&     aDesiredSize,
                           const nsHTMLReflowState& aReflowState,
                           nsReflowStatus&          aStatus)
 {
+  MarkInReflow();
   DO_GLOBAL_REFLOW_COUNT("nsHTMLScrollFrame");
   DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
 
   mHelper.HandleScrollbarStyleSwitching();
 
   ScrollReflowState state(this, aReflowState);
   // sanity check: ensure that if we have no scrollbar, we treat it
   // as hidden.
--- a/layout/generic/nsGridContainerFrame.cpp
+++ b/layout/generic/nsGridContainerFrame.cpp
@@ -1175,16 +1175,17 @@ nsGridContainerFrame::ReflowChildren(Gri
 }
 
 void
 nsGridContainerFrame::Reflow(nsPresContext*           aPresContext,
                              nsHTMLReflowMetrics&     aDesiredSize,
                              const nsHTMLReflowState& aReflowState,
                              nsReflowStatus&          aStatus)
 {
+  MarkInReflow();
   DO_GLOBAL_REFLOW_COUNT("nsGridContainerFrame");
   DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
 
   if (IsFrameTreeTooDeep(aReflowState, aDesiredSize, aStatus)) {
     return;
   }
 
 #ifdef DEBUG
--- a/layout/generic/nsHTMLCanvasFrame.cpp
+++ b/layout/generic/nsHTMLCanvasFrame.cpp
@@ -246,16 +246,17 @@ nsHTMLCanvasFrame::ComputeSize(nsRenderi
 }
 
 void
 nsHTMLCanvasFrame::Reflow(nsPresContext*           aPresContext,
                           nsHTMLReflowMetrics&     aMetrics,
                           const nsHTMLReflowState& aReflowState,
                           nsReflowStatus&          aStatus)
 {
+  MarkInReflow();
   DO_GLOBAL_REFLOW_COUNT("nsHTMLCanvasFrame");
   DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus);
   NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
                   ("enter nsHTMLCanvasFrame::Reflow: availSize=%d,%d",
                   aReflowState.AvailableWidth(), aReflowState.AvailableHeight()));
 
   NS_PRECONDITION(mState & NS_FRAME_IN_REFLOW, "frame is not in reflow");
 
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -1762,27 +1762,16 @@ public:
    * @param aXMost  computed intrinsic width of the tight bounding rectangle
    *
    */
   virtual nsresult GetPrefWidthTightBounds(nsRenderingContext* aContext,
                                            nscoord* aX,
                                            nscoord* aXMost);
 
   /**
-   * Pre-reflow hook. Before a frame is reflowed this method will be called.
-   * This call will always be invoked at least once before a subsequent Reflow
-   * and DidReflow call. It may be called more than once, In general you will
-   * receive on WillReflow notification before each Reflow request.
-   *
-   * XXX Is this really the semantics we want? Because we have the NS_FRAME_IN_REFLOW
-   * bit we can ensure we don't call it more than once...
-   */
-  virtual void WillReflow(nsPresContext* aPresContext) = 0;
-
-  /**
    * The frame is given an available size and asked for its desired
    * size.  This is the frame's opportunity to reflow its children.
    *
    * If the frame has the NS_FRAME_IS_DIRTY bit set then it is
    * responsible for completely reflowing itself and all of its
    * descendants.
    *
    * Otherwise, if the frame has the NS_FRAME_HAS_DIRTY_CHILDREN bit
@@ -3016,16 +3005,24 @@ private:
       list = new nsTArray<nsWeakPtr>();
       Properties().Set(PaintedPresShellsProperty(), list);
     }
     
     return list;
   }
 
 protected:
+  void MarkInReflow() {
+#ifdef DEBUG_dbaron_off
+    // bug 81268
+    NS_ASSERTION(!(mState & NS_FRAME_IN_REFLOW), "frame is already in reflow");
+#endif
+    mState |= NS_FRAME_IN_REFLOW;
+  }
+
   nsFrameState     mState;
 
   // When there is an overflow area only slightly larger than mRect,
   // we store a set of four 1-byte deltas from the edges of mRect
   // rather than allocating a whole separate rectangle property.
   // Note that these are unsigned values, all measured "outwards"
   // from the edges of mRect, so /mLeft/ and /mTop/ are reversed from
   // our normal coordinate system.
--- a/layout/generic/nsImageFrame.cpp
+++ b/layout/generic/nsImageFrame.cpp
@@ -853,16 +853,17 @@ nsImageFrame::GetIntrinsicRatio()
 }
 
 void
 nsImageFrame::Reflow(nsPresContext*          aPresContext,
                      nsHTMLReflowMetrics&     aMetrics,
                      const nsHTMLReflowState& aReflowState,
                      nsReflowStatus&          aStatus)
 {
+  MarkInReflow();
   DO_GLOBAL_REFLOW_COUNT("nsImageFrame");
   DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus);
   NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
                   ("enter nsImageFrame::Reflow: availSize=%d,%d",
                   aReflowState.AvailableWidth(), aReflowState.AvailableHeight()));
 
   NS_PRECONDITION(mState & NS_FRAME_IN_REFLOW, "frame is not in reflow");
 
--- a/layout/generic/nsInlineFrame.cpp
+++ b/layout/generic/nsInlineFrame.cpp
@@ -328,16 +328,17 @@ ReparentChildListStyle(nsPresContext* aP
 }
 
 void
 nsInlineFrame::Reflow(nsPresContext*          aPresContext,
                       nsHTMLReflowMetrics&     aMetrics,
                       const nsHTMLReflowState& aReflowState,
                       nsReflowStatus&          aStatus)
 {
+  MarkInReflow();
   DO_GLOBAL_REFLOW_COUNT("nsInlineFrame");
   DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus);
   if (nullptr == aReflowState.mLineLayout) {
     NS_ERROR("must have non-null aReflowState.mLineLayout");
     return;
   }
   if (IsFrameTreeTooDeep(aReflowState, aMetrics, aStatus)) {
     return;
@@ -1059,16 +1060,17 @@ nsFirstLineFrame::PullOneFrame(nsPresCon
 }
 
 void
 nsFirstLineFrame::Reflow(nsPresContext* aPresContext,
                          nsHTMLReflowMetrics& aMetrics,
                          const nsHTMLReflowState& aReflowState,
                          nsReflowStatus& aStatus)
 {
+  MarkInReflow();
   if (nullptr == aReflowState.mLineLayout) {
     return;  // XXX does this happen? why?
   }
 
   nsIFrame* lineContainer = aReflowState.mLineLayout->LineContainerFrame();
 
   // Check for an overflow list with our prev-in-flow
   nsFirstLineFrame* prevInFlow = (nsFirstLineFrame*)GetPrevInFlow();
--- a/layout/generic/nsLeafFrame.cpp
+++ b/layout/generic/nsLeafFrame.cpp
@@ -52,16 +52,17 @@ nsLeafFrame::ComputeAutoSize(nsRendering
 }
 
 void
 nsLeafFrame::Reflow(nsPresContext* aPresContext,
                     nsHTMLReflowMetrics& aMetrics,
                     const nsHTMLReflowState& aReflowState,
                     nsReflowStatus& aStatus)
 {
+  MarkInReflow();
   DO_GLOBAL_REFLOW_COUNT("nsLeafFrame");
   NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
                  ("enter nsLeafFrame::Reflow: aMaxSize=%d,%d",
                   aReflowState.AvailableWidth(), aReflowState.AvailableHeight()));
 
   NS_PRECONDITION(mState & NS_FRAME_IN_REFLOW, "frame is not in reflow");
 
   DoReflow(aPresContext, aMetrics, aReflowState, aStatus);
--- a/layout/generic/nsLineLayout.cpp
+++ b/layout/generic/nsLineLayout.cpp
@@ -926,20 +926,18 @@ nsLineLayout::ReflowFrame(nsIFrame* aFra
   // line, even if we don't succeed.)  (Note also that we can only make
   // this IsPercentageAware check *after* we've constructed our
   // nsHTMLReflowState, because that construction may be what forces aFrame
   // to lazily initialize its (possibly-percent-valued) intrinsic size.)
   if (mGotLineBox && IsPercentageAware(aFrame)) {
     mLineBox->DisableResizeReflowOptimization();
   }
 
-  // Let frame know that are reflowing it. Note that we don't bother
-  // positioning the frame yet, because we're probably going to end up
-  // moving it when we do the block-direction alignment
-  aFrame->WillReflow(mPresContext);
+  // Note that we don't bother positioning the frame yet, because we're probably
+  // going to end up moving it when we do the block-direction alignment.
 
   // Adjust spacemanager coordinate system for the frame.
   nsHTMLReflowMetrics metrics(lineWM);
 #ifdef DEBUG
   metrics.ISize(lineWM) = nscoord(0xdeadbeef);
   metrics.BSize(lineWM) = nscoord(0xdeadbeef);
 #endif
   nscoord tI = pfd->mBounds.LineLeft(lineWM, ContainerWidth());
--- a/layout/generic/nsPageContentFrame.cpp
+++ b/layout/generic/nsPageContentFrame.cpp
@@ -21,16 +21,17 @@ NS_NewPageContentFrame(nsIPresShell* aPr
 NS_IMPL_FRAMEARENA_HELPERS(nsPageContentFrame)
 
 void
 nsPageContentFrame::Reflow(nsPresContext*           aPresContext,
                            nsHTMLReflowMetrics&     aDesiredSize,
                            const nsHTMLReflowState& aReflowState,
                            nsReflowStatus&          aStatus)
 {
+  MarkInReflow();
   DO_GLOBAL_REFLOW_COUNT("nsPageContentFrame");
   DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
   aStatus = NS_FRAME_COMPLETE;  // initialize out parameter
 
   if (GetPrevInFlow() && (GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
     nsresult rv = aPresContext->PresShell()->FrameConstructor()
                     ->ReplicateFixedFrames(this);
     if (NS_FAILED(rv)) {
--- a/layout/generic/nsPageFrame.cpp
+++ b/layout/generic/nsPageFrame.cpp
@@ -50,16 +50,17 @@ nsPageFrame::~nsPageFrame()
 }
 
 void
 nsPageFrame::Reflow(nsPresContext*           aPresContext,
                                   nsHTMLReflowMetrics&     aDesiredSize,
                                   const nsHTMLReflowState& aReflowState,
                                   nsReflowStatus&          aStatus)
 {
+  MarkInReflow();
   DO_GLOBAL_REFLOW_COUNT("nsPageFrame");
   DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
   aStatus = NS_FRAME_COMPLETE;  // initialize out parameter
 
   NS_ASSERTION(mFrames.FirstChild() &&
                nsGkAtoms::pageContentFrame == mFrames.FirstChild()->GetType(),
                "pageFrame must have a pageContentFrame child");
 
--- a/layout/generic/nsPlaceholderFrame.cpp
+++ b/layout/generic/nsPlaceholderFrame.cpp
@@ -136,16 +136,17 @@ nsPlaceholderFrame::Reflow(nsPresContext
     if (isInContinuationOrIBSplit) {
       NS_WARNING("Out-of-flow frame got reflowed before its placeholder");
     } else {
       NS_ERROR("Out-of-flow frame got reflowed before its placeholder");
     }
   }
 #endif
 
+  MarkInReflow();
   DO_GLOBAL_REFLOW_COUNT("nsPlaceholderFrame");
   DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
   aDesiredSize.ClearSize();
 
   aStatus = NS_FRAME_COMPLETE;
   NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
 }
 
--- a/layout/generic/nsPluginFrame.cpp
+++ b/layout/generic/nsPluginFrame.cpp
@@ -490,16 +490,17 @@ nsPluginFrame::GetDesiredSize(nsPresCont
 }
 
 void
 nsPluginFrame::Reflow(nsPresContext*           aPresContext,
                       nsHTMLReflowMetrics&     aMetrics,
                       const nsHTMLReflowState& aReflowState,
                       nsReflowStatus&          aStatus)
 {
+  MarkInReflow();
   DO_GLOBAL_REFLOW_COUNT("nsPluginFrame");
   DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus);
 
   // Get our desired size
   GetDesiredSize(aPresContext, aReflowState, aMetrics);
   aMetrics.SetOverflowAreasToDesiredBounds();
   FinishAndStoreOverflow(&aMetrics);
 
--- a/layout/generic/nsRubyBaseContainerFrame.cpp
+++ b/layout/generic/nsRubyBaseContainerFrame.cpp
@@ -421,16 +421,17 @@ struct nsRubyBaseContainerFrame::ReflowS
 };
 
 /* virtual */ void
 nsRubyBaseContainerFrame::Reflow(nsPresContext* aPresContext,
                                  nsHTMLReflowMetrics& aDesiredSize,
                                  const nsHTMLReflowState& aReflowState,
                                  nsReflowStatus& aStatus)
 {
+  MarkInReflow();
   DO_GLOBAL_REFLOW_COUNT("nsRubyBaseContainerFrame");
   DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
   aStatus = NS_FRAME_COMPLETE;
 
   if (!aReflowState.mLineLayout) {
     NS_ASSERTION(
       aReflowState.mLineLayout,
       "No line layout provided to RubyBaseContainerFrame reflow method.");
--- a/layout/generic/nsRubyFrame.cpp
+++ b/layout/generic/nsRubyFrame.cpp
@@ -129,16 +129,17 @@ nsRubyFrame::AddInlinePrefISize(nsRender
 }
 
 /* virtual */ void
 nsRubyFrame::Reflow(nsPresContext* aPresContext,
                     nsHTMLReflowMetrics& aDesiredSize,
                     const nsHTMLReflowState& aReflowState,
                     nsReflowStatus& aStatus)
 {
+  MarkInReflow();
   DO_GLOBAL_REFLOW_COUNT("nsRubyFrame");
   DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
   
   if (!aReflowState.mLineLayout) {
     NS_ASSERTION(aReflowState.mLineLayout,
                  "No line layout provided to RubyFrame reflow method.");
     aStatus = NS_FRAME_COMPLETE;
     return;
--- a/layout/generic/nsRubyTextContainerFrame.cpp
+++ b/layout/generic/nsRubyTextContainerFrame.cpp
@@ -117,16 +117,17 @@ nsRubyTextContainerFrame::UpdateSpanFlag
 }
 
 /* virtual */ void
 nsRubyTextContainerFrame::Reflow(nsPresContext* aPresContext,
                                  nsHTMLReflowMetrics& aDesiredSize,
                                  const nsHTMLReflowState& aReflowState,
                                  nsReflowStatus& aStatus)
 {
+  MarkInReflow();
   DO_GLOBAL_REFLOW_COUNT("nsRubyTextContainerFrame");
   DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
 
   // Although a ruby text container may have continuations, returning
   // NS_FRAME_COMPLETE here is still safe, since its parent, ruby frame,
   // ignores the status, and continuations of the ruby base container
   // will take care of our continuations.
   aStatus = NS_FRAME_COMPLETE;
--- a/layout/generic/nsSimplePageSequenceFrame.cpp
+++ b/layout/generic/nsSimplePageSequenceFrame.cpp
@@ -156,16 +156,17 @@ nsSimplePageSequenceFrame::ComputeCenter
 }
 
 void
 nsSimplePageSequenceFrame::Reflow(nsPresContext*          aPresContext,
                                   nsHTMLReflowMetrics&     aDesiredSize,
                                   const nsHTMLReflowState& aReflowState,
                                   nsReflowStatus&          aStatus)
 {
+  MarkInReflow();
   NS_PRECONDITION(aPresContext->IsRootPaginatedDocument(),
                   "A Page Sequence is only for real pages");
   DO_GLOBAL_REFLOW_COUNT("nsSimplePageSequenceFrame");
   DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
   NS_FRAME_TRACE_REFLOW_IN("nsSimplePageSequenceFrame::Reflow");
 
   aStatus = NS_FRAME_COMPLETE;  // we're always complete
 
--- a/layout/generic/nsSubDocumentFrame.cpp
+++ b/layout/generic/nsSubDocumentFrame.cpp
@@ -753,16 +753,17 @@ nsSubDocumentFrame::ComputeSize(nsRender
 }
 
 void
 nsSubDocumentFrame::Reflow(nsPresContext*           aPresContext,
                            nsHTMLReflowMetrics&     aDesiredSize,
                            const nsHTMLReflowState& aReflowState,
                            nsReflowStatus&          aStatus)
 {
+  MarkInReflow();
   DO_GLOBAL_REFLOW_COUNT("nsSubDocumentFrame");
   DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
   NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
      ("enter nsSubDocumentFrame::Reflow: maxSize=%d,%d",
       aReflowState.AvailableWidth(), aReflowState.AvailableHeight()));
 
   aStatus = NS_FRAME_COMPLETE;
 
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -7998,16 +7998,17 @@ struct NewlineProperty {
 };
 
 void
 nsTextFrame::Reflow(nsPresContext*           aPresContext,
                     nsHTMLReflowMetrics&     aMetrics,
                     const nsHTMLReflowState& aReflowState,
                     nsReflowStatus&          aStatus)
 {
+  MarkInReflow();
   DO_GLOBAL_REFLOW_COUNT("nsTextFrame");
   DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus);
 
   // XXX If there's no line layout, we shouldn't even have created this
   // frame. This may happen if, for example, this is text inside a table
   // but not inside a cell. For now, just don't reflow.
   if (!aReflowState.mLineLayout) {
     ClearMetrics(aMetrics);
--- a/layout/generic/nsVideoFrame.cpp
+++ b/layout/generic/nsVideoFrame.cpp
@@ -237,16 +237,17 @@ public:
 };
 
 void
 nsVideoFrame::Reflow(nsPresContext*           aPresContext,
                      nsHTMLReflowMetrics&     aMetrics,
                      const nsHTMLReflowState& aReflowState,
                      nsReflowStatus&          aStatus)
 {
+  MarkInReflow();
   DO_GLOBAL_REFLOW_COUNT("nsVideoFrame");
   DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus);
   NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
                   ("enter nsVideoFrame::Reflow: availSize=%d,%d",
                   aReflowState.AvailableWidth(), aReflowState.AvailableHeight()));
 
   NS_PRECONDITION(mState & NS_FRAME_IN_REFLOW, "frame is not in reflow");
 
--- a/layout/generic/nsViewportFrame.cpp
+++ b/layout/generic/nsViewportFrame.cpp
@@ -170,16 +170,17 @@ ViewportFrame::AdjustReflowStateAsContai
 }
 
 void
 ViewportFrame::Reflow(nsPresContext*           aPresContext,
                       nsHTMLReflowMetrics&     aDesiredSize,
                       const nsHTMLReflowState& aReflowState,
                       nsReflowStatus&          aStatus)
 {
+  MarkInReflow();
   DO_GLOBAL_REFLOW_COUNT("ViewportFrame");
   DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
   NS_FRAME_TRACE_REFLOW_IN("ViewportFrame::Reflow");
 
   // Initialize OUT parameters
   aStatus = NS_FRAME_COMPLETE;
 
   // Because |Reflow| sets mComputedHeight on the child to
--- a/layout/mathml/nsMathMLContainerFrame.cpp
+++ b/layout/mathml/nsMathMLContainerFrame.cpp
@@ -886,16 +886,18 @@ nsMathMLContainerFrame::ReflowChild(nsIF
 }
 
 void
 nsMathMLContainerFrame::Reflow(nsPresContext*           aPresContext,
                                nsHTMLReflowMetrics&     aDesiredSize,
                                const nsHTMLReflowState& aReflowState,
                                nsReflowStatus&          aStatus)
 {
+  MarkInReflow();
+  mPresentationData.flags &= ~NS_MATHML_ERROR;
   aDesiredSize.Width() = aDesiredSize.Height() = 0;
   aDesiredSize.SetBlockStartAscent(0);
   aDesiredSize.mBoundingMetrics = nsBoundingMetrics();
 
   /////////////
   // Reflow children
   // Asking each child to cache its bounding metrics
 
--- a/layout/mathml/nsMathMLContainerFrame.h
+++ b/layout/mathml/nsMathMLContainerFrame.h
@@ -110,23 +110,16 @@ public:
                            nsHTMLReflowMetrics& aDesiredSize);
 
   virtual void
   Reflow(nsPresContext*          aPresContext,
          nsHTMLReflowMetrics&     aDesiredSize,
          const nsHTMLReflowState& aReflowState,
          nsReflowStatus&          aStatus) override;
 
-  virtual void
-  WillReflow(nsPresContext* aPresContext) override
-  {
-    mPresentationData.flags &= ~NS_MATHML_ERROR;
-    nsContainerFrame::WillReflow(aPresContext);
-  }
-
   virtual void DidReflow(nsPresContext*           aPresContext,
             const nsHTMLReflowState*  aReflowState,
             nsDidReflowStatus         aStatus) override
 
   {
     mPresentationData.flags &= ~NS_MATHML_STRETCH_DONE;
     return nsContainerFrame::DidReflow(aPresContext, aReflowState, aStatus);
   }
--- a/layout/mathml/nsMathMLSelectedFrame.cpp
+++ b/layout/mathml/nsMathMLSelectedFrame.cpp
@@ -132,16 +132,18 @@ nsMathMLSelectedFrame::ComputeSize(nsRen
 
 // Only reflow the selected child ...
 void
 nsMathMLSelectedFrame::Reflow(nsPresContext*          aPresContext,
                               nsHTMLReflowMetrics&     aDesiredSize,
                               const nsHTMLReflowState& aReflowState,
                               nsReflowStatus&          aStatus)
 {
+  MarkInReflow();
+  mPresentationData.flags &= ~NS_MATHML_ERROR;
   aStatus = NS_FRAME_COMPLETE;
   aDesiredSize.ClearSize();
   aDesiredSize.SetBlockStartAscent(0);
   mBoundingMetrics = nsBoundingMetrics();
   nsIFrame* childFrame = GetSelectedFrame();
   if (childFrame) {
     WritingMode wm = childFrame->GetWritingMode();
     LogicalSize availSize = aReflowState.ComputedSize(wm);
--- a/layout/mathml/nsMathMLTokenFrame.cpp
+++ b/layout/mathml/nsMathMLTokenFrame.cpp
@@ -121,16 +121,19 @@ nsMathMLTokenFrame::InsertFrames(ChildLi
 }
 
 void
 nsMathMLTokenFrame::Reflow(nsPresContext*          aPresContext,
                            nsHTMLReflowMetrics&     aDesiredSize,
                            const nsHTMLReflowState& aReflowState,
                            nsReflowStatus&          aStatus)
 {
+  MarkInReflow();
+  mPresentationData.flags &= ~NS_MATHML_ERROR;
+
   // initializations needed for empty markup like <mtag></mtag>
   aDesiredSize.ClearSize();
   aDesiredSize.SetBlockStartAscent(0);
   aDesiredSize.mBoundingMetrics = nsBoundingMetrics();
 
   nsIFrame* childFrame = GetFirstPrincipalChild();
   while (childFrame) {
     // ask our children to compute their bounding metrics
--- a/layout/mathml/nsMathMLmfencedFrame.cpp
+++ b/layout/mathml/nsMathMLmfencedFrame.cpp
@@ -210,16 +210,18 @@ ApplyUnstretchedMetrics(nsPresContext*  
 }
 
 void
 nsMathMLmfencedFrame::Reflow(nsPresContext*          aPresContext,
                              nsHTMLReflowMetrics&     aDesiredSize,
                              const nsHTMLReflowState& aReflowState,
                              nsReflowStatus&          aStatus)
 {
+  MarkInReflow();
+  mPresentationData.flags &= ~NS_MATHML_ERROR;
   aDesiredSize.ClearSize();
   aDesiredSize.SetBlockStartAscent(0);
   aDesiredSize.mBoundingMetrics = nsBoundingMetrics();
 
   int32_t i;
   const nsStyleFont* font = StyleFont();
   float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(this);
   nsRefPtr<nsFontMetrics> fm;
--- a/layout/mathml/nsMathMLmpaddedFrame.cpp
+++ b/layout/mathml/nsMathMLmpaddedFrame.cpp
@@ -303,16 +303,17 @@ nsMathMLmpaddedFrame::UpdateValue(int32_
 }
 
 void
 nsMathMLmpaddedFrame::Reflow(nsPresContext*          aPresContext,
                              nsHTMLReflowMetrics&     aDesiredSize,
                              const nsHTMLReflowState& aReflowState,
                              nsReflowStatus&          aStatus)
 {
+  mPresentationData.flags &= ~NS_MATHML_ERROR;
   ProcessAttributes();
 
   ///////////////
   // Let the base class format our content like an inferred mrow
   nsMathMLContainerFrame::Reflow(aPresContext, aDesiredSize,
                                  aReflowState, aStatus);
   //NS_ASSERTION(NS_FRAME_IS_COMPLETE(aStatus), "bad status");
 }
--- a/layout/mathml/nsMathMLmrootFrame.cpp
+++ b/layout/mathml/nsMathMLmrootFrame.cpp
@@ -159,18 +159,20 @@ nsMathMLmrootFrame::GetRadicalXOffsets(n
 }
 
 void
 nsMathMLmrootFrame::Reflow(nsPresContext*          aPresContext,
                            nsHTMLReflowMetrics&     aDesiredSize,
                            const nsHTMLReflowState& aReflowState,
                            nsReflowStatus&          aStatus)
 {
+  MarkInReflow();
   nsReflowStatus childStatus;
 
+  mPresentationData.flags &= ~NS_MATHML_ERROR;
   aDesiredSize.ClearSize();
   aDesiredSize.SetBlockStartAscent(0);
 
   nsBoundingMetrics bmSqr, bmBase, bmIndex;
   nsRenderingContext& renderingContext = *aReflowState.rendContext;
 
   //////////////////
   // Reflow Children
--- a/layout/mathml/nsMathMLmspaceFrame.cpp
+++ b/layout/mathml/nsMathMLmspaceFrame.cpp
@@ -93,16 +93,18 @@ nsMathMLmspaceFrame::ProcessAttributes(n
 }
 
 void
 nsMathMLmspaceFrame::Reflow(nsPresContext*          aPresContext,
                             nsHTMLReflowMetrics&     aDesiredSize,
                             const nsHTMLReflowState& aReflowState,
                             nsReflowStatus&          aStatus)
 {
+  MarkInReflow();
+  mPresentationData.flags &= ~NS_MATHML_ERROR;
   ProcessAttributes(aPresContext);
 
   mBoundingMetrics = nsBoundingMetrics();
   mBoundingMetrics.width = mWidth;
   mBoundingMetrics.ascent = mHeight;
   mBoundingMetrics.descent = mDepth;
   mBoundingMetrics.leftBearing = 0;
   mBoundingMetrics.rightBearing = mBoundingMetrics.width;
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..f5fdf5a2eb67cd107998b1e349d62eff34d54ab8
GIT binary patch
literal 1084
zc$_QucXMN4WB>vd8wOqw&H94@2XJ!>@dc_=05YY3SjKPNi<ADr`bG>4j7xxgCm;?B
zOHAQP&P^-;ih;~=1kz3n{LJr@%SwRym~226fw3Y}WqM*UP#uc|kk0|a|Nk>Er{`3r
z0oC~c`Sn2D!=X3JKO;3U1!#@|kZ%aYX74o?h-Ux=fnq>$200*B;h4Z$n~_^m0Tg=y
z<O_hX;1q_|ocv^<Iwl7op9_o&7(#LrD?sjR0rKU6Sb@Ql!8R{3Hx;Pv48$HL_9_8U
ztAhOE5(Wn5KR`9oK&<tD9fN7bt>lE1gan2p<%j;(kALRwGb%9N&(LKcv-QI1*2`W0
zpKk~=c*|I1DKW!g5rd&W*G`~DsGeJ&>-n5TUdys6B&<zHNO54*VaVSG@;y{OA@M<C
zLK*|3nnGF%L#L&|i$)d(mOl*b5c$1_f3!AEZERfGoypJ4?9AViR&yZX#0eIW)P%<=
z4UBGVDgqOp%{uhpz=IPHPOSNGze$m6g*NLlHU_~v+%mvey~Cggj@>(8oB}d4At9|H
zL6XglO<)7#Cy9eKVr;AmXPH_Q_C5apU*OY!#T8y`Zf0x}lN%>;dKefkU~*$S)G#yA
zgPEb7iR~ZAFkl>l!UYW7ApS@S0Q%%g(yXHg&Kx+mVEe?0j`!1qt+@|JFYe7*Q=VgH
zXv{d1M|!5?p*0E*ec$Bt)B}Z@j`XsMvR>_FohmA6882gh&g6CeETv!;<_k-v{4b8s
zp7E{W>=U+G`KvUzj>k*v|5KWn_j7M+Xv@dbox=auIT$cAGyI>W!3?xNw|{TmAp?=t
z{zezARRQgG2i^u96nNsY$SEgx$%ZL<Q*S7DC@390(ZaaRueK?#w#_bBeyRGgw=L_R
ze2#tnPlox$Lusz(#r@*vxft)+ZWnlTxmddV$HzO43##TZ-&w(aUh#f><VvgXGxK7@
z4|B!J*laK8<v*Wm^YC;rd+gOuG7eYPKA-SDc!|M{haBBP+G_%xFC1rL&SE+%F*$9G
z)y73hPtMHNJH7AOxtqb0wmD>#xks<Nw)KlRv*#AM(`V+KT6Id*v@AU*ZhBj(u~}r!
z2ahvvmkEkQ6rEwr_vuQlT#%NR_4dl8*oQW}FFg$k*Zn&-=d+;Fk)9X#EsZv>HBJt1
zRdLo8E_}UWu}0tGV-Hk~i}N<!_#NLlTXgr{TW^o;*-%~QvZb#*Dto8F6Y*USy=Q~t
zi=p}dSspisu8EAY8yZ|^N`x?2?qqxnid{xveq@5AlN5#~<+8i)9liu|?~Re*WpJ)#
IUCY1#0QJ9v>i_@%
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..b96e5094489bb5831ee17995d64a7f3b9efbc6de
GIT binary patch
literal 1084
zc$_QucXMN4WB>vd8wOqw&H94@2XJ!>@dc_=05YY3SjKPNi<ADr`bG>4j7xxgCm;?B
zOG@ER&P^-;ih;~=1kz5-{LF8Y%SwRym~226fw3Y}WqM*UP#uc|kk0|a|Nk>Er{`3r
z0oC~c`Sn2D!=X3JKO;3U1!#@|kZ%aYX74o?h-Ux=fnq>$200*B;h4Z$n~_^m0Tg=y
z<O_hX;1q_|ocv^<Iwl7op9_o&7(#LrD?sjR0rKU6Sb@Ql!8R{3Hx;Pv48$HL_8K8^
zn}Yn}5(Wn5KR`9oK&<tDJ%ee*t>lE1gan2p-go7<AOFnVXH;OkpP|b@X6uF1t(Uw0
zKi?2$@RqU2QeuX~A_iSiu7yC2P(8Ok*Yi1xyq0BCNLZVYkmA6q!;rrX<a?-mLgItO
zgfs?5HHEYkhE7X^7mX|oEPoi<A@X|<|7dNT+Ss_VJCmQ8*_pp3t>!?&i4!a$sR@r$
z8W`Q!R0JkGn|0{Hfd@w(99i?>ev=~C3T@V9Yz#sy+`tfGy~Cggj@>(8oB}d4At9|H
zL6XglO<)7#Cy9eKVr;AmXPH_Q_Few}U*OY!#}!^|Zf0x}lN%>;dKefkU~*$qVlX}&
zz{b!a#r7X$7%&b&;R1$k5Pu{E0DW>LY1YvLXAYcOuzli0$NOo**4&4q7x(6@DbF!8
zG-jO1BR$jc&>Dq@zHf4R>VZN{M|xRBS+Dl8P8F53jF+)LXYx9KmQpYa^Mxf-{uf7R
z&-m7G_6ggp{8buU$KxgT|0zw(`?<F@wB_UJPT~LS91NJ58UD}GU<TTs+rKyOkby{R
zzhY-d$U?r_hTBV<6el*jv`TclOkd`$`i*0efMa3GA%^JXc8=Taoae9Xza;#)>`?fp
zpX*BN`xw7`<Zb=D^LTf9tHAqt)*T;S?(DYyQ^6;&YTt7K^H&Fpk9-dgTlJvp=H}N~
zkqg_KpYyHz_;6>pdrc+Zg<#q9iY|An`hNUtk>tseNeo<gM2eS%S;ufm-vUYDpku3q
zwYT-msl1(f^ZGr@YOPCrGiLtESY34YM1tj%iPwZro>_S-uXv_pwDfHAsfn@2)Vg_n
zl}^81CMpt9c!n|Grz^B{!MVFvj;&m}`%tCV3r~a6b^ngd`D~c9r02(d%V%lV&-ChY
zOR7dKdvq=Ec;T#LYY%#-iQP%r_<g^Sxpryw*4w?ViQoTpWXx5I%HC=4M0}s7_hN8-
zF*N@_%j4$IHIY$vLxbx~i4aE1U5rmavC9a|k4%tslEToWTz2=p!<Rtry)hEJ49>Nz
HYZ({->^Xzt
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..48abdf1464f9e5cf5b6c1e27a876b1cbd0264ccd
GIT binary patch
literal 1084
zc$_QucXMN4WB>vd8wOqw&H94@2XJ!>@dc_=05YY3SjKPNi<ADr`bG>4j7xxgCm;?B
zOHScQ&P^-;ih;~=1kz3nLd@@y%SwRym~226fw3Y}WqM*UP#uc|kk0|a|Nk>Er{`3r
z0oC~c`Sn2D!=X3JKO;3U1!#@|kZ%aYX74o?h-Ux=fnq>$200*B;h4Z$n~_^m0Tg=y
z<O_hX;1q_|ocv^<Iwl7op9_o&7(#LrD?sjR0rKU6Sb@Ql!8R{3Hx;Pv48$HL_Bs(s
zyMp}U5(Wn5KR`9oK&<tD1A}SBt>lE1gan49v-{UyJO250P3nWRI!2L1i>!syxtELn
zKcBcZ;TH3gA_Id)7sk+|+#NuTP(8Ok*Yi1xyq0BCNLZVYkmA6q!;rrX<a?-mLgItO
zgfs?5HHEYkhE7X^7mX|oEPoi<A@X|<|7dNT+Ss_VJCmQ8*_pp3t>!?&i4!a$sR@r$
z8W`Q!R0JkGn|0{Hfd^+EoLTeXev=~C3T@V9Yz#s=+`y1$y~Cggj@>(8oB}d4At9|H
zL6XglO<)7#Cy9eKVr;AmXPH_Q_I>{UU*OY!#uZ*{Zf0x}lN%>;dKefkU~*$iVlX}|
zz{b#N#12jnz&Hej3mCdV{E-v@^vRW^Sw|0?IdE>l_K6c6@23e{b03ag+?%teJjcw?
zm~keL^i0P?YZM;(zRBsS2MRSE>17pVz1qt<RaDY4UdH~M$?N=CO2I757nV%<UmT%5
z<6FboCv3CwS7~q^kC)j0r!+C|=ib)PmXD`9h5xT}Fkoh8_&-a78EAiQzhT}X2Ladr
zO}UyHPMSpxp2Cia*}X0;8qF@VEPPd}xOI{pBeD%yOI8*&l~_8To3i*x_01(o?zbv8
z&-(dU+(G_+fW`dkJr!XbAK&e4=9oWk_m0E;&lwiJs^%+PENA0hQ=ht=Yh8rx?v>L-
zt1K$sJ&=;0x4YuV^ydt=S3k-)U0M6Q<GufkgA0xciivuyx#(bUK*;%-ph>dRsT-w9
z$EQu)oVQ%(`7P_`KCZVIjf3TLL)S@5>a-hOs#2P18G1Wc&p0_d`SUWPH>WL9HZJ7Z
zbSLjt3zPOKnGeSnMTD|H+Zmi2v#liD!k^QYbJ@A8^Uv&*Q+7Mm@qcb|UfJ0@H#SWO
zT$HMnC;QpL@A3}Qij!xWX6t<W7yf8V(Dv}%cOyi~&i8j*%UrZ|8n@E^gN5F!!STh=
z{QoSEn?u(`M%fJwt}`XBFj($pd<u$PMqqwqf~1oah9>2*yYC&o1aj|<k>F);u4P@z
GzyJXMoO}-e
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0b62964ed54870dd75ce73311b47764b55a1ee88
GIT binary patch
literal 1084
zc$_QucXMN4WB>vd8wOqw&H94@2XJ!>@dc_=05YY3SjKPNi<ADr`bG>4j7xxgCm;?B
zOG)8P&P^-;ih;~=1kz3n{A}-&%SwRym~226fw3Y}WqM*UP#uc|kk0|a|Nk>Er{`3r
z0oC~c`Sn2D!=X3JKO;3U1!#@|kZ%aYX74o?h-Ux=fnq>$200*B;h4Z$n~_^m0Tg=y
z<O_hX;1q_|ocv^<Iwl7op9_o&7(#LrD?sjR0rKU6Sb@Ql!8R{3Hx;Pv48$HL_69L&
zhl2d#5(Wn5KR`9oK&<tDBZFzht>lE1gan49tM~b*9shj0CiOvD9ivF1Mb^UU+{;D(
zpHEzyaEtj#k%2*@3uEA7ZlFaBP(8Ok*Yi1xyq0BCNLZVYkmA6q!;rrX<a?-mLgItO
zgfs?5HHEYkhE7X^7mX|oEPoi<A@X|<|7dNT+Ss_VJCmQ8*_pp3t>!?&i4!a$sR@r$
z8W`Q!R0JkGn|0{Hfd_{k99r|?ev=~C3T@V9Yz#sn+)BV$y~Cggj@>(8oB}d4At9|H
zL6XglO<)7#Cy9eKVr;AmXPH_Q_MQI!U*OaKx)ok*Zf0x}lN%>;dKefkU~*$y#9(~*
z0xLt87dtq~0OJr8E@0>e@kdes&?i@tW*t3n=D@iH+b2$Ryq_j)&3!m}ac|C=@*Fcm
zW5$_0(lZ?otx<UB`zEKS9w^jwq?c8c^=dEcR8dLGcp3Y1Ca?2nDFw4IUsy8be{qEN
zjBgERpRmo!U!}oyJYHh|pVGv<pL<(FTRxud6#l=?!GM{W;r}cRW}yAK{d@Bc8Hlv@
zH@ax83TU@G@HXh6z!QZ)#be8wmc9(?v~A*0(&+OsI&f{J|An>ZFQmU({N>1wxgVz1
z?90A8KbgU{#`$5*yN{0|6%XuwF6LBImG_v>ex5Z)$ob+1>8}qgpZwO=4t?HrbMx!0
zh=tv^in*`<_;BZ#@;;mL3!%E_8C~vH@%^-OXKEI5j}VDSZsuyTn<f$9exZj`Gxchx
zsI}X%Juf%C$$r;k%bLlrvdrdI(%zLz_RE~`c-^_=S;(8RnJUMo9ZL&MjF`UZWKy?!
zQrh-jh2vexS$hmmhP(RC-MMmVuy^^+&n8RM4_?{%OS<^n@eTpwMfJ~T%&avvE}MHg
zV@0Y~o@}Yi(ZVYdds5O4`iimt*Dl<gv^{+H-H0dO4)k|i`xua|E8g~p>Bvs+&EWWA
zX#Rhe$IYQ@BBShv2G^MqR~Rk#Fg^ptE+a5MGC|Tw3PY1}+1>XJUjn)J#z^ooIM=eS
HWncgRg*1d`
--- a/layout/style/test/mochitest.ini
+++ b/layout/style/test/mochitest.ini
@@ -215,16 +215,19 @@ skip-if = android_version == '10' #Andro
 [test_transitions_events.html]
 [test_transitions.html]
 [test_transitions_per_property.html]
 skip-if = buildapp == 'b2g' || toolkit == 'android' #bug 775227 # b2g(times out, needs more time + various failures) b2g-debug(times out, needs more time + various failures) b2g-desktop(times out, needs more time + various failures)
 [test_transitions_step_functions.html]
 [test_transitions_dynamic_changes.html]
 [test_transitions_bug537151.html]
 [test_unclosed_parentheses.html]
+[test_unicode_range_loading.html]
+support-files = ../../reftests/fonts/markA.woff ../../reftests/fonts/markB.woff ../../reftests/fonts/markC.woff ../../reftests/fonts/markD.woff
+skip-if = (toolkit == "gtk2") || (toolkit == "gtk3") # bug 1056479
 [test_units_angle.html]
 [test_units_frequency.html]
 [test_units_length.html]
 [test_units_time.html]
 [test_unprefixing_service.html]
 support-files = unprefixing_service_iframe.html unprefixing_service_utils.js
 [test_unprefixing_service_prefs.html]
 support-files = unprefixing_service_iframe.html unprefixing_service_utils.js
new file mode 100644
--- /dev/null
+++ b/layout/style/test/test_unicode_range_loading.html
@@ -0,0 +1,366 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset=utf-8>
+  <title>unicode-range load tests using font loading api</title>
+  <link rel="author" title="John Daggett" href="mailto:jdaggett@mozilla.com">
+  <link rel="help" href="http://www.w3.org/TR/css-fonts-3/#unicode-range-desc" />
+  <link rel="help" href="http://dev.w3.org/csswg/css-font-loading/" />
+  <meta name="assert" content="unicode-range descriptor defines precisely which fonts should be loaded" />
+  <script type="text/javascript" src="/resources/testharness.js"></script>
+  <script type="text/javascript" src="/resources/testharnessreport.js"></script>
+  <style type="text/css">
+  </style>
+</head>
+<body>
+<div id="log"></div>
+<pre id="display"></pre>
+<style id="testfonts"></style>
+<style id="teststyle"></style>
+<div id="testcontent"></div>
+
+<script>
+
+const kSheetFonts = 1;
+const kSheetStyles = 2;
+
+const redSquDataURL = "data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 10' width='100%' height='100%'><rect fill='red' x='0' y='0' width='10' height='10'/></svg>";
+
+var unicodeRangeTests = [
+  { test: "simple load sanity check, unused fonts not loaded",
+    fonts: [{ family: "a", src: "markA", descriptors: { }, loaded: false}],
+    content: "AAA", style: { "font-family": "unused" } },
+  { test: "simple load sanity check, font for a used character loaded",
+    fonts: [{ family: "a", src: "markA", descriptors: { unicodeRange: "u+41" }, loaded: true}],
+    content: "AAA" },
+  { test: "simple load sanity check, font for an unused character not loaded",
+    fonts: [{ family: "a", src: "markA", descriptors: { unicodeRange: "u+41" }, loaded: false}],
+    content: "BBB" },
+  { test: "simple load sanity check, with two fonts only font for used character loaded A",
+    fonts: [{ family: "a", src: "markA", descriptors: { unicodeRange: "u+41" }, loaded: true},
+            { family: "a", src: "markB", descriptors: { unicodeRange: "u+42" }, loaded: false}],
+    content: "AAA" },
+  { test: "simple load sanity check, with two fonts only font for used character loaded B",
+    fonts: [{ family: "a", src: "markA", descriptors: { unicodeRange: "u+41" }, loaded: false},
+            { family: "a", src: "markB", descriptors: { unicodeRange: "u+42" }, loaded: true}],
+    content: "BBB" },
+  { test: "simple load sanity check, two fonts but neither supports characters used",
+    fonts: [{ family: "a", src: "markA", descriptors: { unicodeRange: "u+41" }, loaded: false},
+            { family: "a", src: "markB", descriptors: { unicodeRange: "u+42" }, loaded: false}],
+    content: "CCC" },
+  { test: "simple load sanity check, two fonts and both are used",
+    fonts: [{ family: "a", src: "markA", descriptors: { unicodeRange: "u+41" }, loaded: true},
+            { family: "a", src: "markB", descriptors: { unicodeRange: "u+42" }, loaded: true}],
+    content: "ABC" },
+  { test: "simple load sanity check, one with Han ranges",
+    fonts: [{ family: "a", src: "markA", descriptors: { unicodeRange: "u+3???,u+5???,u+7???,u+8???" }, loaded: true},
+            { family: "a", src: "markB", descriptors: { unicodeRange: "u+42" }, loaded: false}],
+    content: "納豆嫌い" },
+  { test: "simple load sanity check, two fonts with different styles A",
+    fonts: [{ family: "a", src: "markA", descriptors: { unicodeRange: "u+41" }, loaded: true},
+            { family: "a", src: "markB", descriptors: { weight: "bold", unicodeRange: "u+42" }, loaded: false}],
+    content: "ABC" },
+  { test: "simple load sanity check, two fonts with different styles B",
+    fonts: [{ family: "a", src: "markA", descriptors: { weight: "bold", unicodeRange: "u+41" }, loaded: false},
+            { family: "a", src: "markB", descriptors: { unicodeRange: "u+42" }, loaded: true}],
+    content: "ABC" },
+  { test: "multiple fonts with overlapping ranges, all with default ranges, only last one supports character used",
+    fonts: [{ family: "a", src: "markC", descriptors: { }, loaded: true},
+            { family: "a", src: "markA", descriptors: { }, loaded: true},
+            { family: "a", src: "markB", descriptors: { }, loaded: true}],
+    content: "CCC" },
+  { test: "multiple fonts with overlapping ranges, all with default ranges, first one supports character used",
+    fonts: [{ family: "a", src: "markB", descriptors: { }, loaded: false},
+            { family: "a", src: "markA", descriptors: { }, loaded: false},
+            { family: "a", src: "markC", descriptors: { }, loaded: true}],
+    content: "CCC" },
+  { test: "multiple fonts with overlapping ranges, one with default value in the fallback position",
+    fonts: [{ family: "a", src: "markC", descriptors: { }, loaded: true},
+            { family: "a", src: "markA", descriptors: { unicodeRange: "u+41" }, loaded: true},
+            { family: "a", src: "markB", descriptors: { unicodeRange: "u+42" }, loaded: true}],
+    content: "ABC" },
+  { test: "multiple fonts with overlapping ranges, one with default value in the primary use position, fallback to one",
+    fonts: [{ family: "a", src: "markA", descriptors: { unicodeRange: "u+41" }, loaded: true},
+            { family: "a", src: "markB", descriptors: { unicodeRange: "u+42" }, loaded: false},
+            { family: "a", src: "markC", descriptors: { }, loaded: true}],
+    content: "AAA" },
+  { test: "multiple fonts with overlapping ranges, one with default value in the primary use position, fallback to two",
+    fonts: [{ family: "a", src: "markA", descriptors: { unicodeRange: "u+41" }, loaded: true},
+            { family: "a", src: "markB", descriptors: { unicodeRange: "u+42" }, loaded: true},
+            { family: "a", src: "markC", descriptors: { }, loaded: true}],
+    content: "ABC" },
+  { test: "multiple fonts with overlapping ranges, one with default value in the primary use position, no fallback",
+    fonts: [{ family: "a", src: "markA", descriptors: { unicodeRange: "u+41" }, loaded: false},
+            { family: "a", src: "markB", descriptors: { unicodeRange: "u+42" }, loaded: false},
+            { family: "a", src: "markC", descriptors: { }, loaded: true}],
+    content: "CCC" },
+  { test: "metrics only case, ex-sized image, single font with space in range",
+    fonts: [{ family: "a", src: "markA", descriptors: { unicodeRange: "u+0??" }, loaded: true}],
+    content: "<img style='width: 2ex' src=\"" + redSquDataURL + "\">" },
+  { test: "metrics only case, ex-sized image, single font with space outside range",
+    fonts: [{ family: "a", src: "markA", descriptors: { unicodeRange: "u+1??" }, loaded: false}],
+    content: "<img style='width: 2ex' src=\"" + redSquDataURL + "\">" },
+  { test: "metrics only case, ch-sized image, single font with space in range",
+    fonts: [{ family: "a", src: "markA", descriptors: { unicodeRange: "u+0??" }, loaded: true}],
+    content: "<img style='width: 2ch' src=\"" + redSquDataURL + "\">" },
+  { test: "metrics only case, ch-sized image, single font with space outside range",
+    fonts: [{ family: "a", src: "markA", descriptors: { unicodeRange: "u+1??" }, loaded: false}],
+    content: "<img style='width: 2ch' src=\"" + redSquDataURL + "\">" },
+];
+
+// map font loading descriptor names to @font-face rule descriptor names
+var mapDescriptorNames = {
+  style: "font-style",
+  weight: "font-weight",
+  stretch: "font-stretch",
+  unicodeRange: "unicode-range",
+  variant: "font-variant",
+  featureSettings: "font-feature-settings"
+};
+
+var kBaseFontURL;
+if ("SpecialPowers" in window) {
+  kBaseFontURL = "";
+} else {
+  kBaseFontURL = "fonts/";
+}
+
+var mapFontURLs = {
+  markA: "url(" + kBaseFontURL + "markA.woff" + ")",
+  markB: "url(" + kBaseFontURL + "markB.woff" + ")",
+  markC: "url(" + kBaseFontURL + "markC.woff" + ")",
+  markD: "url(" + kBaseFontURL + "markD.woff" + ")",
+
+  /* twourl versions include a bogus url followed by a valid url */
+  markAtwourl: "url(" + kBaseFontURL + "bogus-markA.woff" + "), url(" + kBaseFontURL + "markA.woff" + ")",
+  markBtwourl: "url(" + kBaseFontURL + "bogus-markB.woff" + "), url(" + kBaseFontURL + "markB.woff" + ")",
+  markCtwourl: "url(" + kBaseFontURL + "bogus-markC.woff" + "), url(" + kBaseFontURL + "markC.woff" + ")",
+  markDtwourl: "url(" + kBaseFontURL + "bogus-markD.woff" + "), url(" + kBaseFontURL + "markD.woff" + ")",
+
+  /* localfont versions include a bogus local ref followed by a valid url */
+  markAlocalfirst: "local(bogus-markA), url(" + kBaseFontURL + "markA.woff" + ")",
+  markBlocalfirst: "local(bogus-markB), url(" + kBaseFontURL + "markB.woff" + ")",
+  markClocalfirst: "local(bogus-markC), url(" + kBaseFontURL + "markC.woff" + ")",
+  markDlocalfirst: "local(bogus-markD), url(" + kBaseFontURL + "markD.woff" + ")",
+};
+
+function familyName(name, i) {
+  return "test" + i + "-" + name;
+}
+
+function fontFaceRule(name, fontdata, ft) {
+  var desc = [];
+  desc.push("font-family: " + name);
+  var srckey = fontdata.src + ft;
+  desc.push("src: " + mapFontURLs[srckey]);
+  for (var d in fontdata.descriptors) {
+    desc.push(mapDescriptorNames[d] + ": " + fontdata.descriptors[d]);
+  }
+  return "@font-face { " + desc.join(";") + " }";
+}
+
+function clearRules(sheetIndex) {
+  var sheet = document.styleSheets[sheetIndex];
+  while(sheet.cssRules.length > 0) {
+    sheet.deleteRule(0);
+  }
+}
+
+function clearAllRulesAndFonts() {
+  clearRules(kSheetFonts);
+  clearRules(kSheetStyles);
+  document.fonts.clear();
+}
+
+function addStyleRulesAndText(testdata, i) {
+  // add style rules for testcontent
+  var sheet = document.styleSheets[kSheetStyles];
+  while(sheet.cssRules.length > 0) {
+    sheet.deleteRule(0);
+  }
+  var rule = [];
+  var family = familyName(testdata.fonts[0].family, i);
+  rule.push("#testcontent { font-family: " + family);
+  if ("style" in testdata) {
+    for (s in testdata.style) {
+      rule.push(s + ": " + testdata.style[s]);
+    }
+  }
+  rule.push("}");
+  sheet.insertRule(rule.join("; "), 0);
+
+  var content = document.getElementById("testcontent");
+  content.innerHTML = testdata.content;
+  content.offsetHeight;
+}
+
+// work arounds
+function getFonts() {
+  if ("forEach" in document.fonts) {
+    return document.fonts;
+  }
+  return Array.from(document.fonts);
+}
+
+function getSize() {
+  if ("size" in document.fonts) {
+    return document.fonts.size;
+  }
+  return getFonts().length;
+}
+
+function getReady() {
+  if (typeof(document.fonts.ready) == "function") {
+    return document.fonts.ready();
+  }
+  return document.fonts.ready;
+}
+
+function setTimeoutPromise(aDelay) {
+  return new Promise(function(aResolve, aReject) {
+    setTimeout(aResolve, aDelay);
+  });
+}
+
+function addFontFaceRules(testdata, i, ft) {
+  var sheet = document.styleSheets[kSheetFonts];
+  var createdFonts = [];
+  testdata.fonts.forEach(function(f) {
+    var n = sheet.cssRules.length;
+    var fn = familyName(f.family, i);
+    sheet.insertRule(fontFaceRule(fn, f, ft), n);
+    var newfont;
+    var fonts = getFonts();
+    try {
+      fonts.forEach(function(font) { newfont = font; });
+      createdFonts.push({family: fn, data: f, font: newfont});
+    } catch (e) {
+      console.log(e);
+    }
+  });
+  return createdFonts;
+}
+
+function addDocumentFonts(testdata, i, ft) {
+  var createdFonts = [];
+  testdata.fonts.forEach(function(fd) {
+    var fn = familyName(fd.family, i);
+    var srckey = fd.src + ft;
+    var f = new FontFace(fn, mapFontURLs[srckey], fd.descriptors);
+    document.fonts.add(f);
+    createdFonts.push({family: fn, data: fd, font: f});
+  });
+  return createdFonts;
+}
+
+var q = Promise.resolve();
+
+function runTests() {
+  function setupTests() {
+    setup({explicit_done: true});
+  }
+
+  function checkFontsBeforeLoad(name, testdata, fd) {
+    test(function() {
+      assert_equals(document.fonts.status, "loaded", "before initializing test, no fonts should be loading - found: " + document.fonts.status);
+      var size = getSize();
+      assert_equals(size, testdata.fonts.length,
+                    "fonts where not added to the font set object");
+      var i = 0;
+      fonts = getFonts();
+      fonts.forEach(function(ff) {
+        assert_equals(ff.status, "unloaded", "added fonts should be in unloaded state");
+      });
+    }, name + " before load");
+  }
+
+  function checkFontsAfterLoad(name, testdata, fd, afterTimeout) {
+    test(function() {
+      assert_equals(document.fonts.status, "loaded", "after ready promise resolved, no fonts should be loading");
+      var i = 0;
+      fd.forEach(function(f) {
+        assert_true(f.font instanceof FontFace, "font needs to be an instance of FontFace object");
+        if (f.data.loaded) {
+          assert_equals(f.font.status, "loaded", "font not loaded - font " + i + " " + f.data.src + " "
+                        + JSON.stringify(f.data.descriptors) + " for content " + testdata.content);
+        } else {
+          assert_equals(f.font.status, "unloaded", "font loaded - font " + i + " " + f.data.src + " "
+                        + JSON.stringify(f.data.descriptors) + " for content " + testdata.content);
+        }
+        i++;
+      });
+    }, name + " after load" + (afterTimeout ? " and timeout" : ""));
+  }
+
+  function testFontLoads(testdata, i, name, fd) {
+    checkFontsBeforeLoad(name, testdata, fd);
+    addStyleRulesAndText(testdata, i);
+
+    var ready = getReady();
+    return ready.then(function() {
+      checkFontsAfterLoad(name, testdata, fd, false);
+    }).then(function() {
+      return setTimeoutPromise(0).then(function() {
+        checkFontsAfterLoad(name, testdata, fd, true);
+      });
+    }).then(function() {
+      var ar = getReady();
+      return ar.then(function() {
+        test(function() {
+          assert_equals(document.fonts.status, "loaded", "after ready promise fulfilled once, fontset should not be loading");
+          var fonts = getFonts();
+          fonts.forEach(function(f) {
+            assert_not_equals(f.status, "loading", "after ready promise fulfilled once, no font should be loading");
+          });
+        }, name + " test done check");
+      });
+    }).then(function() {
+      clearAllRulesAndFonts();
+    });
+  }
+
+  function testUnicodeRangeFontFace(testdata, i, ft) {
+    var name = "TEST " + i + " " + testdata.test + " (@font-face rules)" + (ft != "" ? " " + ft : ft);
+
+    var fd = addFontFaceRules(testdata, i, ft);
+    return testFontLoads(testdata, i, name, fd);
+  }
+
+  function testUnicodeRangeDocumentFonts(testdata, i, ft) {
+    var name = "TEST " + i + " " + testdata.test + " (document.fonts)" + (ft != "" ? " " + ft : ft);
+
+    var fd = addDocumentFonts(testdata, i, ft);
+    return testFontLoads(testdata, i, name, fd);
+  }
+
+  q = q.then(function() {
+    setupTests();
+  });
+
+  var fontTypes = ["", "twourl", "localfirst"];
+
+  unicodeRangeTests.forEach(function(testdata, i) {
+    fontTypes.forEach(function(ft) {
+      q = q.then(function() {
+        return testUnicodeRangeFontFace(testdata, i, ft);
+      }).then(function() {
+        return testUnicodeRangeDocumentFonts(testdata, i, ft);
+      });
+    });
+  });
+
+  q = q.then(function() {
+    done();
+  });
+}
+
+if ("fonts" in document) {
+  runTests();
+} else {
+  test(function() {
+    assert_true(true, "CSS Font Loading API is not enabled.");
+  }, "CSS Font Loading API is not enabled");
+}
+</script>
+</body>
+</html>
--- a/layout/svg/SVGTextFrame.cpp
+++ b/layout/svg/SVGTextFrame.cpp
@@ -5258,17 +5258,16 @@ SVGTextFrame::DoReflow()
   nsHTMLReflowMetrics desiredSize(reflowState);
   nsReflowStatus status;
 
   NS_ASSERTION(reflowState.ComputedPhysicalBorderPadding() == nsMargin(0, 0, 0, 0) &&
                reflowState.ComputedPhysicalMargin() == nsMargin(0, 0, 0, 0),
                "style system should ensure that :-moz-svg-text "
                "does not get styled");
 
-  kid->WillReflow(presContext);
   kid->Reflow(presContext, desiredSize, reflowState, status);
   kid->DidReflow(presContext, &reflowState, nsDidReflowStatus::FINISHED);
   kid->SetSize(wm, desiredSize.Size(wm));
 
   mState &= ~NS_STATE_SVG_TEXT_IN_REFLOW;
 
   TextNodeCorrespondenceRecorder::RecordCorrespondence(this);
 }
--- a/layout/svg/nsSVGForeignObjectFrame.cpp
+++ b/layout/svg/nsSVGForeignObjectFrame.cpp
@@ -514,16 +514,17 @@ void nsSVGForeignObjectFrame::RequestRef
     return;
 
   PresContext()->PresShell()->FrameNeedsReflow(kid, aType, NS_FRAME_IS_DIRTY);
 }
 
 void
 nsSVGForeignObjectFrame::DoReflow()
 {
+  MarkInReflow();
   // Skip reflow if we're zero-sized, unless this is our first reflow.
   if (IsDisabled() &&
       !(GetStateBits() & NS_FRAME_FIRST_REFLOW))
     return;
 
   nsPresContext *presContext = PresContext();
   nsIFrame* kid = GetFirstPrincipalChild();
   if (!kid)
--- a/layout/svg/nsSVGOuterSVGFrame.cpp
+++ b/layout/svg/nsSVGOuterSVGFrame.cpp
@@ -340,16 +340,17 @@ nsSVGOuterSVGFrame::ComputeSize(nsRender
 }
 
 void
 nsSVGOuterSVGFrame::Reflow(nsPresContext*           aPresContext,
                            nsHTMLReflowMetrics&     aDesiredSize,
                            const nsHTMLReflowState& aReflowState,
                            nsReflowStatus&          aStatus)
 {
+  MarkInReflow();
   DO_GLOBAL_REFLOW_COUNT("nsSVGOuterSVGFrame");
   DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
   NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
                   ("enter nsSVGOuterSVGFrame::Reflow: availSize=%d,%d",
                   aReflowState.AvailableWidth(), aReflowState.AvailableHeight()));
 
   NS_PRECONDITION(mState & NS_FRAME_IN_REFLOW, "frame is not in reflow");
 
--- a/layout/tables/nsTableCellFrame.cpp
+++ b/layout/tables/nsTableCellFrame.cpp
@@ -852,20 +852,21 @@ CalcUnpaginagedHeight(nsPresContext*    
       computedHeight += row->GetUnpaginatedHeight(aPresContext);
     }
   }
   return computedHeight;
 }
 
 void
 nsTableCellFrame::Reflow(nsPresContext*           aPresContext,
-                                   nsHTMLReflowMetrics&     aDesiredSize,
-                                   const nsHTMLReflowState& aReflowState,
-                                   nsReflowStatus&          aStatus)
+                         nsHTMLReflowMetrics&     aDesiredSize,
+                         const nsHTMLReflowState& aReflowState,
+                         nsReflowStatus&          aStatus)
 {
+  MarkInReflow();
   DO_GLOBAL_REFLOW_COUNT("nsTableCellFrame");
   DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
 
   if (aReflowState.mFlags.mSpecialHeightReflow) {
     FirstInFlow()->AddStateBits(NS_TABLE_CELL_HAD_SPECIAL_REFLOW);
   }
 
   // see if a special height reflow needs to occur due to having a pct height
--- a/layout/tables/nsTableColFrame.cpp
+++ b/layout/tables/nsTableColFrame.cpp
@@ -86,16 +86,17 @@ void nsTableColFrame::SetContinuousBCBor
 }
 
 void
 nsTableColFrame::Reflow(nsPresContext*          aPresContext,
                                   nsHTMLReflowMetrics&     aDesiredSize,
                                   const nsHTMLReflowState& aReflowState,
                                   nsReflowStatus&          aStatus)
 {
+  MarkInReflow();
   DO_GLOBAL_REFLOW_COUNT("nsTableColFrame");
   DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
   aDesiredSize.ClearSize();
   const nsStyleVisibility* colVis = StyleVisibility();
   bool collapseCol = (NS_STYLE_VISIBILITY_COLLAPSE == colVis->mVisible);
   if (collapseCol) {
     nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
     tableFrame->SetNeedToCollapse(true);
--- a/layout/tables/nsTableColGroupFrame.cpp
+++ b/layout/tables/nsTableColGroupFrame.cpp
@@ -345,20 +345,21 @@ nsTableColGroupFrame::GetLogicalSkipSide
   if (nullptr != GetNextInFlow()) {
     skip |= eLogicalSideBitsBEnd;
   }
   return skip;
 }
 
 void
 nsTableColGroupFrame::Reflow(nsPresContext*          aPresContext,
-                                       nsHTMLReflowMetrics&     aDesiredSize,
-                                       const nsHTMLReflowState& aReflowState,
-                                       nsReflowStatus&          aStatus)
+                             nsHTMLReflowMetrics&     aDesiredSize,
+                             const nsHTMLReflowState& aReflowState,
+                             nsReflowStatus&          aStatus)
 {
+  MarkInReflow();
   DO_GLOBAL_REFLOW_COUNT("nsTableColGroupFrame");
   DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
   NS_ASSERTION(nullptr!=mContent, "bad state -- null content for frame");
   
   const nsStyleVisibility* groupVis = StyleVisibility();
   bool collapseGroup = (NS_STYLE_VISIBILITY_COLLAPSE == groupVis->mVisible);
   if (collapseGroup) {
     nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
--- a/layout/tables/nsTableFrame.cpp
+++ b/layout/tables/nsTableFrame.cpp
@@ -1773,20 +1773,21 @@ nsTableFrame::RequestSpecialHeightReflow
  * part of nsIFrame::Reflow.  It should then call nsIFrame::Reflow on
  * the contents of the cells to do the necessary vertical resizing.
  *
  ******************************************************************************************/
 
 /* Layout the entire inner table. */
 void
 nsTableFrame::Reflow(nsPresContext*           aPresContext,
-                               nsHTMLReflowMetrics&     aDesiredSize,
-                               const nsHTMLReflowState& aReflowState,
-                               nsReflowStatus&          aStatus)
-{
+                     nsHTMLReflowMetrics&     aDesiredSize,
+                     const nsHTMLReflowState& aReflowState,
+                     nsReflowStatus&          aStatus)
+{
+  MarkInReflow();
   DO_GLOBAL_REFLOW_COUNT("nsTableFrame");
   DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
   bool isPaginated = aPresContext->IsPaginated();
 
   aStatus = NS_FRAME_COMPLETE;
   if (!GetPrevInFlow() && !mTableLayoutStrategy) {
     NS_ASSERTION(false, "strategy should have been created in Init");
     return;
--- a/layout/tables/nsTableOuterFrame.cpp
+++ b/layout/tables/nsTableOuterFrame.cpp
@@ -781,16 +781,17 @@ nsTableOuterFrame::UpdateReflowMetrics(u
 }
 
 void
 nsTableOuterFrame::Reflow(nsPresContext*           aPresContext,
                                     nsHTMLReflowMetrics&     aDesiredSize,
                                     const nsHTMLReflowState& aOuterRS,
                                     nsReflowStatus&          aStatus)
 {
+  MarkInReflow();
   DO_GLOBAL_REFLOW_COUNT("nsTableOuterFrame");
   DISPLAY_REFLOW(aPresContext, this, aOuterRS, aDesiredSize, aStatus);
 
   uint8_t captionSide = GetCaptionSide();
 
   // Initialize out parameters
   aDesiredSize.ClearSize();
   aStatus = NS_FRAME_COMPLETE;
--- a/layout/tables/nsTableRowFrame.cpp
+++ b/layout/tables/nsTableRowFrame.cpp
@@ -1032,16 +1032,17 @@ nsTableRowFrame::ReflowChildren(nsPresCo
   * This method stacks cells horizontally according to HTML 4.0 rules.
   */
 void
 nsTableRowFrame::Reflow(nsPresContext*          aPresContext,
                         nsHTMLReflowMetrics&     aDesiredSize,
                         const nsHTMLReflowState& aReflowState,
                         nsReflowStatus&          aStatus)
 {
+  MarkInReflow();
   DO_GLOBAL_REFLOW_COUNT("nsTableRowFrame");
   DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
 
   nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
   const nsStyleVisibility* rowVis = StyleVisibility();
   bool collapseRow = (NS_STYLE_VISIBILITY_COLLAPSE == rowVis->mVisible);
   if (collapseRow) {
     tableFrame->SetNeedToCollapse(true);
--- a/layout/tables/nsTableRowGroupFrame.cpp
+++ b/layout/tables/nsTableRowGroupFrame.cpp
@@ -1277,16 +1277,17 @@ nsTableRowGroupFrame::SplitRowGroup(nsPr
   * Rows are responsible for layout of their children.
   */
 void
 nsTableRowGroupFrame::Reflow(nsPresContext*           aPresContext,
                              nsHTMLReflowMetrics&     aDesiredSize,
                              const nsHTMLReflowState& aReflowState,
                              nsReflowStatus&          aStatus)
 {
+  MarkInReflow();
   DO_GLOBAL_REFLOW_COUNT("nsTableRowGroupFrame");
   DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
 
   aStatus     = NS_FRAME_COMPLETE;
 
   // Row geometry may be going to change so we need to invalidate any row cursor.
   ClearRowCursor();
 
--- a/layout/xul/nsBoxFrame.cpp
+++ b/layout/xul/nsBoxFrame.cpp
@@ -624,16 +624,17 @@ nsBoxFrame::GetPrefISize(nsRenderingCont
 }
 
 void
 nsBoxFrame::Reflow(nsPresContext*          aPresContext,
                    nsHTMLReflowMetrics&     aDesiredSize,
                    const nsHTMLReflowState& aReflowState,
                    nsReflowStatus&          aStatus)
 {
+  MarkInReflow();
   // If you make changes to this method, please keep nsLeafBoxFrame::Reflow
   // in sync, if the changes are applicable there.
 
   DO_GLOBAL_REFLOW_COUNT("nsBoxFrame");
   DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
 
   NS_ASSERTION(aReflowState.ComputedWidth() >=0 &&
                aReflowState.ComputedHeight() >= 0, "Computed Size < 0");
--- a/layout/xul/nsButtonBoxFrame.cpp
+++ b/layout/xul/nsButtonBoxFrame.cpp
@@ -17,29 +17,83 @@
 #include "mozilla/dom/Element.h"
 #include "mozilla/EventStateManager.h"
 #include "mozilla/EventStates.h"
 #include "mozilla/MouseEvents.h"
 #include "mozilla/TextEvents.h"
 
 using namespace mozilla;
 
+
+NS_IMPL_ISUPPORTS(nsButtonBoxFrame::nsButtonBoxListener, nsIDOMEventListener)
+
+nsresult
+nsButtonBoxFrame::nsButtonBoxListener::HandleEvent(nsIDOMEvent* aEvent)
+{
+  if (!mButtonBoxFrame) {
+    return NS_OK;
+  }
+
+  nsAutoString eventType;
+  aEvent->GetType(eventType);
+
+  if (eventType.EqualsLiteral("blur")) {
+    mButtonBoxFrame->Blurred();
+    return NS_OK;
+  }
+
+  NS_ABORT();
+
+  return NS_OK;
+}
+
 //
 // NS_NewXULButtonFrame
 //
 // Creates a new Button frame and returns it
 //
 nsIFrame*
 NS_NewButtonBoxFrame (nsIPresShell* aPresShell, nsStyleContext* aContext)
 {
   return new (aPresShell) nsButtonBoxFrame(aContext);
 }
 
 NS_IMPL_FRAMEARENA_HELPERS(nsButtonBoxFrame)
 
+nsButtonBoxFrame::nsButtonBoxFrame(nsStyleContext* aContext) :
+  nsBoxFrame(aContext, false),
+  mButtonBoxListener(nullptr),
+  mIsHandlingKeyEvent(false)
+{
+  UpdateMouseThrough();
+}
+
+void
+nsButtonBoxFrame::Init(nsIContent*       aContent,
+                       nsContainerFrame* aParent,
+                       nsIFrame*         aPrevInFlow)
+{
+  nsBoxFrame::Init(aContent, aParent, aPrevInFlow);
+
+  mButtonBoxListener = new nsButtonBoxListener(this);
+
+  mContent->AddSystemEventListener(NS_LITERAL_STRING("blur"), mButtonBoxListener, false);
+}
+
+void
+nsButtonBoxFrame::DestroyFrom(nsIFrame* aDestructRoot)
+{
+  mContent->RemoveSystemEventListener(NS_LITERAL_STRING("blur"), mButtonBoxListener, false);
+
+  mButtonBoxListener->mButtonBoxFrame = nullptr;
+  mButtonBoxListener = nullptr;
+
+  nsBoxFrame::DestroyFrom(aDestructRoot);
+}
+
 void
 nsButtonBoxFrame::BuildDisplayListForChildren(nsDisplayListBuilder*   aBuilder,
                                               const nsRect&           aDirtyRect,
                                               const nsDisplayListSet& aLists)
 {
   // override, since we don't want children to get events
   if (aBuilder->IsForEventDelivery())
     return;
@@ -62,16 +116,17 @@ nsButtonBoxFrame::HandleEvent(nsPresCont
       if (!keyEvent) {
         break;
       }
       if (NS_VK_SPACE == keyEvent->keyCode) {
         EventStateManager* esm = aPresContext->EventStateManager();
         // :hover:active state
         esm->SetContentState(mContent, NS_EVENT_STATE_HOVER);
         esm->SetContentState(mContent, NS_EVENT_STATE_ACTIVE);
+        mIsHandlingKeyEvent = true;
       }
       break;
     }
 
 // On mac, Return fires the default button, not the focused one.
 #ifndef XP_MACOSX
     case NS_KEY_PRESS: {
       WidgetKeyboardEvent* keyEvent = aEvent->AsKeyboardEvent();
@@ -90,16 +145,17 @@ nsButtonBoxFrame::HandleEvent(nsPresCont
 #endif
 
     case NS_KEY_UP: {
       WidgetKeyboardEvent* keyEvent = aEvent->AsKeyboardEvent();
       if (!keyEvent) {
         break;
       }
       if (NS_VK_SPACE == keyEvent->keyCode) {
+        mIsHandlingKeyEvent = false;
         // only activate on keyup if we're already in the :hover:active state
         NS_ASSERTION(mContent->IsElement(), "How do we have a non-element?");
         EventStates buttonState = mContent->AsElement()->State();
         if (buttonState.HasAllStates(NS_EVENT_STATE_ACTIVE |
                                      NS_EVENT_STATE_HOVER)) {
           // return to normal state
           EventStateManager* esm = aPresContext->EventStateManager();
           esm->SetContentState(nullptr, NS_EVENT_STATE_ACTIVE);
@@ -117,17 +173,33 @@ nsButtonBoxFrame::HandleEvent(nsPresCont
       }
       break;
     }
   }
 
   return nsBoxFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
 }
 
-void 
+void
+nsButtonBoxFrame::Blurred()
+{
+  NS_ASSERTION(mContent->IsElement(), "How do we have a non-element?");
+  EventStates buttonState = mContent->AsElement()->State();
+  if (mIsHandlingKeyEvent &&
+      buttonState.HasAllStates(NS_EVENT_STATE_ACTIVE |
+                               NS_EVENT_STATE_HOVER)) {
+    // return to normal state
+    EventStateManager* esm = PresContext()->EventStateManager();
+    esm->SetContentState(nullptr, NS_EVENT_STATE_ACTIVE);
+    esm->SetContentState(nullptr, NS_EVENT_STATE_HOVER);
+  }
+  mIsHandlingKeyEvent = false;
+}
+
+void
 nsButtonBoxFrame::DoMouseClick(WidgetGUIEvent* aEvent, bool aTrustEvent)
 {
   // Don't execute if we're disabled.
   if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::disabled,
                             nsGkAtoms::_true, eCaseMatters))
     return;
 
   // Execute the oncommand event handler.
--- a/layout/xul/nsButtonBoxFrame.h
+++ b/layout/xul/nsButtonBoxFrame.h
@@ -1,49 +1,76 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 #ifndef nsButtonBoxFrame_h___
 #define nsButtonBoxFrame_h___
 
 #include "mozilla/Attributes.h"
+#include "nsIDOMEventListener.h"
 #include "nsBoxFrame.h"
 
 class nsButtonBoxFrame : public nsBoxFrame
 {
 public:
   NS_DECL_FRAMEARENA_HELPERS
 
   friend nsIFrame* NS_NewButtonBoxFrame(nsIPresShell* aPresShell);
 
-  explicit nsButtonBoxFrame(nsStyleContext* aContext)
-    :nsBoxFrame(aContext, false) {
-    UpdateMouseThrough();
-  }
+  explicit nsButtonBoxFrame(nsStyleContext* aContext);
+
+  virtual void Init(nsIContent*       aContent,
+                    nsContainerFrame* aParent,
+                    nsIFrame*         aPrevInFlow) override;
 
   virtual void BuildDisplayListForChildren(nsDisplayListBuilder*   aBuilder,
                                            const nsRect&           aDirtyRect,
                                            const nsDisplayListSet& aLists) override;
 
+  virtual void DestroyFrom(nsIFrame* aDestructRoot) override;
+
   virtual nsresult HandleEvent(nsPresContext* aPresContext, 
                                mozilla::WidgetGUIEvent* aEvent,
                                nsEventStatus* aEventStatus) override;
 
   virtual void MouseClicked(nsPresContext* aPresContext,
                             mozilla::WidgetGUIEvent* aEvent)
   { DoMouseClick(aEvent, false); }
 
+  void Blurred();
+
 #ifdef DEBUG_FRAME_DUMP
   virtual nsresult GetFrameName(nsAString& aResult) const override {
     return MakeFrameName(NS_LITERAL_STRING("ButtonBoxFrame"), aResult);
   }
 #endif
 
   /**
    * Our implementation of MouseClicked. 
    * @param aTrustEvent if true and aEvent as null, then assume the event was trusted
    */
   void DoMouseClick(mozilla::WidgetGUIEvent* aEvent, bool aTrustEvent);
   void UpdateMouseThrough() override { AddStateBits(NS_FRAME_MOUSE_THROUGH_NEVER); }
+
+private:
+  class nsButtonBoxListener final : public nsIDOMEventListener
+  {
+  public:
+    explicit nsButtonBoxListener(nsButtonBoxFrame* aButtonBoxFrame) :
+      mButtonBoxFrame(aButtonBoxFrame)
+      { }
+
+    NS_IMETHOD HandleEvent(nsIDOMEvent* aEvent) override;
+
+    NS_DECL_ISUPPORTS
+
+  private:
+    friend class nsButtonBoxFrame;
+    virtual ~nsButtonBoxListener() { }
+    nsButtonBoxFrame* mButtonBoxFrame;
+  };
+
+  nsRefPtr<nsButtonBoxListener> mButtonBoxListener;
+  bool mIsHandlingKeyEvent;
 }; // class nsButtonBoxFrame
 
 #endif /* nsButtonBoxFrame_h___ */
--- a/layout/xul/nsLeafBoxFrame.cpp
+++ b/layout/xul/nsLeafBoxFrame.cpp
@@ -196,16 +196,17 @@ nsLeafBoxFrame::Reflow(nsPresContext*   
                      const nsHTMLReflowState& aReflowState,
                      nsReflowStatus&          aStatus)
 {
   // This is mostly a copy of nsBoxFrame::Reflow().
   // We aren't able to share an implementation because of the frame
   // class hierarchy.  If you make changes here, please keep
   // nsBoxFrame::Reflow in sync.
 
+  MarkInReflow();
   DO_GLOBAL_REFLOW_COUNT("nsLeafBoxFrame");
   DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
 
   NS_ASSERTION(aReflowState.ComputedWidth() >=0 &&
                aReflowState.ComputedHeight() >= 0, "Computed Size < 0");
 
 #ifdef DO_NOISY_REFLOW
   printf("\n-------------Starting LeafBoxFrame Reflow ----------------------------\n");
--- a/security/manager/ssl/src/nsNSSModule.cpp
+++ b/security/manager/ssl/src/nsNSSModule.cpp
@@ -164,17 +164,17 @@ static nsresult                         
                                                                               \
     return rv;                                                                \
 }
 
 NS_NSS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nssLoadingComponent, nsNSSComponent,
                                         Init)
 
 using namespace mozilla::psm;
-  
+
 namespace {
 
 // Use the special factory constructor for everything this module implements,
 // because all code could potentially require the NSS library.
 // Our factory constructor takes an additional boolean parameter.
 // Only for the nsNSSComponent, set this to true.
 // All other classes must have this set to false.
 
@@ -194,19 +194,19 @@ NS_NSS_GENERIC_FACTORY_CONSTRUCTOR_BYPRO
                                              nsNSSCertListFakeTransport)
 #ifdef MOZ_XUL
 NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEnsure, nsCertTree)
 #endif
 NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEnsure, nsPkcs11)
 NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEnsure, nsCertPicker)
 NS_NSS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nssEnsure, nsNTLMAuthModule, InitTest)
 NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEnsureChromeOrContent, nsCryptoHash)
-NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEnsure, nsCryptoHMAC)
-NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEnsure, nsKeyObject)
-NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEnsure, nsKeyObjectFactory)
+NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEnsureChromeOrContent, nsCryptoHMAC)
+NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEnsureChromeOrContent, nsKeyObject)
+NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEnsureChromeOrContent, nsKeyObjectFactory)
 NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEnsure, nsDataSignatureVerifier)
 NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEnsure, nsRandomGenerator)
 NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEnsureOnChromeOnly, nsSSLStatus)
 NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEnsureOnChromeOnly, TransportSecurityInfo)
 
 typedef mozilla::psm::NSSErrorsService NSSErrorsService;
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(NSSErrorsService, Init)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsNSSVersion)
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -579,39 +579,16 @@ CanShowProfileManager()
 {
 #if defined(XP_WIN)
   return XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Desktop;
 #else
   return true;
 #endif
 }
 
-static bool
-KeyboardMayHaveIME()
-{
-#ifdef XP_WIN
-  // http://msdn.microsoft.com/en-us/library/windows/desktop/dd318693%28v=vs.85%29.aspx
-  HKL locales[10];
-  int result = GetKeyboardLayoutList(10, locales);
-  for (int i = 0; i < result; i++) {
-    int kb = (uintptr_t)locales[i] & 0xFFFF;
-    if (kb == 0x0411 ||  // japanese
-        kb == 0x0412 ||  // korean
-        kb == 0x0C04 ||  // HK Chinese
-        kb == 0x0804 || kb == 0x0004 || // Hans Chinese
-        kb == 0x7C04 || kb ==  0x0404)  { //Hant Chinese
-
-      return true;
-    }
-  }
-#endif
-
-  return false;
-}
-
 bool gSafeMode = false;
 
 /**
  * The nsXULAppInfo object implements nsIFactory so that it can be its own
  * singleton.
  */
 class nsXULAppInfo : public nsIXULAppInfo,
 #ifdef NIGHTLY_BUILD
@@ -883,23 +860,16 @@ nsXULAppInfo::GetAccessibilityEnabled(bo
   *aResult = GetAccService() != nullptr;
 #else
   *aResult = false;
 #endif
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsXULAppInfo::GetKeyboardMayHaveIME(bool* aResult)
-{
-  *aResult = KeyboardMayHaveIME();
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 nsXULAppInfo::GetAccessibilityIsUIA(bool* aResult)
 {
   *aResult = false;
 #if defined(ACCESSIBILITY) && defined(XP_WIN)
   // This is the same check the a11y service does to identify uia clients.
   if (GetAccService() != nullptr &&
       (::GetModuleHandleW(L"uiautomation") ||
        ::GetModuleHandleW(L"uiautomationcore"))) {
@@ -4652,26 +4622,21 @@ mozilla::BrowserTabsRemoteAutostart()
   bool testPref = Preferences::GetBool("layers.offmainthreadcomposition.testing.enabled", false);
   if (testPref && optInPref) {
     gBrowserTabsRemoteAutostart = true;
   }
 #else
   // Nightly builds, update gBrowserTabsRemoteAutostart based on all the
   // e10s remote relayed prefs we watch.
   bool disabledForA11y = Preferences::GetBool("browser.tabs.remote.autostart.disabled-because-using-a11y", false);
-  // Only disable for IME for the automatic pref, not the opt-in one.
-  bool disabledForIME = trialPref && KeyboardMayHaveIME();
-
   if (prefEnabled) {
     if (gSafeMode) {
       LogE10sBlockedReason("Safe mode");
     } else if (disabledForA11y) {
       LogE10sBlockedReason("An accessibility tool is or was active. See bug 1115956.");
-    } else if (disabledForIME) {
-      LogE10sBlockedReason("The keyboard being used has activated IME");
     } else {
       gBrowserTabsRemoteAutostart = true;
     }
   }
 #endif
 
 #if defined(XP_MACOSX)
   // If for any reason we suspect acceleration will be disabled, disabled
--- a/widget/windows/nsTextStore.cpp
+++ b/widget/windows/nsTextStore.cpp
@@ -13,16 +13,17 @@
 #ifdef MOZ_METRO
 #include "winrt/MetroWidget.h"
 #endif
 #include "nsPrintfCString.h"
 #include "WinUtils.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/TextEvents.h"
 #include "mozilla/WindowsVersion.h"
+#include "nsIXULRuntime.h"
 
 #define INPUTSCOPE_INIT_GUID
 #define TEXTATTRS_INIT_GUID
 #include "nsTextStore.h"
 
 using namespace mozilla;
 using namespace mozilla::widget;
 
@@ -4618,17 +4619,18 @@ nsTextStore::Initialize()
   if (sThreadMgr) {
     PR_LOG(sTextStoreLog, PR_LOG_ERROR,
       ("TSF:   nsTextStore::Initialize() FAILED due to already initialized"));
     return;
   }
 
   bool enableTsf =
     Preferences::GetBool(kPrefNameForceEnableTSF, false) ||
-    (IsVistaOrLater() && Preferences::GetBool(kPrefNameEnableTSF, false));
+    (IsVistaOrLater() && Preferences::GetBool(kPrefNameEnableTSF, false) &&
+     !BrowserTabsRemoteAutostart());
   PR_LOG(sTextStoreLog, PR_LOG_ALWAYS,
     ("TSF:   nsTextStore::Initialize(), TSF is %s",
      enableTsf ? "enabled" : "disabled"));
   if (!enableTsf) {
     return;
   }
 
   // XXX MSDN documents that ITfInputProcessorProfiles is available only on
--- a/xpcom/system/nsIXULRuntime.idl
+++ b/xpcom/system/nsIXULRuntime.idl
@@ -18,17 +18,17 @@ bool BrowserTabsRemoteAutostart();
 
 /**
  * Provides information about the XUL runtime.
  * @status UNSTABLE - This interface is not frozen and will probably change in
  *                    future releases. If you need this functionality to be
  *                    stable/frozen, please contact Benjamin Smedberg.
  */
 
-[scriptable, uuid(5754b56e-f392-426d-aec0-3ba7c49aff32)]
+[scriptable, uuid(c4cd11c4-6e8e-49da-85a8-dad3b7605bc3)]
 interface nsIXULRuntime : nsISupports
 {
   /**
    * Whether the application was launched in safe mode.
    */
   readonly attribute boolean inSafeMode;
 
   /**
@@ -92,23 +92,16 @@ interface nsIXULRuntime : nsISupports
   readonly attribute boolean browserTabsRemoteAutostart;
 
   /**
    * If true, the accessibility service is running.
    */
   readonly attribute boolean accessibilityEnabled;
 
   /**
-   * This returns a very rough approximation of whether IME is likely
-   * to be used for the browser session. DO NOT USE! This is temporary
-   * and will be removed.
-   */
-  readonly attribute boolean keyboardMayHaveIME;
-
-  /**
    * Indicates if the active accessibility client is UIA.
    * DO NOT USE! This is temporary and will be removed.
    */
   readonly attribute boolean accessibilityIsUIA;
 
   /**
    * Indicates whether the current Firefox build is 64-bit.
    */