Merge m-c to autoland
authorPhil Ringnalda <philringnalda@gmail.com>
Thu, 06 Oct 2016 20:24:09 -0700
changeset 359722 7affb66131bb766fab016f554ab1d0b97da72d41
parent 359721 e4333cfe24b97169233450f9010474464eeaff85 (current diff)
parent 359705 4b9944879c9a60a9aba4a744a7401bc38e0f39c4 (diff)
child 359723 04bbd8b53f9ddd08b0580e5f8428dfff9aa39f09
push id6795
push userjlund@mozilla.com
push dateMon, 23 Jan 2017 14:19:46 +0000
treeherdermozilla-beta@76101b503191 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone52.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to autoland
accessible/windows/msaa/ChildIDThunk.cpp
accessible/windows/msaa/ChildIDThunk.h
dom/canvas/test/test_setlinedash.html
layout/base/nsBidi.cpp
new file mode 100644
--- /dev/null
+++ b/accessible/interfaces/msaa/AccessibleMarshal.rc
@@ -0,0 +1,5 @@
+/* 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/. */
+
+1 typelib ISimpleDOMNode.tlb
--- a/accessible/interfaces/msaa/moz.build
+++ b/accessible/interfaces/msaa/moz.build
@@ -21,15 +21,21 @@ DEFINES['REGISTER_PROXY_DLL'] = True
 DEFFILE = SRCDIR + '/AccessibleMarshal.def'
 
 OS_LIBS += [
     'kernel32',
     'rpcrt4',
     'oleaut32',
 ]
 
+GENERATED_FILES += [
+    'ISimpleDOMNode.tlb',
+]
+
+RCINCLUDE = 'AccessibleMarshal.rc'
+
 # The Windows MIDL code generator creates things like:
 #
 #   #endif !_MIDL_USE_GUIDDEF_
 #
 # which clang-cl complains about.  MSVC doesn't, so turn this warning off.
 if CONFIG['CLANG_CL']:
     CFLAGS += ['-Wno-extra-tokens']
deleted file mode 100644
--- a/accessible/windows/msaa/ChildIDThunk.cpp
+++ /dev/null
@@ -1,282 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "ChildIDThunk.h"
-#include "MainThreadUtils.h"
-#include "mozilla/mscom/InterceptorLog.h"
-
-using namespace mozilla::mscom;
-
-namespace mozilla {
-namespace a11y {
-
-ChildIDThunk::ChildIDThunk()
-  : mRefCnt(0)
-{
-}
-
-HRESULT
-ChildIDThunk::QueryInterface(REFIID riid, void** ppv)
-{
-  if (!ppv) {
-    return E_INVALIDARG;
-  }
-
-  if (riid == IID_IUnknown || riid == IID_ICallFrameEvents ||
-      riid == IID_IInterceptorSink) {
-    *ppv = static_cast<IInterceptorSink*>(this);
-    AddRef();
-    return S_OK;
-  }
-
-  *ppv = nullptr;
-  return E_NOINTERFACE;
-}
-
-ULONG
-ChildIDThunk::AddRef()
-{
-  return (ULONG) InterlockedIncrement((LONG*)&mRefCnt);
-}
-
-ULONG
-ChildIDThunk::Release()
-{
-  ULONG newRefCnt = (ULONG) InterlockedDecrement((LONG*)&mRefCnt);
-  if (newRefCnt == 0) {
-    MOZ_ASSERT(NS_IsMainThread());
-    delete this;
-  }
-  return newRefCnt;
-}
-
-enum IAccessibleVTableIndex
-{
-  eget_accName = 10,
-  eget_accValue = 11,
-  eget_accDescription = 12,
-  eget_accRole = 13,
-  eget_accState = 14,
-  eget_accHelp = 15,
-  eget_accHelpTopic = 16,
-  eget_accKeyboardShortcut = 17,
-  eget_accDefaultAction = 20,
-  eaccSelect = 21,
-  eaccLocation = 22,
-  eaccNavigate = 23,
-  eaccDoDefaultAction = 25,
-  eput_accName = 26,
-  eput_accValue = 27
-};
-
-Maybe<ULONG>
-ChildIDThunk::IsMethodInteresting(const ULONG aMethodIdx)
-{
-  switch (aMethodIdx) {
-    case eget_accName:
-    case eget_accValue:
-    case eget_accDescription:
-    case eget_accRole:
-    case eget_accState:
-    case eget_accHelp:
-    case eget_accKeyboardShortcut:
-    case eget_accDefaultAction:
-    case eaccDoDefaultAction:
-    case eput_accName:
-    case eput_accValue:
-      return Some(0UL);
-    case eget_accHelpTopic:
-    case eaccNavigate:
-      return Some(1UL);
-    case eaccSelect:
-      return Some(2UL);
-    case eaccLocation:
-      return Some(4UL);
-    default:
-      return Nothing();
-  }
-}
-
-bool
-ChildIDThunk::IsChildIDSelf(ICallFrame* aFrame, const ULONG aChildIDIdx,
-                            LONG& aOutChildID)
-{
-  VARIANT paramValue;
-  HRESULT hr = aFrame->GetParam(aChildIDIdx, &paramValue);
-  MOZ_ASSERT(SUCCEEDED(hr));
-  if (FAILED(hr)) {
-    return false;
-  }
-
-  const bool isVariant = paramValue.vt & VT_VARIANT;
-  MOZ_ASSERT(isVariant);
-  if (!isVariant) {
-    return false;
-  }
-
-  VARIANT& childID = *(paramValue.pvarVal);
-  if (childID.vt != VT_I4) {
-    return false;
-  }
-
-  aOutChildID = childID.lVal;
-  return childID.lVal == CHILDID_SELF;
-}
-
-/**
- * ICallFrame::SetParam always returns E_NOTIMPL, so we'll just have to work
- * around this by manually poking at the parameter's location on the stack.
- */
-/* static */ HRESULT
-ChildIDThunk::SetChildIDParam(ICallFrame* aFrame, const ULONG aParamIdx,
-                              const LONG aChildID)
-{
-  void* stackBase = aFrame->GetStackLocation();
-  MOZ_ASSERT(stackBase);
-  if (!stackBase) {
-    return E_UNEXPECTED;
-  }
-
-  CALLFRAMEPARAMINFO paramInfo;
-  HRESULT hr = aFrame->GetParamInfo(aParamIdx, &paramInfo);
-  MOZ_ASSERT(SUCCEEDED(hr));
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  // We expect this childID to be a VARIANT in-parameter
-  MOZ_ASSERT(paramInfo.fIn);
-  MOZ_ASSERT(paramInfo.cbParam == sizeof(VARIANT));
-  if (!paramInfo.fIn || paramInfo.cbParam != sizeof(VARIANT)) {
-    return E_UNEXPECTED;
-  }
-
-  // Given the stack base and param offset, calculate the address of the param
-  // and replace its value
-  VARIANT* pInParam = reinterpret_cast<VARIANT*>(
-      reinterpret_cast<PBYTE>(stackBase) + paramInfo.stackOffset);
-  MOZ_ASSERT(pInParam->vt == VT_I4);
-  if (pInParam->vt != VT_I4) {
-    return E_UNEXPECTED;
-  }
-  pInParam->lVal = aChildID;
-  return S_OK;
-}
-
-HRESULT
-ChildIDThunk::ResolveTargetInterface(REFIID aIid, IUnknown** aOut)
-{
-  MOZ_ASSERT(aOut);
-  *aOut = nullptr;
-
-  RefPtr<IInterceptor> interceptor;
-  HRESULT hr = mInterceptor->Resolve(IID_IInterceptor,
-                                     (void**)getter_AddRefs(interceptor));
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  InterceptorTargetPtr target;
-  hr = interceptor->GetTargetForIID(aIid, target);
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  RefPtr<IUnknown> refTarget = target.get();
-  refTarget.forget(aOut);
-  return S_OK;
-}
-
-HRESULT
-ChildIDThunk::OnCall(ICallFrame* aFrame)
-{
-#if defined(MOZILLA_INTERNAL_API)
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(XRE_IsParentProcess());
-#endif
-
-  IID iid;
-  ULONG method;
-  HRESULT hr = aFrame->GetIIDAndMethod(&iid, &method);
-  MOZ_ASSERT(SUCCEEDED(hr));
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  RefPtr<IUnknown> target;
-  hr = ResolveTargetInterface(iid, getter_AddRefs(target));
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  Maybe<ULONG> childIDIdx;
-  LONG childID;
-  if (iid != IID_IAccessible ||
-      (childIDIdx = IsMethodInteresting(method)).isNothing() ||
-      IsChildIDSelf(aFrame, childIDIdx.value(), childID)) {
-    // We're only interested in methods which accept a child ID parameter which
-    // is not equal to CHILDID_SELF. Otherwise we just invoke against the
-    // original target interface.
-    hr = aFrame->Invoke(target);
-    if (SUCCEEDED(hr)) {
-      InterceptorLog::Event(aFrame, target);
-    }
-    return hr;
-  }
-
-  // Otherwise we need to resolve the child node...
-  RefPtr<IAccessible> acc;
-  hr = target->QueryInterface(iid, (void**)getter_AddRefs(acc));
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  VARIANT vChildID;
-  VariantInit(&vChildID);
-  vChildID.vt = VT_I4;
-  vChildID.lVal = childID;
-
-  RefPtr<IDispatch> disp;
-  hr = acc->get_accChild(vChildID, getter_AddRefs(disp));
-  if (FAILED(hr)) {
-    aFrame->SetReturnValue(hr);
-    return S_OK;
-  }
-
-  RefPtr<IAccessible> directAccessible;
-  // Yes, given our implementation we could just downcast, but that's not very COMy
-  hr = disp->QueryInterface(IID_IAccessible,
-                            (void**)getter_AddRefs(directAccessible));
-  if (FAILED(hr)) {
-    aFrame->SetReturnValue(hr);
-    return S_OK;
-  }
-
-  // Now that we have the IAccessible for the child ID we need to replace it
-  // in the activation record with a self-referant child ID
-  hr = SetChildIDParam(aFrame, childIDIdx.value(), CHILDID_SELF);
-  MOZ_ASSERT(SUCCEEDED(hr));
-  if (FAILED(hr)) {
-    return hr;
-  }
-
-  hr = aFrame->Invoke(directAccessible);
-  if (SUCCEEDED(hr)) {
-    InterceptorLog::Event(aFrame, directAccessible);
-  }
-  return hr;
-}
-
-HRESULT
-ChildIDThunk::SetInterceptor(IWeakReference* aInterceptor)
-{
-  mInterceptor = aInterceptor;
-  return S_OK;
-}
-
-} // namespace a11y
-} // namespace mozilla
-
deleted file mode 100644
--- a/accessible/windows/msaa/ChildIDThunk.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef mozilla_a11y_ChildIDThunk_h
-#define mozilla_a11y_ChildIDThunk_h
-
-#include "mozilla/mscom/Interceptor.h"
-#include "mozilla/Maybe.h"
-#include "mozilla/RefPtr.h"
-
-#include <oleacc.h>
-#include <callobj.h>
-
-namespace mozilla {
-namespace a11y {
-
-class ChildIDThunk : public mozilla::mscom::IInterceptorSink
-{
-public:
-  ChildIDThunk();
-
-  // IUnknown
-  STDMETHODIMP QueryInterface(REFIID riid, void** ppv) override;
-  STDMETHODIMP_(ULONG) AddRef() override;
-  STDMETHODIMP_(ULONG) Release() override;
-
-  // ICallFrameEvents
-  STDMETHODIMP OnCall(ICallFrame* aFrame) override;
-
-  // IInterceptorSink
-  STDMETHODIMP SetInterceptor(mozilla::mscom::IWeakReference* aInterceptor) override;
-
-private:
-  HRESULT ResolveTargetInterface(REFIID aIid, IUnknown** aOut);
-
-  static mozilla::Maybe<ULONG> IsMethodInteresting(const ULONG aMethodIdx);
-  static bool IsChildIDSelf(ICallFrame* aFrame, const ULONG aChildIdIdx,
-                            LONG& aOutChildID);
-  static HRESULT SetChildIDParam(ICallFrame* aFrame, const ULONG aParamIdx,
-                                 const LONG aChildID);
-
-private:
-  ULONG mRefCnt;
-  RefPtr<mozilla::mscom::IWeakReference> mInterceptor;
-};
-
-} // namespace a11y
-} // namespace mozilla
-
-#endif // mozilla_a11y_ChildIDThunk_h
-
--- a/accessible/windows/msaa/RootAccessibleWrap.cpp
+++ b/accessible/windows/msaa/RootAccessibleWrap.cpp
@@ -1,92 +1,35 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "RootAccessibleWrap.h"
 
-#include "ChildIDThunk.h"
 #include "Compatibility.h"
-#include "mozilla/mscom/interceptor.h"
 #include "nsCoreUtils.h"
-#include "nsIXULRuntime.h"
 #include "nsWinUtils.h"
 
 using namespace mozilla::a11y;
-using namespace mozilla::mscom;
 
 ////////////////////////////////////////////////////////////////////////////////
 // Constructor/destructor
 
 RootAccessibleWrap::
   RootAccessibleWrap(nsIDocument* aDocument, nsIPresShell* aPresShell) :
   RootAccessible(aDocument, aPresShell)
 {
 }
 
 RootAccessibleWrap::~RootAccessibleWrap()
 {
 }
 
 ////////////////////////////////////////////////////////////////////////////////
-// Accessible
-
-void
-RootAccessibleWrap::GetNativeInterface(void** aOutAccessible)
-{
-  MOZ_ASSERT(aOutAccessible);
-  if (!aOutAccessible) {
-    return;
-  }
-
-  if (mInterceptor &&
-      SUCCEEDED(mInterceptor->Resolve(IID_IAccessible, aOutAccessible))) {
-    return;
-  }
-
-  *aOutAccessible = nullptr;
-
-  RefPtr<IAccessible> rootAccessible;
-  RootAccessible::GetNativeInterface((void**)getter_AddRefs(rootAccessible));
-
-  if (!mozilla::BrowserTabsRemoteAutostart() || XRE_IsContentProcess()) {
-    // We only need to wrap this interface if our process is non-content e10s
-    rootAccessible.forget(aOutAccessible);
-    return;
-  }
-
-  // Otherwise, we need to wrap that IAccessible with an interceptor
-  RefPtr<IInterceptorSink> eventSink(MakeAndAddRef<ChildIDThunk>());
-
-  RefPtr<IAccessible> interceptor;
-  HRESULT hr = CreateInterceptor<IAccessible>(
-      STAUniquePtr<IAccessible>(rootAccessible.forget().take()), eventSink,
-      getter_AddRefs(interceptor));
-  if (FAILED(hr)) {
-    return;
-  }
-
-  RefPtr<IWeakReferenceSource> weakRefSrc;
-  hr = interceptor->QueryInterface(IID_IWeakReferenceSource,
-                                   (void**)getter_AddRefs(weakRefSrc));
-  if (FAILED(hr)) {
-    return;
-  }
-
-  hr = weakRefSrc->GetWeakReference(getter_AddRefs(mInterceptor));
-  if (FAILED(hr)) {
-    return;
-  }
-
-  interceptor.forget(aOutAccessible);
-}
-
-////////////////////////////////////////////////////////////////////////////////
 // RootAccessible
 
 void
 RootAccessibleWrap::DocumentActivated(DocAccessible* aDocument)
 {
   if (Compatibility::IsDolphin() &&
       nsCoreUtils::IsTabDocument(aDocument->DocumentNode())) {
     uint32_t count = mChildDocuments.Length();
--- a/accessible/windows/msaa/RootAccessibleWrap.h
+++ b/accessible/windows/msaa/RootAccessibleWrap.h
@@ -4,36 +4,24 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_a11y_RootAccessibleWrap_h__
 #define mozilla_a11y_RootAccessibleWrap_h__
 
 #include "RootAccessible.h"
 
 namespace mozilla {
-namespace mscom {
-
-struct IWeakReference;
-
-} // namespace mscom
-
 namespace a11y {
 
 class RootAccessibleWrap : public RootAccessible
 {
 public:
   RootAccessibleWrap(nsIDocument* aDocument, nsIPresShell* aPresShell);
   virtual ~RootAccessibleWrap();
 
-  // Accessible
-  virtual void GetNativeInterface(void** aOutAccessible) override;
-
   // RootAccessible
   virtual void DocumentActivated(DocAccessible* aDocument);
-
-private:
-  RefPtr<mscom::IWeakReference> mInterceptor;
 };
 
 } // namespace a11y
 } // namespace mozilla
 
 #endif
--- a/accessible/windows/msaa/moz.build
+++ b/accessible/windows/msaa/moz.build
@@ -15,17 +15,16 @@ EXPORTS.mozilla.a11y += [
     'IDSet.h',
     'MsaaIdGenerator.h',
 ]
 
 UNIFIED_SOURCES += [
     'AccessibleWrap.cpp',
     'ApplicationAccessibleWrap.cpp',
     'ARIAGridAccessibleWrap.cpp',
-    'ChildIDThunk.cpp',
     'Compatibility.cpp',
     'DocAccessibleWrap.cpp',
     'EnumVariant.cpp',
     'HTMLTableAccessibleWrap.cpp',
     'HTMLWin32ObjectAccessible.cpp',
     'HyperTextAccessibleWrap.cpp',
     'ImageAccessibleWrap.cpp',
     'IUnknownImpl.cpp',
--- a/browser/base/content/browser-fullScreenAndPointerLock.js
+++ b/browser/base/content/browser-fullScreenAndPointerLock.js
@@ -380,17 +380,17 @@ var FullScreen = {
   receiveMessage: function(aMessage) {
     let browser = aMessage.target;
     switch (aMessage.name) {
       case "DOMFullscreen:Request": {
         this._windowUtils.remoteFrameFullscreenChanged(browser);
         break;
       }
       case "DOMFullscreen:NewOrigin": {
-        // Don't show the warning if we've already exit fullscreen.
+        // Don't show the warning if we've already exited fullscreen.
         if (document.fullscreen) {
           PointerlockFsWarning.showFullScreen(aMessage.data.originNoSuffix);
         }
         break;
       }
       case "DOMFullscreen:Exit": {
         this._windowUtils.remoteFrameFullscreenReverted();
         break;
--- a/browser/components/customizableui/CustomizeMode.jsm
+++ b/browser/components/customizableui/CustomizeMode.jsm
@@ -144,22 +144,22 @@ CustomizeMode.prototype = {
     }
     if (this._customizing) {
       this.exit();
     } else {
       this.enter();
     }
   },
 
-  swatchForTheme: function(aDocument) {
-   let lwthemeButton = aDocument.getElementById("customization-lwtheme-button");
-   let lwthemeIcon = aDocument.getAnonymousElementByAttribute(lwthemeButton,
-          "class", "button-icon");
-   lwthemeIcon.style.backgroundImage = LightweightThemeManager.currentTheme ?
-     "url(" + LightweightThemeManager.currentTheme.iconURL + ")" : "";
+  _updateLWThemeButtonIcon: function() {
+    let lwthemeButton = this.document.getElementById("customization-lwtheme-button");
+    let lwthemeIcon = this.document.getAnonymousElementByAttribute(lwthemeButton,
+                        "class", "button-icon");
+    lwthemeIcon.style.backgroundImage = LightweightThemeManager.currentTheme ?
+      "url(" + LightweightThemeManager.currentTheme.iconURL + ")" : "";
   },
 
   setTab: function(aTab) {
     if (gTab == aTab) {
       return;
     }
 
     if (gTab) {
@@ -351,17 +351,17 @@ CustomizeMode.prototype = {
       window.setTimeout(() => {
         // Force layout reflow to ensure the animation runs,
         // and make it async so it doesn't affect the timing.
         this.visiblePalette.clientTop;
         this.visiblePalette.setAttribute("showing", "true");
       }, 0);
       this._updateEmptyPaletteNotice();
 
-      this.swatchForTheme(document);
+      this._updateLWThemeButtonIcon();
       this.maybeShowTip(panelHolder);
 
       this._handler.isEnteringCustomizeMode = false;
       panelContents.removeAttribute("customize-transitioning");
 
       CustomizableUI.dispatchToolboxEvent("customizationready", {}, window);
       this._enableOutlinesTimeout = window.setTimeout(() => {
         this.document.getElementById("nav-bar").setAttribute("showoutline", "true");
@@ -1170,17 +1170,17 @@ CustomizeMode.prototype = {
     btn.disabled = true;
     return Task.spawn(function*() {
       this._removePanelCustomizationPlaceholders();
       yield this.depopulatePalette();
       yield this._unwrapToolbarItems();
 
       CustomizableUI.reset();
 
-      this.swatchForTheme(this.document);
+      this._updateLWThemeButtonIcon();
 
       yield this._wrapToolbarItems();
       this.populatePalette();
 
       this.persistCurrentSets(true);
 
       this._updateResetButton();
       this._updateUndoResetButton();
@@ -1198,17 +1198,17 @@ CustomizeMode.prototype = {
 
     return Task.spawn(function*() {
       this._removePanelCustomizationPlaceholders();
       yield this.depopulatePalette();
       yield this._unwrapToolbarItems();
 
       CustomizableUI.undoReset();
 
-      this.swatchForTheme(this.document);
+      this._updateLWThemeButtonIcon();
 
       yield this._wrapToolbarItems();
       this.populatePalette();
 
       this.persistCurrentSets(true);
 
       this._updateResetButton();
       this._updateUndoResetButton();
@@ -1340,27 +1340,33 @@ CustomizeMode.prototype = {
     let getMoreURL = Services.urlFormatter.formatURLPref("lightweightThemes.getMoreURL");
     this.window.openUILinkIn(getMoreURL, "tab");
   },
 
   onLWThemesMenuShowing: function(aEvent) {
     const DEFAULT_THEME_ID = "{972ce4c6-7e08-4474-a285-3208198ce6fd}";
     const RECENT_LWT_COUNT = 5;
 
-    this.resetLWThemesMenu(aEvent.target);
+    this._clearLWThemesMenu(aEvent.target);
 
     function previewTheme(aEvent) {
       LightweightThemeManager.previewTheme(aEvent.target.theme.id != DEFAULT_THEME_ID ?
                                            aEvent.target.theme : null);
     }
 
     function resetPreview() {
       LightweightThemeManager.resetPreview();
     }
 
+    let onThemeSelected = panel => {
+      this._updateLWThemeButtonIcon();
+      this._onUIChange();
+      panel.hidePopup();
+    };
+
     AddonManager.getAddonByID(DEFAULT_THEME_ID, function(aDefaultTheme) {
       let doc = this.window.document;
 
       function buildToolbarButton(aTheme) {
         let tbb = doc.createElement("toolbarbutton");
         tbb.theme = aTheme;
         tbb.setAttribute("label", aTheme.name);
         if (aDefaultTheme == aTheme) {
@@ -1398,70 +1404,66 @@ CustomizeMode.prototype = {
         themes.push(lwt);
       }
 
       let footer = doc.getElementById("customization-lwtheme-menu-footer");
       let panel = footer.parentNode;
       let themesInMyThemesSection = 0;
       let recommendedLabel = doc.getElementById("customization-lwtheme-menu-recommended");
       for (let theme of themes) {
-        let tbb = buildToolbarButton(theme);
-        tbb.addEventListener("command", function() {
-          if ("userDisabled" in this.theme)
-            this.theme.userDisabled = false;
+        let button = buildToolbarButton(theme);
+        button.addEventListener("command", () => {
+          if ("userDisabled" in button.theme)
+            button.theme.userDisabled = false;
           else
-            LightweightThemeManager.currentTheme = this.theme;
-          this.parentNode.hidePopup();
+            LightweightThemeManager.currentTheme = button.theme;
+          onThemeSelected(panel);
         });
-        panel.insertBefore(tbb, recommendedLabel);
+        panel.insertBefore(button, recommendedLabel);
         themesInMyThemesSection++;
       }
 
       let lwthemePrefs = Services.prefs.getBranch("lightweightThemes.");
       let recommendedThemes = lwthemePrefs.getComplexValue("recommendedThemes",
                                                            Ci.nsISupportsString).data;
       recommendedThemes = JSON.parse(recommendedThemes);
       let sb = Services.strings.createBundle("chrome://browser/locale/lightweightThemes.properties");
       for (let theme of recommendedThemes) {
         theme.name = sb.GetStringFromName("lightweightThemes." + theme.id + ".name");
         theme.description = sb.GetStringFromName("lightweightThemes." + theme.id + ".description");
-        let tbb = buildToolbarButton(theme);
-        tbb.addEventListener("command", function() {
-          LightweightThemeManager.setLocalTheme(this.theme);
-          recommendedThemes = recommendedThemes.filter((aTheme) => { return aTheme.id != this.theme.id; });
+        let button = buildToolbarButton(theme);
+        button.addEventListener("command", () => {
+          LightweightThemeManager.setLocalTheme(button.theme);
+          recommendedThemes = recommendedThemes.filter((aTheme) => { return aTheme.id != button.theme.id; });
           let string = Cc["@mozilla.org/supports-string;1"]
                          .createInstance(Ci.nsISupportsString);
           string.data = JSON.stringify(recommendedThemes);
           lwthemePrefs.setComplexValue("recommendedThemes",
                                        Ci.nsISupportsString, string);
-          this.parentNode.hidePopup();
+          onThemeSelected(panel);
         });
-        panel.insertBefore(tbb, footer);
+        panel.insertBefore(button, footer);
       }
       let hideRecommendedLabel = (footer.previousSibling == recommendedLabel);
       recommendedLabel.hidden = hideRecommendedLabel;
     }.bind(this));
   },
 
-  resetLWThemesMenu: function(target) {
-    let doc = target.ownerDocument;
-    let footer = doc.getElementById("customization-lwtheme-menu-footer");
-    let recommendedLabel = doc.getElementById("customization-lwtheme-menu-recommended");
-    this.swatchForTheme(doc);
+  _clearLWThemesMenu: function(panel) {
+    let footer = this.document.getElementById("customization-lwtheme-menu-footer");
+    let recommendedLabel = this.document.getElementById("customization-lwtheme-menu-recommended");
     for (let element of [footer, recommendedLabel]) {
       while (element.previousSibling &&
              element.previousSibling.localName == "toolbarbutton") {
         element.previousSibling.remove();
       }
     }
-    target.removeAttribute("height");
 
-    if (LightweightThemeManager.currentTheme) {
-      this._onUIChange();
-    }
+    // Workaround for bug 1059934
+    panel.removeAttribute("height");
   },
 
   _onUIChange: function() {
     this._changed = true;
     if (!this.resetting) {
       this._updateResetButton();
       this._updateUndoResetButton();
       this._updateEmptyPaletteNotice();
--- a/build/clang-plugin/clang-plugin.cpp
+++ b/build/clang-plugin/clang-plugin.cpp
@@ -171,16 +171,38 @@ private:
     virtual void run(const MatchFinder::MatchResult &Result);
   };
 
   class SprintfLiteralChecker : public MatchFinder::MatchCallback {
   public:
     virtual void run(const MatchFinder::MatchResult &Result);
   };
 
+  class OverrideBaseCallChecker : public MatchFinder::MatchCallback {
+  public:
+    virtual void run(const MatchFinder::MatchResult &Result);
+  private:
+    void evaluateExpression(const Stmt *StmtExpr,
+        std::list<const CXXMethodDecl*> &MethodList);
+    void getRequiredBaseMethod(const CXXMethodDecl* Method,
+        std::list<const CXXMethodDecl*>& MethodsList);
+    void findBaseMethodCall(const CXXMethodDecl* Method,
+        std::list<const CXXMethodDecl*>& MethodsList);
+    bool isRequiredBaseMethod(const CXXMethodDecl *Method);
+  };
+
+/*
+ *  This is a companion checker for OverrideBaseCallChecker that rejects
+ *  the usage of MOZ_REQUIRED_BASE_METHOD on non-virtual base methods.
+ */
+  class OverrideBaseCallUsageChecker : public MatchFinder::MatchCallback {
+  public:
+    virtual void run(const MatchFinder::MatchResult &Result);
+  };
+
   ScopeChecker Scope;
   ArithmeticArgChecker ArithmeticArg;
   TrivialCtorDtorChecker TrivialCtorDtor;
   NaNExprChecker NaNExpr;
   NoAddRefReleaseOnReturnChecker NoAddRefReleaseOnReturn;
   RefCountedInsideLambdaChecker RefCountedInsideLambda;
   ExplicitOperatorBoolChecker ExplicitOperatorBool;
   NoDuplicateRefCntMemberChecker NoDuplicateRefCntMember;
@@ -189,16 +211,18 @@ private:
   NonMemMovableMemberChecker NonMemMovableMember;
   ExplicitImplicitChecker ExplicitImplicit;
   NoAutoTypeChecker NoAutoType;
   NoExplicitMoveConstructorChecker NoExplicitMoveConstructor;
   RefCountedCopyConstructorChecker RefCountedCopyConstructor;
   AssertAssignmentChecker AssertAttribution;
   KungFuDeathGripChecker KungFuDeathGrip;
   SprintfLiteralChecker SprintfLiteral;
+  OverrideBaseCallChecker OverrideBaseCall;
+  OverrideBaseCallUsageChecker OverrideBaseCallUsage;
   MatchFinder AstMatcher;
 };
 
 namespace {
 
 std::string getDeclarationNamespace(const Decl *Declaration) {
   const DeclContext *DC =
       Declaration->getDeclContext()->getEnclosingNamespaceContext();
@@ -923,16 +947,34 @@ AST_MATCHER(CallExpr, isSnprintfLikeFunc
 
 AST_MATCHER(CXXRecordDecl, isLambdaDecl) {
   return Node.isLambda();
 }
 
 AST_MATCHER(QualType, isRefPtr) {
   return typeIsRefPtr(Node);
 }
+
+AST_MATCHER(CXXRecordDecl, hasBaseClasses) {
+  const CXXRecordDecl *Decl = Node.getCanonicalDecl();
+
+  // Must have definition and should inherit other classes
+  return Decl && Decl->hasDefinition() && Decl->getNumBases();
+}
+
+AST_MATCHER(CXXMethodDecl, isRequiredBaseMethod) {
+  const CXXMethodDecl *Decl = Node.getCanonicalDecl();
+  return Decl
+      && MozChecker::hasCustomAnnotation(Decl, "moz_required_base_method");
+}
+
+AST_MATCHER(CXXMethodDecl, isNonVirtual) {
+  const CXXMethodDecl *Decl = Node.getCanonicalDecl();
+  return Decl && !Decl->isVirtual();
+}
 }
 }
 
 namespace {
 
 void CustomTypeAnnotation::dumpAnnotationReason(DiagnosticsEngine &Diag,
                                                 QualType T,
                                                 SourceLocation Loc) {
@@ -1261,16 +1303,23 @@ DiagnosticsMatcher::DiagnosticsMatcher()
         allOf(hasArgument(0, ignoringParenImpCasts(declRefExpr().bind("buffer"))),
                              anyOf(hasArgument(1, sizeOfExpr(hasIgnoringParenImpCasts(declRefExpr().bind("size")))),
                                    hasArgument(1, integerLiteral().bind("immediate")),
                                    hasArgument(1, declRefExpr(to(varDecl(hasType(isConstQualified()),
                                                                          hasInitializer(integerLiteral().bind("constant")))))))))
         .bind("funcCall"),
       &SprintfLiteral
   );
+
+  AstMatcher.addMatcher(cxxRecordDecl(hasBaseClasses()).bind("class"),
+      &OverrideBaseCall);
+
+  AstMatcher.addMatcher(
+      cxxMethodDecl(isNonVirtual(), isRequiredBaseMethod()).bind("method"),
+      &OverrideBaseCallUsage);
 }
 
 // These enum variants determine whether an allocation has occured in the code.
 enum AllocationVariety {
   AV_None,
   AV_Global,
   AV_Automatic,
   AV_Temporary,
@@ -1955,16 +2004,126 @@ void DiagnosticsMatcher::SprintfLiteralC
 
     if (Type->getSize().ule(Literal->getValue())) {
       Diag.Report(D->getLocStart(), ErrorID) << Name << Replacement;
       Diag.Report(D->getLocStart(), NoteID) << Name;
     }
   }
 }
 
+bool DiagnosticsMatcher::OverrideBaseCallChecker::isRequiredBaseMethod(
+    const CXXMethodDecl *Method) {
+  return MozChecker::hasCustomAnnotation(Method, "moz_required_base_method");
+}
+
+void DiagnosticsMatcher::OverrideBaseCallChecker::evaluateExpression(
+    const Stmt *StmtExpr, std::list<const CXXMethodDecl*> &MethodList) {
+  // Continue while we have methods in our list
+  if (!MethodList.size()) {
+    return;
+  }
+
+  if (auto MemberFuncCall = dyn_cast<CXXMemberCallExpr>(StmtExpr)) {
+    if (auto Method = dyn_cast<CXXMethodDecl>(
+        MemberFuncCall->getDirectCallee())) {
+      findBaseMethodCall(Method, MethodList);
+    }
+  }
+
+  for (auto S : StmtExpr->children()) {
+    if (S) {
+      evaluateExpression(S, MethodList);
+    }
+  }
+}
+
+void DiagnosticsMatcher::OverrideBaseCallChecker::getRequiredBaseMethod(
+    const CXXMethodDecl *Method,
+    std::list<const CXXMethodDecl*>& MethodsList) {
+
+  if (isRequiredBaseMethod(Method)) {
+    MethodsList.push_back(Method);
+  } else {
+    // Loop through all it's base methods.
+    for (auto BaseMethod = Method->begin_overridden_methods();
+        BaseMethod != Method->end_overridden_methods(); BaseMethod++) {
+      getRequiredBaseMethod(*BaseMethod, MethodsList);
+    }
+  }
+}
+
+void DiagnosticsMatcher::OverrideBaseCallChecker::findBaseMethodCall(
+    const CXXMethodDecl* Method,
+    std::list<const CXXMethodDecl*>& MethodsList) {
+
+  MethodsList.remove(Method);
+  // Loop also through all it's base methods;
+  for (auto BaseMethod = Method->begin_overridden_methods();
+      BaseMethod != Method->end_overridden_methods(); BaseMethod++) {
+    findBaseMethodCall(*BaseMethod, MethodsList);
+  }
+}
+
+void DiagnosticsMatcher::OverrideBaseCallChecker::run(
+    const MatchFinder::MatchResult &Result) {
+  DiagnosticsEngine &Diag = Result.Context->getDiagnostics();
+  unsigned OverrideBaseCallCheckID = Diag.getDiagnosticIDs()->getCustomDiagID(
+      DiagnosticIDs::Error,
+      "Method %0 must be called in all overrides, but is not called in "
+      "this override defined for class %1");
+  const CXXRecordDecl *Decl = Result.Nodes.getNodeAs<CXXRecordDecl>("class");
+
+  // Loop through the methods and look for the ones that are overridden.
+  for (auto Method : Decl->methods()) {
+    // If this method doesn't override other methods or it doesn't have a body,
+    // continue to the next declaration.
+    if (!Method->size_overridden_methods() || !Method->hasBody()) {
+      continue;
+    }
+
+    // Preferred the usage of list instead of vector in order to avoid
+    // calling erase-remove when deleting items
+    std::list<const CXXMethodDecl*> MethodsList;
+    // For each overridden method push it to a list if it meets our
+    // criteria
+    for (auto BaseMethod = Method->begin_overridden_methods();
+        BaseMethod != Method->end_overridden_methods(); BaseMethod++) {
+      getRequiredBaseMethod(*BaseMethod, MethodsList);
+    }
+
+    // If no method has been found then no annotation was used
+    // so checking is not needed
+    if (!MethodsList.size()) {
+      continue;
+    }
+
+    // Loop through the body of our method and search for calls to
+    // base methods
+    evaluateExpression(Method->getBody(), MethodsList);
+
+    // If list is not empty pop up errors
+    for (auto BaseMethod : MethodsList) {
+      Diag.Report(Method->getLocation(), OverrideBaseCallCheckID)
+          << BaseMethod->getQualifiedNameAsString()
+          << Decl->getName();
+    }
+  }
+}
+
+void DiagnosticsMatcher::OverrideBaseCallUsageChecker::run(
+    const MatchFinder::MatchResult &Result) {
+  DiagnosticsEngine &Diag = Result.Context->getDiagnostics();
+  unsigned ErrorID = Diag.getDiagnosticIDs()->getCustomDiagID(
+      DiagnosticIDs::Error,
+      "MOZ_REQUIRED_BASE_METHOD can be used only on virtual methods");
+  const CXXMethodDecl *Method = Result.Nodes.getNodeAs<CXXMethodDecl>("method");
+
+  Diag.Report(Method->getLocation(), ErrorID);
+}
+
 class MozCheckAction : public PluginASTAction {
 public:
   ASTConsumerPtr CreateASTConsumer(CompilerInstance &CI,
                                    StringRef FileName) override {
 #if CLANG_VERSION_FULL >= 306
     std::unique_ptr<MozChecker> Checker(llvm::make_unique<MozChecker>(CI));
     ASTConsumerPtr Other(Checker->getOtherConsumer());
 
new file mode 100644
--- /dev/null
+++ b/build/clang-plugin/tests/TestOverrideBaseCall.cpp
@@ -0,0 +1,175 @@
+#define MOZ_REQUIRED_BASE_METHOD __attribute__((annotate("moz_required_base_method")))
+
+class Base {
+public:
+  virtual void fo() MOZ_REQUIRED_BASE_METHOD {
+  }
+
+  virtual int foRet() MOZ_REQUIRED_BASE_METHOD {
+    return 0;
+  }
+};
+
+class BaseOne : public Base {
+public:
+  virtual void fo() MOZ_REQUIRED_BASE_METHOD {
+    Base::fo();
+  }
+};
+
+class BaseSecond : public Base {
+public:
+  virtual void fo() MOZ_REQUIRED_BASE_METHOD {
+   Base::fo();
+  }
+};
+
+class Deriv : public BaseOne, public BaseSecond {
+public:
+  void func() {
+  }
+
+  void fo() {
+    func();
+    BaseSecond::fo();
+    BaseOne::fo();
+  }
+};
+
+class DerivSimple : public Base {
+public:
+  void fo() { // expected-error {{Method Base::fo must be called in all overrides, but is not called in this override defined for class DerivSimple}}
+  }
+};
+
+class BaseVirtualOne : public virtual Base {
+};
+
+class BaseVirtualSecond: public virtual Base {
+};
+
+class DerivVirtual : public BaseVirtualOne, public BaseVirtualSecond {
+public:
+  void fo() {
+    Base::fo();
+  }
+};
+
+class DerivIf : public Base {
+public:
+  void fo() {
+    if (true) {
+      Base::fo();
+    }
+  }
+};
+
+class DerivIfElse : public Base {
+public:
+  void fo() {
+    if (true) {
+      Base::fo();
+    } else {
+      Base::fo();
+    }
+  }
+};
+
+class DerivFor : public Base {
+public:
+  void fo() {
+    for (int i = 0; i < 10; i++) {
+      Base::fo();
+    }
+  }
+};
+
+class DerivDoWhile : public Base {
+public:
+  void fo() {
+    do {
+      Base::fo();
+    } while(false);
+  }
+};
+
+class DerivWhile : public Base {
+public:
+  void fo() {
+    while (true) {
+      Base::fo();
+      break;
+    }
+  }
+};
+
+class DerivAssignment : public Base {
+public:
+  int foRet() {
+    return foRet();
+  }
+};
+
+class BaseOperator {
+private:
+  int value;
+public:
+  BaseOperator() : value(0) {
+  }
+  virtual BaseOperator& operator++() MOZ_REQUIRED_BASE_METHOD {
+    value++;
+    return *this;
+  }
+};
+
+class DerivOperatorErr : public BaseOperator {
+private:
+  int value;
+public:
+  DerivOperatorErr() : value(0) {
+  }
+  DerivOperatorErr& operator++() { // expected-error {{Method BaseOperator::operator++ must be called in all overrides, but is not called in this override defined for class DerivOperatorErr}}
+    value++;
+    return *this;
+  }
+};
+
+class DerivOperator : public BaseOperator {
+private:
+  int value;
+public:
+  DerivOperator() : value(0) {
+  }
+  DerivOperator& operator++() {
+    BaseOperator::operator++();
+    value++;
+    return *this;
+  }
+};
+
+class DerivPrime : public Base {
+public:
+  void fo() {
+    Base::fo();
+  }
+};
+
+class DerivSecondErr : public DerivPrime {
+public:
+  void fo() { // expected-error {{Method Base::fo must be called in all overrides, but is not called in this override defined for class DerivSecondErr}}
+  }
+};
+
+class DerivSecond : public DerivPrime {
+public:
+  void fo() {
+    Base::fo();
+  }
+};
+
+class DerivSecondIndirect : public DerivPrime {
+public:
+  void fo() {
+    DerivPrime::fo();
+  }
+};
new file mode 100644
--- /dev/null
+++ b/build/clang-plugin/tests/TestOverrideBaseCallAnnotation.cpp
@@ -0,0 +1,47 @@
+#define MOZ_REQUIRED_BASE_METHOD __attribute__((annotate("moz_required_base_method")))
+
+class Base {
+public:
+  virtual void fo() MOZ_REQUIRED_BASE_METHOD {
+  }
+};
+
+class BaseNonVirtual {
+public:
+  void fo() MOZ_REQUIRED_BASE_METHOD { // expected-error {{MOZ_REQUIRED_BASE_METHOD can be used only on virtual methods}}
+  }
+};
+
+class Deriv : public BaseNonVirtual {
+public:
+  virtual void fo() MOZ_REQUIRED_BASE_METHOD {
+  }
+};
+
+class DerivVirtual : public Base {
+public:
+  void fo() MOZ_REQUIRED_BASE_METHOD {
+    Base::fo();
+  }
+};
+
+class BaseOperator {
+public:
+  BaseOperator& operator++() MOZ_REQUIRED_BASE_METHOD { // expected-error {{MOZ_REQUIRED_BASE_METHOD can be used only on virtual methods}}
+    return *this;
+  }
+};
+
+class DerivOperator : public BaseOperator {
+public:
+  virtual DerivOperator& operator++() {
+    return *this;
+  }
+};
+
+class DerivPrimeOperator : public DerivOperator {
+public:
+  DerivPrimeOperator& operator++() {
+    return *this;
+  }
+};
--- a/build/clang-plugin/tests/moz.build
+++ b/build/clang-plugin/tests/moz.build
@@ -27,16 +27,18 @@ SOURCES += [
     'TestNoAutoType.cpp',
     'TestNoDuplicateRefCntMember.cpp',
     'TestNoExplicitMoveConstructor.cpp',
     'TestNonHeapClass.cpp',
     'TestNonMemMovable.cpp',
     'TestNonMemMovableStd.cpp',
     'TestNonTemporaryClass.cpp',
     'TestNoRefcountedInsideLambdas.cpp',
+    'TestOverrideBaseCall.cpp',
+    'TestOverrideBaseCallAnnotation.cpp',
     'TestRefCountedCopyConstructor.cpp',
     'TestSprintfLiteral.cpp',
     'TestStackClass.cpp',
     'TestTrivialCtorDtor.cpp',
 ]
 
 DISABLE_STL_WRAPPING = True
 NO_VISIBILITY_FLAGS = True
--- a/devtools/client/debugger/new/test/mochitest/browser.ini
+++ b/devtools/client/debugger/new/test/mochitest/browser.ini
@@ -1,11 +1,12 @@
 [DEFAULT]
 tags = devtools
 subsuite = devtools
+skip-if = (os == 'linux' && debug && bits == 32)
 support-files =
   head.js
   !/devtools/client/commandline/test/helpers.js
   !/devtools/client/framework/test/shared-head.js
   examples/bundle.js
   examples/bundle.js.map
   examples/doc-scripts.html
   examples/doc-script-switching.html
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -10804,21 +10804,26 @@ nsDocShell::DoURILoad(nsIURI* aURI,
       // null, then the element got removed from the DOM while we were trying
       // to load this resource. This docshell is scheduled for destruction
       // already, so bail out here.
       return NS_OK;
     }
   }
 
   // Getting the right triggeringPrincipal needs to be updated and is only
-  // ready for use once bug 1182569 landed.
-  // Until then, we cannot rely on the triggeringPrincipal for TYPE_DOCUMENT
-  // or TYPE_SUBDOCUMENT loads.  Notice the triggeringPrincipal falls back to
-  // systemPrincipal below.
+  // ready for use once bug 1182569 landed. Until then, we cannot rely on
+  // the triggeringPrincipal for TYPE_DOCUMENT loads. Please note that the
+  // triggeringPrincipal falls back to the systemPrincipal below.
   nsCOMPtr<nsIPrincipal> triggeringPrincipal = aTriggeringPrincipal;
+
+  // Make sure that we always get a non null triggeringPrincipal for
+  // loads of type TYPE_SUBDOCUMENT.
+  MOZ_ASSERT(aContentPolicyType != nsIContentPolicy::TYPE_SUBDOCUMENT ||
+             triggeringPrincipal, "Need a valid triggeringPrincipal");
+
   if (!triggeringPrincipal) {
     if (aReferrerURI) {
       rv = CreatePrincipalFromReferrer(aReferrerURI,
                                        getter_AddRefs(triggeringPrincipal));
       NS_ENSURE_SUCCESS(rv, rv);
     } else {
       triggeringPrincipal = nsContentUtils::GetSystemPrincipal();
     }
--- a/dom/base/nsHostObjectProtocolHandler.cpp
+++ b/dom/base/nsHostObjectProtocolHandler.cpp
@@ -21,16 +21,18 @@
 #include "nsContentUtils.h"
 #include "nsError.h"
 #include "nsHostObjectURI.h"
 #include "nsIMemoryReporter.h"
 #include "nsIPrincipal.h"
 #include "nsIUUIDGenerator.h"
 #include "nsNetUtil.h"
 
+#define RELEASING_TIMER 1000
+
 using mozilla::DOMMediaStream;
 using mozilla::dom::BlobImpl;
 using mozilla::dom::MediaSource;
 using mozilla::ErrorResult;
 using mozilla::net::LoadInfo;
 
 // -----------------------------------------------------------------------
 // Hash table
@@ -63,16 +65,19 @@ struct DataInfo
   ObjectType mObjectType;
 
   RefPtr<BlobImpl> mBlobImpl;
   RefPtr<DOMMediaStream> mMediaStream;
   RefPtr<MediaSource> mMediaSource;
 
   nsCOMPtr<nsIPrincipal> mPrincipal;
   nsCString mStack;
+
+  // WeakReferences of nsHostObjectURI objects.
+  nsTArray<nsWeakPtr> mURIs;
 };
 
 static nsClassHashtable<nsCStringHashKey, DataInfo>* gDataTable;
 
 static DataInfo*
 GetDataInfo(const nsACString& aUri)
 {
   if (!gDataTable) {
@@ -97,16 +102,17 @@ GetDataInfo(const nsACString& aUri)
   if (pos < 0) {
     gDataTable->Get(aUri, &res);
   } else {
     gDataTable->Get(StringHead(aUri, pos), &res);
   }
 
   return res;
 }
+
 static DataInfo*
 GetDataInfoFromURI(nsIURI* aURI)
 {
   if (!aURI) {
     return nullptr;
   }
 
   nsCString spec;
@@ -412,16 +418,62 @@ class BlobURLsReporter final : public ns
     } else {
       path += url;
     }
   }
 };
 
 NS_IMPL_ISUPPORTS(BlobURLsReporter, nsIMemoryReporter)
 
+class ReleasingTimerHolder final : public nsITimerCallback
+{
+public:
+  NS_DECL_ISUPPORTS
+
+  static void
+  Create(nsTArray<nsWeakPtr>&& aArray)
+  {
+    RefPtr<ReleasingTimerHolder> holder = new ReleasingTimerHolder(Move(aArray));
+    holder->mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
+    if (NS_WARN_IF(!holder->mTimer)) {
+      return;
+    }
+
+    nsresult rv = holder->mTimer->InitWithCallback(holder, RELEASING_TIMER,
+                                                   nsITimer::TYPE_ONE_SHOT);
+    NS_ENSURE_SUCCESS_VOID(rv);
+  }
+
+  NS_IMETHOD
+  Notify(nsITimer* aTimer) override
+  {
+    for (uint32_t i = 0; i < mURIs.Length(); ++i) {
+      nsCOMPtr<nsIURI> uri = do_QueryReferent(mURIs[i]);
+      if (uri) {
+        static_cast<nsHostObjectURI*>(uri.get())->ForgetBlobImpl();
+      }
+    }
+
+    return NS_OK;
+  }
+
+private:
+  explicit ReleasingTimerHolder(nsTArray<nsWeakPtr>&& aArray)
+    : mURIs(aArray)
+  {}
+
+  ~ReleasingTimerHolder()
+  {}
+
+  nsTArray<nsWeakPtr> mURIs;
+  nsCOMPtr<nsITimer> mTimer;
+};
+
+NS_IMPL_ISUPPORTS(ReleasingTimerHolder, nsITimerCallback)
+
 } // namespace mozilla
 
 template<typename T>
 static nsresult
 AddDataEntryInternal(const nsACString& aURI, T aObject,
                      nsIPrincipal* aPrincipal)
 {
   if (!gDataTable) {
@@ -538,41 +590,45 @@ nsHostObjectProtocolHandler::GetAllBlobU
     aRegistrations.AppendElement(mozilla::dom::BlobURLRegistrationData(
       nsCString(iter.Key()), blobParent, nullptr,
       IPC::Principal(info->mPrincipal)));
   }
 
   return true;
 }
 
-void
+/*static */ void
 nsHostObjectProtocolHandler::RemoveDataEntry(const nsACString& aUri,
                                              bool aBroadcastToOtherProcesses)
 {
   if (!gDataTable) {
     return;
   }
 
   DataInfo* info = GetDataInfo(aUri);
   if (!info) {
     return;
   }
 
   if (aBroadcastToOtherProcesses && info->mObjectType == DataInfo::eBlobImpl) {
     mozilla::BroadcastBlobURLUnregistration(aUri, info);
   }
 
+  if (!info->mURIs.IsEmpty()) {
+    ReleasingTimerHolder::Create(Move(info->mURIs));
+  }
+
   gDataTable->Remove(aUri);
   if (gDataTable->Count() == 0) {
     delete gDataTable;
     gDataTable = nullptr;
   }
 }
 
-void
+/* static */ void
 nsHostObjectProtocolHandler::RemoveDataEntries()
 {
   MOZ_ASSERT(XRE_IsContentProcess());
 
   if (!gDataTable) {
     return;
   }
 
@@ -582,17 +638,17 @@ nsHostObjectProtocolHandler::RemoveDataE
 }
 
 /* static */ bool
 nsHostObjectProtocolHandler::HasDataEntry(const nsACString& aUri)
 {
   return !!GetDataInfo(aUri);
 }
 
-nsresult
+/* static */ nsresult
 nsHostObjectProtocolHandler::GenerateURIString(const nsACString &aScheme,
                                                nsIPrincipal* aPrincipal,
                                                nsACString& aUri)
 {
   nsresult rv;
   nsCOMPtr<nsIUUIDGenerator> uuidgen =
     do_GetService("@mozilla.org/uuid-generator;1", &rv);
   NS_ENSURE_SUCCESS(rv, rv);
@@ -618,41 +674,41 @@ nsHostObjectProtocolHandler::GenerateURI
     aUri.Append('/');
   }
 
   aUri += Substring(chars + 1, chars + NSID_LENGTH - 2);
 
   return NS_OK;
 }
 
-nsresult
+/* static */ nsresult
 nsHostObjectProtocolHandler::GenerateURIStringForBlobURL(nsIPrincipal* aPrincipal,
                                                          nsACString& aUri)
 {
   return
     GenerateURIString(NS_LITERAL_CSTRING(BLOBURI_SCHEME), aPrincipal, aUri);
 }
 
-nsIPrincipal*
+/* static */ nsIPrincipal*
 nsHostObjectProtocolHandler::GetDataEntryPrincipal(const nsACString& aUri)
 {
   if (!gDataTable) {
     return nullptr;
   }
 
   DataInfo* res = GetDataInfo(aUri);
 
   if (!res) {
     return nullptr;
   }
 
   return res->mPrincipal;
 }
 
-void
+/* static */ void
 nsHostObjectProtocolHandler::Traverse(const nsACString& aUri,
                                       nsCycleCollectionTraversalCallback& aCallback)
 {
   if (!gDataTable) {
     return;
   }
 
   DataInfo* res;
@@ -711,16 +767,20 @@ nsHostObjectProtocolHandler::NewURI(cons
   }
 
   rv = uri->SetSpec(aSpec);
   NS_ENSURE_SUCCESS(rv, rv);
 
   NS_TryToSetImmutable(uri);
   uri.forget(aResult);
 
+  if (info && info->mObjectType == DataInfo::eBlobImpl) {
+    info->mURIs.AppendElement(do_GetWeakReference(*aResult));
+  }
+
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsHostObjectProtocolHandler::NewChannel2(nsIURI* uri,
                                          nsILoadInfo* aLoadInfo,
                                          nsIChannel** result)
 {
--- a/dom/base/nsHostObjectProtocolHandler.h
+++ b/dom/base/nsHostObjectProtocolHandler.h
@@ -83,17 +83,17 @@ public:
   static bool
   GetAllBlobURLEntries(nsTArray<mozilla::dom::BlobURLRegistrationData>& aRegistrations,
                        mozilla::dom::ContentParent* aCP);
 
 protected:
   virtual ~nsHostObjectProtocolHandler() {}
 
 private:
-  static void Init(void);
+  static void Init();
 };
 
 class nsBlobProtocolHandler : public nsHostObjectProtocolHandler
 {
 public:
   NS_IMETHOD GetScheme(nsACString &result) override;
 };
 
--- a/dom/base/nsHostObjectURI.cpp
+++ b/dom/base/nsHostObjectURI.cpp
@@ -18,16 +18,17 @@ static NS_DEFINE_CID(kThisSimpleURIImple
                      NS_THIS_SIMPLEURI_IMPLEMENTATION_CID);
 
 NS_IMPL_ADDREF_INHERITED(nsHostObjectURI, mozilla::net::nsSimpleURI)
 NS_IMPL_RELEASE_INHERITED(nsHostObjectURI, mozilla::net::nsSimpleURI)
 
 NS_INTERFACE_MAP_BEGIN(nsHostObjectURI)
   NS_INTERFACE_MAP_ENTRY(nsIURIWithBlobImpl)
   NS_INTERFACE_MAP_ENTRY(nsIURIWithPrincipal)
+  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
   if (aIID.Equals(kHOSTOBJECTURICID))
     foundInterface = static_cast<nsIURI*>(this);
   else if (aIID.Equals(kThisSimpleURIImplementationCID)) {
     // Need to return explicitly here, because if we just set foundInterface
     // to null the NS_INTERFACE_MAP_END_INHERITING will end up calling into
     // nsSimplURI::QueryInterface and finding something for this CID.
     *aInstancePtr = nullptr;
     return NS_NOINTERFACE;
@@ -133,25 +134,30 @@ nsHostObjectURI::Deserialize(const mozil
   }
 
   const HostObjectURIParams& hostParams = aParams.get_HostObjectURIParams();
 
   if (!mozilla::net::nsSimpleURI::Deserialize(hostParams.simpleParams())) {
     return false;
   }
 
-  // XXXbaku: when we will have shared blobURL maps, we can populate mBlobImpl
-  // here asll well.
-
   if (hostParams.principal().type() == OptionalPrincipalInfo::Tvoid_t) {
     return true;
   }
 
   mPrincipal = PrincipalInfoToPrincipal(hostParams.principal().get_PrincipalInfo());
-  return mPrincipal != nullptr;
+  if (!mPrincipal) {
+    return false;
+  }
+
+  // If this fails, we still want to complete the operation. Probably this
+  // blobURL has been revoked in the meantime.
+  NS_GetBlobForBlobURI(this, getter_AddRefs(mBlobImpl));
+
+  return true;
 }
 
 NS_IMETHODIMP
 nsHostObjectURI::SetScheme(const nsACString& aScheme)
 {
   // Disallow setting the scheme, since that could cause us to be associated
   // with a different protocol handler that doesn't expect us to be carrying
   // around a principal with nsIURIWithPrincipal.
@@ -271,8 +277,15 @@ nsHostObjectURI::GetFlags(uint32_t *aFla
 }
 
 NS_IMETHODIMP 
 nsHostObjectURI::GetClassIDNoAlloc(nsCID *aClassIDNoAlloc)
 {
   *aClassIDNoAlloc = kHOSTOBJECTURICID;
   return NS_OK;
 }
+
+void
+nsHostObjectURI::ForgetBlobImpl()
+{
+  MOZ_ASSERT(mBlobImpl);
+  mBlobImpl = nullptr;
+}
--- a/dom/base/nsHostObjectURI.h
+++ b/dom/base/nsHostObjectURI.h
@@ -12,25 +12,28 @@
 #include "nsCOMPtr.h"
 #include "nsIClassInfo.h"
 #include "nsIPrincipal.h"
 #include "nsISerializable.h"
 #include "nsIURIWithBlobImpl.h"
 #include "nsIURIWithPrincipal.h"
 #include "nsSimpleURI.h"
 #include "nsIIPCSerializableURI.h"
+#include "nsWeakReference.h"
+
 
 /**
  * These URIs refer to host objects: Blobs, with scheme "blob",
  * MediaStreams, with scheme "mediastream", and MediaSources, with scheme
  * "mediasource".
  */
 class nsHostObjectURI : public mozilla::net::nsSimpleURI
                       , public nsIURIWithPrincipal
                       , public nsIURIWithBlobImpl
+                      , public nsSupportsWeakReference
 {
 public:
   nsHostObjectURI(nsIPrincipal* aPrincipal,
                   mozilla::dom::BlobImpl* aBlobImpl)
     : mozilla::net::nsSimpleURI()
     , mPrincipal(aPrincipal)
     , mBlobImpl(aBlobImpl)
   {}
@@ -59,16 +62,18 @@ public:
   virtual mozilla::net::nsSimpleURI* StartClone(RefHandlingEnum refHandlingMode,
                                                 const nsACString& newRef) override
   {
     nsHostObjectURI* url = new nsHostObjectURI();
     SetRefOnClone(url, refHandlingMode, newRef);
     return url;
   }
 
+  void ForgetBlobImpl();
+
   nsCOMPtr<nsIPrincipal> mPrincipal;
   RefPtr<mozilla::dom::BlobImpl> mBlobImpl;
 
 protected:
   virtual ~nsHostObjectURI() {}
 };
 
 #define NS_HOSTOBJECTURI_CID \
--- a/dom/base/test/browser.ini
+++ b/dom/base/test/browser.ini
@@ -1,10 +1,11 @@
 [DEFAULT]
 support-files =
+  empty.html
   file_bug1011748_redirect.sjs
   file_bug1011748_OK.sjs
   file_messagemanager_unload.html
   file_use_counter_outer.html
   file_use_counter_svg_getElementById.svg
   file_use_counter_svg_currentScale.svg
   file_use_counter_svg_background.html
   file_use_counter_svg_list_style_image.html
@@ -21,8 +22,9 @@ tags = mcb
 [browser_messagemanager_loadprocessscript.js]
 [browser_messagemanager_targetframeloader.js]
 [browser_messagemanager_unload.js]
 [browser_pagehide_on_tab_close.js]
 skip-if = e10s # this tests non-e10s behavior. it's not expected to work in e10s.
 [browser_state_notifications.js]
 skip-if = true # Bug 1271028
 [browser_use_counters.js]
+[browser_bug1307747.js]
new file mode 100644
--- /dev/null
+++ b/dom/base/test/browser_bug1307747.js
@@ -0,0 +1,32 @@
+/* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; js-indent-level: 2 -*- */
+
+var {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
+
+const BASE_URI = "http://mochi.test:8888/browser/dom/base/test/empty.html";
+
+add_task(function* test_initialize() {
+  let tab = gBrowser.addTab(BASE_URI);
+  let browser = gBrowser.getBrowserForTab(tab);
+
+  let blob = yield ContentTask.spawn(browser, null, function() {
+    let blob = new Blob([new Array(1024*1024).join('123456789ABCDEF')]);
+    return blob;
+  });
+
+  ok(blob, "We have a blob!");
+  is(blob.size, new Array(1024*1024).join('123456789ABCDEF').length, "The size matches");
+
+  let status = yield ContentTask.spawn(browser, blob, function(blob) {
+    return new Promise(resolve => {
+      let fr = new content.FileReader();
+      fr.readAsText(blob);
+      fr.onloadend = function() {
+        resolve(fr.result == new Array(1024*1024).join('123456789ABCDEF'));
+      }
+    });
+  });
+
+  ok(status, "Data match!");
+
+  yield BrowserTestUtils.removeTab(tab);
+});
new file mode 100644
--- a/dom/canvas/test/mochitest.ini
+++ b/dom/canvas/test/mochitest.ini
@@ -256,17 +256,16 @@ tags = imagebitmap
 [test_toDataURL_alpha.html]
 [test_toDataURL_lowercase_ascii.html]
 [test_toDataURL_parameters.html]
 [test_windingRuleUndefined.html]
 [test_2d.fillText.gradient.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # bug 1040965
 [test_2d_composite_canvaspattern_setTransform.html]
 [test_createPattern_broken.html]
-[test_setlinedash.html]
 [test_filter.html]
 skip-if = (e10s && debug && os == 'win')
 [test_filter_tainted.html]
 skip-if = (e10s && debug && os == 'win')
 [test_filter_tainted_source_graphics.html]
 skip-if = (e10s && debug && os == 'win')
 [test_filter_tainted_displacement_map.html]
 skip-if = (e10s && debug && os == 'win')
--- a/dom/canvas/test/test_canvas.html
+++ b/dom/canvas/test/test_canvas.html
@@ -21371,101 +21371,16 @@ function test_getImageData_after_zero_ca
     var same = false;
     ok(imgdata.data.length === oldimgdata.data.length, "not the same length");
     for (var i = 0; i < imgdata.data.length; ++i)
         same = imgdata.data[i] === oldimgdata.data[i];
     ok(same, "changing dimensions broke canvas");
 }
 </script>
 
-<p>Canvas test: linedash</p>
-<canvas id="c687" width="150" height="50"></canvas>
-<script type="text/javascript">
-
-function test_linedash() {
-  var c = document.getElementById("c687");
-  var ctx = c.getContext("2d");
-  ok(ctx.lineDashOffset==0, "initial dash offset is not 0");
-
-  ctx.setLineDash([15, 10]);
-  ctx.lineDashOffset = 5;
-  ctx.strokeRect (10,10,100,100);
-
-  var lineDash = ctx.getLineDash();
-  ok(lineDash[0]==15&&lineDash[1]==10, "dash pattern [15, 10] is wrong");
-  ok(ctx.lineDashOffset==5, "dash offset is wrong");
-
-  ctx.setLineDash([5, 10, 15]);
-  ctx.strokeRect(20, 20, 120, 120);
-  lineDash = ctx.getLineDash();
-  ok(lineDash[0]==5
-    && lineDash[1]==10
-    && lineDash[2]==15
-    && lineDash[3]==5
-    && lineDash[4]==10
-    && lineDash[5]==15, "dash pattern [5, 10, 15] is wrong");
-
-  ctx.setLineDash(["1", 2]);
-  lineDash = ctx.getLineDash();
-  ok(lineDash[0] == 1 && lineDash[1] == 2, "dash pattern ['1', 2] is wrong");
-
-  ctx.clearRect(0, 0, 700, 700);
-  ok(ctx.lineDashOffset==5, "dash offset is wrong");
-
-  ctx.setLineDash([20, 10]);
-  ctx.lineDashOffset = 0;
-  ctx.lineWidth = 4; // To make the test immune to plaform anti-aliasing discrepancies
-  ctx.strokeStyle = '#00FF00';
-  ctx.strokeRect(10.5, 10.5, 30, 30);
-
-  isPixel(ctx, 25, 10, 0, 255, 0, 255, 0);
-  isPixel(ctx, 35, 10, 0, 0, 0, 0, 0);
-  isPixel(ctx, 40, 25, 0, 255, 0, 255, 0);
-  isPixel(ctx, 40, 35, 0, 0, 0, 0, 0);
-  isPixel(ctx, 25, 40, 0, 255, 0, 255, 0);
-  isPixel(ctx, 15, 40, 0, 0, 0, 0, 0);
-  isPixel(ctx, 10, 25, 0, 255, 0, 255, 0);
-  isPixel(ctx, 10, 15, 0, 0, 0, 0, 0);
-
-  // Verify that lineDashOffset works as expected
-  ctx.lineDashOffset = 20;
-  ctx.strokeRect(50.5, 10.5, 30, 30);
-  isPixel(ctx, 55, 10, 0, 0, 0, 0, 0);
-  isPixel(ctx, 65, 10, 0, 255, 0, 255, 0);
-  isPixel(ctx, 80, 15, 0, 0, 0, 0, 0);
-  isPixel(ctx, 80, 25, 0, 255, 0, 255, 0);
-  isPixel(ctx, 75, 40, 0, 0, 0, 0, 0);
-  isPixel(ctx, 65, 40, 0, 255, 0, 255, 0);
-  isPixel(ctx, 50, 35, 0, 0, 0, 0, 0);
-  isPixel(ctx, 50, 25, 0, 255, 0, 255, 0);
-
-  // Verify negative lineDashOffset
-  ctx.lineDashOffset = -10;
-  ctx.strokeRect(90.5, 10.5, 30, 30);
-  isPixel(ctx, 95, 10, 0, 0, 0, 0, 0);
-  isPixel(ctx, 105, 10, 0, 255, 0, 255, 0);
-  isPixel(ctx, 120, 15, 0, 0, 0, 0, 0);
-  isPixel(ctx, 120, 25, 0, 255, 0, 255, 0);
-  isPixel(ctx, 115, 40, 0, 0, 0, 0, 0);
-  isPixel(ctx, 105, 40, 0, 255, 0, 255, 0);
-  isPixel(ctx, 90, 35, 0, 0, 0, 0, 0);
-  isPixel(ctx, 90, 25, 0, 255, 0, 255, 0);
-
-  // Ensure that all zeros or negative pattern does not cause error state in context, see bug 1169609
-  ctx.setLineDash([0, 0]);
-  ctx.strokeRect(130.5, 10.5, 10, 10);
-  ctx.setLineDash([-1]);
-  ctx.strokeRect(130.5, 10.5, 10, 10);
-  isPixel(ctx, 135, 15, 0, 0, 0, 0, 0);
-  ctx.fillStyle = '#00FF00';
-  ctx.fillRect(130, 10, 10, 10);
-  isPixel(ctx, 135, 15, 0, 255, 0, 255, 0);
-}
-</script>
-
 <p>Canvas test: test_opaque</p>
 <canvas id="c688" width="150" height="50"></canvas>
 <script type="text/javascript">
 
 function test_opaque() {
   var c = document.getElementById("c688");
   var ctx = c.getContext("2d", {alpha: false});
   ctx.fillStyle = "green";
@@ -25710,22 +25625,16 @@ function runTests() {
  }
  try {
   test_getImageData_after_zero_canvas();
  } catch(e) {
   throw e;
   ok(false, "unexpected exception thrown in: test_getImageData_after_zero_canvas");
  }
  try {
-  test_linedash();
- } catch(e) {
-  throw e;
-  ok(false, "unexpected exception thrown in: test_linedash");
- }
-try {
   test_opaque();
  } catch(e) {
   throw e;
   ok(false, "unexpected exception thrown in: test_opaque");
  }
  try {
   test_2d_transformation_reset_transform();
  } catch (e) {
deleted file mode 100644
--- a/dom/canvas/test/test_setlinedash.html
+++ /dev/null
@@ -1,42 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-<title>Canvas test: toDataURL parameters (Bug 564388)</title>
-<script src="/tests/SimpleTest/SimpleTest.js"></script>
-<link rel="stylesheet" href="/tests/SimpleTest/test.css">
-</head>
-<body>
-<p>
-The line dash setting should not be changed when bad values are passed to setLineDash().
-</p>
-<p> See:
-<a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-setlinedash">
-http://www.whatwg.org/specs/web-apps/current-work/multipage/the-canvas-element.html#dom-context-2d-setlinedash
-</a>
-</p>
-<p>Mozilla
-    <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1006656">Bug 1006656</a>
-</p>
-<canvas id="canvas"><p class="fallback">FAIL (fallback content)</p></canvas>
-
-<script>
-var canvas = document.getElementById('canvas');
-var ctx = canvas.getContext('2d');
-
-ctx.setLineDash([1,2,3,4,5,6,7,8,9,10]);
-isDeeply(ctx.getLineDash(), [1,2,3,4,5,6,7,8,9,10], "line dash sanity");
-
-ctx.setLineDash([1,2,3,4,5,6,7,8,9,10]);
-ctx.setLineDash([Infinity]);
-isDeeply(ctx.getLineDash(), [1,2,3,4,5,6,7,8,9,10], "Inf doesn't reset line dash");
-
-ctx.setLineDash([1,2,3,4,5,6,7,8,9,10]);
-ctx.setLineDash([NaN]);
-isDeeply(ctx.getLineDash(), [1,2,3,4,5,6,7,8,9,10], "NaN doesn't reset line dash");
-
-ctx.setLineDash([1,2,3,4,5,6,7,8,9,10]);
-ctx.setLineDash([-1]);
-isDeeply(ctx.getLineDash(), [1,2,3,4,5,6,7,8,9,10], "Negative doesn't reset line dash");
-</script>
-
-</html>
--- a/dom/ipc/Blob.cpp
+++ b/dom/ipc/Blob.cpp
@@ -707,48 +707,48 @@ CreateBlobImpl(const nsID& aKnownBlobIDD
   DebugOnly<bool> isMutable;
   MOZ_ASSERT(NS_SUCCEEDED(blobImpl->GetMutable(&isMutable)));
   MOZ_ASSERT(!isMutable);
 
   return blobImpl.forget();
 }
 
 already_AddRefed<BlobImpl>
-CreateBlobImpl(const IPCStream& aStream,
+CreateBlobImpl(const BlobDataStream& aStream,
                const CreateBlobImplMetadata& aMetadata)
 {
   MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
 
-  nsCOMPtr<nsIInputStream> inputStream = DeserializeIPCStream(aStream);
+  nsCOMPtr<nsIInputStream> inputStream = DeserializeIPCStream(aStream.stream());
   if (!inputStream) {
     ASSERT_UNLESS_FUZZING();
     return nullptr;
   }
 
-  uint64_t available;
-  MOZ_ALWAYS_SUCCEEDS(inputStream->Available(&available));
+  uint64_t length = aStream.length();
 
   RefPtr<BlobImpl> blobImpl;
   if (!aMetadata.mHasRecursed && aMetadata.IsFile()) {
-    if (available) {
+    if (length) {
       blobImpl =
         new BlobImplStream(inputStream,
                            aMetadata.mName,
                            aMetadata.mContentType,
                            aMetadata.mLastModifiedDate,
-                           available);
+                           length);
     } else {
       blobImpl =
         new EmptyBlobImpl(aMetadata.mName,
                           aMetadata.mContentType,
                           aMetadata.mLastModifiedDate);
     }
-  } else if (available) {
+  } else if (length) {
     blobImpl =
-      new BlobImplStream(inputStream, aMetadata.mContentType, available);
+      new BlobImplStream(inputStream, aMetadata.mContentType,
+                         length);
   } else {
     blobImpl = new EmptyBlobImpl(aMetadata.mContentType);
   }
 
   MOZ_ALWAYS_SUCCEEDS(blobImpl->SetMutable(false));
 
   return blobImpl.forget();
 }
@@ -766,18 +766,18 @@ CreateBlobImplFromBlobData(const BlobDat
   RefPtr<BlobImpl> blobImpl;
 
   switch (aBlobData.type()) {
     case BlobData::TnsID: {
       blobImpl = CreateBlobImpl(aBlobData.get_nsID(), aMetadata);
       break;
     }
 
-    case BlobData::TIPCStream: {
-      blobImpl = CreateBlobImpl(aBlobData.get_IPCStream(), aMetadata);
+    case BlobData::TBlobDataStream: {
+      blobImpl = CreateBlobImpl(aBlobData.get_BlobDataStream(), aMetadata);
       break;
     }
 
     case BlobData::TArrayOfBlobData: {
       blobImpl = CreateBlobImpl(aBlobData.get_ArrayOfBlobData(), aMetadata);
       break;
     }
 
@@ -943,23 +943,26 @@ BlobDataFromBlobImpl(ChildManagerType* a
     BlobChild* actor = remoteBlob->GetBlobChild();
     MOZ_ASSERT(actor);
 
     aBlobData = actor->ParentID();
     return;
   }
 
   ErrorResult rv;
+  uint64_t length = aBlobImpl->GetSize(rv);
+  MOZ_ALWAYS_TRUE(!rv.Failed());
+
   nsCOMPtr<nsIInputStream> inputStream;
   aBlobImpl->GetInternalStream(getter_AddRefs(inputStream), rv);
   MOZ_ALWAYS_TRUE(!rv.Failed());
 
   UniquePtr<AutoIPCStream> autoStream(new AutoIPCStream());
   autoStream->Serialize(inputStream, aManager);
-  aBlobData = autoStream->TakeValue();
+  aBlobData = BlobDataStream(autoStream->TakeValue(), length);
 
   aIPCStreams.AppendElement(Move(autoStream));
 }
 
 RemoteInputStream::RemoteInputStream(BlobImpl* aBlobImpl,
                                      uint64_t aStart,
                                      uint64_t aLength)
   : mMonitor("RemoteInputStream.mMonitor")
--- a/dom/ipc/DOMTypes.ipdlh
+++ b/dom/ipc/DOMTypes.ipdlh
@@ -34,23 +34,29 @@ struct MessagePortIdentifier
 
 struct ClonedMessageData
 {
   SerializedStructuredCloneBuffer data;
   PBlob[] blobs;
   MessagePortIdentifier[] identfiers;
 };
 
+struct BlobDataStream
+{
+  IPCStream stream;
+  uint64_t length;
+};
+
 union BlobData
 {
   // For remote blobs.
   nsID;
 
   // For memory-backed blobs.
-  IPCStream;
+  BlobDataStream;
 
   // For multiplex blobs.
   BlobData[];
 };
 
 union OptionalBlobData
 {
   BlobData;
--- a/dom/ipc/PPluginWidget.ipdl
+++ b/dom/ipc/PPluginWidget.ipdl
@@ -26,38 +26,36 @@ namespace plugins {
  * be torn down first by the tab, followed by the deref'ing of the nsIWidget
  * via layout.
  */
 sync protocol PPluginWidget {
   manager PBrowser;
 
 parent:
   async __delete__();
-  sync Create() returns (nsresult aResult);
+
+  /**
+   * Used to set the ID of a scroll capture container from the parent process,
+   * so that we can create a proxy container in the layer tree.
+   * @param aScrollCaptureId async container ID of the parent container
+   * @param aPluginInstanceId plugin ID on which to set the scroll capture ID
+   */
+  sync Create() returns (nsresult aResult, uint64_t aScrollCaptureId,
+                         uintptr_t aPluginInstanceId);
   async SetFocus(bool aRaise);
 
   /**
    * Returns NS_NATIVE_PLUGIN_PORT and its variants: a sharable native
    * window for plugins. On Linux, this returns an XID for a socket widget
    * embedded in the chrome side native window. On Windows this returns the
    * native HWND of the plugin widget.
    */
   sync GetNativePluginPort() returns (uintptr_t value);
 
   /**
    * Sends an NS_NATIVE_CHILD_WINDOW to be adopted by the widget's native window
    * on the chrome side. This is only currently used on Windows.
    */
   sync SetNativeChildWindow(uintptr_t childWindow);
-
-child:
-  /**
-   * Used to set the ID of a scroll capture container from the parent process,
-   * so that we can create a proxy container in the layer tree.
-   * @param aScrollCaptureId async container ID of the parent container
-   * @param aPluginInstanceId plugin ID on which to set the scroll capture ID
-   */
-  async SetScrollCaptureId(uint64_t aScrollCaptureId,
-                           uintptr_t aPluginInstanceId);
 };
 
 }
 }
--- a/dom/media/tests/mochitest/mochitest.ini
+++ b/dom/media/tests/mochitest/mochitest.ini
@@ -34,17 +34,17 @@ skip-if = android_version == '18' # andr
 skip-if = android_version == '18' # android(Bug 1189784, timeouts on 4.3 emulator)
 [test_dataChannel_basicDataOnly.html]
 [test_dataChannel_basicVideo.html]
 skip-if = android_version == '18' # android(Bug 1189784, timeouts on 4.3 emulator)
 [test_dataChannel_bug1013809.html]
 [test_dataChannel_noOffer.html]
 [test_enumerateDevices.html]
 [test_ondevicechange.html]
-skip-if = os == 'win' || os == 'android'
+skip-if = os == 'android'
 [test_getUserMedia_audioCapture.html]
 skip-if = android_version == '18' # android(Bug 1189784, timeouts on 4.3 emulator)
 [test_getUserMedia_addTrackRemoveTrack.html]
 [test_getUserMedia_addtrack_removetrack_events.html]
 [test_getUserMedia_basicAudio.html]
 [test_getUserMedia_basicVideo.html]
 [test_getUserMedia_basicVideo_playAfterLoadedmetadata.html]
 [test_getUserMedia_basicScreenshare.html]
--- a/dom/plugins/base/nsPluginHost.cpp
+++ b/dom/plugins/base/nsPluginHost.cpp
@@ -141,38 +141,17 @@ static const char *kPrefLoadInParentPref
 static const char *kPrefDisableFullPage = "plugin.disable_full_page_plugin_for_types";
 static const char *kPrefJavaMIME = "plugin.java.mime";
 
 // How long we wait before unloading an idle plugin process.
 // Defaults to 30 seconds.
 static const char *kPrefUnloadPluginTimeoutSecs = "dom.ipc.plugins.unloadTimeoutSecs";
 static const uint32_t kDefaultPluginUnloadingTimeout = 30;
 
-// Version of cached plugin info
-// 0.01 first implementation
-// 0.02 added caching of CanUnload to fix bug 105935
-// 0.03 changed name, description and mime desc from string to bytes, bug 108246
-// 0.04 added new mime entry point on Mac, bug 113464
-// 0.05 added new entry point check for the default plugin, bug 132430
-// 0.06 strip off suffixes in mime description strings, bug 53895
-// 0.07 changed nsIRegistry to flat file support for caching plugins info
-// 0.08 mime entry point on MachO, bug 137535
-// 0.09 the file encoding is changed to UTF-8, bug 420285
-// 0.10 added plugin versions on appropriate platforms, bug 427743
-// 0.11 file name and full path fields now store expected values on all platforms, bug 488181
-// 0.12 force refresh due to quicktime pdf claim fix, bug 611197
-// 0.13 add architecture and list of invalid plugins, bug 616271
-// 0.14 force refresh due to locale comparison fix, bug 611296
-// 0.15 force refresh due to bug in reading Java plist MIME data, bug 638171
-// 0.16 version bump to avoid importing the plugin flags in newer versions
-// 0.17 added flag on whether plugin is loaded from an XPI
-// The current plugin registry version (and the maximum version we know how to read)
-static const char *kPluginRegistryVersion = "0.17";
-// The minimum registry version we know how to read
-static const char *kMinimumRegistryVersion = "0.9";
+static const char *kPluginRegistryVersion = "0.18";
 
 static const char kDirectoryServiceContractID[] = "@mozilla.org/file/directory_service;1";
 
 #define kPluginRegistryFilename NS_LITERAL_CSTRING("pluginreg.dat")
 
 #ifdef PLUGIN_LOGGING
 LazyLogModule nsPluginLogging::gNPNLog(NPN_LOG_NAME);
 LazyLogModule nsPluginLogging::gNPPLog(NPP_LOG_NAME);
@@ -2091,17 +2070,17 @@ nsPluginHost::AddPluginTag(nsPluginTag* 
       }
     }
   }
 }
 
 static bool
 PluginInfoIsFlash(const nsPluginInfo& info)
 {
-  if (strcmp(info.fDescription, "Shockwave Flash") != 0) {
+  if (strcmp(info.fName, "Shockwave Flash") != 0) {
     return false;
   }
   for (uint32_t i = 0; i < info.fVariantCount; ++i) {
     if (info.fMimeTypeArray[i] &&
         (!strcmp(info.fMimeTypeArray[i], "application/x-shockwave-flash") ||
          !strcmp(info.fMimeTypeArray[i], "application/x-shockwave-flash-test"))) {
       return true;
     }
@@ -2985,20 +2964,16 @@ nsPluginHost::WritePluginInfo()
 nsresult
 nsPluginHost::ReadPluginInfo()
 {
   MOZ_ASSERT(XRE_IsParentProcess());
 
   const long PLUGIN_REG_MIMETYPES_ARRAY_SIZE = 12;
   const long PLUGIN_REG_MAX_MIMETYPES = 1000;
 
-  // we need to import the legacy flags from the plugin registry once
-  const bool pluginStateImported =
-    Preferences::GetDefaultBool("plugin.importedState", false);
-
   nsresult rv;
 
   nsCOMPtr<nsIProperties> directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID,&rv));
   if (NS_FAILED(rv))
     return rv;
 
   directoryService->Get(NS_APP_USER_PROFILE_50_DIR, NS_GET_IID(nsIFile),
                         getter_AddRefs(mPluginRegFile));
@@ -3080,138 +3055,78 @@ nsPluginHost::ReadPluginInfo()
   // VersionLiteral, kPluginRegistryVersion
   if (2 != reader.ParseLine(values, 2))
     return rv;
 
   // VersionLiteral
   if (PL_strcmp(values[0], "Version"))
     return rv;
 
-  // kPluginRegistryVersion
-  int32_t vdiff = mozilla::CompareVersions(values[1], kPluginRegistryVersion);
-  mozilla::Version version(values[1]);
-  // If this is a registry from some future version then don't attempt to read it
-  if (vdiff > 0)
+  // If we're reading an old registry, ignore it
+  if (strcmp(values[1], kPluginRegistryVersion) != 0) {
     return rv;
-  // If this is a registry from before the minimum then don't attempt to read it
-  if (version < kMinimumRegistryVersion)
+  }
+
+  char* archValues[6];
+  if (!reader.NextLine()) {
+    return rv;
+  }
+
+  // ArchLiteral, Architecture
+  if (2 != reader.ParseLine(archValues, 2)) {
     return rv;
-
-  // Registry v0.10 and upwards includes the plugin version field
-  bool regHasVersion = (version >= "0.10");
-
-  // Registry v0.13 and upwards includes the architecture
-  if (version >= "0.13") {
-    char* archValues[6];
-
-    if (!reader.NextLine()) {
-      return rv;
-    }
-
-    // ArchLiteral, Architecture
-    if (2 != reader.ParseLine(archValues, 2)) {
-      return rv;
-    }
-
-    // ArchLiteral
-    if (PL_strcmp(archValues[0], "Arch")) {
-      return rv;
-    }
-
-    nsCOMPtr<nsIXULRuntime> runtime = do_GetService("@mozilla.org/xre/runtime;1");
-    if (!runtime) {
-      return rv;
-    }
-
-    nsAutoCString arch;
-    if (NS_FAILED(runtime->GetXPCOMABI(arch))) {
-      return rv;
-    }
-
-    // If this is a registry from a different architecture then don't attempt to read it
-    if (PL_strcmp(archValues[1], arch.get())) {
-      return rv;
-    }
+  }
+
+  // ArchLiteral
+  if (PL_strcmp(archValues[0], "Arch")) {
+    return rv;
+  }
+
+  nsCOMPtr<nsIXULRuntime> runtime = do_GetService("@mozilla.org/xre/runtime;1");
+  if (!runtime) {
+    return rv;
   }
 
-  // Registry v0.13 and upwards includes the list of invalid plugins
-  const bool hasInvalidPlugins = (version >= "0.13");
-
-  // Registry v0.16 and upwards always have 0 for their plugin flags, prefs are used instead
-  const bool hasValidFlags = (version < "0.16");
-
-  // Registry v0.17 and upwards store whether the plugin comes from an XPI.
-  const bool hasFromExtension = (version >= "0.17");
-
-#if defined(XP_MACOSX)
-  const bool hasFullPathInFileNameField = false;
-#else
-  const bool hasFullPathInFileNameField = (version < "0.11");
-#endif
+  nsAutoCString arch;
+  if (NS_FAILED(runtime->GetXPCOMABI(arch))) {
+    return rv;
+  }
+
+  // If this is a registry from a different architecture then don't attempt to read it
+  if (PL_strcmp(archValues[1], arch.get())) {
+    return rv;
+  }
 
   if (!ReadSectionHeader(reader, "PLUGINS"))
     return rv;
 
   while (reader.NextLine()) {
-    const char *filename;
-    const char *fullpath;
-    nsAutoCString derivedFileName;
-
-    if (hasInvalidPlugins && *reader.LinePtr() == '[') {
+    if (*reader.LinePtr() == '[') {
       break;
     }
 
-    if (hasFullPathInFileNameField) {
-      fullpath = reader.LinePtr();
-      if (!reader.NextLine())
-        return rv;
-      // try to derive a file name from the full path
-      if (fullpath) {
-        nsCOMPtr<nsIFile> file = do_CreateInstance("@mozilla.org/file/local;1");
-        file->InitWithNativePath(nsDependentCString(fullpath));
-        file->GetNativeLeafName(derivedFileName);
-        filename = derivedFileName.get();
-      } else {
-        filename = nullptr;
-      }
-
-      // skip the next line, useless in this version
-      if (!reader.NextLine())
-        return rv;
-    } else {
-      filename = reader.LinePtr();
-      if (!reader.NextLine())
-        return rv;
-
-      fullpath = reader.LinePtr();
-      if (!reader.NextLine())
-        return rv;
-    }
+    const char* filename = reader.LinePtr();
+    if (!reader.NextLine())
+      return rv;
+
+    const char* fullpath = reader.LinePtr();
+    if (!reader.NextLine())
+      return rv;
 
     const char *version;
-    if (regHasVersion) {
-      version = reader.LinePtr();
-      if (!reader.NextLine())
-        return rv;
-    } else {
-      version = "0";
-    }
+    version = reader.LinePtr();
+    if (!reader.NextLine())
+      return rv;
 
     // lastModifiedTimeStamp|canUnload|tag.mFlag|fromExtension
-    const int count = hasFromExtension ? 4 : 3;
-    if (reader.ParseLine(values, count) != count)
+    if (4 != reader.ParseLine(values, 4))
       return rv;
 
-    // If this is an old plugin registry mark this plugin tag to be refreshed
-    int64_t lastmod = (vdiff == 0) ? nsCRT::atoll(values[0]) : -1;
-    uint32_t tagflag = atoi(values[2]);
-    bool fromExtension = false;
-    if (hasFromExtension) {
-      fromExtension = atoi(values[3]);
-    }
+    int64_t lastmod = nsCRT::atoll(values[0]);
+    bool fromExtension = atoi(values[3]);
     if (!reader.NextLine())
       return rv;
 
     char *description = reader.LinePtr();
     if (!reader.NextLine())
       return rv;
 
 #if MOZ_WIDGET_ANDROID
@@ -3277,61 +3192,52 @@ nsPluginHost::ReadPluginInfo()
       (const char* const*)mimetypes,
       (const char* const*)mimedescriptions,
       (const char* const*)extensions,
       mimetypecount, lastmod, fromExtension, true);
     if (heapalloced)
       delete [] heapalloced;
 
     // Import flags from registry into prefs for old registry versions
-    if (hasValidFlags && !pluginStateImported) {
-      tag->ImportFlagsToPrefs(tagflag);
-    }
-
     MOZ_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_BASIC,
       ("LoadCachedPluginsInfo : Loading Cached plugininfo for %s\n", tag->FileName().get()));
 
     if (!ShouldAddPlugin(tag)) {
       continue;
     }
 
     tag->mNext = mCachedPlugins;
     mCachedPlugins = tag;
   }
 
 // On Android we always want to try to load a plugin again (Flash). Bug 935676.
 #ifndef MOZ_WIDGET_ANDROID
-  if (hasInvalidPlugins) {
-    if (!ReadSectionHeader(reader, "INVALID")) {
+  if (!ReadSectionHeader(reader, "INVALID")) {
+    return rv;
+  }
+
+  while (reader.NextLine()) {
+    const char *fullpath = reader.LinePtr();
+    if (!reader.NextLine()) {
       return rv;
     }
 
-    while (reader.NextLine()) {
-      const char *fullpath = reader.LinePtr();
-      if (!reader.NextLine()) {
-        return rv;
-      }
-
-      const char *lastModifiedTimeStamp = reader.LinePtr();
-      int64_t lastmod = (vdiff == 0) ? nsCRT::atoll(lastModifiedTimeStamp) : -1;
-
-      RefPtr<nsInvalidPluginTag> invalidTag = new nsInvalidPluginTag(fullpath, lastmod);
-
-      invalidTag->mNext = mInvalidPlugins;
-      if (mInvalidPlugins) {
-        mInvalidPlugins->mPrev = invalidTag;
-      }
-      mInvalidPlugins = invalidTag;
+    const char *lastModifiedTimeStamp = reader.LinePtr();
+    int64_t lastmod = nsCRT::atoll(lastModifiedTimeStamp);
+
+    RefPtr<nsInvalidPluginTag> invalidTag = new nsInvalidPluginTag(fullpath, lastmod);
+
+    invalidTag->mNext = mInvalidPlugins;
+    if (mInvalidPlugins) {
+      mInvalidPlugins->mPrev = invalidTag;
     }
+    mInvalidPlugins = invalidTag;
   }
 #endif
 
-  // flip the pref so we don't import the legacy flags again
-  Preferences::SetBool("plugin.importedState", true);
-
   return NS_OK;
 }
 
 void
 nsPluginHost::RemoveCachedPluginsInfo(const char *filePath, nsPluginTag **result)
 {
   RefPtr<nsPluginTag> prev;
   RefPtr<nsPluginTag> tag = mCachedPlugins;
--- a/dom/plugins/base/nsPluginTags.cpp
+++ b/dom/plugins/base/nsPluginTags.cpp
@@ -752,23 +752,16 @@ nsPluginTag::GetNiceFileName()
 
 NS_IMETHODIMP
 nsPluginTag::GetNiceName(nsACString & aResult)
 {
   aResult = GetNiceFileName();
   return NS_OK;
 }
 
-void nsPluginTag::ImportFlagsToPrefs(uint32_t flags)
-{
-  if (!(flags & NS_PLUGIN_FLAG_ENABLED)) {
-    SetPluginState(ePluginState_Disabled);
-  }
-}
-
 NS_IMETHODIMP
 nsPluginTag::GetBlocklistState(uint32_t *aResult)
 {
 #if defined(MOZ_WIDGET_ANDROID)
   *aResult = nsIBlocklistService::STATE_NOT_BLOCKED;
   return NS_OK;
 #else
   if (mCachedBlocklistStateValid) {
--- a/dom/plugins/base/nsPluginTags.h
+++ b/dom/plugins/base/nsPluginTags.h
@@ -146,19 +146,16 @@ public:
   bool IsEnabled() override;
   void SetEnabled(bool enabled);
   bool IsClicktoplay();
   bool IsBlocklisted();
 
   PluginState GetPluginState();
   void SetPluginState(PluginState state);
 
-  // import legacy flags from plugin registry into the preferences
-  void ImportFlagsToPrefs(uint32_t flag);
-
   bool HasSameNameAndMimes(const nsPluginTag *aPluginTag) const;
   const nsCString& GetNiceFileName() override;
 
   bool IsFromExtension() const;
 
   RefPtr<nsPluginTag> mNext;
   uint32_t      mId;
 
--- a/dom/plugins/ipc/PluginWidgetChild.cpp
+++ b/dom/plugins/ipc/PluginWidgetChild.cpp
@@ -30,35 +30,16 @@ PluginWidgetChild::PluginWidgetChild() :
 }
 
 PluginWidgetChild::~PluginWidgetChild()
 {
   PWLOG("PluginWidgetChild::~PluginWidgetChild()\n");
   MOZ_COUNT_DTOR(PluginWidgetChild);
 }
 
-bool
-PluginWidgetChild::RecvSetScrollCaptureId(const uint64_t& aScrollCaptureId,
-                                          const uintptr_t& aPluginInstanceId)
-{
-#if defined(XP_WIN)
-  PluginInstanceParent* instance =
-    PluginInstanceParent::LookupPluginInstanceByID(aPluginInstanceId);
-  if (instance) {
-    Unused << NS_WARN_IF(NS_FAILED(instance->SetScrollCaptureId(aScrollCaptureId)));
-  }
-
-  return true;
-#else
-  MOZ_ASSERT_UNREACHABLE(
-    "PluginWidgetChild::RecvSetScrollCaptureId calls not expected.");
-  return false;
-#endif
-}
-
 // Called by the proxy widget when it is destroyed by layout. Only gets
 // called once.
 void
 PluginWidgetChild::ProxyShutdown()
 {
   PWLOG("PluginWidgetChild::ProxyShutdown()\n");
   if (mWidget) {
     mWidget = nullptr;
--- a/dom/plugins/ipc/PluginWidgetChild.h
+++ b/dom/plugins/ipc/PluginWidgetChild.h
@@ -14,19 +14,16 @@ class PluginWidgetProxy;
 namespace plugins {
 
 class PluginWidgetChild : public PPluginWidgetChild
 {
 public:
   PluginWidgetChild();
   virtual ~PluginWidgetChild();
 
-  bool RecvSetScrollCaptureId(const uint64_t& aScrollCaptureId,
-                              const uintptr_t& aPluginInstanceId) override;
-
   virtual void ActorDestroy(ActorDestroyReason aWhy) override;
 
   void SetWidget(mozilla::widget::PluginWidgetProxy* aWidget) {
     mWidget = aWidget;
   }
   void ProxyShutdown();
 
 private:
--- a/dom/plugins/ipc/PluginWidgetParent.cpp
+++ b/dom/plugins/ipc/PluginWidgetParent.cpp
@@ -79,20 +79,24 @@ PluginWidgetParent::SetParent(nsIWidget*
 }
 
 // When plugins run in chrome, nsPluginNativeWindow(Plat) implements platform
 // specific functionality that wraps plugin widgets. With e10s we currently
 // bypass this code on Window, and reuse a bit of it on Linux. Content still
 // makes use of some of the utility functions as well.
 
 bool
-PluginWidgetParent::RecvCreate(nsresult* aResult)
+PluginWidgetParent::RecvCreate(nsresult* aResult, uint64_t* aScrollCaptureId,
+                               uintptr_t* aPluginInstanceId)
 {
   PWLOG("PluginWidgetParent::RecvCreate()\n");
 
+  *aScrollCaptureId = 0;
+  *aPluginInstanceId = 0;
+
   mWidget = do_CreateInstance(kWidgetCID, aResult);
   NS_ASSERTION(NS_SUCCEEDED(*aResult), "widget create failure");
 
 #if defined(MOZ_WIDGET_GTK)
   // We need this currently just for GTK in setting up a socket widget
   // we can send over to content -> plugin.
   PLUG_NewPluginNativeWindow((nsPluginNativeWindow**)&mWrapper);
   if (!mWrapper) {
@@ -137,20 +141,19 @@ PluginWidgetParent::RecvCreate(nsresult*
   PWLOG("Plugin XID=%p\n", (void*)mWrapper->window);
 #elif defined(XP_WIN)
   DebugOnly<DWORD> winres =
     ::SetPropW((HWND)mWidget->GetNativeData(NS_NATIVE_WINDOW),
                mozilla::dom::kPluginWidgetContentParentProperty,
                GetTabParent()->Manager()->AsContentParent());
   NS_ASSERTION(winres, "SetPropW call failure");
 
-  uint64_t scrollCaptureId = mWidget->CreateScrollCaptureContainer();
-  uintptr_t pluginId =
+  *aScrollCaptureId = mWidget->CreateScrollCaptureContainer();
+  *aPluginInstanceId =
     reinterpret_cast<uintptr_t>(mWidget->GetNativeData(NS_NATIVE_PLUGIN_ID));
-  Unused << SendSetScrollCaptureId(scrollCaptureId, pluginId);
 #endif
 
   // This is a special call we make to nsBaseWidget to register this
   // window as a remote plugin window which is expected to receive
   // visibility updates from the compositor, which ships this data
   // over with corresponding layer updates.
   mWidget->RegisterPluginWindowForRemoteUpdates();
 
--- a/dom/plugins/ipc/PluginWidgetParent.h
+++ b/dom/plugins/ipc/PluginWidgetParent.h
@@ -24,17 +24,18 @@ namespace plugins {
 
 class PluginWidgetParent : public PPluginWidgetParent
 {
 public:
   PluginWidgetParent();
   virtual ~PluginWidgetParent();
 
   virtual void ActorDestroy(ActorDestroyReason aWhy) override;
-  virtual bool RecvCreate(nsresult* aResult) override;
+  virtual bool RecvCreate(nsresult* aResult, uint64_t* aScrollCaptureId,
+                          uintptr_t* aPluginInstanceId) override;
   virtual bool RecvSetFocus(const bool& aRaise) override;
   virtual bool RecvGetNativePluginPort(uintptr_t* value) override;
   bool RecvSetNativeChildWindow(const uintptr_t& aChildWindow) override;
 
   // Helper for compositor checks on the channel
   bool ActorDestroyed() { return !mWidget; }
 
   // Called by PBrowser when it receives a Destroy() call from the child.
--- a/dom/plugins/test/unit/test_persist_in_prefs.js
+++ b/dom/plugins/test/unit/test_persist_in_prefs.js
@@ -5,89 +5,40 @@
 
 Components.utils.import("resource://gre/modules/Services.jsm");
 
 // Plugin registry uses different field delimeters on different platforms
 var DELIM = mozinfo.os == "win" ? "|" : ":";
 
 var gProfD = do_get_profile_startup();
 
-// Writes out some plugin registry to the profile
-function write_registry(version, info) {
-  let runtime = Cc["@mozilla.org/xre/runtime;1"].getService(Ci.nsIXULRuntime);
-
-  let header = "Generated File. Do not edit.\n\n";
-  header += "[HEADER]\n";
-  header += "Version" + DELIM + version + DELIM + "$\n";
-  header += "Arch" + DELIM + runtime.XPCOMABI + DELIM + "$\n";
-  header += "\n";
-  header += "[PLUGINS]\n";
-
-  let registry = gProfD.clone();
-  registry.append("pluginreg.dat");
-  let foStream = Components.classes["@mozilla.org/network/file-output-stream;1"]
-                           .createInstance(Components.interfaces.nsIFileOutputStream);
-  // write, create, truncate
-  foStream.init(registry, 0x02 | 0x08 | 0x20, 0o666, 0);
-
-  let charset = "UTF-8"; // Can be any character encoding name that Mozilla supports
-  let os = Cc["@mozilla.org/intl/converter-output-stream;1"].
-           createInstance(Ci.nsIConverterOutputStream);
-  os.init(foStream, charset, 0, 0x0000);
-
-  os.writeString(header);
-  os.writeString(info);
-  os.close();
-}
-
 function run_test() {
   allow_all_plugins();
 
   do_check_true(gIsWindows || gIsOSX || gIsLinux);
 
   let file = get_test_plugin_no_symlink();
   if (!file)
     do_throw("Plugin library not found");
 
   const pluginDir = file.parent;
   const tempDir = do_get_tempdir();
   const suffix = get_platform_specific_plugin_suffix();
   const pluginName = file.leafName.substring(0, file.leafName.length - suffix.length).toLowerCase();
   const pluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
   const statePref = "plugin.state." + pluginName;
 
-  // write plugin registry data
-  let registry = "";
-
-  registry += file.leafName + DELIM + "$\n";
-  registry += file.path + DELIM + "$\n";
-  registry += "1.0.0.0" + DELIM + "$\n";
-  registry += file.lastModifiedTime + DELIM + "0" + DELIM + "0" + DELIM + "$\n";
-  registry += "Plug-in for testing purposes." + DELIM + "$\n";
-  registry += "Test Plug-in" + DELIM + "$\n";
-  registry += "1\n";
-  registry += "0" + DELIM + "application/x-test" + DELIM + "Test mimetype" + DELIM + "tst" + DELIM + "$\n";
-
-  registry += "\n";
-  registry += "[INVALID]\n";
-  registry += "\n";
-  write_registry("0.15", registry);
-
   // Initialise profile folder
   do_get_profile();
 
   let plugin = get_test_plugintag();
   if (!plugin)
     do_throw("Plugin tag not found");
 
-  // check that the expected plugin state was loaded correctly from the registry
-  do_check_true(plugin.disabled);
-  do_check_false(plugin.clicktoplay);
-  // ... and imported into prefs, with 0 being the disabled state
-  do_check_eq(0, Services.prefs.getIntPref(statePref));
+  plugin.enabledState = Ci.nsIPluginTag.STATE_DISABLED;
 
   // prepare a copy of the plugin and backup the original
   file.copyTo(null, "nptestcopy" + suffix);
   let copy = pluginDir.clone();
   copy.append("nptestcopy" + suffix);
   file.moveTo(tempDir, null);
 
   // test that the settings persist through a few variations of test-plugin names
--- a/dom/xhr/XMLHttpRequestMainThread.cpp
+++ b/dom/xhr/XMLHttpRequestMainThread.cpp
@@ -174,17 +174,17 @@ XMLHttpRequestMainThread::XMLHttpRequest
     mUploadTransferred(0), mUploadTotal(0), mUploadComplete(true),
     mProgressSinceLastProgressEvent(false),
     mRequestSentTime(0), mTimeoutMilliseconds(0),
     mErrorLoad(false), mErrorParsingXML(false),
     mWaitingForOnStopRequest(false),
     mProgressTimerIsActive(false),
     mIsHtml(false),
     mWarnAboutSyncHtml(false),
-    mLoadTotal(0),
+    mLoadTotal(-1),
     mIsSystem(false),
     mIsAnon(false),
     mFirstStartRequestSeen(false),
     mInLoadProgressEvent(false),
     mResultJSON(JS::UndefinedValue()),
     mResultArrayBuffer(nullptr),
     mIsMappedArrayBuffer(false),
     mXPCOMifier(nullptr)
@@ -1043,19 +1043,19 @@ XMLHttpRequestMainThread::CloseRequestWi
   if (mState != State::unsent &&
       !(mState == State::opened && !mFlagSend) &&
       mState != State::done) {
     ChangeState(State::done, true);
 
     if (!mFlagSyncLooping) {
       if (mUpload && !mUploadComplete) {
         mUploadComplete = true;
-        DispatchProgressEvent(mUpload, aType, 0, 0);
+        DispatchProgressEvent(mUpload, aType, 0, -1);
       }
-      DispatchProgressEvent(this, aType, 0, 0);
+      DispatchProgressEvent(this, aType, 0, -1);
     }
   }
 
   // The ChangeState call above calls onreadystatechange handlers which
   // if they load a new url will cause XMLHttpRequestMainThread::Open to clear
   // the abort state bit. If this occurs we're not uninitialized (bug 361773).
   if (mFlagAborted) {
     ChangeState(State::unsent, false);  // IE seems to do it
@@ -1319,27 +1319,27 @@ XMLHttpRequestMainThread::DispatchProgre
   // If blocked by CORS, zero-out the stats on progress events
   // and never fire "progress" or "load" events at all.
   if (IsDeniedCrossSiteCORSRequest()) {
     if (aType == ProgressEventType::progress ||
         aType == ProgressEventType::load) {
       return;
     }
     aLoaded = 0;
-    aTotal = 0;
+    aTotal = -1;
   }
 
   if (aType == ProgressEventType::progress) {
     mInLoadProgressEvent = true;
   }
 
   ProgressEventInit init;
   init.mBubbles = false;
   init.mCancelable = false;
-  init.mLengthComputable = aTotal != 0; // XHR spec step 6.1
+  init.mLengthComputable = aTotal != -1; // XHR spec step 6.1
   init.mLoaded = aLoaded;
   init.mTotal = (aTotal == -1) ? 0 : aTotal;
 
   const nsAString& typeString = ProgressEventTypeStrings[(uint8_t)aType];
   RefPtr<ProgressEvent> event =
     ProgressEvent::Constructor(aTarget, typeString, init);
   event->SetTrusted(true);
 
@@ -2202,17 +2202,21 @@ XMLHttpRequestMainThread::ChangeStateToD
              "ChangeStateToDone() called before async HTML parsing is done.");
 
   mFlagSend = false;
 
   if (mTimeoutTimer) {
     mTimeoutTimer->Cancel();
   }
 
-  mLoadTotal = mLoadTransferred;
+  if (mLoadTransferred) {
+    mLoadTotal = mLoadTransferred;
+  } else {
+    mLoadTotal = -1;
+  }
 
   // Per spec, fire the last download progress event, if any,
   // before readystatechange=4/done. (Note that 0-sized responses
   // will have not sent a progress event yet, so one must be sent here).
   if (!mFlagSynchronous &&
       (!mLoadTransferred || mProgressSinceLastProgressEvent)) {
     DispatchProgressEvent(this, ProgressEventType::progress,
                           mLoadTransferred, mLoadTotal);
@@ -2220,26 +2224,26 @@ XMLHttpRequestMainThread::ChangeStateToD
   }
 
   // Per spec, fire readystatechange=4/done before final error events.
   ChangeState(State::done, true);
 
   // Per spec, if we failed in the upload phase, fire a final error
   // and loadend events for the upload after readystatechange=4/done.
   if (!mFlagSynchronous && mUpload && !mUploadComplete) {
-    DispatchProgressEvent(mUpload, ProgressEventType::error, 0, 0);
+    DispatchProgressEvent(mUpload, ProgressEventType::error, 0, -1);
   }
 
   // Per spec, fire download's load/error and loadend events after
   // readystatechange=4/done (and of course all upload events).
   DispatchProgressEvent(this,
                         mErrorLoad ? ProgressEventType::error :
                                      ProgressEventType::load,
                         mErrorLoad ? 0 : mLoadTransferred,
-                        mErrorLoad ? 0 : mLoadTotal);
+                        mErrorLoad ? -1 : mLoadTotal);
 
   if (mErrorLoad) {
     // By nulling out channel here we make it so that Send() can test
     // for that and throw. Also calling the various status
     // methods/members will not throw.
     // This matches what IE does.
     mChannel = nullptr;
   }
@@ -2822,17 +2826,17 @@ XMLHttpRequestMainThread::SendInternal(c
   //     if there are no event listeners set and we are doing
   //     an asynchronous call.
 
   mUploadTransferred = 0;
   mUploadTotal = 0;
   // By default we don't have any upload, so mark upload complete.
   mUploadComplete = true;
   mErrorLoad = false;
-  mLoadTotal = 0;
+  mLoadTotal = -1;
   nsCOMPtr<nsIInputStream> uploadStream;
   nsAutoCString uploadContentType;
   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mChannel));
   if (aBody && httpChannel &&
       !mRequestMethod.EqualsLiteral("GET") &&
       !mRequestMethod.EqualsLiteral("HEAD")) {
 
     nsAutoCString charset;
@@ -2959,17 +2963,17 @@ XMLHttpRequestMainThread::SendInternal(c
     // up doing our AsyncOpen on a null channel if the reentered AsyncOpen fails.
     StopProgressEventTimer();
 
     // Upload phase beginning; start the progress event timer if necessary.
     if (mUpload && mUpload->HasListenersFor(nsGkAtoms::onprogress)) {
       StartProgressEventTimer();
     }
     // Dispatch loadstart events
-    DispatchProgressEvent(this, ProgressEventType::loadstart, 0, 0);
+    DispatchProgressEvent(this, ProgressEventType::loadstart, 0, -1);
     if (mUpload && !mUploadComplete) {
       DispatchProgressEvent(mUpload, ProgressEventType::loadstart,
                             0, mUploadTotal);
     }
   }
 
   if (!mChannel) {
     // Per spec, silently fail on async request failures; throw for sync.
@@ -3319,18 +3323,19 @@ XMLHttpRequestMainThread::OnProgress(nsI
     }
     mUploadTransferred = loaded;
     mProgressSinceLastProgressEvent = true;
 
     if (!mFlagSynchronous && !mProgressTimerIsActive) {
       StartProgressEventTimer();
     }
   } else {
-    mLoadTotal = lengthComputable ? aProgressMax : 0;
+    mLoadTotal = lengthComputable ? aProgressMax : -1;
     mLoadTransferred = aProgress;
+  
     // OnDataAvailable() handles mProgressSinceLastProgressEvent
     // for the download phase.
   }
 
   if (mProgressEventSink) {
     mProgressEventSink->OnProgress(aRequest, aContext, aProgress,
                                    aProgressMax);
   }
--- a/dom/xhr/XMLHttpRequestMainThread.h
+++ b/dom/xhr/XMLHttpRequestMainThread.h
@@ -712,17 +712,17 @@ protected:
 
   bool mErrorLoad;
   bool mErrorParsingXML;
   bool mWaitingForOnStopRequest;
   bool mProgressTimerIsActive;
   bool mIsHtml;
   bool mWarnAboutMultipartHtml;
   bool mWarnAboutSyncHtml;
-  int64_t mLoadTotal; // 0 if not known.
+  int64_t mLoadTotal; // -1 if not known.
   // Amount of script-exposed (i.e. after undoing gzip compresion) data
   // received.
   uint64_t mDataAvailable;
   // Number of HTTP message body bytes received so far. This quantity is
   // in the same units as Content-Length and mLoadTotal, and hence counts
   // compressed bytes when the channel has gzip Content-Encoding. If the
   // channel does not have Content-Encoding, this will be the same as
   // mDataReceived except between the OnProgress that changes mLoadTransferred
--- a/gfx/layers/apz/test/mochitest/mochitest.ini
+++ b/gfx/layers/apz/test/mochitest/mochitest.ini
@@ -61,10 +61,9 @@ skip-if = (toolkit == 'android') # mouse
 [test_touch_listeners_impacting_wheel.html]
 skip-if = (toolkit == 'android') || (toolkit == 'cocoa') # wheel events not supported on mobile, and synthesized wheel smooth-scrolling not supported on OS X
 [test_bug1253683.html]
 skip-if = (os == 'android') || (os == 'b2g') # uses wheel events which are not supported on mobile
 [test_group_zoom.html]
 skip-if = (toolkit != 'android') # only android supports zoom
 [test_group_pointerevents.html]
 # Windows touch injection doesn't work in automation, but this test can be run locally on a windows touch device.
-# On OS X we don't support touch events at all.
-skip-if = (toolkit == 'windows') || (toolkit == 'cocoa')
+skip-if = (toolkit == 'windows')
--- a/gfx/skia/README_MOZILLA
+++ b/gfx/skia/README_MOZILLA
@@ -1,9 +1,9 @@
-This is an import of Skia from 2014-07-28 (b1ab5fdd11bb358d738c1bfa63737dc65174a281)
+This is an import of Skia. See skia/include/core/SkMilestone.h for the milestone number.
 
 To update to a new version of Skia:
 
 - Clone Skia from upstream using the instructions here: https://sites.google.com/site/skiadocs/user-documentation/downloading
 - Copy the entire source tree from a Skia clone to mozilla-central/gfx/skia/skia
 - cd gfx/skia && ./gyp_mozbuild
 
 Once that's done, use git status to view the files that have changed. Keep an eye on GrUserConfig.h
--- a/gfx/thebes/gfxUserFontSet.cpp
+++ b/gfx/thebes/gfxUserFontSet.cpp
@@ -418,18 +418,18 @@ gfxUserFontEntry::LoadNextSrc()
     // or a download begins successfully
     while (mSrcIndex < numSrc) {
         gfxFontFaceSrc& currSrc = mSrcList[mSrcIndex];
 
         // src local ==> lookup and load immediately
 
         if (currSrc.mSourceType == gfxFontFaceSrc::eSourceType_Local) {
             // Don't look up local fonts if the font whitelist is being used.
-            gfxFontEntry* fe = gfxPlatformFontList::PlatformFontList()->
-                                 IsFontFamilyWhitelistActive() ?
+            gfxPlatformFontList* pfl = gfxPlatformFontList::PlatformFontList();
+            gfxFontEntry* fe = pfl && pfl->IsFontFamilyWhitelistActive() ?
                 nullptr :
                 gfxPlatform::GetPlatform()->LookupLocalFont(currSrc.mLocalName,
                                                             mWeight,
                                                             mStretch,
                                                             mStyle);
             nsTArray<gfxUserFontSet*> fontSets;
             GetUserFontSets(fontSets);
             for (gfxUserFontSet* fontSet : fontSets) {
--- a/ipc/glue/MessageChannel.h
+++ b/ipc/glue/MessageChannel.h
@@ -502,17 +502,22 @@ class MessageChannel : HasResultCodes
           }
           // In case the last type is the top one.
           if (curCount > topCount) {
             topName = curName;
             topType = curType;
             topCount = curCount;
           }
 
-          CrashReporter::AnnotatePendingIPC(q.size(), topCount, topName, topType);
+          CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("NumberOfPendingIPC"),
+                                             nsPrintfCString("%zu", q.size()));
+          CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("TopPendingIPCCount"),
+                                             nsPrintfCString("%u", topCount));
+          CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("TopPendingIPCName"),
+                                             nsPrintfCString("%s(0x%x)", topName, topType));
 
           mozalloc_handle_oom(n * sizeof(T));
         }
         return static_cast<T*>(p);
       }
       void deallocate(T* p, size_t n) {
         ::operator delete(p);
       }
--- a/js/src/builtin/Array.js
+++ b/js/src/builtin/Array.js
@@ -872,16 +872,71 @@ function ArrayToString() {
     var func = array.join;
 
     // Steps 5-6.
     if (!IsCallable(func))
         return callFunction(std_Object_toString, array);
     return callContentFunction(func, array);
 }
 
+// ES2017 draft rev f8a9be8ea4bd97237d176907a1e3080dce20c68f
+// 22.1.3.27 Array.prototype.toLocaleString ([ reserved1 [ , reserved2 ] ])
+// ES2017 Intl draft rev 78bbe7d1095f5ff3760ac4017ed366026e4cb276
+// 13.4.1 Array.prototype.toLocaleString ([ locales [ , options ]])
+function ArrayToLocaleString(locales, options) {
+    // Step 1 (ToObject already performed in native code).
+    assert(IsObject(this), "|this| should be an object");
+    var array = this;
+
+    // Step 2.
+    var len = ToLength(array.length);
+
+    // Step 4.
+    if (len === 0)
+        return "";
+
+    // Step 5.
+    var firstElement = array[0];
+
+    // Steps 6-7.
+    var R;
+    if (firstElement === undefined || firstElement === null) {
+        R = "";
+    } else {
+#if EXPOSE_INTL_API
+        R = ToString(callContentFunction(firstElement.toLocaleString, firstElement, locales, options));
+#else
+        R = ToString(callContentFunction(firstElement.toLocaleString, firstElement));
+#endif
+    }
+
+    // Step 3 (reordered).
+    // We don't (yet?) implement locale-dependent separators.
+    var separator = ",";
+
+    // Steps 8-9.
+    for (var k = 1; k < len; k++) {
+        // Step 9.b.
+        var nextElement = array[k];
+
+        // Steps 9.a, 9.c-e.
+        R += separator;
+        if (!(nextElement === undefined || nextElement === null)) {
+#if EXPOSE_INTL_API
+            R += ToString(callContentFunction(nextElement.toLocaleString, nextElement, locales, options));
+#else
+            R += ToString(callContentFunction(nextElement.toLocaleString, nextElement));
+#endif
+        }
+    }
+
+    // Step 10.
+    return R;
+}
+
 // ES 2016 draft Mar 25, 2016 22.1.2.5.
 function ArraySpecies() {
     // Step 1.
     return this;
 }
 
 // ES 2016 draft Mar 25, 2016 9.4.2.3.
 function ArraySpeciesCreate(originalArray, length) {
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -1991,30 +1991,32 @@ SetJitCompilerOption(JSContext* cx, unsi
 static bool
 GetJitCompilerOptions(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     RootedObject info(cx, JS_NewPlainObject(cx));
     if (!info)
         return false;
 
+    uint32_t intValue = 0;
     RootedValue value(cx);
 
 #define JIT_COMPILER_MATCH(key, string)                                \
     opt = JSJITCOMPILER_ ## key;                                       \
-    value.setInt32(JS_GetGlobalJitCompilerOption(cx, opt));            \
-    if (!JS_SetProperty(cx, info, string, value))                      \
-        return false;
+    if (JS_GetGlobalJitCompilerOption(cx, opt, &intValue)) {           \
+        value.setInt32(intValue);                                      \
+        if (!JS_SetProperty(cx, info, string, value))                  \
+            return false;                                              \
+    }
 
     JSJitCompilerOption opt = JSJITCOMPILER_NOT_AN_OPTION;
     JIT_COMPILER_OPTIONS(JIT_COMPILER_MATCH);
 #undef JIT_COMPILER_MATCH
 
     args.rval().setObject(*info);
-
     return true;
 }
 
 static bool
 SetIonCheckGraphCoherency(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     jit::JitOptions.checkGraphConsistency = ToBoolean(args.get(0));
--- a/js/src/builtin/TypedArray.js
+++ b/js/src/builtin/TypedArray.js
@@ -153,17 +153,17 @@ function TypedArrayEntries() {
     // Step 2-6.
     IsTypedArrayEnsuringArrayBuffer(O);
 
     // Step 7.
     return CreateArrayIterator(O, ITEM_KIND_KEY_AND_VALUE);
 }
 
 // ES6 draft rev30 (2014/12/24) 22.2.3.7 %TypedArray%.prototype.every(callbackfn[, thisArg]).
-function TypedArrayEvery(callbackfn, thisArg = undefined) {
+function TypedArrayEvery(callbackfn/*, thisArg*/) {
     // Steps 1-2.
     var O = this;
 
     // This function is not generic.
     // We want to make sure that we have an attached buffer, per spec prose.
     var isTypedArray = IsTypedArrayEnsuringArrayBuffer(O);
 
     // If we got here, `this` is either a typed array or a cross-compartment
@@ -178,17 +178,17 @@ function TypedArrayEvery(callbackfn, thi
 
     // Step 6.
     if (arguments.length === 0)
         ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, "%TypedArray%.prototype.every");
     if (!IsCallable(callbackfn))
         ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn));
 
     // Step 7.
-    var T = thisArg;
+    var T = arguments.length > 1 ? arguments[1] : void 0;
 
     // Steps 8-9.
     // Omit steps 9.a-9.c and the 'if' clause in step 9.d, since there are no holes in typed arrays.
     for (var k = 0; k < len; k++) {
         // Steps 9.d.i-9.d.ii.
         var kValue = O[k];
 
         // Steps 9.d.iii-9.d.iv.
@@ -240,17 +240,17 @@ function TypedArrayFill(value, start = 0
         O[k] = value;
     }
 
     // Step 13.
     return O;
 }
 
 // ES6 draft 32 (2015-02-02) 22.2.3.9 %TypedArray%.prototype.filter(callbackfn[, thisArg])
-function TypedArrayFilter(callbackfn, thisArg = undefined) {
+function TypedArrayFilter(callbackfn/*, thisArg*/) {
     // Step 1.
     var O = this;
 
     // Steps 2-3.
     // This function is not generic.
     // We want to make sure that we have an attached buffer, per spec prose.
     var isTypedArray = IsTypedArrayEnsuringArrayBuffer(O);
 
@@ -266,17 +266,17 @@ function TypedArrayFilter(callbackfn, th
 
     // Step 5.
     if (arguments.length === 0)
         ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, "%TypedArray%.prototype.filter");
     if (!IsCallable(callbackfn))
         ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn));
 
     // Step 6.
-    var T = thisArg;
+    var T = arguments.length > 1 ? arguments[1] : void 0;
 
     // Step 7.
     var defaultConstructor = _ConstructorForTypedArray(O);
 
     // Steps 8-9.
     var C = SpeciesConstructor(O, defaultConstructor);
 
     // Step 10.
@@ -288,20 +288,18 @@ function TypedArrayFilter(callbackfn, th
     // Steps 11, 13 and 13.g.
     for (var k = 0; k < len; k++) {
         // Steps 13.b-c.
         var kValue = O[k];
         // Steps 13.d-e.
         var selected = ToBoolean(callContentFunction(callbackfn, T, kValue, k, O));
         // Step 13.f.
         if (selected) {
-            // Step 13.f.i.
-            callFunction(std_Array_push, kept, kValue);
-            // Step 13.f.ii.
-            captured++;
+            // Steps 13.f.i-ii.
+            kept[captured++] = kValue;
         }
     }
 
     // Steps 14-15.
     var A = new C(captured);
 
     // Steps 16 and 17.c.
     for (var n = 0; n < captured; n++) {
@@ -309,17 +307,17 @@ function TypedArrayFilter(callbackfn, th
         A[n] = kept[n];
     }
 
     // Step 18.
     return A;
 }
 
 // ES6 draft rev28 (2014/10/14) 22.2.3.10 %TypedArray%.prototype.find(predicate[, thisArg]).
-function TypedArrayFind(predicate, thisArg = undefined) {
+function TypedArrayFind(predicate/*, thisArg*/) {
     // Steps 1-2.
     var O = this;
 
     // This function is not generic.
     // We want to make sure that we have an attached buffer, per spec prose.
     var isTypedArray = IsTypedArrayEnsuringArrayBuffer(O);
 
     // If we got here, `this` is either a typed array or a cross-compartment
@@ -334,34 +332,34 @@ function TypedArrayFind(predicate, thisA
 
     // Step 6.
     if (arguments.length === 0)
         ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, "%TypedArray%.prototype.find");
     if (!IsCallable(predicate))
         ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, predicate));
 
     // Step 7.
-    var T = thisArg;
+    var T = arguments.length > 1 ? arguments[1] : void 0;
 
     // Steps 8-9.
     // Steps a (implicit), and g.
     for (var k = 0; k < len; k++) {
         // Steps a-c.
         var kValue = O[k];
         // Steps d-f.
         if (callContentFunction(predicate, T, kValue, k, O))
             return kValue;
     }
 
     // Step 10.
     return undefined;
 }
 
 // ES6 draft rev28 (2014/10/14) 22.2.3.11 %TypedArray%.prototype.findIndex(predicate[, thisArg]).
-function TypedArrayFindIndex(predicate, thisArg = undefined) {
+function TypedArrayFindIndex(predicate/*, thisArg*/) {
     // Steps 1-2.
     var O = this;
 
     // This function is not generic.
     // We want to make sure that we have an attached buffer, per spec prose.
     var isTypedArray = IsTypedArrayEnsuringArrayBuffer(O);
 
     // If we got here, `this` is either a typed array or a cross-compartment
@@ -376,32 +374,32 @@ function TypedArrayFindIndex(predicate, 
 
     // Step 6.
     if (arguments.length === 0)
         ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, "%TypedArray%.prototype.findIndex");
     if (!IsCallable(predicate))
         ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, predicate));
 
     // Step 7.
-    var T = thisArg;
+    var T = arguments.length > 1 ? arguments[1] : void 0;
 
     // Steps 8-9.
     // Steps a (implicit), and g.
     for (var k = 0; k < len; k++) {
         // Steps a-f.
         if (callContentFunction(predicate, T, O[k], k, O))
             return k;
     }
 
     // Step 10.
     return -1;
 }
 
 // ES6 draft rev31 (2015-01-15) 22.1.3.10 %TypedArray%.prototype.forEach(callbackfn[,thisArg])
-function TypedArrayForEach(callbackfn, thisArg = undefined) {
+function TypedArrayForEach(callbackfn/*, thisArg*/) {
     // Step 1-2.
     var O = this;
 
     // This function is not generic.
     // We want to make sure that we have an attached buffer, per spec prose.
     var isTypedArray = IsTypedArrayEnsuringArrayBuffer(O);
 
     // If we got here, `this` is either a typed array or a cross-compartment
@@ -416,17 +414,17 @@ function TypedArrayForEach(callbackfn, t
 
     // Step 5.
     if (arguments.length === 0)
         ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'TypedArray.prototype.forEach');
     if (!IsCallable(callbackfn))
         ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn));
 
     // Step 6.
-    var T = thisArg;
+    var T = arguments.length > 1 ? arguments[1] : void 0;
 
     // Step 7-8.
     // Step 7, 8a (implicit) and 8e.
     for (var k = 0; k < len; k++) {
         // Step 8b-8c are unnecessary since the condition always holds true for TypedArray.
         // Step 8d.
         callContentFunction(callbackfn, T, O[k], k, O);
     }
@@ -583,17 +581,17 @@ function TypedArrayLastIndexOf(searchEle
             return k;
     }
 
     // Step 12.
     return -1;
 }
 
 // ES6 draft rev32 (2015-02-02) 22.2.3.18 %TypedArray%.prototype.map(callbackfn [, thisArg]).
-function TypedArrayMap(callbackfn, thisArg = undefined) {
+function TypedArrayMap(callbackfn/*, thisArg*/) {
     // Step 1.
     var O = this;
 
     // Steps 2-3.
     // This function is not generic.
     // We want to make sure that we have an attached buffer, per spec prose.
     var isTypedArray = IsTypedArrayEnsuringArrayBuffer(O);
 
@@ -609,17 +607,17 @@ function TypedArrayMap(callbackfn, thisA
 
     // Step 5.
     if (arguments.length === 0)
         ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, '%TypedArray%.prototype.map');
     if (!IsCallable(callbackfn))
         ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn));
 
     // Step 6.
-    var T = thisArg;
+    var T = arguments.length > 1 ? arguments[1] : void 0;
 
     // Step 7.
     var defaultConstructor = _ConstructorForTypedArray(O);
 
     // Steps 8-9.
     var C = SpeciesConstructor(O, defaultConstructor);
 
     // Steps 10-11.
@@ -945,17 +943,17 @@ function TypedArraySlice(start, end) {
         n++;
     }
 
     // Step 19.
     return A;
 }
 
 // ES6 draft rev30 (2014/12/24) 22.2.3.25 %TypedArray%.prototype.some(callbackfn[, thisArg]).
-function TypedArraySome(callbackfn, thisArg = undefined) {
+function TypedArraySome(callbackfn/*, thisArg*/) {
     // Steps 1-2.
     var O = this;
 
     // This function is not generic.
     // We want to make sure that we have an attached buffer, per spec prose.
     var isTypedArray = IsTypedArrayEnsuringArrayBuffer(O);
 
     // If we got here, `this` is either a typed array or a cross-compartment
@@ -970,17 +968,17 @@ function TypedArraySome(callbackfn, this
 
     // Step 6.
     if (arguments.length === 0)
         ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, "%TypedArray%.prototype.some");
     if (!IsCallable(callbackfn))
         ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn));
 
     // Step 7.
-    var T = thisArg;
+    var T = arguments.length > 1 ? arguments[1] : void 0;
 
     // Steps 8-9.
     // Omit steps 9.a-9.c and the 'if' clause in step 9.d, since there are no holes in typed arrays.
     for (var k = 0; k < len; k++) {
         // Steps 9.d.i-9.d.ii.
         var kValue = O[k];
 
         // Steps 9.d.iii-9.d.iv.
@@ -1332,30 +1330,31 @@ function TypedArrayFrom(constructor, tar
     if (usingIterator !== undefined) {
         // Steps 10.a-b.
         var iterator = GetIterator(items, usingIterator);
 
         // Step 10.c.
         var values = new List();
 
         // Steps 10.d-e.
+        var i = 0;
         while (true) {
             // Steps 10.e.i-ii.
             var next = callContentFunction(iterator.next, iterator);
             if (!IsObject(next))
                 ThrowTypeError(JSMSG_NEXT_RETURNED_PRIMITIVE);
 
             // Steps 10.e.iii-vi.
             if (next.done)
                 break;
-            callFunction(std_Array_push, values, next.value);
+            values[i++] = next.value;
         }
 
         // Step 10.f.
-        var len = values.length;
+        var len = i;
 
         // Steps 10.g-h.
         // There is no need to implement the 22.2.2.1.2 - TypedArrayAllocOrInit() method,
         // since `%TypedArray%(object)` currently doesn't call this self-hosted TypedArrayFrom().
         var targetObj = new C(len);
 
         // Steps 10.i-j.
         for (var k = 0; k < len; k++) {
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug1165905.js
@@ -0,0 +1,6 @@
+load(libdir + "asserts.js");
+var oldOpts = getJitCompilerOptions();
+for (var k in oldOpts)
+    setJitCompilerOption(k, oldOpts[k]);
+var newOpts = getJitCompilerOptions();
+assertDeepEq(oldOpts, newOpts);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/testStringFromCodePoint.js
@@ -0,0 +1,32 @@
+setJitCompilerOption("ion.warmup.trigger", 20);
+
+function testBailout() {
+    function f(v, r) {
+        for (var i = 0; i < 50; ++i) {
+            // Ensure DCE and LICM don't eliminate calls to fromCodePoint in
+            // case the input argument is not a valid code point.
+            if (i === 0) {
+                r();
+            }
+            String.fromCodePoint(v);
+            String.fromCodePoint(v);
+            String.fromCodePoint(v);
+        }
+    }
+
+    var result = [];
+    function r() {
+        result.push("ok");
+    }
+
+    do {
+        result.length = 0;
+        try {
+            f(0, r);
+            f(0, r);
+            f(0x10ffff + 1, r);
+        } catch (e) {}
+        assertEq(result.length, 3);
+    } while (!inIon());
+}
+testBailout();
--- a/js/src/jit/BaselineCacheIR.cpp
+++ b/js/src/jit/BaselineCacheIR.cpp
@@ -690,16 +690,46 @@ BaselineCacheIRCompiler::emitGuardIsObje
     FailurePath* failure;
     if (!addFailurePath(&failure))
         return false;
     masm.branchTestObject(Assembler::NotEqual, input, failure->label());
     return true;
 }
 
 bool
+BaselineCacheIRCompiler::emitGuardType()
+{
+    ValueOperand input = allocator.useRegister(masm, reader.valOperandId());
+    JSValueType type = reader.valueType();
+
+    FailurePath* failure;
+    if (!addFailurePath(&failure))
+        return false;
+
+    switch (type) {
+      case JSVAL_TYPE_STRING:
+        masm.branchTestString(Assembler::NotEqual, input, failure->label());
+        break;
+      case JSVAL_TYPE_SYMBOL:
+        masm.branchTestSymbol(Assembler::NotEqual, input, failure->label());
+        break;
+      case JSVAL_TYPE_DOUBLE:
+        masm.branchTestNumber(Assembler::NotEqual, input, failure->label());
+        break;
+      case JSVAL_TYPE_BOOLEAN:
+        masm.branchTestBoolean(Assembler::NotEqual, input, failure->label());
+        break;
+      default:
+        MOZ_CRASH("Unexpected type");
+    }
+
+    return true;
+}
+
+bool
 BaselineCacheIRCompiler::emitGuardShape()
 {
     Register obj = allocator.useRegister(masm, reader.objOperandId());
     AutoScratchRegister scratch(allocator, masm);
 
     FailurePath* failure;
     if (!addFailurePath(&failure))
         return false;
--- a/js/src/jit/BaselineInspector.cpp
+++ b/js/src/jit/BaselineInspector.cpp
@@ -809,19 +809,23 @@ BaselineInspector::commonSetPropFunction
     return true;
 }
 
 static MIRType
 GetCacheIRExpectedInputType(ICCacheIR_Monitored* stub)
 {
     CacheIRReader reader(stub->stubInfo());
 
-    // For now, all CacheIR stubs expect an object.
-    MOZ_ALWAYS_TRUE(reader.matchOp(CacheOp::GuardIsObject, ObjOperandId(0)));
-    return MIRType::Object;
+    if (reader.matchOp(CacheOp::GuardIsObject, ValOperandId(0)))
+        return MIRType::Object;
+    if (reader.matchOp(CacheOp::GuardType, ValOperandId(0))) {
+        JSValueType type = reader.valueType();
+        return MIRTypeFromValueType(type);
+    }
+    MOZ_CRASH("Unexpected instruction");
 }
 
 MIRType
 BaselineInspector::expectedPropertyAccessInputType(jsbytecode* pc)
 {
     if (!hasBaselineScript())
         return MIRType::Value;
 
@@ -865,20 +869,16 @@ BaselineInspector::expectedPropertyAcces
           case ICStub::GetElem_UnboxedPropertyName:
           case ICStub::GetElem_String:
           case ICStub::GetElem_Dense:
           case ICStub::GetElem_TypedArray:
           case ICStub::GetElem_UnboxedArray:
             stubType = MIRType::Object;
             break;
 
-          case ICStub::GetProp_Primitive:
-            stubType = MIRTypeFromValueType(stub->toGetProp_Primitive()->primitiveType());
-            break;
-
           case ICStub::GetProp_StringLength:
             stubType = MIRType::String;
             break;
 
           case ICStub::CacheIR_Monitored:
             stubType = GetCacheIRExpectedInputType(stub->toCacheIR_Monitored());
             if (stubType == MIRType::Value)
                 return MIRType::Value;
--- a/js/src/jit/CacheIR.cpp
+++ b/js/src/jit/CacheIR.cpp
@@ -63,18 +63,22 @@ GetPropIRGenerator::tryAttachStub(Maybe<
         if (!emitted_ && !tryAttachUnboxed(*writer, obj, objId))
             return false;
         if (!emitted_ && !tryAttachUnboxedExpando(*writer, obj, objId))
             return false;
         if (!emitted_ && !tryAttachTypedObject(*writer, obj, objId))
             return false;
         if (!emitted_ && !tryAttachModuleNamespace(*writer, obj, objId))
             return false;
+        return true;
     }
 
+    if (!emitted_ && !tryAttachPrimitive(*writer, valId))
+        return false;
+
     return true;
 }
 
 static bool
 IsCacheableNoProperty(JSContext* cx, JSObject* obj, JSObject* holder, Shape* shape, jsid id,
                       jsbytecode* pc)
 {
     if (shape)
@@ -410,8 +414,58 @@ GetPropIRGenerator::tryAttachModuleNames
 
     // Check for the specific namespace object.
     writer.guardSpecificObject(objId, ns);
 
     ObjOperandId envId = writer.loadObject(env);
     EmitLoadSlotResult(writer, envId, env, shape);
     return true;
 }
+
+bool
+GetPropIRGenerator::tryAttachPrimitive(CacheIRWriter& writer, ValOperandId valId)
+{
+    MOZ_ASSERT(!emitted_);
+
+    JSValueType primitiveType;
+    RootedNativeObject proto(cx_);
+    if (val_.isString()) {
+        if (name_ == cx_->names().length) {
+            // String length is special-cased, see js::GetProperty.
+            return true;
+        }
+        primitiveType = JSVAL_TYPE_STRING;
+        proto = MaybeNativeObject(GetBuiltinPrototypePure(cx_->global(), JSProto_String));
+    } else if (val_.isNumber()) {
+        primitiveType = JSVAL_TYPE_DOUBLE;
+        proto = MaybeNativeObject(GetBuiltinPrototypePure(cx_->global(), JSProto_Number));
+    } else if (val_.isBoolean()) {
+        primitiveType = JSVAL_TYPE_BOOLEAN;
+        proto = MaybeNativeObject(GetBuiltinPrototypePure(cx_->global(), JSProto_Boolean));
+    } else if (val_.isSymbol()) {
+        primitiveType = JSVAL_TYPE_SYMBOL;
+        proto = MaybeNativeObject(GetBuiltinPrototypePure(cx_->global(), JSProto_Symbol));
+    } else {
+        MOZ_ASSERT(val_.isNullOrUndefined() || val_.isMagic());
+        return true;
+    }
+    if (!proto)
+        return true;
+
+    // Instantiate this property, for use during Ion compilation.
+    RootedId id(cx_, NameToId(name_));
+    if (IsIonEnabled(cx_))
+        EnsureTrackPropertyTypes(cx_, proto, id);
+
+    // For now, only look for properties directly set on the prototype.
+    Shape* shape = proto->lookup(cx_, id);
+    if (!shape || !shape->hasSlot() || !shape->hasDefaultGetter())
+        return true;
+
+    writer.guardType(valId, primitiveType);
+
+    ObjOperandId protoId = writer.loadObject(proto);
+    writer.guardShape(protoId, proto->lastProperty());
+    EmitLoadSlotResult(writer, protoId, proto, shape);
+
+    emitted_ = true;
+    return true;
+}
--- a/js/src/jit/CacheIR.h
+++ b/js/src/jit/CacheIR.h
@@ -75,16 +75,17 @@ class ObjOperandId : public OperandId
     explicit ObjOperandId(uint16_t id) : OperandId(id) {}
 
     bool operator==(const ObjOperandId& other) const { return id_ == other.id_; }
     bool operator!=(const ObjOperandId& other) const { return id_ != other.id_; }
 };
 
 #define CACHE_IR_OPS(_)                   \
     _(GuardIsObject)                      \
+    _(GuardType)                          \
     _(GuardShape)                         \
     _(GuardGroup)                         \
     _(GuardProto)                         \
     _(GuardClass)                         \
     _(GuardSpecificObject)                \
     _(GuardNoDetachedTypedObjects)        \
     _(GuardNoUnboxedExpando)              \
     _(GuardAndLoadUnboxedExpando)         \
@@ -242,16 +243,21 @@ class MOZ_RAII CacheIRWriter
     uint32_t codeLength() const {
         return buffer_.length();
     }
 
     ObjOperandId guardIsObject(ValOperandId val) {
         writeOpWithOperandId(CacheOp::GuardIsObject, val);
         return ObjOperandId(val.id());
     }
+    void guardType(ValOperandId val, JSValueType type) {
+        writeOpWithOperandId(CacheOp::GuardType, val);
+        static_assert(sizeof(type) == sizeof(uint8_t), "JSValueType should fit in a byte");
+        buffer_.writeByte(uint32_t(type));
+    }
     void guardShape(ObjOperandId obj, Shape* shape) {
         writeOpWithOperandId(CacheOp::GuardShape, obj);
         addStubWord(uintptr_t(shape), StubField::GCType::Shape);
     }
     void guardGroup(ObjOperandId obj, ObjectGroup* group) {
         writeOpWithOperandId(CacheOp::GuardGroup, obj);
         addStubWord(uintptr_t(group), StubField::GCType::ObjectGroup);
     }
@@ -417,16 +423,18 @@ class MOZ_RAII GetPropIRGenerator
                                               ObjOperandId objId);
     MOZ_MUST_USE bool tryAttachTypedObject(CacheIRWriter& writer, HandleObject obj,
                                            ObjOperandId objId);
     MOZ_MUST_USE bool tryAttachObjectLength(CacheIRWriter& writer, HandleObject obj,
                                             ObjOperandId objId);
     MOZ_MUST_USE bool tryAttachModuleNamespace(CacheIRWriter& writer, HandleObject obj,
                                                ObjOperandId objId);
 
+    MOZ_MUST_USE bool tryAttachPrimitive(CacheIRWriter& writer, ValOperandId valId);
+
     GetPropIRGenerator(const GetPropIRGenerator&) = delete;
     GetPropIRGenerator& operator=(const GetPropIRGenerator&) = delete;
 
   public:
     GetPropIRGenerator(JSContext* cx, jsbytecode* pc, HandleValue val, HandlePropertyName name,
                        MutableHandleValue res);
 
     bool emitted() const { return emitted_; }
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -7671,16 +7671,45 @@ CodeGenerator::visitFromCharCode(LFromCh
                   ool->entry());
 
     masm.movePtr(ImmPtr(&GetJitContext()->runtime->staticStrings().unitStaticTable), output);
     masm.loadPtr(BaseIndex(output, code, ScalePointer), output);
 
     masm.bind(ool->rejoin());
 }
 
+typedef JSString* (*StringFromCodePointFn)(JSContext*, int32_t);
+static const VMFunction StringFromCodePointInfo =
+    FunctionInfo<StringFromCodePointFn>(jit::StringFromCodePoint, "StringFromCodePoint");
+
+void
+CodeGenerator::visitFromCodePoint(LFromCodePoint* lir)
+{
+    Register codePoint = ToRegister(lir->codePoint());
+    Register output = ToRegister(lir->output());
+    LSnapshot* snapshot = lir->snapshot();
+
+    OutOfLineCode* ool = oolCallVM(StringFromCodePointInfo, lir, ArgList(codePoint),
+                                   StoreRegisterTo(output));
+
+    // Use a bailout if the input is not a valid code point, because
+    // MFromCodePoint is movable and it'd be observable when a moved
+    // fromCodePoint throws an exception before its actual call site.
+    bailoutCmp32(Assembler::Above, codePoint, Imm32(unicode::NonBMPMax), snapshot);
+
+    // OOL path if code point >= UNIT_STATIC_LIMIT.
+    masm.branch32(Assembler::AboveOrEqual, codePoint, Imm32(StaticStrings::UNIT_STATIC_LIMIT),
+                  ool->entry());
+
+    masm.movePtr(ImmPtr(&GetJitContext()->runtime->staticStrings().unitStaticTable), output);
+    masm.loadPtr(BaseIndex(output, codePoint, ScalePointer), output);
+
+    masm.bind(ool->rejoin());
+}
+
 void
 CodeGenerator::visitSinCos(LSinCos *lir)
 {
     Register temp = ToRegister(lir->temp());
     Register params = ToRegister(lir->temp2());
     FloatRegister input = ToFloatRegister(lir->input());
     FloatRegister outputSin = ToFloatRegister(lir->outputSin());
     FloatRegister outputCos = ToFloatRegister(lir->outputCos());
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -273,16 +273,17 @@ class CodeGenerator final : public CodeG
     void visitIsNullOrLikeUndefinedV(LIsNullOrLikeUndefinedV* lir);
     void visitIsNullOrLikeUndefinedT(LIsNullOrLikeUndefinedT* lir);
     void visitIsNullOrLikeUndefinedAndBranchV(LIsNullOrLikeUndefinedAndBranchV* lir);
     void visitIsNullOrLikeUndefinedAndBranchT(LIsNullOrLikeUndefinedAndBranchT* lir);
     void emitConcat(LInstruction* lir, Register lhs, Register rhs, Register output);
     void visitConcat(LConcat* lir);
     void visitCharCodeAt(LCharCodeAt* lir);
     void visitFromCharCode(LFromCharCode* lir);
+    void visitFromCodePoint(LFromCodePoint* lir);
     void visitSinCos(LSinCos *lir);
     void visitStringSplit(LStringSplit* lir);
     void visitFunctionEnvironment(LFunctionEnvironment* lir);
     void visitCallGetProperty(LCallGetProperty* lir);
     void visitCallGetElement(LCallGetElement* lir);
     void visitCallSetElement(LCallSetElement* lir);
     void visitCallInitElementArray(LCallInitElementArray* lir);
     void visitThrow(LThrow* lir);
--- a/js/src/jit/InlinableNatives.h
+++ b/js/src/jit/InlinableNatives.h
@@ -70,16 +70,17 @@
     _(IsRegExpObject)               \
     _(RegExpPrototypeOptimizable)   \
     _(RegExpInstanceOptimizable)    \
     _(GetFirstDollarIndex)          \
                                     \
     _(String)                       \
     _(StringCharCodeAt)             \
     _(StringFromCharCode)           \
+    _(StringFromCodePoint)          \
     _(StringCharAt)                 \
                                     \
     _(IntrinsicStringReplaceString) \
     _(IntrinsicStringSplitString)   \
                                     \
     _(ObjectCreate)                 \
                                     \
     _(SimdInt32x4)                  \
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -845,16 +845,17 @@ class IonBuilder
     InliningStatus inlineMathFRound(CallInfo& callInfo);
     InliningStatus inlineMathFunction(CallInfo& callInfo, MMathFunction::Function function);
 
     // String natives.
     InliningStatus inlineStringObject(CallInfo& callInfo);
     InliningStatus inlineStrCharCodeAt(CallInfo& callInfo);
     InliningStatus inlineConstantCharCodeAt(CallInfo& callInfo);
     InliningStatus inlineStrFromCharCode(CallInfo& callInfo);
+    InliningStatus inlineStrFromCodePoint(CallInfo& callInfo);
     InliningStatus inlineStrCharAt(CallInfo& callInfo);
 
     // String intrinsics.
     InliningStatus inlineStringReplaceString(CallInfo& callInfo);
     InliningStatus inlineConstantStringSplitString(CallInfo& callInfo);
     InliningStatus inlineStringSplitString(CallInfo& callInfo);
 
     // RegExp intrinsics.
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -1896,16 +1896,29 @@ LIRGenerator::visitFromCharCode(MFromCha
     MOZ_ASSERT(code->type() == MIRType::Int32);
 
     LFromCharCode* lir = new(alloc()) LFromCharCode(useRegister(code));
     define(lir, ins);
     assignSafepoint(lir, ins);
 }
 
 void
+LIRGenerator::visitFromCodePoint(MFromCodePoint* ins)
+{
+    MDefinition* codePoint = ins->getOperand(0);
+
+    MOZ_ASSERT(codePoint->type() == MIRType::Int32);
+
+    LFromCodePoint* lir = new(alloc()) LFromCodePoint(useRegister(codePoint));
+    assignSnapshot(lir, Bailout_BoundsCheck);
+    define(lir, ins);
+    assignSafepoint(lir, ins);
+}
+
+void
 LIRGenerator::visitStart(MStart* start)
 {
     LStart* lir = new(alloc()) LStart;
 
     // Create a snapshot that captures the initial state of the function.
     assignSnapshot(lir, Bailout_ArgumentCheck);
     if (start->block()->graph().entryBlock() == start->block())
         lirGraph_.setEntrySnapshot(lir->snapshot());
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -143,16 +143,17 @@ class LIRGenerator : public LIRGenerator
     void visitAdd(MAdd* ins);
     void visitSub(MSub* ins);
     void visitMul(MMul* ins);
     void visitDiv(MDiv* ins);
     void visitMod(MMod* ins);
     void visitConcat(MConcat* ins);
     void visitCharCodeAt(MCharCodeAt* ins);
     void visitFromCharCode(MFromCharCode* ins);
+    void visitFromCodePoint(MFromCodePoint* ins);
     void visitSinCos(MSinCos *ins);
     void visitStringSplit(MStringSplit* ins);
     void visitStart(MStart* start);
     void visitOsrEntry(MOsrEntry* entry);
     void visitNop(MNop* nop);
     void visitLimitedTruncate(MLimitedTruncate* nop);
     void visitOsrValue(MOsrValue* value);
     void visitOsrEnvironmentChain(MOsrEnvironmentChain* object);
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -197,16 +197,18 @@ IonBuilder::inlineNativeCall(CallInfo& c
 
       // String natives.
       case InlinableNative::String:
         return inlineStringObject(callInfo);
       case InlinableNative::StringCharCodeAt:
         return inlineStrCharCodeAt(callInfo);
       case InlinableNative::StringFromCharCode:
         return inlineStrFromCharCode(callInfo);
+      case InlinableNative::StringFromCodePoint:
+        return inlineStrFromCodePoint(callInfo);
       case InlinableNative::StringCharAt:
         return inlineStrCharAt(callInfo);
 
       // String intrinsics.
       case InlinableNative::IntrinsicStringReplaceString:
         return inlineStringReplaceString(callInfo);
       case InlinableNative::IntrinsicStringSplitString:
         return inlineStringSplitString(callInfo);
@@ -1698,20 +1700,38 @@ IonBuilder::inlineStrFromCharCode(CallIn
 
     if (getInlineReturnType() != MIRType::String)
         return InliningStatus_NotInlined;
     if (callInfo.getArg(0)->type() != MIRType::Int32)
         return InliningStatus_NotInlined;
 
     callInfo.setImplicitlyUsedUnchecked();
 
-    MToInt32* charCode = MToInt32::New(alloc(), callInfo.getArg(0));
-    current->add(charCode);
-
-    MFromCharCode* string = MFromCharCode::New(alloc(), charCode);
+    MFromCharCode* string = MFromCharCode::New(alloc(), callInfo.getArg(0));
+    current->add(string);
+    current->push(string);
+    return InliningStatus_Inlined;
+}
+
+IonBuilder::InliningStatus
+IonBuilder::inlineStrFromCodePoint(CallInfo& callInfo)
+{
+    if (callInfo.argc() != 1 || callInfo.constructing()) {
+        trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
+        return InliningStatus_NotInlined;
+    }
+
+    if (getInlineReturnType() != MIRType::String)
+        return InliningStatus_NotInlined;
+    if (callInfo.getArg(0)->type() != MIRType::Int32)
+        return InliningStatus_NotInlined;
+
+    callInfo.setImplicitlyUsedUnchecked();
+
+    MFromCodePoint* string = MFromCodePoint::New(alloc(), callInfo.getArg(0));
     current->add(string);
     current->push(string);
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningStatus
 IonBuilder::inlineStrCharAt(CallInfo& callInfo)
 {
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -7322,16 +7322,43 @@ class MFromCharCode
     MOZ_MUST_USE bool writeRecoverData(CompactBufferWriter& writer) const override;
     bool canRecoverOnBailout() const override {
         return true;
     }
 
     ALLOW_CLONE(MFromCharCode)
 };
 
+class MFromCodePoint
+  : public MUnaryInstruction,
+    public IntPolicy<0>::Data
+{
+    explicit MFromCodePoint(MDefinition* codePoint)
+      : MUnaryInstruction(codePoint)
+    {
+        setGuard(); // throws on invalid code point
+        setMovable();
+        setResultType(MIRType::String);
+    }
+
+  public:
+    INSTRUCTION_HEADER(FromCodePoint)
+    TRIVIAL_NEW_WRAPPERS
+
+    AliasSet getAliasSet() const override {
+        return AliasSet::None();
+    }
+    bool congruentTo(const MDefinition* ins) const override {
+        return congruentIfOperandsEqual(ins);
+    }
+    bool possiblyCalls() const override {
+        return true;
+    }
+};
+
 class MSinCos
   : public MUnaryInstruction,
     public FloatingPointPolicy<0>::Data
 {
     const MathCache* cache_;
 
     MSinCos(MDefinition *input, const MathCache *cache) : MUnaryInstruction(input), cache_(cache)
     {
--- a/js/src/jit/MOpcodes.h
+++ b/js/src/jit/MOpcodes.h
@@ -101,16 +101,17 @@ namespace jit {
     _(Add)                                                                  \
     _(Sub)                                                                  \
     _(Mul)                                                                  \
     _(Div)                                                                  \
     _(Mod)                                                                  \
     _(Concat)                                                               \
     _(CharCodeAt)                                                           \
     _(FromCharCode)                                                         \
+    _(FromCodePoint)                                                        \
     _(SinCos)                                                               \
     _(StringSplit)                                                          \
     _(Substr)                                                               \
     _(Return)                                                               \
     _(Throw)                                                                \
     _(Box)                                                                  \
     _(Unbox)                                                                \
     _(GuardObject)                                                          \
--- a/js/src/jit/MacroAssembler.cpp
+++ b/js/src/jit/MacroAssembler.cpp
@@ -950,17 +950,17 @@ MacroAssembler::newGCFatInlineString(Reg
 {
     allocateNonObject(result, temp, js::gc::AllocKind::FAT_INLINE_STRING, fail);
 }
 
 void
 MacroAssembler::copySlotsFromTemplate(Register obj, const NativeObject* templateObj,
                                       uint32_t start, uint32_t end)
 {
-    uint32_t nfixed = Min(templateObj->numFixedSlots(), end);
+    uint32_t nfixed = Min(templateObj->numFixedSlotsForCompilation(), end);
     for (unsigned i = start; i < nfixed; i++)
         storeValue(templateObj->getFixedSlot(i), Address(obj, NativeObject::getFixedSlotOffset(i)));
 }
 
 void
 MacroAssembler::fillSlotsWithConstantValue(Address base, Register temp,
                                            uint32_t start, uint32_t end, const Value& v)
 {
@@ -1250,17 +1250,17 @@ MacroAssembler::initGCThing(Register obj
             // then this would need to store emptyObjectElementsShared in that case.
             MOZ_ASSERT(!ntemplate->isSharedMemory());
 
             storePtr(ImmPtr(emptyObjectElements), Address(obj, NativeObject::offsetOfElements()));
 
             initGCSlots(obj, temp, ntemplate, initContents);
 
             if (ntemplate->hasPrivate() && !ntemplate->is<TypedArrayObject>()) {
-                uint32_t nfixed = ntemplate->numFixedSlots();
+                uint32_t nfixed = ntemplate->numFixedSlotsForCompilation();
                 storePtr(ImmPtr(ntemplate->getPrivate()),
                          Address(obj, NativeObject::getPrivateDataOffset(nfixed)));
             }
         }
     } else if (templateObj->is<InlineTypedObject>()) {
         JS::AutoAssertOnGC nogc; // off-thread, so cannot GC
         size_t nbytes = templateObj->as<InlineTypedObject>().size();
         const uint8_t* memory = templateObj->as<InlineTypedObject>().inlineTypedMem(nogc);
--- a/js/src/jit/SharedIC.cpp
+++ b/js/src/jit/SharedIC.cpp
@@ -395,21 +395,16 @@ ICStub::trace(JSTracer* trc)
       case ICStub::GetName_Env6:
         static_cast<ICGetName_Env<6>*>(this)->traceEnvironments(trc);
         break;
       case ICStub::GetIntrinsic_Constant: {
         ICGetIntrinsic_Constant* constantStub = toGetIntrinsic_Constant();
         TraceEdge(trc, &constantStub->value(), "baseline-getintrinsic-constant-value");
         break;
       }
-      case ICStub::GetProp_Primitive: {
-        ICGetProp_Primitive* propStub = toGetProp_Primitive();
-        TraceEdge(trc, &propStub->protoShape(), "baseline-getprop-primitive-stub-shape");
-        break;
-      }
       case ICStub::GetProp_CallDOMProxyNative:
       case ICStub::GetProp_CallDOMProxyWithGenerationNative: {
         ICGetPropCallDOMProxyNativeStub* propStub;
         if (kind() == ICStub::GetProp_CallDOMProxyNative)
             propStub = toGetProp_CallDOMProxyNative();
         else
             propStub = toGetProp_CallDOMProxyWithGenerationNative();
         propStub->receiverGuard().trace(trc);
@@ -2539,71 +2534,16 @@ TryAttachNativeGetAccessorPropStub(JSCon
     }
     if (!newStub)
         return false;
     stub->addNewStub(newStub);
     *attached = true;
     return true;
 }
 
-static bool
-TryAttachPrimitiveGetPropStub(JSContext* cx, SharedStubInfo* info,
-                              ICGetProp_Fallback* stub, HandlePropertyName name,
-                              HandleValue val, HandleValue res, bool* attached)
-{
-    MOZ_ASSERT(!*attached);
-
-    JSValueType primitiveType;
-    RootedNativeObject proto(cx);
-    Rooted<GlobalObject*> global(cx, &info->script()->global());
-    if (val.isString()) {
-        primitiveType = JSVAL_TYPE_STRING;
-        proto = GlobalObject::getOrCreateStringPrototype(cx, global);
-    } else if (val.isSymbol()) {
-        primitiveType = JSVAL_TYPE_SYMBOL;
-        proto = GlobalObject::getOrCreateSymbolPrototype(cx, global);
-    } else if (val.isNumber()) {
-        primitiveType = JSVAL_TYPE_DOUBLE;
-        proto = GlobalObject::getOrCreateNumberPrototype(cx, global);
-    } else {
-        MOZ_ASSERT(val.isBoolean());
-        primitiveType = JSVAL_TYPE_BOOLEAN;
-        proto = GlobalObject::getOrCreateBooleanPrototype(cx, global);
-    }
-    if (!proto)
-        return false;
-
-    // Instantiate this property, for use during Ion compilation.
-    RootedId id(cx, NameToId(name));
-    if (IsIonEnabled(cx))
-        EnsureTrackPropertyTypes(cx, proto, id);
-
-    // For now, only look for properties directly set on the prototype.
-    RootedShape shape(cx, proto->lookup(cx, id));
-    if (!shape || !shape->hasSlot() || !shape->hasDefaultGetter())
-        return true;
-
-    bool isFixedSlot;
-    uint32_t offset;
-    GetFixedOrDynamicSlotOffset(shape, &isFixedSlot, &offset);
-
-    ICStub* monitorStub = stub->fallbackMonitorStub()->firstMonitorStub();
-
-    JitSpew(JitSpew_BaselineIC, "  Generating GetProp_Primitive stub");
-    ICGetProp_Primitive::Compiler compiler(cx, info->engine(), monitorStub, primitiveType, proto,
-                                           isFixedSlot, offset);
-    ICStub* newStub = compiler.getStub(compiler.getStubSpace(info->outerScript(cx)));
-    if (!newStub)
-        return false;
-
-    stub->addNewStub(newStub);
-    *attached = true;
-    return true;
-}
-
 bool
 CheckHasNoSuchProperty(JSContext* cx, JSObject* obj, PropertyName* name,
                        JSObject** lastProto, size_t* protoChainDepthOut)
 {
     size_t depth = 0;
     JSObject* curObj = obj;
     while (curObj) {
         if (curObj->isNative()) {
@@ -2772,23 +2712,16 @@ DoGetPropFallback(JSContext* cx, void* p
 
     if (!TryAttachMagicArgumentsGetPropStub(cx, &info, stub, name, val,
                                             res, &attached))
         return false;
     if (attached)
         return true;
 
 
-    if (val.isString() || val.isNumber() || val.isBoolean()) {
-        if (!TryAttachPrimitiveGetPropStub(cx, &info, stub, name, val, res, &attached))
-            return false;
-        if (attached)
-            return true;
-    }
-
     MOZ_ASSERT(!attached);
     if (!isTemporarilyUnoptimizable)
         stub->noteUnoptimizableAccess();
 
     return true;
 }
 
 typedef bool (*DoGetPropFallbackFn)(JSContext*, void*, ICGetProp_Fallback*,
@@ -2863,63 +2796,16 @@ ICGetProp_StringLength::Compiler::genera
     EmitReturnFromIC(masm);
 
     // Failure case - jump to next stub
     masm.bind(&failure);
     EmitStubGuardFailure(masm);
     return true;
 }
 
-bool
-ICGetProp_Primitive::Compiler::generateStubCode(MacroAssembler& masm)
-{
-    Label failure;
-    switch (primitiveType_) {
-      case JSVAL_TYPE_STRING:
-        masm.branchTestString(Assembler::NotEqual, R0, &failure);
-        break;
-      case JSVAL_TYPE_SYMBOL:
-        masm.branchTestSymbol(Assembler::NotEqual, R0, &failure);
-        break;
-      case JSVAL_TYPE_DOUBLE: // Also used for int32.
-        masm.branchTestNumber(Assembler::NotEqual, R0, &failure);
-        break;
-      case JSVAL_TYPE_BOOLEAN:
-        masm.branchTestBoolean(Assembler::NotEqual, R0, &failure);
-        break;
-      default:
-        MOZ_CRASH("unexpected type");
-    }
-
-    AllocatableGeneralRegisterSet regs(availableGeneralRegs(1));
-    Register holderReg = regs.takeAny();
-    Register scratchReg = regs.takeAny();
-
-    // Verify the shape of the prototype.
-    masm.movePtr(ImmGCPtr(prototype_.get()), holderReg);
-
-    Address shapeAddr(ICStubReg, ICGetProp_Primitive::offsetOfProtoShape());
-    masm.loadPtr(Address(holderReg, ShapedObject::offsetOfShape()), scratchReg);
-    masm.branchPtr(Assembler::NotEqual, shapeAddr, scratchReg, &failure);
-
-    if (!isFixedSlot_)
-        masm.loadPtr(Address(holderReg, NativeObject::offsetOfSlots()), holderReg);
-
-    masm.load32(Address(ICStubReg, ICGetProp_Primitive::offsetOfOffset()), scratchReg);
-    masm.loadValue(BaseIndex(holderReg, scratchReg, TimesOne), R0);
-
-    // Enter type monitor IC to type-check result.
-    EmitEnterTypeMonitorIC(masm);
-
-    // Failure case - jump to next stub
-    masm.bind(&failure);
-    EmitStubGuardFailure(masm);
-    return true;
-}
-
 ICGetPropNativeStub*
 ICGetPropNativeCompiler::getStub(ICStubSpace* space)
 {
     ReceiverGuard guard(obj_);
 
     switch (kind) {
       case ICStub::GetName_Global: {
         MOZ_ASSERT(obj_ != holder_);
@@ -3699,27 +3585,16 @@ BaselineScript::noteAccessedGetter(uint3
 {
     ICEntry& entry = icEntryFromPCOffset(pcOffset);
     ICFallbackStub* stub = entry.fallbackStub();
 
     if (stub->isGetProp_Fallback())
         stub->toGetProp_Fallback()->noteAccessedGetter();
 }
 
-ICGetProp_Primitive::ICGetProp_Primitive(JitCode* stubCode, ICStub* firstMonitorStub,
-                                         JSValueType primitiveType, Shape* protoShape,
-                                         uint32_t offset)
-  : ICMonitoredStub(GetProp_Primitive, stubCode, firstMonitorStub),
-    protoShape_(protoShape),
-    offset_(offset)
-{
-    extra_ = uint16_t(primitiveType);
-    MOZ_ASSERT(JSValueType(extra_) == primitiveType);
-}
-
 ICGetPropNativeStub::ICGetPropNativeStub(ICStub::Kind kind, JitCode* stubCode,
                                          ICStub* firstMonitorStub,
                                          ReceiverGuard guard, uint32_t offset)
   : ICMonitoredStub(kind, stubCode, firstMonitorStub),
     receiverGuard_(guard),
     offset_(offset)
 { }
 
--- a/js/src/jit/SharedIC.h
+++ b/js/src/jit/SharedIC.h
@@ -2369,84 +2369,16 @@ class ICGetProp_Generic : public ICMonit
         {}
 
         ICStub* getStub(ICStubSpace* space) {
             return newStub<ICGetProp_Generic>(space, getStubCode(), firstMonitorStub_);
         }
     };
 };
 
-// Stub for accessing a property on a primitive's prototype.
-class ICGetProp_Primitive : public ICMonitoredStub
-{
-    friend class ICStubSpace;
-
-  protected: // Protected to silence Clang warning.
-    // Shape of String.prototype/Number.prototype to check for.
-    GCPtrShape protoShape_;
-
-    // Fixed or dynamic slot offset.
-    uint32_t offset_;
-
-    ICGetProp_Primitive(JitCode* stubCode, ICStub* firstMonitorStub, JSValueType primitiveType,
-                        Shape* protoShape, uint32_t offset);
-
-  public:
-    GCPtrShape& protoShape() {
-        return protoShape_;
-    }
-    JSValueType primitiveType() const {
-        return JSValueType(extra_);
-    }
-
-    static size_t offsetOfProtoShape() {
-        return offsetof(ICGetProp_Primitive, protoShape_);
-    }
-
-    static size_t offsetOfOffset() {
-        return offsetof(ICGetProp_Primitive, offset_);
-    }
-
-    class Compiler : public ICStubCompiler {
-        ICStub* firstMonitorStub_;
-        JSValueType primitiveType_;
-        RootedObject prototype_;
-        bool isFixedSlot_;
-        uint32_t offset_;
-
-        MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm);
-
-      protected:
-        virtual int32_t getKey() const {
-            static_assert(sizeof(JSValueType) == 1, "JSValueType should fit in one byte");
-            return static_cast<int32_t>(engine_) |
-                  (static_cast<int32_t>(kind) << 1) |
-                  (static_cast<int32_t>(isFixedSlot_) << 17) |
-                  (static_cast<int32_t>(primitiveType_) << 25);
-        }
-
-      public:
-        Compiler(JSContext* cx, Engine engine, ICStub* firstMonitorStub, JSValueType primitiveType,
-                 HandleObject prototype, bool isFixedSlot, uint32_t offset)
-          : ICStubCompiler(cx, ICStub::GetProp_Primitive, engine),
-            firstMonitorStub_(firstMonitorStub),
-            primitiveType_(primitiveType),
-            prototype_(cx, prototype),
-            isFixedSlot_(isFixedSlot),
-            offset_(offset)
-        {}
-
-        ICStub* getStub(ICStubSpace* space) {
-            RootedShape protoShape(cx, prototype_->as<NativeObject>().lastProperty());
-            return newStub<ICGetProp_Primitive>(space, getStubCode(), firstMonitorStub_,
-                                                primitiveType_, protoShape, offset_);
-        }
-    };
-};
-
 // Stub for accessing a string's length.
 class ICGetProp_StringLength : public ICStub
 {
     friend class ICStubSpace;
 
     explicit ICGetProp_StringLength(JitCode* stubCode)
       : ICStub(GetProp_StringLength, stubCode)
     {}
--- a/js/src/jit/SharedICList.h
+++ b/js/src/jit/SharedICList.h
@@ -30,17 +30,16 @@ namespace jit {
     _(Compare_NumberWithUndefined)               \
     _(Compare_String)                            \
     _(Compare_Boolean)                           \
     _(Compare_Object)                            \
     _(Compare_ObjectWithUndefined)               \
     _(Compare_Int32WithBoolean)                  \
                                                  \
     _(GetProp_Fallback)                          \
-    _(GetProp_Primitive)                         \
     _(GetProp_StringLength)                      \
     _(GetProp_CallScripted)                      \
     _(GetProp_CallNative)                        \
     _(GetProp_CallNativeGlobal)                  \
     _(GetProp_CallDOMProxyNative)                \
     _(GetProp_CallDOMProxyWithGenerationNative)  \
     _(GetProp_DOMProxyShadowed)                  \
     _(GetProp_ArgumentsLength)                   \
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -407,16 +407,26 @@ StringFromCharCode(JSContext* cx, int32_
     char16_t c = char16_t(code);
 
     if (StaticStrings::hasUnit(c))
         return cx->staticStrings().getUnit(c);
 
     return NewStringCopyN<CanGC>(cx, &c, 1);
 }
 
+JSString*
+StringFromCodePoint(JSContext* cx, int32_t codePoint)
+{
+    RootedValue rval(cx, Int32Value(codePoint));
+    if (!str_fromCodePoint_one_arg(cx, rval, &rval))
+        return nullptr;
+
+    return rval.toString();
+}
+
 bool
 SetProperty(JSContext* cx, HandleObject obj, HandlePropertyName name, HandleValue value,
             bool strict, jsbytecode* pc)
 {
     RootedId id(cx, NameToId(name));
 
     JSOp op = JSOp(*pc);
 
--- a/js/src/jit/VMFunctions.h
+++ b/js/src/jit/VMFunctions.h
@@ -623,16 +623,17 @@ bool StringsEqual(JSContext* cx, HandleS
 MOZ_MUST_USE bool ArrayPopDense(JSContext* cx, HandleObject obj, MutableHandleValue rval);
 MOZ_MUST_USE bool ArrayPushDense(JSContext* cx, HandleObject obj, HandleValue v, uint32_t* length);
 MOZ_MUST_USE bool ArrayShiftDense(JSContext* cx, HandleObject obj, MutableHandleValue rval);
 JSString* ArrayJoin(JSContext* cx, HandleObject array, HandleString sep);
 
 MOZ_MUST_USE bool
 CharCodeAt(JSContext* cx, HandleString str, int32_t index, uint32_t* code);
 JSFlatString* StringFromCharCode(JSContext* cx, int32_t code);
+JSString* StringFromCodePoint(JSContext* cx, int32_t codePoint);
 
 MOZ_MUST_USE bool
 SetProperty(JSContext* cx, HandleObject obj, HandlePropertyName name, HandleValue value,
             bool strict, jsbytecode* pc);
 
 MOZ_MUST_USE bool
 InterruptCheck(JSContext* cx);
 
--- a/js/src/jit/shared/LIR-shared.h
+++ b/js/src/jit/shared/LIR-shared.h
@@ -4032,16 +4032,31 @@ class LFromCharCode : public LInstructio
         setOperand(0, code);
     }
 
     const LAllocation* code() {
         return this->getOperand(0);
     }
 };
 
+// Convert uint32 code point to a string.
+class LFromCodePoint : public LInstructionHelper<1, 1, 0>
+{
+  public:
+    LIR_HEADER(FromCodePoint)
+
+    explicit LFromCodePoint(const LAllocation& codePoint) {
+        setOperand(0, codePoint);
+    }
+
+    const LAllocation* codePoint() {
+        return this->getOperand(0);
+    }
+};
+
 // Calculates sincos(x) and returns two values (sin/cos).
 class LSinCos : public LCallInstructionHelper<2, 1, 2>
 {
   public:
     LIR_HEADER(SinCos)
 
     LSinCos(const LAllocation &input, const LDefinition &temp, const LDefinition &temp2)
     {
--- a/js/src/jit/shared/LOpcodes-shared.h
+++ b/js/src/jit/shared/LOpcodes-shared.h
@@ -191,16 +191,17 @@
     _(DivPowTwoI)                   \
     _(ModI)                         \
     _(ModPowTwoI)                   \
     _(ModD)                         \
     _(BinaryV)                      \
     _(Concat)                       \
     _(CharCodeAt)                   \
     _(FromCharCode)                 \
+    _(FromCodePoint)                \
     _(SinCos)                       \
     _(StringSplit)                  \
     _(Int32ToDouble)                \
     _(Float32ToDouble)              \
     _(DoubleToFloat32)              \
     _(Int32ToFloat32)               \
     _(ValueToDouble)                \
     _(ValueToInt32)                 \
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -6281,50 +6281,62 @@ JS_SetGlobalJitCompilerOption(JSContext*
       case JSJITCOMPILER_ION_INTERRUPT_WITHOUT_SIGNAL:
         jit::JitOptions.ionInterruptWithoutSignals = !!value;
         break;
       default:
         break;
     }
 }
 
-JS_PUBLIC_API(int)
-JS_GetGlobalJitCompilerOption(JSContext* cx, JSJitCompilerOption opt)
-{
+JS_PUBLIC_API(bool)
+JS_GetGlobalJitCompilerOption(JSContext* cx, JSJitCompilerOption opt, uint32_t* valueOut)
+{
+    MOZ_ASSERT(valueOut);
 #ifndef JS_CODEGEN_NONE
     JSRuntime* rt = cx->runtime();
     switch (opt) {
       case JSJITCOMPILER_BASELINE_WARMUP_TRIGGER:
-        return jit::JitOptions.baselineWarmUpThreshold;
+        *valueOut = jit::JitOptions.baselineWarmUpThreshold;
+        break;
       case JSJITCOMPILER_ION_WARMUP_TRIGGER:
-        return jit::JitOptions.forcedDefaultIonWarmUpThreshold.isSome()
-             ? jit::JitOptions.forcedDefaultIonWarmUpThreshold.ref()
-             : jit::OptimizationInfo::CompilerWarmupThreshold;
+        *valueOut = jit::JitOptions.forcedDefaultIonWarmUpThreshold.isSome()
+                  ? jit::JitOptions.forcedDefaultIonWarmUpThreshold.ref()
+                  : jit::OptimizationInfo::CompilerWarmupThreshold;
+        break;
       case JSJITCOMPILER_ION_FORCE_IC:
-        return jit::JitOptions.forceInlineCaches;
+        *valueOut = jit::JitOptions.forceInlineCaches;
+        break;
       case JSJITCOMPILER_ION_ENABLE:
-        return JS::ContextOptionsRef(cx).ion();
+        *valueOut = JS::ContextOptionsRef(cx).ion();
+        break;
       case JSJITCOMPILER_BASELINE_ENABLE:
-        return JS::ContextOptionsRef(cx).baseline();
+        *valueOut = JS::ContextOptionsRef(cx).baseline();
+        break;
       case JSJITCOMPILER_OFFTHREAD_COMPILATION_ENABLE:
-        return rt->canUseOffthreadIonCompilation();
+        *valueOut = rt->canUseOffthreadIonCompilation();
+        break;
       case JSJITCOMPILER_ASMJS_ATOMICS_ENABLE:
-        return jit::JitOptions.asmJSAtomicsEnable ? 1 : 0;
+        *valueOut = jit::JitOptions.asmJSAtomicsEnable ? 1 : 0;
         break;
       case JSJITCOMPILER_WASM_TEST_MODE:
-        return jit::JitOptions.wasmTestMode ? 1 : 0;
+        *valueOut = jit::JitOptions.wasmTestMode ? 1 : 0;
+        break;
       case JSJITCOMPILER_WASM_FOLD_OFFSETS:
-        return jit::JitOptions.wasmFoldOffsets ? 1 : 0;
+        *valueOut = jit::JitOptions.wasmFoldOffsets ? 1 : 0;
+        break;
       case JSJITCOMPILER_ION_INTERRUPT_WITHOUT_SIGNAL:
-        return jit::JitOptions.ionInterruptWithoutSignals ? 1 : 0;
-      default:
+        *valueOut = jit::JitOptions.ionInterruptWithoutSignals ? 1 : 0;
         break;
+      default:
+        return false;
     }
+#else
+    *valueOut = 0;
 #endif
-    return 0;
+    return true;
 }
 
 /************************************************************************/
 
 #if !defined(STATIC_EXPORTABLE_JS_API) && !defined(STATIC_JS_API) && defined(XP_WIN)
 
 #include "jswin.h"
 
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -5708,18 +5708,18 @@ typedef enum JSJitCompilerOption {
     JIT_COMPILER_OPTIONS(JIT_COMPILER_DECLARE)
 #undef JIT_COMPILER_DECLARE
 
     JSJITCOMPILER_NOT_AN_OPTION
 } JSJitCompilerOption;
 
 extern JS_PUBLIC_API(void)
 JS_SetGlobalJitCompilerOption(JSContext* cx, JSJitCompilerOption opt, uint32_t value);
-extern JS_PUBLIC_API(int)
-JS_GetGlobalJitCompilerOption(JSContext* cx, JSJitCompilerOption opt);
+extern JS_PUBLIC_API(bool)
+JS_GetGlobalJitCompilerOption(JSContext* cx, JSJitCompilerOption opt, uint32_t* valueOut);
 
 /**
  * Convert a uint32_t index into a jsid.
  */
 extern JS_PUBLIC_API(bool)
 JS_IndexToId(JSContext* cx, uint32_t index, JS::MutableHandleId);
 
 /**
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -1123,67 +1123,60 @@ struct ArrayJoinDenseKernelFunctor {
     {}
 
     template <JSValueType Type>
     DenseElementResult operator()() {
         return ArrayJoinDenseKernel<SeparatorOp, Type>(cx, sepOp, obj, length, sb, numProcessed);
     }
 };
 
-template <bool Locale, typename SeparatorOp>
+template <typename SeparatorOp>
 static bool
 ArrayJoinKernel(JSContext* cx, SeparatorOp sepOp, HandleObject obj, uint32_t length,
                StringBuffer& sb)
 {
     uint32_t i = 0;
 
-    if (!Locale && !ObjectMayHaveExtraIndexedProperties(obj)) {
+    if (!ObjectMayHaveExtraIndexedProperties(obj)) {
         ArrayJoinDenseKernelFunctor<SeparatorOp> functor(cx, sepOp, obj, length, sb, &i);
         DenseElementResult result = CallBoxedOrUnboxedSpecialization(functor, obj);
         if (result == DenseElementResult::Failure)
             return false;
     }
 
     if (i != length) {
         RootedValue v(cx);
         while (i < length) {
             if (!CheckForInterrupt(cx))
                 return false;
 
             bool hole;
             if (!GetElement(cx, obj, i, &hole, &v))
                 return false;
             if (!hole && !v.isNullOrUndefined()) {
-                if (Locale) {
-                    RootedValue fun(cx);
-                    if (!GetProperty(cx, v, cx->names().toLocaleString, &fun))
-                        return false;
-
-                    if (!Call(cx, fun, v, &v))
-                        return false;
-                }
                 if (!ValueToStringBuffer(cx, v, sb))
                     return false;
             }
 
             if (++i != length && !sepOp(cx, sb))
                 return false;
         }
     }
 
     return true;
 }
 
-template <bool Locale>
+/* ES5 15.4.4.5 */
 bool
-ArrayJoin(JSContext* cx, CallArgs& args)
+js::array_join(JSContext* cx, unsigned argc, Value* vp)
 {
-    // This method is shared by Array.prototype.join and
-    // Array.prototype.toLocaleString. The steps in ES5 are nearly the same, so
-    // the annotations in this function apply to both toLocaleString and join.
+    JS_CHECK_RECURSION(cx, return false);
+
+    AutoSPSEntry pseudoFrame(cx->runtime(), "Array.prototype.join");
+    CallArgs args = CallArgsFromVp(argc, vp);
 
     // Step 1
     RootedObject obj(cx, ToObject(cx, args.thisv()));
     if (!obj)
         return false;
 
     AutoCycleDetector detector(cx, obj);
     if (!detector.init())
@@ -1196,33 +1189,33 @@ ArrayJoin(JSContext* cx, CallArgs& args)
 
     // Steps 2 and 3
     uint32_t length;
     if (!GetLengthProperty(cx, obj, &length))
         return false;
 
     // Steps 4 and 5
     RootedLinearString sepstr(cx);
-    if (!Locale && args.hasDefined(0)) {
+    if (args.hasDefined(0)) {
         JSString *s = ToString<CanGC>(cx, args[0]);
         if (!s)
             return false;
         sepstr = s->ensureLinear(cx);
         if (!sepstr)
             return false;
     } else {
         sepstr = cx->names().comma;
     }
 
     // Step 6 is implicit in the loops below.
 
     // An optimized version of a special case of steps 7-11: when length==1 and
     // the 0th element is a string, ToString() of that element is a no-op and
     // so it can be immediately returned as the result.
-    if (length == 1 && !Locale && GetAnyBoxedOrUnboxedInitializedLength(obj) == 1) {
+    if (length == 1 && GetAnyBoxedOrUnboxedInitializedLength(obj) == 1) {
         Value elem0 = GetAnyBoxedOrUnboxedDenseElement(obj, 0);
         if (elem0.isString()) {
             args.rval().set(elem0);
             return true;
         }
     }
 
     StringBuffer sb(cx);
@@ -1239,63 +1232,87 @@ ArrayJoin(JSContext* cx, CallArgs& args)
     }
 
     if (length > 0 && !sb.reserve(res.value()))
         return false;
 
     // Various optimized versions of steps 7-10.
     if (seplen == 0) {
         EmptySeparatorOp op;
-        if (!ArrayJoinKernel<Locale>(cx, op, obj, length, sb))
+        if (!ArrayJoinKernel(cx, op, obj, length, sb))
             return false;
     } else if (seplen == 1) {
         char16_t c = sepstr->latin1OrTwoByteChar(0);
         if (c <= JSString::MAX_LATIN1_CHAR) {
             CharSeparatorOp<Latin1Char> op(c);
-            if (!ArrayJoinKernel<Locale>(cx, op, obj, length, sb))
+            if (!ArrayJoinKernel(cx, op, obj, length, sb))
                 return false;
         } else {
             CharSeparatorOp<char16_t> op(c);
-            if (!ArrayJoinKernel<Locale>(cx, op, obj, length, sb))
+            if (!ArrayJoinKernel(cx, op, obj, length, sb))
                 return false;
         }
     } else {
         StringSeparatorOp op(sepstr);
-        if (!ArrayJoinKernel<Locale>(cx, op, obj, length, sb))
+        if (!ArrayJoinKernel(cx, op, obj, length, sb))
             return false;
     }
 
     // Step 11
     JSString *str = sb.finishString();
     if (!str)
         return false;
 
     args.rval().setString(str);
     return true;
 }
 
-/* ES5 15.4.4.3 */
+// ES2017 draft rev f8a9be8ea4bd97237d176907a1e3080dce20c68f
+// 22.1.3.27 Array.prototype.toLocaleString ([ reserved1 [ , reserved2 ] ])
+// ES2017 Intl draft rev 78bbe7d1095f5ff3760ac4017ed366026e4cb276
+// 13.4.1 Array.prototype.toLocaleString ([ locales [ , options ]])
 static bool
 array_toLocaleString(JSContext* cx, unsigned argc, Value* vp)
 {
     JS_CHECK_RECURSION(cx, return false);
 
     CallArgs args = CallArgsFromVp(argc, vp);
-    return ArrayJoin<true>(cx, args);
-}
-
-/* ES5 15.4.4.5 */
-bool
-js::array_join(JSContext* cx, unsigned argc, Value* vp)
-{
-    JS_CHECK_RECURSION(cx, return false);
-
-    AutoSPSEntry pseudoFrame(cx->runtime(), "Array.prototype.join");
-    CallArgs args = CallArgsFromVp(argc, vp);
-    return ArrayJoin<false>(cx, args);
+
+    // Step 1
+    RootedObject obj(cx, ToObject(cx, args.thisv()));
+    if (!obj)
+        return false;
+
+    // Avoid calling into self-hosted code if the array is empty.
+    if (obj->is<ArrayObject>() && obj->as<ArrayObject>().length() == 0) {
+        args.rval().setString(cx->names().empty);
+        return true;
+    }
+    if (obj->is<UnboxedArrayObject>() && obj->as<UnboxedArrayObject>().length() == 0) {
+        args.rval().setString(cx->names().empty);
+        return true;
+    }
+
+    AutoCycleDetector detector(cx, obj);
+    if (!detector.init())
+        return false;
+
+    if (detector.foundCycle()) {
+        args.rval().setString(cx->names().empty);
+        return true;
+    }
+
+    FixedInvokeArgs<2> args2(cx);
+
+    args2[0].set(args.get(0));
+    args2[1].set(args.get(1));
+
+    // Steps 2-10.
+    RootedValue thisv(cx, ObjectValue(*obj));
+    return CallSelfHostedFunction(cx, cx->names().ArrayToLocaleString, thisv, args2, args.rval());
 }
 
 /* vector must point to rooted memory. */
 static bool
 InitArrayElements(JSContext* cx, HandleObject obj, uint32_t start,
                   uint32_t count, const Value* vector,
                   ShouldUpdateTypes updateTypes = ShouldUpdateTypes::Update)
 {
--- a/js/src/jsdate.cpp
+++ b/js/src/jsdate.cpp
@@ -1040,27 +1040,27 @@ ParseDate(const CharT* s, size_t length,
             } else {
                 return false;
             }
             prevc = 0;
         } else if (c == '/' || c == ':' || c == '+' || c == '-') {
             prevc = c;
         } else {
             size_t st = i - 1;
-            int k;
             while (i < length) {
                 c = s[i];
                 if (!(('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z')))
                     break;
                 i++;
             }
 
             if (i <= st + 1)
                 return false;
 
+            int k;
             for (k = ArrayLength(wtb); --k >= 0;) {
                 if (RegionMatches(wtb[k], 0, s, st, i - st)) {
                     int action = ttb[k];
                     if (action != 0) {
                         if (action < 0) {
                             /*
                              * AM/PM. Count 12:30 AM as 00:30, 12:30 PM as
                              * 12:30, instead of blindly adding 12 if PM.
@@ -1264,17 +1264,17 @@ DateObject::fillLocalTimeSlots()
 {
     /* Check if the cache is already populated. */
     if (!getReservedSlot(LOCAL_TIME_SLOT).isUndefined() &&
         getReservedSlot(TZA_SLOT).toDouble() == DateTimeInfo::localTZA())
     {
         return;
     }
 
-    /* Remember timezone used to generate the local cache. */
+    /* Remember time zone used to generate the local cache. */
     setReservedSlot(TZA_SLOT, DoubleValue(DateTimeInfo::localTZA()));
 
     double utcTime = UTCTime().toNumber();
 
     if (!IsFinite(utcTime)) {
         for (size_t ind = COMPONENTS_START_SLOT; ind < RESERVED_SLOTS; ind++)
             setReservedSlot(ind, DoubleValue(utcTime));
         return;
@@ -2422,74 +2422,39 @@ static const char * const days[] =
 {
    "Sun","Mon","Tue","Wed","Thu","Fri","Sat"
 };
 static const char * const months[] =
 {
    "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
 };
 
-
-// Avoid dependence on PRMJ_FormatTimeUSEnglish, because it
-// requires a PRMJTime... which only has 16-bit years.  Sub-ECMA.
-static void
-print_gmt_string(char* buf, size_t size, double utctime)
-{
-    MOZ_ASSERT(NumbersAreIdentical(TimeClip(utctime).toDouble(), utctime));
-    snprintf(buf, size, "%s, %.2d %s %.4d %.2d:%.2d:%.2d GMT",
-             days[int(WeekDay(utctime))],
-             int(DateFromTime(utctime)),
-             months[int(MonthFromTime(utctime))],
-             int(YearFromTime(utctime)),
-             int(HourFromTime(utctime)),
-             int(MinFromTime(utctime)),
-             int(SecFromTime(utctime)));
-}
-
-static void
-print_iso_string(char* buf, size_t size, double utctime)
-{
-    MOZ_ASSERT(NumbersAreIdentical(TimeClip(utctime).toDouble(), utctime));
-    snprintf(buf, size, "%.4d-%.2d-%.2dT%.2d:%.2d:%.2d.%.3dZ",
-             int(YearFromTime(utctime)),
-             int(MonthFromTime(utctime)) + 1,
-             int(DateFromTime(utctime)),
-             int(HourFromTime(utctime)),
-             int(MinFromTime(utctime)),
-             int(SecFromTime(utctime)),
-             int(msFromTime(utctime)));
-}
-
-static void
-print_iso_extended_string(char* buf, size_t size, double utctime)
-{
-    MOZ_ASSERT(NumbersAreIdentical(TimeClip(utctime).toDouble(), utctime));
-    snprintf(buf, size, "%+.6d-%.2d-%.2dT%.2d:%.2d:%.2d.%.3dZ",
-             int(YearFromTime(utctime)),
-             int(MonthFromTime(utctime)) + 1,
-             int(DateFromTime(utctime)),
-             int(HourFromTime(utctime)),
-             int(MinFromTime(utctime)),
-             int(SecFromTime(utctime)),
-             int(msFromTime(utctime)));
-}
-
 /* ES5 B.2.6. */
 MOZ_ALWAYS_INLINE bool
 date_toGMTString_impl(JSContext* cx, const CallArgs& args)
 {
     double utctime = args.thisv().toObject().as<DateObject>().UTCTime().toNumber();
 
-    char buf[100];
-    if (!IsFinite(utctime))
-        SprintfLiteral(buf, js_NaN_date_str);
-    else
-        print_gmt_string(buf, sizeof buf, utctime);
-
-    JSString* str = JS_NewStringCopyZ(cx, buf);
+    JSString* str;
+    if (!IsFinite(utctime)) {
+        str = NewStringCopyZ<CanGC>(cx, js_NaN_date_str);
+    } else {
+        char buf[100];
+        SprintfLiteral(buf, "%s, %.2d %s %.4d %.2d:%.2d:%.2d GMT",
+                       days[int(WeekDay(utctime))],
+                       int(DateFromTime(utctime)),
+                       months[int(MonthFromTime(utctime))],
+                       int(YearFromTime(utctime)),
+                       int(HourFromTime(utctime)),
+                       int(MinFromTime(utctime)),
+                       int(SecFromTime(utctime)));
+
+        str = NewStringCopyZ<CanGC>(cx, buf);
+    }
+
     if (!str)
         return false;
     args.rval().setString(str);
     return true;
 }
 
 static bool
 date_toGMTString(JSContext* cx, unsigned argc, Value* vp)
@@ -2505,22 +2470,37 @@ date_toISOString_impl(JSContext* cx, con
     double utctime = args.thisv().toObject().as<DateObject>().UTCTime().toNumber();
     if (!IsFinite(utctime)) {
         JS_ReportErrorNumberASCII(cx, js::GetErrorMessage, nullptr, JSMSG_INVALID_DATE);
         return false;
     }
 
     char buf[100];
     int year = int(YearFromTime(utctime));
-    if (year < 0 || year > 9999)
-        print_iso_extended_string(buf, sizeof buf, utctime);
-    else
-        print_iso_string(buf, sizeof buf, utctime);
-
-    JSString* str = JS_NewStringCopyZ(cx, buf);
+    if (year < 0 || year > 9999) {
+        SprintfLiteral(buf, "%+.6d-%.2d-%.2dT%.2d:%.2d:%.2d.%.3dZ",
+                       int(YearFromTime(utctime)),
+                       int(MonthFromTime(utctime)) + 1,
+                       int(DateFromTime(utctime)),
+                       int(HourFromTime(utctime)),
+                       int(MinFromTime(utctime)),
+                       int(SecFromTime(utctime)),
+                       int(msFromTime(utctime)));
+    } else {
+        SprintfLiteral(buf, "%.4d-%.2d-%.2dT%.2d:%.2d:%.2d.%.3dZ",
+                       int(YearFromTime(utctime)),
+                       int(MonthFromTime(utctime)) + 1,
+                       int(DateFromTime(utctime)),
+                       int(HourFromTime(utctime)),
+                       int(MinFromTime(utctime)),
+                       int(SecFromTime(utctime)),
+                       int(msFromTime(utctime)));
+    }
+
+    JSString* str = NewStringCopyZ<CanGC>(cx, buf);
     if (!str)
         return false;
     args.rval().setString(str);
     return true;
 
 }
 
 static bool
@@ -2563,225 +2543,215 @@ date_toJSON(JSContext* cx, unsigned argc
                                           JSMSG_BAD_TOISOSTRING_PROP);
         return false;
     }
 
     /* Step 6. */
     return Call(cx, toISO, obj, args.rval());
 }
 
-/* for Date.toLocaleFormat; interface to PRMJTime date struct.
- */
-static void
-new_explode(double timeval, PRMJTime* split)
+/* Interface to PRMJTime date struct. */
+static PRMJTime
+ToPRMJTime(double localTime)
 {
-    double year = YearFromTime(timeval);
-
-    split->tm_usec = int32_t(msFromTime(timeval)) * 1000;
-    split->tm_sec = int8_t(SecFromTime(timeval));
-    split->tm_min = int8_t(MinFromTime(timeval));
-    split->tm_hour = int8_t(HourFromTime(timeval));
-    split->tm_mday = int8_t(DateFromTime(timeval));
-    split->tm_mon = int8_t(MonthFromTime(timeval));
-    split->tm_wday = int8_t(WeekDay(timeval));
-    split->tm_year = year;
-    split->tm_yday = int16_t(DayWithinYear(timeval, year));
-
-    /* not sure how this affects things, but it doesn't seem
-       to matter. */
-    split->tm_isdst = (DaylightSavingTA(timeval) != 0);
+    double year = YearFromTime(localTime);
+
+    PRMJTime prtm;
+    prtm.tm_usec = int32_t(msFromTime(localTime)) * 1000;
+    prtm.tm_sec = int8_t(SecFromTime(localTime));
+    prtm.tm_min = int8_t(MinFromTime(localTime));
+    prtm.tm_hour = int8_t(HourFromTime(localTime));
+    prtm.tm_mday = int8_t(DateFromTime(localTime));
+    prtm.tm_mon = int8_t(MonthFromTime(localTime));
+    prtm.tm_wday = int8_t(WeekDay(localTime));
+    prtm.tm_year = year;
+    prtm.tm_yday = int16_t(DayWithinYear(localTime, year));
+
+    // XXX: DaylightSavingTA expects utc-time argument.
+    prtm.tm_isdst = (DaylightSavingTA(localTime) != 0);
+
+    return prtm;
 }
 
-typedef enum formatspec {
-    FORMATSPEC_FULL, FORMATSPEC_DATE, FORMATSPEC_TIME
-} formatspec;
-
-/* helper function */
+enum class FormatSpec {
+    DateTime,
+    Date,
+    Time
+};
+
 static bool
-date_format(JSContext* cx, double date, formatspec format, MutableHandleValue rval)
+FormatDate(JSContext* cx, double utcTime, FormatSpec format, MutableHandleValue rval)
 {
-    char buf[100];
-    char tzbuf[100];
-    bool usetz;
-    size_t i, tzlen;
-    PRMJTime split;
-
-    if (!IsFinite(date)) {
-        SprintfLiteral(buf, js_NaN_date_str);
+    JSString* str;
+    if (!IsFinite(utcTime)) {
+        str = NewStringCopyZ<CanGC>(cx, js_NaN_date_str);
     } else {
-        MOZ_ASSERT(NumbersAreIdentical(TimeClip(date).toDouble(), date));
-
-        double local = LocalTime(date);
-
-        /* offset from GMT in minutes.  The offset includes daylight savings,
-           if it applies. */
-        int minutes = (int) floor(AdjustTime(date) / msPerMinute);
-
-        /* map 510 minutes to 0830 hours */
-        int offset = (minutes / 60) * 100 + minutes % 60;
-
-        /* print as "Wed Nov 05 19:38:03 GMT-0800 (PST) 1997" The TZA is
-         * printed as 'GMT-0800' rather than as 'PST' to avoid
-         * operating-system dependence on strftime (which
-         * PRMJ_FormatTimeUSEnglish calls, for %Z only.)  win32 prints
-         * PST as 'Pacific Standard Time.'  This way we always know
-         * what we're getting, and can parse it if we produce it.
-         * The OS TZA string is included as a comment.
-         */
-
-        /* get a timezone string from the OS to include as a
-           comment. */
-        new_explode(date, &split);
-        if (PRMJ_FormatTime(tzbuf, sizeof tzbuf, "(%Z)", &split) != 0) {
-
-            /* Decide whether to use the resulting timezone string.
+        MOZ_ASSERT(NumbersAreIdentical(TimeClip(utcTime).toDouble(), utcTime));
+
+        double localTime = LocalTime(utcTime);
+
+        int offset = 0;
+        char tzbuf[100];
+        bool usetz = false;
+        if (format == FormatSpec::DateTime || format == FormatSpec::Time) {
+            /*
+             * Offset from GMT in minutes.  The offset includes daylight
+             * savings, if it applies.
+             */
+            int minutes = (int) floor((localTime - utcTime) / msPerMinute);
+
+            /* Map 510 minutes to 0830 hours. */
+            offset = (minutes / 60) * 100 + minutes % 60;
+
+            /*
+             * Print as "Wed Nov 05 19:38:03 GMT-0800 (PST) 1997".
              *
-             * Reject it if it contains any non-ASCII, non-alphanumeric
-             * characters.  It's then likely in some other character
-             * encoding, and we probably won't display it correctly.
+             * The TZA is printed as 'GMT-0800' rather than as 'PST' to avoid
+             * operating-system dependence on strftime (which PRMJ_FormatTime
+             * calls, for %Z only.)  win32 prints PST as
+             * 'Pacific Standard Time.'  This way we always know what we're
+             * getting, and can parse it if we produce it.  The OS time zone
+             * string is included as a comment.
              */
-            usetz = true;
-            tzlen = strlen(tzbuf);
-            if (tzlen > 100) {
-                usetz = false;
-            } else {
-                for (i = 0; i < tzlen; i++) {
+
+            /* get a time zone string from the OS to include as a comment. */
+            PRMJTime prtm = ToPRMJTime(utcTime);
+            size_t tzlen = PRMJ_FormatTime(tzbuf, sizeof tzbuf, "(%Z)", &prtm);
+            if (tzlen != 0) {
+                /*
+                 * Decide whether to use the resulting time zone string.
+                 *
+                 * Reject it if it contains any non-ASCII, non-alphanumeric
+                 * characters.  It's then likely in some other character
+                 * encoding, and we probably won't display it correctly.
+                 */
+                usetz = true;
+                for (size_t i = 0; i < tzlen; i++) {
                     char16_t c = tzbuf[i];
-                    if (c > 127 ||
-                        !(isalpha(c) || isdigit(c) ||
-                          c == ' ' || c == '(' || c == ')' || c == '.')) {
+                    if (c > 127 || !(isalnum(c) || c == ' ' || c == '(' || c == ')' || c == '.')) {
                         usetz = false;
+                        break;
                     }
                 }
+
+                /* Also reject it if it's not parenthesized or if it's '()'. */
+                if (tzbuf[0] != '(' || tzbuf[1] == ')')
+                    usetz = false;
             }
-
-            /* Also reject it if it's not parenthesized or if it's '()'. */
-            if (tzbuf[0] != '(' || tzbuf[1] == ')')
-                usetz = false;
-        } else
-            usetz = false;
-
+        }
+
+        char buf[100];
         switch (format) {
-          case FORMATSPEC_FULL:
-            /*
-             * Avoid dependence on PRMJ_FormatTimeUSEnglish, because it
-             * requires a PRMJTime... which only has 16-bit years.  Sub-ECMA.
-             */
+          case FormatSpec::DateTime:
             /* Tue Oct 31 2000 09:41:40 GMT-0800 (PST) */
             SprintfLiteral(buf, "%s %s %.2d %.4d %.2d:%.2d:%.2d GMT%+.4d%s%s",
-                           days[int(WeekDay(local))],
-                           months[int(MonthFromTime(local))],
-                           int(DateFromTime(local)),
-                           int(YearFromTime(local)),
-                           int(HourFromTime(local)),
-                           int(MinFromTime(local)),
-                           int(SecFromTime(local)),
+                           days[int(WeekDay(localTime))],
+                           months[int(MonthFromTime(localTime))],
+                           int(DateFromTime(localTime)),
+                           int(YearFromTime(localTime)),
+                           int(HourFromTime(localTime)),
+                           int(MinFromTime(localTime)),
+                           int(SecFromTime(localTime)),
                            offset,
                            usetz ? " " : "",
                            usetz ? tzbuf : "");
             break;
-          case FORMATSPEC_DATE:
+          case FormatSpec::Date:
             /* Tue Oct 31 2000 */
             SprintfLiteral(buf, "%s %s %.2d %.4d",
-                           days[int(WeekDay(local))],
-                           months[int(MonthFromTime(local))],
-                           int(DateFromTime(local)),
-                           int(YearFromTime(local)));
+                           days[int(WeekDay(localTime))],
+                           months[int(MonthFromTime(localTime))],
+                           int(DateFromTime(localTime)),
+                           int(YearFromTime(localTime)));
             break;
-          case FORMATSPEC_TIME:
+          case FormatSpec::Time:
             /* 09:41:40 GMT-0800 (PST) */
             SprintfLiteral(buf, "%.2d:%.2d:%.2d GMT%+.4d%s%s",
-                           int(HourFromTime(local)),
-                           int(MinFromTime(local)),
-                           int(SecFromTime(local)),
+                           int(HourFromTime(localTime)),
+                           int(MinFromTime(localTime)),
+                           int(SecFromTime(localTime)),
                            offset,
                            usetz ? " " : "",
                            usetz ? tzbuf : "");
             break;
         }
+
+        str = NewStringCopyZ<CanGC>(cx, buf);
     }
 
-    JSString* str = JS_NewStringCopyZ(cx, buf);
     if (!str)
         return false;
     rval.setString(str);
     return true;
 }
 
 static bool
 ToLocaleFormatHelper(JSContext* cx, HandleObject obj, const char* format, MutableHandleValue rval)
 {
-    double utctime = obj->as<DateObject>().UTCTime().toNumber();
+    double utcTime = obj->as<DateObject>().UTCTime().toNumber();
 
     char buf[100];
-    if (!IsFinite(utctime)) {
-        SprintfLiteral(buf, js_NaN_date_str);
+    if (!IsFinite(utcTime)) {
+        strcpy(buf, js_NaN_date_str);
     } else {
-        int result_len;
-        double local = LocalTime(utctime);
-        PRMJTime split;
-        new_explode(local, &split);
+        double localTime = LocalTime(utcTime);
+        PRMJTime prtm = ToPRMJTime(localTime);
 
         /* Let PRMJTime format it. */
-        result_len = PRMJ_FormatTime(buf, sizeof buf, format, &split);
+        size_t result_len = PRMJ_FormatTime(buf, sizeof buf, format, &prtm);
 
         /* If it failed, default to toString. */
         if (result_len == 0)
-            return date_format(cx, utctime, FORMATSPEC_FULL, rval);
+            return FormatDate(cx, utcTime, FormatSpec::DateTime, rval);
 
         /* Hacked check against undesired 2-digit year 00/00/00 form. */
         if (strcmp(format, "%x") == 0 && result_len >= 6 &&
             /* Format %x means use OS settings, which may have 2-digit yr, so
                hack end of 3/11/22 or 11.03.22 or 11Mar22 to use 4-digit yr...*/
             !isdigit(buf[result_len - 3]) &&
             isdigit(buf[result_len - 2]) && isdigit(buf[result_len - 1]) &&
             /* ...but not if starts with 4-digit year, like 2022/3/11. */
             !(isdigit(buf[0]) && isdigit(buf[1]) &&
-              isdigit(buf[2]) && isdigit(buf[3]))) {
-            double localtime = obj->as<DateObject>().cachedLocalTime();
-            int year = IsNaN(localtime) ? 0 : (int) YearFromTime(localtime);
-            snprintf(buf + (result_len - 2), (sizeof buf) - (result_len - 2),
-                     "%d", year);
+              isdigit(buf[2]) && isdigit(buf[3])))
+        {
+            int year = int(YearFromTime(localTime));
+            snprintf(buf + (result_len - 2), (sizeof buf) - (result_len - 2), "%d", year);
         }
 
     }
 
     if (cx->runtime()->localeCallbacks && cx->runtime()->localeCallbacks->localeToUnicode)
         return cx->runtime()->localeCallbacks->localeToUnicode(cx, buf, rval);
 
-    JSString* str = JS_NewStringCopyZ(cx, buf);
+    JSString* str = NewStringCopyZ<CanGC>(cx, buf);
     if (!str)
         return false;
     rval.setString(str);
     return true;
 }
 
 #if !EXPOSE_INTL_API
-static bool
-ToLocaleStringHelper(JSContext* cx, Handle<DateObject*> dateObj, MutableHandleValue rval)
+/* ES5 15.9.5.5. */
+MOZ_ALWAYS_INLINE bool
+date_toLocaleString_impl(JSContext* cx, const CallArgs& args)
 {
     /*
      * Use '%#c' for windows, because '%c' is backward-compatible and non-y2k
      * with msvc; '%#c' requests that a full year be used in the result string.
      */
-    return ToLocaleFormatHelper(cx, dateObj,
+    static const char format[] =
 #if defined(_WIN32) && !defined(__MWERKS__)
-                          "%#c"
+                                   "%#c"
 #else
-                          "%c"
+                                   "%c"
 #endif
-                         , rval);
-}
-
-/* ES5 15.9.5.5. */
-MOZ_ALWAYS_INLINE bool
-date_toLocaleString_impl(JSContext* cx, const CallArgs& args)
-{
+                                   ;
+
     Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
-    return ToLocaleStringHelper(cx, dateObj, args.rval());
+    return ToLocaleFormatHelper(cx, dateObj, format, args.rval());
 }
 
 static bool
 date_toLocaleString(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     return CallNonGenericMethod<IsDate, date_toLocaleString_impl>(cx, args);
 }
@@ -2834,23 +2804,25 @@ date_toLocaleFormat_impl(JSContext* cx, 
 {
     Rooted<DateObject*> dateObj(cx, &args.thisv().toObject().as<DateObject>());
 
     if (args.length() == 0) {
         /*
          * Use '%#c' for windows, because '%c' is backward-compatible and non-y2k
          * with msvc; '%#c' requests that a full year be used in the result string.
          */
-        return ToLocaleFormatHelper(cx, dateObj,
+        static const char format[] =
 #if defined(_WIN32) && !defined(__MWERKS__)
-                              "%#c"
+                                       "%#c"
 #else
-                              "%c"
+                                       "%c"
 #endif
-                             , args.rval());
+                                       ;
+
+        return ToLocaleFormatHelper(cx, dateObj, format, args.rval());
     }
 
     RootedString fmt(cx, ToString<CanGC>(cx, args[0]));
     if (!fmt)
         return false;
 
     JSAutoByteString fmtbytes(cx, fmt);
     if (!fmtbytes)
@@ -2865,33 +2837,33 @@ date_toLocaleFormat(JSContext* cx, unsig
     CallArgs args = CallArgsFromVp(argc, vp);
     return CallNonGenericMethod<IsDate, date_toLocaleFormat_impl>(cx, args);
 }
 
 /* ES5 15.9.5.4. */
 MOZ_ALWAYS_INLINE bool
 date_toTimeString_impl(JSContext* cx, const CallArgs& args)
 {
-    return date_format(cx, args.thisv().toObject().as<DateObject>().UTCTime().toNumber(),
-                       FORMATSPEC_TIME, args.rval());
+    return FormatDate(cx, args.thisv().toObject().as<DateObject>().UTCTime().toNumber(),
+                      FormatSpec::Time, args.rval());
 }
 
 static bool
 date_toTimeString(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     return CallNonGenericMethod<IsDate, date_toTimeString_impl>(cx, args);
 }
 
 /* ES5 15.9.5.3. */
 MOZ_ALWAYS_INLINE bool
 date_toDateString_impl(JSContext* cx, const CallArgs& args)
 {
-    return date_format(cx, args.thisv().toObject().as<DateObject>().UTCTime().toNumber(),
-                       FORMATSPEC_DATE, args.rval());
+    return FormatDate(cx, args.thisv().toObject().as<DateObject>().UTCTime().toNumber(),
+                      FormatSpec::Date, args.rval());
 }
 
 static bool
 date_toDateString(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     return CallNonGenericMethod<IsDate, date_toDateString_impl>(cx, args);
 }
@@ -2950,17 +2922,17 @@ date_toString_impl(JSContext* cx, const 
         RootedValue unboxed(cx);
         if (!Unbox(cx, obj, &unboxed))
             return false;
 
         tv = unboxed.toNumber();
     }
 
     // Step 4.
-    return date_format(cx, tv, FORMATSPEC_FULL, args.rval());
+    return FormatDate(cx, tv, FormatSpec::DateTime, args.rval());
 }
 
 bool
 date_toString(JSContext* cx, unsigned argc, Value* vp)
 {
     // Step 1.
     CallArgs args = CallArgsFromVp(argc, vp);
     return CallNonGenericMethod<IsObject, date_toString_impl>(cx, args);
@@ -3088,17 +3060,17 @@ NewDateObject(JSContext* cx, const CallA
 
     args.rval().setObject(*obj);
     return true;
 }
 
 static bool
 ToDateString(JSContext* cx, const CallArgs& args, ClippedTime t)
 {
-    return date_format(cx, t.toDouble(), FORMATSPEC_FULL, args.rval());
+    return FormatDate(cx, t.toDouble(), FormatSpec::DateTime, args.rval());
 }
 
 static bool
 DateNoArguments(JSContext* cx, const CallArgs& args)
 {
     MOZ_ASSERT(args.length() == 0);
 
     ClippedTime now = NowAsMillis();
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -3741,17 +3741,16 @@ JSObject::allocKindForTenure(const js::N
 
     AllocKind kind = GetGCObjectFixedSlotsKind(as<NativeObject>().numFixedSlots());
     MOZ_ASSERT(!IsBackgroundFinalized(kind));
     if (!CanBeFinalizedInBackground(kind, getClass()))
         return kind;
     return GetBackgroundAllocKind(kind);
 }
 
-
 void
 JSObject::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::ClassInfo* info)
 {
     if (is<NativeObject>() && as<NativeObject>().hasDynamicSlots())
         info->objectsMallocHeapSlots += mallocSizeOf(as<NativeObject>().slots_);
 
     if (is<NativeObject>() && as<NativeObject>().hasDynamicElements()) {
         js::ObjectElements* elements = as<NativeObject>().getElementsHeader();
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -2794,18 +2794,18 @@ ToCodePoint(JSContext* cx, HandleValue c
             JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_A_CODEPOINT, numStr);
         return false;
     }
 
     *codePoint = uint32_t(nextCP);
     return true;
 }
 
-static bool
-str_fromCodePoint_one_arg(JSContext* cx, HandleValue code, MutableHandleValue rval)
+bool
+js::str_fromCodePoint_one_arg(JSContext* cx, HandleValue code, MutableHandleValue rval)
 {
     // Steps 1-4 (omitted).
 
     // Steps 5.a-d.
     uint32_t codePoint;
     if (!ToCodePoint(cx, code, &codePoint))
         return false;
 
@@ -2850,18 +2850,18 @@ str_fromCodePoint_few_args(JSContext* cx
         return false;
 
     args.rval().setString(str);
     return true;
 }
 
 // ES2017 draft rev 40edb3a95a475c1b251141ac681b8793129d9a6d
 // 21.1.2.2 String.fromCodePoint(...codePoints)
-static bool
-str_fromCodePoint(JSContext* cx, unsigned argc, Value* vp)
+bool
+js::str_fromCodePoint(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     // Optimize the single code-point case.
     if (args.length() == 1)
         return str_fromCodePoint_one_arg(cx, args[0], args.rval());
 
     // Optimize the case where the result will definitely fit in an inline
@@ -2904,18 +2904,18 @@ str_fromCodePoint(JSContext* cx, unsigne
     }
 
     args.rval().setString(str);
     return true;
 }
 
 static const JSFunctionSpec string_static_methods[] = {
     JS_INLINABLE_FN("fromCharCode", js::str_fromCharCode, 1, 0, StringFromCharCode),
-
-    JS_FN("fromCodePoint",               str_fromCodePoint,             1,0),
+    JS_INLINABLE_FN("fromCodePoint", js::str_fromCodePoint, 1, 0, StringFromCodePoint),
+
     JS_SELF_HOSTED_FN("raw",             "String_static_raw",           2,JSFUN_HAS_REST),
     JS_SELF_HOSTED_FN("substring",       "String_static_substring",     3,0),
     JS_SELF_HOSTED_FN("substr",          "String_static_substr",        3,0),
     JS_SELF_HOSTED_FN("slice",           "String_static_slice",         3,0),
 
     JS_SELF_HOSTED_FN("match",           "String_generic_match",        2,0),
     JS_SELF_HOSTED_FN("replace",         "String_generic_replace",      3,0),
     JS_SELF_HOSTED_FN("search",          "String_generic_search",       2,0),
--- a/js/src/jsstr.h
+++ b/js/src/jsstr.h
@@ -313,16 +313,22 @@ DeflateStringToBuffer(JSContext* maybecx
                       size_t charsLength, char* bytes, size_t* length);
 
 extern bool
 str_fromCharCode(JSContext* cx, unsigned argc, Value* vp);
 
 extern bool
 str_fromCharCode_one_arg(JSContext* cx, HandleValue code, MutableHandleValue rval);
 
+extern bool
+str_fromCodePoint(JSContext* cx, unsigned argc, Value* vp);
+
+extern bool
+str_fromCodePoint_one_arg(JSContext* cx, HandleValue code, MutableHandleValue rval);
+
 /* String methods exposed so they can be installed in the self-hosting global. */
 
 extern bool
 str_includes(JSContext* cx, unsigned argc, Value* vp);
 
 extern bool
 str_indexOf(JSContext* cx, unsigned argc, Value* vp);
 
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -5520,17 +5520,20 @@ SetARMHwCapFlags(JSContext* cx, unsigned
 
     jit::ParseARMHwCapFlags(flagsList.ptr());
 #endif
 
     args.rval().setUndefined();
     return true;
 }
 
-#ifdef __AFL_HAVE_MANUAL_CONTROL
+#ifndef __AFL_HAVE_MANUAL_CONTROL
+# define __AFL_LOOP(x) true
+#endif
+
 static bool
 WasmLoop(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     RootedObject callee(cx, &args.callee());
 
     if (args.length() < 1 || args.length() > 2) {
         ReportUsageErrorASCII(cx, callee, "Wrong number of arguments");
@@ -5547,40 +5550,35 @@ WasmLoop(JSContext* cx, unsigned argc, V
         if (!args.get(1).isObject()) {
             ReportUsageErrorASCII(cx, callee, "Second argument, if present, must be an Object");
             return false;
         }
         importObj = &args[1].toObject();
     }
 
     RootedString givenPath(cx, args[0].toString());
-    RootedString str(cx, ResolvePath(cx, givenPath, RootRelative));
-    if (!str)
-        return false;
-
-    JSAutoByteString filename(cx, str);
+    RootedString filename(cx, ResolvePath(cx, givenPath, RootRelative));
     if (!filename)
         return false;
 
     while (__AFL_LOOP(1000)) {
-        Rooted<JSObject*> ret(cx, FileAsTypedArray(cx, filename.ptr()));
+        Rooted<JSObject*> ret(cx, FileAsTypedArray(cx, filename));
         if (!ret)
             return false;
 
         Rooted<TypedArrayObject*> typedArray(cx, &ret->as<TypedArrayObject>());
         RootedWasmInstanceObject instanceObj(cx);
         if (!wasm::Eval(cx, typedArray, importObj, &instanceObj)) {
             // Clear any pending exceptions, we don't care about them
             cx->clearPendingException();
         }
     }
 
     return true;
 }
-#endif // __AFL_HAVE_MANUAL_CONTROL
 
 static const JSFunctionSpecWithHelp shell_functions[] = {
     JS_FN_HELP("version", Version, 0, 0,
 "version([number])",
 "  Get or force a script compilation version number."),
 
     JS_FN_HELP("options", Options, 0, 0,
 "options([option ...])",
@@ -6095,22 +6093,20 @@ TestAssertRecoveredOnBailout,
 "crash([message])",
 "  Crashes the process with a MOZ_CRASH."),
 
     JS_FN_HELP("setARMHwCapFlags", SetARMHwCapFlags, 1, 0,
 "setARMHwCapFlags(\"flag1,flag2 flag3\")",
 "  On non-ARM, no-op. On ARM, set the hardware capabilities. The list of \n"
 "  flags is available by calling this function with \"help\" as the flag's name"),
 
-#ifdef __AFL_HAVE_MANUAL_CONTROL
     JS_FN_HELP("wasmLoop", WasmLoop, 2, 0,
 "wasmLoop(filename, imports)",
 "  Performs an AFL-style persistent loop reading data from the given file and passing it\n"
 "  to the 'wasmEval' function together with the specified imports object."),
-#endif
 
     JS_FS_HELP_END
 };
 
 static const JSFunctionSpecWithHelp console_functions[] = {
     JS_FN_HELP("log", Print, 0, 0,
 "log([exp ...])",
 "  Evaluate and print expressions to stdout.\n"
new file mode 100644
new file mode 100644
--- /dev/null
+++ b/js/src/tests/Intl/Array/toLocaleString-date.js
@@ -0,0 +1,53 @@
+if (typeof Intl === "object") {
+    const localeSep = [,,].toLocaleString();
+
+    const date = new Date(Date.UTC(2012, 11, 12, 3, 0, 0));
+
+    assertEq([date].toLocaleString("en-us", {timeZone: "UTC"}), "12/12/2012, 3:00:00 AM");
+    assertEq([date].toLocaleString(["de", "en"], {timeZone: "UTC"}), "12.12.2012, 03:00:00");
+    assertEq([date].toLocaleString("th-th", {timeZone: "UTC"}), "12/12/2555 03:00:00");
+    assertEq([date].toLocaleString("th-th-u-nu-thai", {timeZone: "UTC"}), "๑๒/๑๒/๒๕๕๕ ๐๓:๐๐:๐๐");
+
+    const sampleValues = [
+        date, new Date(0),
+    ];
+    const sampleLocales = [
+        void 0,
+        "en",
+        "th-th-u-nu-thai",
+        "ja-jp",
+        "ar-ma-u-ca-islamicc",
+        ["tlh", "de"],
+    ];
+    const numericFormatOptions = {
+        timeZone: "UTC",
+        year: "numeric", month: "numeric", day: "numeric",
+        hour: "numeric", minute: "numeric", second: "numeric",
+    };
+    const longFormatOptions = {
+        timeZone: "UTC",
+        year: "numeric", month: "long", day: "numeric",
+        hour: "numeric", minute: "numeric", second: "numeric"
+    };
+    const sampleOptions = [
+        {timeZone: "UTC"},
+        longFormatOptions,
+    ];
+
+    for (let locale of sampleLocales) {
+        for (let options of sampleOptions) {
+            let dtfOptions;
+            if (options === longFormatOptions) {
+                dtfOptions = longFormatOptions;
+            } else {
+                dtfOptions = numericFormatOptions;
+            }
+            let dtf = new Intl.DateTimeFormat(locale, dtfOptions);
+            let expected = sampleValues.map(dtf.format).join(localeSep);
+            assertEq(sampleValues.toLocaleString(locale, options), expected);
+        }
+    }
+}
+
+if (typeof reportCompare === "function")
+    reportCompare(true, true);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/Intl/Array/toLocaleString-number.js
@@ -0,0 +1,34 @@
+if (typeof Intl === "object") {
+    const localeSep = [,,].toLocaleString();
+
+    assertEq([NaN].toLocaleString("ar"), "ليس رقم");
+    assertEq([NaN].toLocaleString(["zh-hant", "ar"]), "非數值");
+    assertEq([Infinity].toLocaleString("dz"), "གྲངས་མེད");
+    assertEq([-Infinity].toLocaleString(["fr", "en"]), "-∞");
+
+    const sampleValues = [
+        -0, +0, -1, +1, -2, +2, -0.5, +0.5,
+    ];
+    const sampleLocales = [
+        void 0,
+        "en",
+        "th-th-u-nu-thai",
+        ["tlh", "de"],
+    ];
+    const sampleOptions = [
+        void 0,
+        {},
+        {style: "percent"},
+        {style: "currency", currency: "USD", minimumIntegerDigits: 4},
+    ];
+    for (let locale of sampleLocales) {
+        for (let options of sampleOptions) {
+            let nf = new Intl.NumberFormat(locale, options);
+            let expected = sampleValues.map(nf.format).join(localeSep);
+            assertEq(sampleValues.toLocaleString(locale, options), expected);
+        }
+    }
+}
+
+if (typeof reportCompare === "function")
+    reportCompare(true, true);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/Intl/Array/toLocaleString.js
@@ -0,0 +1,35 @@
+if (typeof Intl === "object") {
+    const localeSep = [,,].toLocaleString();
+
+    // Missing arguments are passed as |undefined|.
+    const objNoArgs = {
+        toLocaleString() {
+            assertEq(arguments.length, 2);
+            assertEq(arguments[0], undefined);
+            assertEq(arguments[1], undefined);
+            return "pass";
+        }
+    };
+    // - Single element case.
+    assertEq([objNoArgs].toLocaleString(), "pass");
+    // - More than one element.
+    assertEq([objNoArgs, objNoArgs].toLocaleString(), "pass" + localeSep + "pass");
+
+    // Ensure "locales" and "options" arguments are passed to the array elements.
+    const locales = {}, options = {};
+    const objWithArgs = {
+        toLocaleString() {
+            assertEq(arguments.length, 2);
+            assertEq(arguments[0], locales);
+            assertEq(arguments[1], options);
+            return "pass";
+        }
+    };
+    // - Single element case.
+    assertEq([objWithArgs].toLocaleString(locales, options), "pass");
+    // - More than one element.
+    assertEq([objWithArgs, objWithArgs].toLocaleString(locales, options), "pass" + localeSep + "pass");
+}
+
+if (typeof reportCompare === "function")
+    reportCompare(true, true);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Array/toLocaleString-nointl.js
@@ -0,0 +1,26 @@
+if (typeof Intl !== "object") {
+    const localeSep = [,,].toLocaleString();
+
+    const obj = {
+        toLocaleString() {
+            assertEq(arguments.length, 0);
+            return "pass";
+        }
+    };
+
+    // Ensure no arguments are passed to the array elements.
+    // - Single element case.
+    assertEq([obj].toLocaleString(), "pass");
+    // - More than one element.
+    assertEq([obj, obj].toLocaleString(), "pass" + localeSep + "pass");
+
+    // Ensure no arguments are passed to the array elements even if supplied.
+    const locales = {}, options = {};
+    // - Single element case.
+    assertEq([obj].toLocaleString(locales, options), "pass");
+    // - More than one element.
+    assertEq([obj, obj].toLocaleString(locales, options), "pass" + localeSep + "pass");
+}
+
+if (typeof reportCompare === "function")
+    reportCompare(true, true);
--- a/js/src/threading/posix/ConditionVariable.cpp
+++ b/js/src/threading/posix/ConditionVariable.cpp
@@ -18,18 +18,19 @@
 #include "threading/posix/MutexPlatformData.h"
 
 using mozilla::CheckedInt;
 using mozilla::TimeDuration;
 using mozilla::TimeStamp;
 
 static const long NanoSecPerSec = 1000000000;
 
-// Android & macOS 10.12 has the clock functions, but not pthread_condattr_setclock.
-#if defined(HAVE_CLOCK_MONOTONIC) && !defined(__ANDROID__) && !defined(__APPLE__)
+// Android 32-bit & macOS 10.12 has the clock functions, but not pthread_condattr_setclock.
+#if defined(HAVE_CLOCK_MONOTONIC) && \
+    !(defined(__ANDROID__) && !defined(__LP64__)) && !defined(__APPLE__)
 # define USE_CLOCK_API
 #endif
 
 #ifdef USE_CLOCK_API
 // The C++ specification defines std::condition_variable::wait_for in terms of
 // std::chrono::steady_clock, which is closest to CLOCK_MONOTONIC.
 static const clockid_t WhichClock = CLOCK_MONOTONIC;
 
--- a/js/src/vm/CommonPropertyNames.h
+++ b/js/src/vm/CommonPropertyNames.h
@@ -18,16 +18,17 @@
     macro(Any, Any, "Any") \
     macro(apply, apply, "apply") \
     macro(arguments, arguments, "arguments") \
     macro(ArrayBufferSpecies, ArrayBufferSpecies, "ArrayBufferSpecies") \
     macro(ArrayIterator, ArrayIterator, "Array Iterator") \
     macro(ArrayIteratorNext, ArrayIteratorNext, "ArrayIteratorNext") \
     macro(ArraySpecies, ArraySpecies, "ArraySpecies") \
     macro(ArraySpeciesCreate, ArraySpeciesCreate, "ArraySpeciesCreate") \
+    macro(ArrayToLocaleString, ArrayToLocaleString, "ArrayToLocaleString") \
     macro(ArrayType, ArrayType, "ArrayType") \
     macro(ArrayValues, ArrayValues, "ArrayValues") \
     macro(ArrayValuesAt, ArrayValuesAt, "ArrayValuesAt") \
     macro(as, as, "as") \
     macro(Async, Async, "Async") \
     macro(Bool8x16, Bool8x16, "Bool8x16") \
     macro(Bool16x8, Bool16x8, "Bool16x8") \
     macro(Bool32x4, Bool32x4, "Bool32x4") \
--- a/js/src/vm/NativeObject.cpp
+++ b/js/src/vm/NativeObject.cpp
@@ -257,16 +257,34 @@ js::NativeObject::lookup(ExclusiveContex
 Shape*
 js::NativeObject::lookupPure(jsid id)
 {
     MOZ_ASSERT(isNative());
     return Shape::searchNoHashify(lastProperty(), id);
 }
 
 uint32_t
+js::NativeObject::numFixedSlotsForCompilation() const
+{
+    // This is an alternative method for getting the number of fixed slots in an
+    // object. It requires more logic and memory accesses than numFixedSlots()
+    // but is safe to be called from the compilation thread, even if the main
+    // thread is actively mutating the VM.
+
+    // The compiler does not have access to nursery things.
+    MOZ_ASSERT(!IsInsideNursery(this));
+
+    if (this->is<ArrayObject>())
+        return 0;
+
+    gc::AllocKind kind = asTenured().getAllocKind();
+    return gc::GetGCKindSlots(kind, getClass());
+}
+
+uint32_t
 js::NativeObject::dynamicSlotsCount(uint32_t nfixed, uint32_t span, const Class* clasp)
 {
     if (span <= nfixed)
         return 0;
     span -= nfixed;
 
     // Increase the slots to SLOT_CAPACITY_MIN to decrease the likelihood
     // the dynamic slots need to get increased again. ArrayObjects ignore
--- a/js/src/vm/NativeObject.h
+++ b/js/src/vm/NativeObject.h
@@ -627,16 +627,17 @@ class NativeObject : public ShapedObject
 
     uint32_t numFixedSlots() const {
         return reinterpret_cast<const shadow::Object*>(this)->numFixedSlots();
     }
     uint32_t numUsedFixedSlots() const {
         uint32_t nslots = lastProperty()->slotSpan(getClass());
         return Min(nslots, numFixedSlots());
     }
+    uint32_t numFixedSlotsForCompilation() const;
 
     uint32_t slotSpan() const {
         if (inDictionaryMode())
             return lastProperty()->base()->slotSpan();
         return lastProperty()->slotSpan();
     }
 
     /* Whether a slot is at a fixed offset from this object. */
--- a/js/src/vm/TypedArrayObject.cpp
+++ b/js/src/vm/TypedArrayObject.cpp
@@ -1355,31 +1355,31 @@ TypedArrayObject::set(JSContext* cx, uns
 TypedArrayObject::protoFunctions[] = {
     JS_SELF_HOSTED_FN("subarray", "TypedArraySubarray", 2, 0),
 #if 0 /* disabled until perf-testing is completed */
     JS_SELF_HOSTED_FN("set", "TypedArraySet", 2, 0),
 #else
     JS_FN("set", TypedArrayObject::set, 1, 0),
 #endif
     JS_SELF_HOSTED_FN("copyWithin", "TypedArrayCopyWithin", 3, 0),
-    JS_SELF_HOSTED_FN("every", "TypedArrayEvery", 2, 0),
+    JS_SELF_HOSTED_FN("every", "TypedArrayEvery", 1, 0),
     JS_SELF_HOSTED_FN("fill", "TypedArrayFill", 3, 0),
-    JS_SELF_HOSTED_FN("filter", "TypedArrayFilter", 2, 0),
-    JS_SELF_HOSTED_FN("find", "TypedArrayFind", 2, 0),
-    JS_SELF_HOSTED_FN("findIndex", "TypedArrayFindIndex", 2, 0),
-    JS_SELF_HOSTED_FN("forEach", "TypedArrayForEach", 2, 0),
+    JS_SELF_HOSTED_FN("filter", "TypedArrayFilter", 1, 0),
+    JS_SELF_HOSTED_FN("find", "TypedArrayFind", 1, 0),
+    JS_SELF_HOSTED_FN("findIndex", "TypedArrayFindIndex", 1, 0),
+    JS_SELF_HOSTED_FN("forEach", "TypedArrayForEach", 1, 0),
     JS_SELF_HOSTED_FN("indexOf", "TypedArrayIndexOf", 2, 0),
     JS_SELF_HOSTED_FN("join", "TypedArrayJoin", 1, 0),
     JS_SELF_HOSTED_FN("lastIndexOf", "TypedArrayLastIndexOf", 2, 0),
-    JS_SELF_HOSTED_FN("map", "TypedArrayMap", 2, 0),
+    JS_SELF_HOSTED_FN("map", "TypedArrayMap", 1, 0),
     JS_SELF_HOSTED_FN("reduce", "TypedArrayReduce", 1, 0),
     JS_SELF_HOSTED_FN("reduceRight", "TypedArrayReduceRight", 1, 0),
     JS_SELF_HOSTED_FN("reverse", "TypedArrayReverse", 0, 0),
     JS_SELF_HOSTED_FN("slice", "TypedArraySlice", 2, 0),
-    JS_SELF_HOSTED_FN("some", "TypedArraySome", 2, 0),
+    JS_SELF_HOSTED_FN("some", "TypedArraySome", 1, 0),
     JS_SELF_HOSTED_FN("sort", "TypedArraySort", 1, 0),
     JS_SELF_HOSTED_FN("entries", "TypedArrayEntries", 0, 0),
     JS_SELF_HOSTED_FN("keys", "TypedArrayKeys", 0, 0),
     JS_SELF_HOSTED_FN("values", "TypedArrayValues", 0, 0),
     JS_SELF_HOSTED_SYM_FN(iterator, "TypedArrayValues", 0, 0),
     JS_SELF_HOSTED_FN("includes", "TypedArrayIncludes", 2, 0),
     JS_SELF_HOSTED_FN("toString", "ArrayToString", 0, 0),
     JS_SELF_HOSTED_FN("toLocaleString", "TypedArrayToLocaleString", 2, 0),
--- a/layout/base/moz.build
+++ b/layout/base/moz.build
@@ -126,17 +126,16 @@ UNIFIED_SOURCES += [
     'DisplayListClipState.cpp',
     'DottedCornerFinder.cpp',
     'FrameLayerBuilder.cpp',
     'FramePropertyTable.cpp',
     'GeometryUtils.cpp',
     'LayoutLogging.cpp',
     'MaskLayerImageCache.cpp',
     'MobileViewportManager.cpp',
-    'nsBidi.cpp',
     'nsBidiPresUtils.cpp',
     'nsCaret.cpp',
     'nsCounterManager.cpp',
     'nsCSSColorUtils.cpp',
     'nsCSSFrameConstructor.cpp',
     'nsCSSRendering.cpp',
     'nsCSSRenderingBorders.cpp',
     'nsDisplayList.cpp',
@@ -161,16 +160,31 @@ UNIFIED_SOURCES += [
     'ScrollbarStyles.cpp',
     'ServoRestyleManager.cpp',
     'StackArena.cpp',
     'StaticPresData.cpp',
     'TouchManager.cpp',
     'ZoomConstraintsClient.cpp',
 ]
 
+if CONFIG['ENABLE_INTL_API']:
+    EXPORTS += [
+        'nsBidi_ICU.h',
+    ]
+    UNIFIED_SOURCES += [
+        'nsBidi_ICU.cpp',
+    ]
+else:
+    EXPORTS += [
+        'nsBidi_noICU.h',
+    ]
+    UNIFIED_SOURCES += [
+        'nsBidi_noICU.cpp',
+    ]
+
 # nsPresArena.cpp needs to be built separately because it uses plarena.h.
 # nsRefreshDriver.cpp needs to be built separately because of name clashes in the OS X headers
 SOURCES += [
     'nsPresArena.cpp',
     'nsRefreshDriver.cpp',
 ]
 
 if CONFIG['ENABLE_TESTS']:
--- a/layout/base/nsBidi.h
+++ b/layout/base/nsBidi.h
@@ -2,808 +2,15 @@
  *
  * 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 nsBidi_h__
 #define nsBidi_h__
 
-#include "nsBidiUtils.h"
-#include "nsIFrame.h" // for frame property declaration
-
-// Bidi reordering engine from ICU
-/*
- * javadoc-style comments are intended to be transformed into HTML
- * using DOC++ - see
- * http://www.zib.de/Visual/software/doc++/index.html .
- *
- * The HTML documentation is created with
- *  doc++ -H nsBidi.h
- */
-
-/**
- * @mainpage BIDI algorithm for Mozilla (from ICU)
- *
- * <h2>BIDI algorithm for Mozilla</h2>
- *
- * This is an implementation of the Unicode Bidirectional algorithm.
- * The algorithm is defined in the
- * <a href="http://www.unicode.org/unicode/reports/tr9/">Unicode Technical Report 9</a>,
- * version 5, also described in The Unicode Standard, Version 3.0 .<p>
- *
- * <h3>General remarks about the API:</h3>
- *
- * The <quote>limit</quote> of a sequence of characters is the position just after their
- * last character, i.e., one more than that position.<p>
- *
- * Some of the API functions provide access to <quote>runs</quote>.
- * Such a <quote>run</quote> is defined as a sequence of characters
- * that are at the same embedding level
- * after performing the BIDI algorithm.<p>
- *
- * @author Markus W. Scherer. Ported to Mozilla by Simon Montagu
- * @version 1.0
- */
-
-/**
- * nsBidiLevel is the type of the level values in this
- * Bidi implementation.
- * It holds an embedding level and indicates the visual direction
- * by its bit 0 (even/odd value).<p>
- *
- * <li><code>aParaLevel</code> can be set to the
- * pseudo-level values <code>NSBIDI_DEFAULT_LTR</code>
- * and <code>NSBIDI_DEFAULT_RTL</code>.</li></ul>
- *
- * @see nsBidi::SetPara
- *
- * <p>The related constants are not real, valid level values.
- * <code>NSBIDI_DEFAULT_XXX</code> can be used to specify
- * a default for the paragraph level for
- * when the <code>SetPara</code> function
- * shall determine it but there is no
- * strongly typed character in the input.<p>
- *
- * Note that the value for <code>NSBIDI_DEFAULT_LTR</code> is even
- * and the one for <code>NSBIDI_DEFAULT_RTL</code> is odd,
- * just like with normal LTR and RTL level values -
- * these special values are designed that way. Also, the implementation
- * assumes that NSBIDI_MAX_EXPLICIT_LEVEL is odd.
- *
- * @see NSBIDI_DEFAULT_LTR
- * @see NSBIDI_DEFAULT_RTL
- * @see NSBIDI_LEVEL_OVERRIDE
- * @see NSBIDI_MAX_EXPLICIT_LEVEL
- */
-typedef uint8_t nsBidiLevel;
-
-/** Paragraph level setting.
- *  If there is no strong character, then set the paragraph level to 0 (left-to-right).
- */
-#define NSBIDI_DEFAULT_LTR 0xfe
-
-/** Paragraph level setting.
- *  If there is no strong character, then set the paragraph level to 1 (right-to-left).
- */
-#define NSBIDI_DEFAULT_RTL 0xff
-
-/**
- * Maximum explicit embedding level.
- * (The maximum resolved level can be up to <code>NSBIDI_MAX_EXPLICIT_LEVEL+1</code>).
- *
- */
-#define NSBIDI_MAX_EXPLICIT_LEVEL 125
-
-/** Bit flag for level input.
- *  Overrides directional properties.
- */
-#define NSBIDI_LEVEL_OVERRIDE 0x80
-
-/**
- * Special value which can be returned by the mapping functions when a logical
- * index has no corresponding visual index or vice-versa.
- * @see GetVisualIndex
- * @see GetVisualMap
- * @see GetLogicalIndex
- * @see GetLogicalMap
- */
-#define NSBIDI_MAP_NOWHERE (-1)
-
-/**
- * <code>nsBidiDirection</code> values indicate the text direction.
- */
-enum nsBidiDirection {
-  /** All left-to-right text This is a 0 value. */
-  NSBIDI_LTR,
-  /** All right-to-left text This is a 1 value. */
-  NSBIDI_RTL,
-  /** Mixed-directional text. */
-  NSBIDI_MIXED
-};
-
-/* miscellaneous definitions ------------------------------------------------ */
-
-/* helper macros for each allocated array member */
-#define GETDIRPROPSMEMORY(length) nsBidi::GetMemory((void **)&mDirPropsMemory, \
-                                                    &mDirPropsSize, \
-                                                    (length))
-
-#define GETLEVELSMEMORY(length) nsBidi::GetMemory((void **)&mLevelsMemory, \
-                                                  &mLevelsSize, \
-                                                  (length))
-
-#define GETRUNSMEMORY(length) nsBidi::GetMemory((void **)&mRunsMemory, \
-                                                &mRunsSize, \
-                                                (length)*sizeof(Run))
-
-#define GETISOLATESMEMORY(length) nsBidi::GetMemory((void **)&mIsolatesMemory, \
-                                                    &mIsolatesSize, \
-                                                    (length)*sizeof(Isolate))
-
-#define GETOPENINGSMEMORY(length) nsBidi::GetMemory((void **)&mOpeningsMemory, \
-                                                    &mOpeningsSize, \
-                                                    (length)*sizeof(Opening))
-
-/*
- * Sometimes, bit values are more appropriate
- * to deal with directionality properties.
- * Abbreviations in these macro names refer to names
- * used in the Bidi algorithm.
- */
-typedef uint8_t DirProp;
-
-#define DIRPROP_FLAG(dir) (1UL<<(dir))
-
-/* special flag for multiple runs from explicit embedding codes */
-#define DIRPROP_FLAG_MULTI_RUNS (1UL<<31)
-
-/* are there any characters that are LTR or RTL? */
-#define MASK_LTR (DIRPROP_FLAG(L)|DIRPROP_FLAG(EN)|DIRPROP_FLAG(ENL)| \
-                  DIRPROP_FLAG(ENR)|DIRPROP_FLAG(AN)|DIRPROP_FLAG(LRE)| \
-                  DIRPROP_FLAG(LRO)|DIRPROP_FLAG(LRI))
-#define MASK_RTL (DIRPROP_FLAG(R)|DIRPROP_FLAG(AL)|DIRPROP_FLAG(RLE)| \
-                  DIRPROP_FLAG(RLO)|DIRPROP_FLAG(RLI))
-#define MASK_R_AL (DIRPROP_FLAG(R)|DIRPROP_FLAG(AL))
-
-/* explicit embedding codes */
-#define MASK_EXPLICIT (DIRPROP_FLAG(LRE)|DIRPROP_FLAG(LRO)|DIRPROP_FLAG(RLE)|DIRPROP_FLAG(RLO)|DIRPROP_FLAG(PDF))
-
-/* explicit isolate codes */
-#define MASK_ISO (DIRPROP_FLAG(LRI)|DIRPROP_FLAG(RLI)|DIRPROP_FLAG(FSI)|DIRPROP_FLAG(PDI))
-
-#define MASK_BN_EXPLICIT (DIRPROP_FLAG(BN)|MASK_EXPLICIT)
-
-/* paragraph and segment separators */
-#define MASK_B_S (DIRPROP_FLAG(B)|DIRPROP_FLAG(S))
-
-/* all types that are counted as White Space or Neutral in some steps */
-#define MASK_WS (MASK_B_S|DIRPROP_FLAG(WS)|MASK_BN_EXPLICIT|MASK_ISO)
-
-/* types that are neutrals or could becomes neutrals in (Wn) */
-#define MASK_POSSIBLE_N (DIRPROP_FLAG(O_N)|DIRPROP_FLAG(CS)|DIRPROP_FLAG(ES)|DIRPROP_FLAG(ET)|MASK_WS)
-
-/*
- * These types may be changed to "e",
- * the embedding type (L or R) of the run,
- * in the Bidi algorithm (N2)
- */
-#define MASK_EMBEDDING (DIRPROP_FLAG(NSM)|MASK_POSSIBLE_N)
-
-/* the dirProp's L and R are defined to 0 and 1 values in nsCharType */
-#define GET_LR_FROM_LEVEL(level) ((DirProp)((level)&1))
-
-#define IS_DEFAULT_LEVEL(level) (((level)&0xfe)==0xfe)
-
-/*
- * The following bit is used for the directional isolate status.
- * Stack entries corresponding to isolate sequences are greater than ISOLATE.
- */
-#define ISOLATE 0x0100
-
-/* number of isolate entries allocated initially without malloc */
-#define SIMPLE_ISOLATES_SIZE 5
-
-/* number of isolate run entries for paired brackets allocated initially without malloc */
-#define SIMPLE_OPENINGS_COUNT 8
-
-/* handle surrogate pairs --------------------------------------------------- */
-
-#define IS_FIRST_SURROGATE(uchar) (((uchar)&0xfc00)==0xd800)
-#define IS_SECOND_SURROGATE(uchar) (((uchar)&0xfc00)==0xdc00)
-
-/* get the UTF-32 value directly from the surrogate pseudo-characters */
-#define SURROGATE_OFFSET ((0xd800<<10UL)+0xdc00-0x10000)
-#define GET_UTF_32(first, second) (((first)<<10UL)+(second)-SURROGATE_OFFSET)
-
-#if !ENABLE_INTL_API // these are provided by ICU if present in the build
-
-#define UTF_ERROR_VALUE 0xffff
-/* definitions with forward iteration --------------------------------------- */
-
-/*
- * all the macros that go forward assume that
- * the initial offset is 0<=i<length;
- * they update the offset
- */
-
-/* fast versions, no error-checking */
-
-#define UTF16_APPEND_CHAR_UNSAFE(s, i, c){ \
-                                         if((uint32_t)(c)<=0xffff) { \
-                                         (s)[(i)++]=(char16_t)(c); \
-                                         } else { \
-                                         (s)[(i)++]=(char16_t)((c)>>10)+0xd7c0; \
-                                         (s)[(i)++]=(char16_t)(c)&0x3ff|0xdc00; \
-                                         } \
-}
-
-/* safe versions with error-checking and optional regularity-checking */
-
-#define UTF16_APPEND_CHAR_SAFE(s, i, length, c) { \
-                                                if((PRUInt32)(c)<=0xffff) { \
-                                                (s)[(i)++]=(char16_t)(c); \
-                                                } else if((PRUInt32)(c)<=0x10ffff) { \
-                                                if((i)+1<(length)) { \
-                                                (s)[(i)++]=(char16_t)((c)>>10)+0xd7c0; \
-                                                (s)[(i)++]=(char16_t)(c)&0x3ff|0xdc00; \
-                                                } else /* not enough space */ { \
-                                                (s)[(i)++]=UTF_ERROR_VALUE; \
-                                                } \
-                                                } else /* c>0x10ffff, write error value */ { \
-                                                (s)[(i)++]=UTF_ERROR_VALUE; \
-                                                } \
-}
-
-/* definitions with backward iteration -------------------------------------- */
-
-/*
- * all the macros that go backward assume that
- * the valid buffer range starts at offset 0
- * and that the initial offset is 0<i<=length;
- * they update the offset
- */
-
-/* fast versions, no error-checking */
-
-/*
- * Get a single code point from an offset that points behind the last
- * of the code units that belong to that code point.
- * Assume 0<=i<length.
- */
-#define UTF16_PREV_CHAR_UNSAFE(s, i, c) { \
-                                        (c)=(s)[--(i)]; \
-                                        if(IS_SECOND_SURROGATE(c)) { \
-                                        (c)=GET_UTF_32((s)[--(i)], (c)); \
-                                        } \
-}
-
-#define UTF16_BACK_1_UNSAFE(s, i) { \
-                                  if(IS_SECOND_SURROGATE((s)[--(i)])) { \
-                                  --(i); \
-                                  } \
-}
-
-#define UTF16_BACK_N_UNSAFE(s, i, n) { \
-                                     int32_t __N=(n); \
-                                     while(__N>0) { \
-                                     UTF16_BACK_1_UNSAFE(s, i); \
-                                     --__N; \
-                                     } \
-}
-
-/* safe versions with error-checking and optional regularity-checking */
-
-#define UTF16_PREV_CHAR_SAFE(s, start, i, c, strict) { \
-                                                     (c)=(s)[--(i)]; \
-                                                     if(IS_SECOND_SURROGATE(c)) { \
-                                                     char16_t __c2; \
-                                                     if((i)>(start) && IS_FIRST_SURROGATE(__c2=(s)[(i)-1])) { \
-                                                     --(i); \
-                                                     (c)=GET_UTF_32(__c2, (c)); \
-      /* strict: ((c)&0xfffe)==0xfffe is caught by UTF_IS_ERROR() */ \
-                                                     } else if(strict) {\
-      /* unmatched second surrogate */ \
-                                                     (c)=UTF_ERROR_VALUE; \
-                                                     } \
-                                                     } else if(strict && IS_FIRST_SURROGATE(c)) { \
-      /* unmatched first surrogate */ \
-                                                     (c)=UTF_ERROR_VALUE; \
-  /* else strict: (c)==0xfffe is caught by UTF_IS_ERROR() */ \
-                                                     } \
-}
-
-#define UTF16_BACK_1_SAFE(s, start, i) { \
-                                       if(IS_SECOND_SURROGATE((s)[--(i)]) && (i)>(start) && IS_FIRST_SURROGATE((s)[(i)-1])) { \
-                                       --(i); \
-                                       } \
-}
-
-#define UTF16_BACK_N_SAFE(s, start, i, n) { \
-                                          int32_t __N=(n); \
-                                          while(__N>0 && (i)>(start)) { \
-                                          UTF16_BACK_1_SAFE(s, start, i); \
-                                          --__N; \
-                                          } \
-}
-
-#define UTF_PREV_CHAR_UNSAFE(s, i, c)                UTF16_PREV_CHAR_UNSAFE(s, i, c)
-#define UTF_PREV_CHAR_SAFE(s, start, i, c, strict)   UTF16_PREV_CHAR_SAFE(s, start, i, c, strict)
-#define UTF_BACK_1_UNSAFE(s, i)                      UTF16_BACK_1_UNSAFE(s, i)
-#define UTF_BACK_1_SAFE(s, start, i)                 UTF16_BACK_1_SAFE(s, start, i)
-#define UTF_BACK_N_UNSAFE(s, i, n)                   UTF16_BACK_N_UNSAFE(s, i, n)
-#define UTF_BACK_N_SAFE(s, start, i, n)              UTF16_BACK_N_SAFE(s, start, i, n)
-#define UTF_APPEND_CHAR_UNSAFE(s, i, c)              UTF16_APPEND_CHAR_UNSAFE(s, i, c)
-#define UTF_APPEND_CHAR_SAFE(s, i, length, c)        UTF16_APPEND_CHAR_SAFE(s, i, length, c)
-
-#define UTF_PREV_CHAR(s, start, i, c)                UTF_PREV_CHAR_SAFE(s, start, i, c, false)
-#define UTF_BACK_1(s, start, i)                      UTF_BACK_1_SAFE(s, start, i)
-#define UTF_BACK_N(s, start, i, n)                   UTF_BACK_N_SAFE(s, start, i, n)
-#define UTF_APPEND_CHAR(s, i, length, c)             UTF_APPEND_CHAR_SAFE(s, i, length, c)
-
-#endif // !ENABLE_INTL_API
-
-struct Isolate {
-  int32_t start1;
-  int16_t stateImp;
-  int16_t state;
-};
-
-// For bracket matching
-
-#define FOUND_L DIRPROP_FLAG(L)
-#define FOUND_R DIRPROP_FLAG(R)
-
-struct Opening {
-  int32_t position;                   /* position of opening bracket */
-  int32_t match;                      /* matching char or -position of closing bracket */
-  int32_t contextPos;                 /* position of last strong char found before opening */
-  uint16_t flags;                     /* bits for L or R/AL found within the pair */
-  DirProp contextDir;                 /* L or R according to last strong char before opening */
-  uint8_t filler;                     /* to complete a nice multiple of 4 chars */
-};
-
-struct IsoRun {
-  int32_t  contextPos;                /* position of char determining context */
-  uint16_t start;                     /* index of first opening entry for this run */
-  uint16_t limit;                     /* index after last opening entry for this run */
-  nsBidiLevel level;                  /* level of this run */
-  DirProp lastStrong;                 /* bidi class of last strong char found in this run */
-  DirProp lastBase;                   /* bidi class of last base char found in this run */
-  DirProp contextDir;                 /* L or R to use as context for following openings */
-};
-
-class nsBidi;
-
-/* Run structure for reordering --------------------------------------------- */
-
-typedef struct Run {
-  int32_t logicalStart;  /* first character of the run; b31 indicates even/odd level */
-  int32_t visualLimit;   /* last visual position of the run +1 */
-} Run;
-
-/* in a Run, logicalStart will get this bit set if the run level is odd */
-#define INDEX_ODD_BIT (1UL<<31)
-
-#define MAKE_INDEX_ODD_PAIR(index, level) (index|((uint32_t)level<<31))
-#define ADD_ODD_BIT_FROM_LEVEL(x, level)  ((x)|=((uint32_t)level<<31))
-#define REMOVE_ODD_BIT(x)          ((x)&=~INDEX_ODD_BIT)
-
-#define GET_INDEX(x)   ((x)&~INDEX_ODD_BIT)
-#define GET_ODD_BIT(x) ((uint32_t)(x)>>31)
-#define IS_ODD_RUN(x)  (((x)&INDEX_ODD_BIT)!=0)
-#define IS_EVEN_RUN(x) (((x)&INDEX_ODD_BIT)==0)
-
-typedef uint32_t Flags;
-
-enum { DirProp_L=0, DirProp_R=1, DirProp_EN=2, DirProp_AN=3, DirProp_ON=4, DirProp_S=5, DirProp_B=6 }; /* reduced dirProp */
-
-#define IMPTABLEVELS_COLUMNS (DirProp_B + 2)
-typedef const uint8_t ImpTab[][IMPTABLEVELS_COLUMNS];
-typedef const uint8_t (*PImpTab)[IMPTABLEVELS_COLUMNS];
-
-typedef const uint8_t ImpAct[];
-typedef const uint8_t *PImpAct;
-
-struct LevState {
-    PImpTab pImpTab;                    /* level table pointer          */
-    PImpAct pImpAct;                    /* action map array             */
-    int32_t startON;                    /* start of ON sequence         */
-    int32_t state;                      /* current state                */
-    int32_t runStart;                   /* start position of the run    */
-    nsBidiLevel runLevel;               /* run level before implicit solving */
-};
-
-namespace mozilla {
-
-// Pseudo bidi embedding level indicating nonexistence.
-static const nsBidiLevel kBidiLevelNone = 0xff;
-
-struct FrameBidiData
-{
-  nsBidiLevel baseLevel;
-  nsBidiLevel embeddingLevel;
-  // The embedding level of virtual bidi formatting character before
-  // this frame if any. kBidiLevelNone is used to indicate nonexistence
-  // or unnecessity of such virtual character.
-  nsBidiLevel precedingControl;
-};
-
-} // namespace mozilla
-
-/**
- * This class holds information about a paragraph of text
- * with Bidi-algorithm-related details, or about one line of
- * such a paragraph.<p>
- * Reordering can be done on a line, or on a paragraph which is
- * then interpreted as one single line.<p>
- *
- * On construction, the class is initially empty. It is assigned
- * the Bidi properties of a paragraph by <code>SetPara</code>
- * or the Bidi properties of a line of a paragraph by
- * <code>SetLine</code>.<p>
- * A Bidi class can be reused for as long as it is not deallocated
- * by calling its destructor.<p>
- * <code>SetPara</code> will allocate additional memory for
- * internal structures as necessary.
- */
-class nsBidi
-{
-public:
-  /** @brief Default constructor.
-   *
-   * The nsBidi object is initially empty. It is assigned
-   * the Bidi properties of a paragraph by <code>SetPara()</code>
-   * or the Bidi properties of a line of a paragraph by
-   * <code>GetLine()</code>.<p>
-   * This object can be reused for as long as it is not destroyed.<p>
-   * <code>SetPara()</code> will allocate additional memory for
-   * internal structures as necessary.
-   *
-   */
-  nsBidi();
-
-  /** @brief Destructor. */
-  virtual ~nsBidi();
-
-
-  /**
-   * Perform the Unicode Bidi algorithm. It is defined in the
-   * <a href="http://www.unicode.org/unicode/reports/tr9/">Unicode Technical Report 9</a>,
-   * version 5,
-   * also described in The Unicode Standard, Version 3.0 .<p>
-   *
-   * This function takes a single plain text paragraph with or without
-   * externally specified embedding levels from <quote>styled</quote> text
-   * and computes the left-right-directionality of each character.<p>
-   *
-   * If the entire paragraph consists of text of only one direction, then
-   * the function may not perform all the steps described by the algorithm,
-   * i.e., some levels may not be the same as if all steps were performed.
-   * This is not relevant for unidirectional text.<br>
-   * For example, in pure LTR text with numbers the numbers would get
-   * a resolved level of 2 higher than the surrounding text according to
-   * the algorithm. This implementation may set all resolved levels to
-   * the same value in such a case.<p>
-   *
-   * The text must be externally split into separate paragraphs (rule P1).
-   * Paragraph separators (B) should appear at most at the very end.
-   *
-   * @param aText is a pointer to the single-paragraph text that the
-   *      Bidi algorithm will be performed on
-   *      (step (P1) of the algorithm is performed externally).
-   *      <strong>The text must be (at least) <code>aLength</code> long.</strong>
-   *
-   * @param aLength is the length of the text; if <code>aLength==-1</code> then
-   *      the text must be zero-terminated.
-   *
-   * @param aParaLevel specifies the default level for the paragraph;
-   *      it is typically 0 (LTR) or 1 (RTL).
-   *      If the function shall determine the paragraph level from the text,
-   *      then <code>aParaLevel</code> can be set to
-   *      either <code>NSBIDI_DEFAULT_LTR</code>
-   *      or <code>NSBIDI_DEFAULT_RTL</code>;
-   *      if there is no strongly typed character, then
-   *      the desired default is used (0 for LTR or 1 for RTL).
-   *      Any other value between 0 and <code>NSBIDI_MAX_EXPLICIT_LEVEL</code> is also valid,
-   *      with odd levels indicating RTL.
-   */
-  nsresult SetPara(const char16_t *aText, int32_t aLength, nsBidiLevel aParaLevel);
-
-  /**
-   * Get the directionality of the text.
-   *
-   * @param aDirection receives a <code>NSBIDI_XXX</code> value that indicates if the entire text
-   *       represented by this object is unidirectional,
-   *       and which direction, or if it is mixed-directional.
-   *
-   * @see nsBidiDirection
-   */
-  nsresult GetDirection(nsBidiDirection* aDirection);
-
-  /**
-   * Get the paragraph level of the text.
-   *
-   * @param aParaLevel receives a <code>NSBIDI_XXX</code> value indicating the paragraph level
-   *
-   * @see nsBidiLevel
-   */
-  nsresult GetParaLevel(nsBidiLevel* aParaLevel);
-
-  /**
-   * Get a logical run.
-   * This function returns information about a run and is used
-   * to retrieve runs in logical order.<p>
-   * This is especially useful for line-breaking on a paragraph.
-   *
-   * @param aLogicalStart is the first character of the run.
-   *
-   * @param aLogicalLimit will receive the limit of the run.
-   *      The l-value that you point to here may be the
-   *      same expression (variable) as the one for
-   *      <code>aLogicalStart</code>.
-   *      This pointer can be <code>nullptr</code> if this
-   *      value is not necessary.
-   *
-   * @param aLevel will receive the level of the run.
-   *      This pointer can be <code>nullptr</code> if this
-   *      value is not necessary.
-   */
-  nsresult GetLogicalRun(int32_t aLogicalStart, int32_t* aLogicalLimit, nsBidiLevel* aLevel);
-
-  /**
-   * Get the number of runs.
-   * This function may invoke the actual reordering on the
-   * <code>nsBidi</code> object, after <code>SetPara</code>
-   * may have resolved only the levels of the text. Therefore,
-   * <code>CountRuns</code> may have to allocate memory,
-   * and may fail doing so.
-   *
-   * @param aRunCount will receive the number of runs.
-   */
-  nsresult CountRuns(int32_t* aRunCount);
-
-  /**
-   * Get one run's logical start, length, and directionality,
-   * which can be 0 for LTR or 1 for RTL.
-   * In an RTL run, the character at the logical start is
-   * visually on the right of the displayed run.
-   * The length is the number of characters in the run.<p>
-   * <code>CountRuns</code> should be called
-   * before the runs are retrieved.
-   *
-   * @param aRunIndex is the number of the run in visual order, in the
-   *      range <code>[0..CountRuns-1]</code>.
-   *
-   * @param aLogicalStart is the first logical character index in the text.
-   *      The pointer may be <code>nullptr</code> if this index is not needed.
-   *
-   * @param aLength is the number of characters (at least one) in the run.
-   *      The pointer may be <code>nullptr</code> if this is not needed.
-   *
-   * @param aDirection will receive the directionality of the run,
-   *       <code>NSBIDI_LTR==0</code> or <code>NSBIDI_RTL==1</code>,
-   *       never <code>NSBIDI_MIXED</code>.
-   *
-   * @see CountRuns<p>
-   *
-   * Example:
-   * @code
-   *  int32_t i, count, logicalStart, visualIndex=0, length;
-   *  nsBidiDirection dir;
-   *  pBidi->CountRuns(&count);
-   *  for(i=0; i<count; ++i) {
-   *    pBidi->GetVisualRun(i, &logicalStart, &length, &dir);
-   *    if(NSBIDI_LTR==dir) {
-   *      do { // LTR
-   *        show_char(text[logicalStart++], visualIndex++);
-   *      } while(--length>0);
-   *    } else {
-   *      logicalStart+=length;  // logicalLimit
-   *      do { // RTL
-   *        show_char(text[--logicalStart], visualIndex++);
-   *      } while(--length>0);
-   *    }
-   *  }
-   * @endcode
-   *
-   * Note that in right-to-left runs, code like this places
-   * modifier letters before base characters and second surrogates
-   * before first ones.
-   */
-  nsresult GetVisualRun(int32_t aRunIndex, int32_t* aLogicalStart, int32_t* aLength, nsBidiDirection* aDirection);
-
-  /**
-   * This is a convenience function that does not use a nsBidi object.
-   * It is intended to be used for when an application has determined the levels
-   * of objects (character sequences) and just needs to have them reordered (L2).
-   * This is equivalent to using <code>GetVisualMap</code> on a
-   * <code>nsBidi</code> object.
-   *
-   * @param aLevels is an array with <code>aLength</code> levels that have been determined by
-   *      the application.
-   *
-   * @param aLength is the number of levels in the array, or, semantically,
-   *      the number of objects to be reordered.
-   *      It must be <code>aLength>0</code>.
-   *
-   * @param aIndexMap is a pointer to an array of <code>aLength</code>
-   *      indexes which will reflect the reordering of the characters.
-   *      The array does not need to be initialized.<p>
-   *      The index map will result in <code>aIndexMap[aVisualIndex]==aLogicalIndex</code>.
-   */
-  static nsresult ReorderVisual(const nsBidiLevel *aLevels, int32_t aLength, int32_t *aIndexMap);
-
-  /**
-   * Reverse a Right-To-Left run of Unicode text.
-   *
-   * This function preserves the integrity of characters with multiple
-   * code units and (optionally) modifier letters.
-   * Characters can be replaced by mirror-image characters
-   * in the destination buffer. Note that "real" mirroring has
-   * to be done in a rendering engine by glyph selection
-   * and that for many "mirrored" characters there are no
-   * Unicode characters as mirror-image equivalents.
-   * There are also options to insert or remove Bidi control
-   * characters; see the description of the <code>aDestSize</code>
-   * and <code>aOptions</code> parameters and of the option bit flags.
-   *
-   * Since no Bidi controls are inserted here, this function will never
-   * write more than <code>aSrcLength</code> characters to <code>aDest</code>.
-   *
-   * @param aSrc A pointer to the RTL run text.
-   *
-   * @param aSrcLength The length of the RTL run.
-   *                 If the <code>NSBIDI_REMOVE_BIDI_CONTROLS</code> option
-   *                 is set, then the destination length may be less than
-   *                 <code>aSrcLength</code>.
-   *                 If this option is not set, then the destination length
-   *                 will be exactly <code>aSrcLength</code>.
-   *
-   * @param aDest A pointer to where the reordered text is to be copied.
-   *             <code>aSrc[aSrcLength]</code> and <code>aDest[aSrcLength]</code>
-   *             must not overlap.
-   *
-   * @param aOptions A bit set of options for the reordering that control
-   *                how the reordered text is written.
-   *
-   * @param aDestSize will receive the number of characters that were written to <code>aDest</code>.
-   */
-  nsresult WriteReverse(const char16_t *aSrc, int32_t aSrcLength, char16_t *aDest, uint16_t aOptions, int32_t *aDestSize);
-
-  NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(BidiDataProperty, mozilla::FrameBidiData)
-
-  static mozilla::FrameBidiData GetBidiData(nsIFrame* aFrame)
-  {
-    return aFrame->Properties().Get(BidiDataProperty());
-  }
-
-  static nsBidiLevel GetBaseLevel(nsIFrame* aFrame)
-  {
-    return GetBidiData(aFrame).baseLevel;
-  }
-
-  static nsBidiLevel GetEmbeddingLevel(nsIFrame* aFrame)
-  {
-    return GetBidiData(aFrame).embeddingLevel;
-  }
-
-protected:
-  friend class nsBidiPresUtils;
-
-  class BracketData {
-  public:
-    explicit BracketData(const nsBidi* aBidi);
-    ~BracketData();
-
-    void ProcessBoundary(int32_t aLastDirControlCharPos,
-                         nsBidiLevel aContextLevel,
-                         nsBidiLevel aEmbeddingLevel,
-                         const DirProp* aDirProps);
-    void ProcessLRI_RLI(nsBidiLevel aLevel);
-    void ProcessPDI();
-    bool AddOpening(char16_t aMatch, int32_t aPosition);
-    void FixN0c(int32_t aOpeningIndex, int32_t aNewPropPosition,
-                DirProp aNewProp, DirProp* aDirProps);
-    DirProp ProcessClosing(int32_t aOpenIdx, int32_t aPosition,
-                           DirProp* aDirProps);
-    bool ProcessChar(int32_t aPosition, char16_t aCh, DirProp* aDirProps,
-                     nsBidiLevel* aLevels);
-
-  private:
-    // array of opening entries which should be enough in most cases;
-    // no malloc() needed
-    Opening  mSimpleOpenings[SIMPLE_OPENINGS_COUNT];
-    Opening* mOpenings;      // pointer to current array of entries,
-                             // either mSimpleOpenings or malloced array
-
-    Opening* mOpeningsMemory;
-    size_t   mOpeningsSize;
-
-    // array of nested isolated sequence entries; can never exceed
-    // UBIDI_MAX_EXPLICIT_LEVEL
-    //   + 1 for index 0
-    //   + 1 for before the first isolated sequence
-    IsoRun  mIsoRuns[NSBIDI_MAX_EXPLICIT_LEVEL+2];
-    int32_t mIsoRunLast;     // index of last used entry in mIsoRuns
-
-    int32_t mOpeningsCount;  // number of allocated entries in mOpenings
-  };
-
-  /** length of the current text */
-  int32_t mLength;
-
-  /** memory sizes in bytes */
-  size_t mDirPropsSize, mLevelsSize, mRunsSize;
-  size_t mIsolatesSize;
-
-  /** allocated memory */
-  DirProp* mDirPropsMemory;
-  nsBidiLevel* mLevelsMemory;
-  Run* mRunsMemory;
-  Isolate* mIsolatesMemory;
-
-  DirProp* mDirProps;
-  nsBidiLevel* mLevels;
-
-  /** the paragraph level */
-  nsBidiLevel mParaLevel;
-
-  /** flags is a bit set for which directional properties are in the text */
-  Flags mFlags;
-
-  /** the overall paragraph or line directionality - see nsBidiDirection */
-  nsBidiDirection mDirection;
-
-  /** characters after trailingWSStart are WS and are */
-  /* implicitly at the paraLevel (rule (L1)) - levels may not reflect that */
-  int32_t mTrailingWSStart;
-
-  /** fields for line reordering */
-  int32_t mRunCount;     /* ==-1: runs not set up yet */
-  Run* mRuns;
-
-  /** for non-mixed text, we only need a tiny array of runs (no malloc()) */
-  Run mSimpleRuns[1];
-
-  /* maxium of current nesting depth of isolate sequences */
-  /* Within ResolveExplicitLevels() and checkExpicitLevels(), this is the maximal
-     nesting encountered.
-     Within ResolveImplicitLevels(), this is the index of the current isolates
-     stack entry. */
-  int32_t mIsolateCount;
-  Isolate* mIsolates;
-
-  /** for simple text, have a small stack (no malloc()) */
-  Isolate mSimpleIsolates[SIMPLE_ISOLATES_SIZE];
-
-private:
-
-  void Init();
-
-  static bool GetMemory(void **aMemory, size_t* aSize, size_t aSizeNeeded);
-
-  void Free();
-
-  void GetDirProps(const char16_t *aText);
-
-  void ResolveExplicitLevels(nsBidiDirection *aDirection, const char16_t *aText);
-
-  nsBidiDirection DirectionFromFlags(Flags aFlags);
-
-  void ProcessPropertySeq(LevState *pLevState, uint8_t _prop, int32_t start, int32_t limit);
-
-  void ResolveImplicitLevels(int32_t aStart, int32_t aLimit, DirProp aSOR, DirProp aEOR);
-
-  void AdjustWSLevels();
-
-  void SetTrailingWSStart();
-
-  bool GetRuns();
-
-  void GetSingleRun(nsBidiLevel aLevel);
-
-  void ReorderLine(nsBidiLevel aMinLevel, nsBidiLevel aMaxLevel);
-
-  static bool PrepareReorder(const nsBidiLevel *aLevels, int32_t aLength, int32_t *aIndexMap, nsBidiLevel *aMinLevel, nsBidiLevel *aMaxLevel);
-};
+#if ENABLE_INTL_API
+#include "nsBidi_ICU.h"
+#else
+#include "nsBidi_noICU.h"
+#endif
 
 #endif // _nsBidi_h_
--- a/layout/base/nsBidiPresUtils.cpp
+++ b/layout/base/nsBidiPresUtils.cpp
@@ -181,17 +181,17 @@ struct BidiParagraphData {
   /**
    * mParaLevel can be NSBIDI_DEFAULT_LTR as well as NSBIDI_LTR or NSBIDI_RTL.
    * GetParaLevel() returns the actual (resolved) paragraph level which is
    * always either NSBIDI_LTR or NSBIDI_RTL
    */
   nsBidiLevel GetParaLevel()
   {
     nsBidiLevel paraLevel = mParaLevel;
-    if (IS_DEFAULT_LEVEL(paraLevel)) {
+    if (paraLevel == NSBIDI_DEFAULT_LTR || paraLevel == NSBIDI_DEFAULT_RTL) {
       mBidiEngine->GetParaLevel(&paraLevel);
     }
     return paraLevel;
   }
 
   nsBidiDirection GetDirection()
   {
     nsBidiDirection dir;
@@ -756,17 +756,17 @@ nsBidiPresUtils::ResolveParagraph(nsBloc
       aBpd->GetDirection() == NSBIDI_LTR && aBpd->GetParaLevel() == 0) {
     // We have a single left-to-right frame in a left-to-right paragraph,
     // without bidi isolation from the surrounding text.
     // Make sure that the embedding level and base level frame properties aren't
     // set (because if they are this frame used to have some other direction,
     // so we can't do this optimization), and we're done.
     nsIFrame* frame = aBpd->FrameAt(0);
     if (frame != NS_BIDI_CONTROL_FRAME) {
-      FrameBidiData bidiData = nsBidi::GetBidiData(frame);
+      FrameBidiData bidiData = frame->GetBidiData();
       if (!bidiData.embeddingLevel && !bidiData.baseLevel) {
 #ifdef DEBUG
 #ifdef NOISY_BIDI
         printf("early return for single direction frame %p\n", (void*)frame);
 #endif
 #endif
         frame->AddStateBits(NS_FRAME_IS_BIDI);
         return NS_OK;
@@ -789,17 +789,17 @@ nsBidiPresUtils::ResolveParagraph(nsBloc
     if (precedingControl >= embeddingLevel ||
         precedingControl >= lastEmbedingLevel) {
       bidiData.precedingControl = kBidiLevelNone;
     } else {
       bidiData.precedingControl = precedingControl;
     }
     precedingControl = kBidiLevelNone;
     lastEmbedingLevel = embeddingLevel;
-    propTable->Set(frame, nsBidi::BidiDataProperty(), bidiData);
+    propTable->Set(frame, nsIFrame::BidiDataProperty(), bidiData);
   };
 
   for (; ;) {
     if (fragmentLength <= 0) {
       // Get the next frame from mLogicalFrames
       if (++frameIndex >= frameCount) {
         break;
       }
@@ -1267,33 +1267,33 @@ nsBidiPresUtils::GetFirstLeaf(nsIFrame* 
                  realFrame : firstChild;
   }
   return firstLeaf;
 }
 
 FrameBidiData
 nsBidiPresUtils::GetFrameBidiData(nsIFrame* aFrame)
 {
-  return nsBidi::GetBidiData(GetFirstLeaf(aFrame));
+  return GetFirstLeaf(aFrame)->GetBidiData();
 }
 
 nsBidiLevel
 nsBidiPresUtils::GetFrameEmbeddingLevel(nsIFrame* aFrame)
 {
-  return nsBidi::GetEmbeddingLevel(GetFirstLeaf(aFrame));
+  return GetFirstLeaf(aFrame)->GetEmbeddingLevel();
 }
 
 nsBidiLevel
 nsBidiPresUtils::GetFrameBaseLevel(nsIFrame* aFrame)
 {
   nsIFrame* firstLeaf = aFrame;
   while (!IsBidiLeaf(firstLeaf)) {
     firstLeaf = firstLeaf->PrincipalChildList().FirstChild();
   }
-  return nsBidi::GetBaseLevel(firstLeaf);
+  return firstLeaf->GetBaseLevel();
 }
 
 void
 nsBidiPresUtils::IsFirstOrLast(nsIFrame* aFrame,
                                const nsContinuationStates* aContinuationStates,
                                bool aSpanDirMatchesLineDir,
                                bool& aIsFirst /* out */,
                                bool& aIsLast /* out */)
@@ -1750,24 +1750,24 @@ nsBidiPresUtils::EnsureBidiContinuation(
 }
 
 void
 nsBidiPresUtils::RemoveBidiContinuation(BidiParagraphData *aBpd,
                                         nsIFrame*       aFrame,
                                         int32_t         aFirstIndex,
                                         int32_t         aLastIndex)
 {
-  FrameBidiData bidiData = nsBidi::GetBidiData(aFrame);
+  FrameBidiData bidiData = aFrame->GetBidiData();
   bidiData.precedingControl = kBidiLevelNone;
   for (int32_t index = aFirstIndex + 1; index <= aLastIndex; index++) {
     nsIFrame* frame = aBpd->FrameAt(index);
     if (frame != NS_BIDI_CONTROL_FRAME) {
       // Make the frame and its continuation ancestors fluid,
       // so they can be reused or deleted by normal reflow code
-      frame->Properties().Set(nsBidi::BidiDataProperty(), bidiData);
+      frame->Properties().Set(nsIFrame::BidiDataProperty(), bidiData);
       frame->AddStateBits(NS_FRAME_IS_BIDI);
       while (frame) {
         nsIFrame* prev = frame->GetPrevContinuation();
         if (prev) {
           MakeContinuationFluid(prev, frame);
           frame = frame->GetParent();
         } else {
           break;
@@ -1921,17 +1921,17 @@ nsBidiPresUtils::CalculateCharType(nsBid
     } else if (IS_ARABIC_ALPHABETIC(ch) ) {
       charType = eCharType_RightToLeftArabic;
     } else {
       if (NS_IS_HIGH_SURROGATE(ch) && offset + 1 < aCharTypeLimit &&
           NS_IS_LOW_SURROGATE(aText[offset + 1])) {
         ch = SURROGATE_TO_UCS4(ch, aText[offset + 1]);
         charLen = 2;
       }
-      charType = GetBidiCat(ch);
+      charType = unicode::GetBidiCat(ch);
     }
 
     if (!CHARTYPE_IS_WEAK(charType) ) {
 
       if (strongTypeFound
           && (charType != aPrevCharType)
           && (CHARTYPE_IS_RTL(charType) || CHARTYPE_IS_RTL(aPrevCharType) ) ) {
         // Stop at this point to ensure uni-directionality of the text
new file mode 100644
--- /dev/null
+++ b/layout/base/nsBidi_ICU.cpp
@@ -0,0 +1,68 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsBidi_ICU.h"
+#include "ICUUtils.h"
+
+nsBidi::nsBidi()
+{
+  mBiDi = ubidi_open();
+}
+
+nsBidi::~nsBidi()
+{
+  ubidi_close(mBiDi);
+}
+
+nsresult nsBidi::SetPara(const char16_t *aText, int32_t aLength,
+                         nsBidiLevel aParaLevel)
+{
+  UErrorCode error = U_ZERO_ERROR;
+  ubidi_setPara(mBiDi, reinterpret_cast<const UChar*>(aText), aLength,
+                aParaLevel, nullptr, &error);
+  return ICUUtils::UErrorToNsResult(error);
+}
+
+nsresult nsBidi::GetDirection(nsBidiDirection* aDirection)
+{
+  *aDirection = nsBidiDirection(ubidi_getDirection(mBiDi));
+  return NS_OK;
+}
+
+nsresult nsBidi::GetParaLevel(nsBidiLevel* aParaLevel)
+{
+  *aParaLevel = ubidi_getParaLevel(mBiDi);
+  return NS_OK;
+}
+
+nsresult nsBidi::GetLogicalRun(int32_t aLogicalStart, int32_t* aLogicalLimit,
+                               nsBidiLevel* aLevel)
+{
+  ubidi_getLogicalRun(mBiDi, aLogicalStart, aLogicalLimit, aLevel);
+  return NS_OK;
+}
+
+nsresult nsBidi::CountRuns(int32_t* aRunCount)
+{
+  UErrorCode errorCode = U_ZERO_ERROR;
+  *aRunCount = ubidi_countRuns(mBiDi, &errorCode);
+  return ICUUtils::UErrorToNsResult(errorCode);
+}
+
+nsresult nsBidi::GetVisualRun(int32_t aRunIndex, int32_t* aLogicalStart,
+                              int32_t* aLength, nsBidiDirection* aDirection)
+{
+  *aDirection = nsBidiDirection(ubidi_getVisualRun(mBiDi, aRunIndex,
+                                                   aLogicalStart, aLength));
+  return NS_OK;
+}
+
+nsresult nsBidi::ReorderVisual(const nsBidiLevel* aLevels, int32_t aLength,
+                               int32_t* aIndexMap)
+{
+  ubidi_reorderVisual(aLevels, aLength, aIndexMap);
+  return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/layout/base/nsBidi_ICU.h
@@ -0,0 +1,190 @@
+/* -*- 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 nsBidi_ICU_h__
+#define nsBidi_ICU_h__
+
+#include "unicode/ubidi.h"
+#include "nsIFrame.h" // for nsBidiLevel/nsBidiDirection declarations
+
+// nsBidi implemented as a simple wrapper around the bidi reordering engine
+// from ICU.
+// We could eliminate this and let callers use the ICU functions directly
+// once we no longer care about building without ICU available.
+
+class nsBidi
+{
+public:
+  /** @brief Default constructor.
+   *
+   * The nsBidi object is initially empty. It is assigned
+   * the Bidi properties of a paragraph by <code>SetPara()</code>.
+   */
+  explicit nsBidi();
+
+  /** @brief Destructor. */
+  virtual ~nsBidi();
+
+
+  /**
+   * Perform the Unicode Bidi algorithm.
+   *
+   * @param aText is a pointer to the single-paragraph text that the
+   *      Bidi algorithm will be performed on
+   *      (step (P1) of the algorithm is performed externally).
+   *      <strong>The text must be (at least) <code>aLength</code> long.</strong>
+   *
+   * @param aLength is the length of the text; if <code>aLength==-1</code> then
+   *      the text must be zero-terminated.
+   *
+   * @param aParaLevel specifies the default level for the paragraph;
+   *      it is typically 0 (LTR) or 1 (RTL).
+   *      If the function shall determine the paragraph level from the text,
+   *      then <code>aParaLevel</code> can be set to
+   *      either <code>NSBIDI_DEFAULT_LTR</code>
+   *      or <code>NSBIDI_DEFAULT_RTL</code>;
+   *      if there is no strongly typed character, then
+   *      the desired default is used (0 for LTR or 1 for RTL).
+   *      Any other value between 0 and <code>NSBIDI_MAX_EXPLICIT_LEVEL</code>
+   *      is also valid, with odd levels indicating RTL.
+   */
+  nsresult SetPara(const char16_t *aText, int32_t aLength,
+                   nsBidiLevel aParaLevel);
+
+  /**
+   * Get the directionality of the text.
+   *
+   * @param aDirection receives a <code>NSBIDI_XXX</code> value that indicates
+   *       if the entire text represented by this object is unidirectional,
+   *       and which direction, or if it is mixed-directional.
+   *
+   * @see nsBidiDirection
+   */
+  nsresult GetDirection(nsBidiDirection* aDirection);
+
+  /**
+   * Get the paragraph level of the text.
+   *
+   * @param aParaLevel receives a <code>NSBIDI_XXX</code> value indicating
+   *                   the paragraph level
+   *
+   * @see nsBidiLevel
+   */
+  nsresult GetParaLevel(nsBidiLevel* aParaLevel);
+
+  /**
+   * Get a logical run.
+   * This function returns information about a run and is used
+   * to retrieve runs in logical order.<p>
+   * This is especially useful for line-breaking on a paragraph.
+   *
+   * @param aLogicalStart is the first character of the run.
+   *
+   * @param aLogicalLimit will receive the limit of the run.
+   *      The l-value that you point to here may be the
+   *      same expression (variable) as the one for
+   *      <code>aLogicalStart</code>.
+   *      This pointer can be <code>nullptr</code> if this
+   *      value is not necessary.
+   *
+   * @param aLevel will receive the level of the run.
+   *      This pointer can be <code>nullptr</code> if this
+   *      value is not necessary.
+   */
+  nsresult GetLogicalRun(int32_t aLogicalStart, int32_t* aLogicalLimit,
+                         nsBidiLevel* aLevel);
+
+  /**
+   * Get the number of runs.
+   * This function may invoke the actual reordering on the
+   * <code>nsBidi</code> object, after <code>SetPara</code>
+   * may have resolved only the levels of the text. Therefore,
+   * <code>CountRuns</code> may have to allocate memory,
+   * and may fail doing so.
+   *
+   * @param aRunCount will receive the number of runs.
+   */
+  nsresult CountRuns(int32_t* aRunCount);
+
+  /**
+   * Get one run's logical start, length, and directionality,
+   * which can be 0 for LTR or 1 for RTL.
+   * In an RTL run, the character at the logical start is
+   * visually on the right of the displayed run.
+   * The length is the number of characters in the run.<p>
+   * <code>CountRuns</code> should be called
+   * before the runs are retrieved.
+   *
+   * @param aRunIndex is the number of the run in visual order, in the
+   *      range <code>[0..CountRuns-1]</code>.
+   *
+   * @param aLogicalStart is the first logical character index in the text.
+   *      The pointer may be <code>nullptr</code> if this index is not needed.
+   *
+   * @param aLength is the number of characters (at least one) in the run.
+   *      The pointer may be <code>nullptr</code> if this is not needed.
+   *
+   * @param aDirection will receive the directionality of the run,
+   *       <code>NSBIDI_LTR==0</code> or <code>NSBIDI_RTL==1</code>,
+   *       never <code>NSBIDI_MIXED</code>.
+   *
+   * @see CountRuns<p>
+   *
+   * Example:
+   * @code
+   *  int32_t i, count, logicalStart, visualIndex=0, length;
+   *  nsBidiDirection dir;
+   *  pBidi->CountRuns(&count);
+   *  for(i=0; i<count; ++i) {
+   *    pBidi->GetVisualRun(i, &logicalStart, &length, &dir);
+   *    if(NSBIDI_LTR==dir) {
+   *      do { // LTR
+   *        show_char(text[logicalStart++], visualIndex++);
+   *      } while(--length>0);
+   *    } else {
+   *      logicalStart+=length;  // logicalLimit
+   *      do { // RTL
+   *        show_char(text[--logicalStart], visualIndex++);
+   *      } while(--length>0);
+   *    }
+   *  }
+   * @endcode
+   *
+   * Note that in right-to-left runs, code like this places
+   * modifier letters before base characters and second surrogates
+   * before first ones.
+   */
+  nsresult GetVisualRun(int32_t aRunIndex, int32_t* aLogicalStart,
+                        int32_t* aLength, nsBidiDirection* aDirection);
+
+  /**
+   * This is a convenience function that does not use a nsBidi object.
+   * It is intended to be used for when an application has determined the levels
+   * of objects (character sequences) and just needs to have them reordered (L2).
+   * This is equivalent to using <code>GetVisualMap</code> on a
+   * <code>nsBidi</code> object.
+   *
+   * @param aLevels is an array with <code>aLength</code> levels that have been
+   *      determined by the application.
+   *
+   * @param aLength is the number of levels in the array, or, semantically,
+   *      the number of objects to be reordered.
+   *      It must be <code>aLength>0</code>.
+   *
+   * @param aIndexMap is a pointer to an array of <code>aLength</code>
+   *      indexes which will reflect the reordering of the characters.
+   *      The array does not need to be initialized.<p>
+   *      The index map will result in
+   *        <code>aIndexMap[aVisualIndex]==aLogicalIndex</code>.
+   */
+  static nsresult ReorderVisual(const nsBidiLevel* aLevels, int32_t aLength,
+                                int32_t* aIndexMap);
+
+protected:
+  UBiDi* mBiDi;
+};
+
+#endif // _nsBidi_ICU_h_
rename from layout/base/nsBidi.cpp
rename to layout/base/nsBidi_noICU.cpp
copy from layout/base/nsBidi.h
copy to layout/base/nsBidi_noICU.h
--- a/layout/base/nsBidi.h
+++ b/layout/base/nsBidi_noICU.h
@@ -1,16 +1,16 @@
 /* -*- 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 nsBidi_h__
-#define nsBidi_h__
+#ifndef nsBidi_noICU_h__
+#define nsBidi_noICU_h__
 
 #include "nsBidiUtils.h"
 #include "nsIFrame.h" // for frame property declaration
 
 // Bidi reordering engine from ICU
 /*
  * javadoc-style comments are intended to be transformed into HTML
  * using DOC++ - see
@@ -40,91 +40,25 @@
  * that are at the same embedding level
  * after performing the BIDI algorithm.<p>
  *
  * @author Markus W. Scherer. Ported to Mozilla by Simon Montagu
  * @version 1.0
  */
 
 /**
- * nsBidiLevel is the type of the level values in this
- * Bidi implementation.
- * It holds an embedding level and indicates the visual direction
- * by its bit 0 (even/odd value).<p>
- *
- * <li><code>aParaLevel</code> can be set to the
- * pseudo-level values <code>NSBIDI_DEFAULT_LTR</code>
- * and <code>NSBIDI_DEFAULT_RTL</code>.</li></ul>
- *
- * @see nsBidi::SetPara
- *
- * <p>The related constants are not real, valid level values.
- * <code>NSBIDI_DEFAULT_XXX</code> can be used to specify
- * a default for the paragraph level for
- * when the <code>SetPara</code> function
- * shall determine it but there is no
- * strongly typed character in the input.<p>
- *
- * Note that the value for <code>NSBIDI_DEFAULT_LTR</code> is even
- * and the one for <code>NSBIDI_DEFAULT_RTL</code> is odd,
- * just like with normal LTR and RTL level values -
- * these special values are designed that way. Also, the implementation
- * assumes that NSBIDI_MAX_EXPLICIT_LEVEL is odd.
- *
- * @see NSBIDI_DEFAULT_LTR
- * @see NSBIDI_DEFAULT_RTL
- * @see NSBIDI_LEVEL_OVERRIDE
- * @see NSBIDI_MAX_EXPLICIT_LEVEL
- */
-typedef uint8_t nsBidiLevel;
-
-/** Paragraph level setting.
- *  If there is no strong character, then set the paragraph level to 0 (left-to-right).
- */
-#define NSBIDI_DEFAULT_LTR 0xfe
-
-/** Paragraph level setting.
- *  If there is no strong character, then set the paragraph level to 1 (right-to-left).
- */
-#define NSBIDI_DEFAULT_RTL 0xff
-
-/**
- * Maximum explicit embedding level.
- * (The maximum resolved level can be up to <code>NSBIDI_MAX_EXPLICIT_LEVEL+1</code>).
- *
- */
-#define NSBIDI_MAX_EXPLICIT_LEVEL 125
-
-/** Bit flag for level input.
- *  Overrides directional properties.
- */
-#define NSBIDI_LEVEL_OVERRIDE 0x80
-
-/**
  * Special value which can be returned by the mapping functions when a logical
  * index has no corresponding visual index or vice-versa.
  * @see GetVisualIndex
  * @see GetVisualMap
  * @see GetLogicalIndex
  * @see GetLogicalMap
  */
 #define NSBIDI_MAP_NOWHERE (-1)
 
-/**
- * <code>nsBidiDirection</code> values indicate the text direction.
- */
-enum nsBidiDirection {
-  /** All left-to-right text This is a 0 value. */
-  NSBIDI_LTR,
-  /** All right-to-left text This is a 1 value. */
-  NSBIDI_RTL,
-  /** Mixed-directional text. */
-  NSBIDI_MIXED
-};
-
 /* miscellaneous definitions ------------------------------------------------ */
 
 /* helper macros for each allocated array member */
 #define GETDIRPROPSMEMORY(length) nsBidi::GetMemory((void **)&mDirPropsMemory, \
                                                     &mDirPropsSize, \
                                                     (length))
 
 #define GETLEVELSMEMORY(length) nsBidi::GetMemory((void **)&mLevelsMemory, \
@@ -407,33 +341,16 @@ struct LevState {
     PImpTab pImpTab;                    /* level table pointer          */
     PImpAct pImpAct;                    /* action map array             */
     int32_t startON;                    /* start of ON sequence         */
     int32_t state;                      /* current state                */
     int32_t runStart;                   /* start position of the run    */
     nsBidiLevel runLevel;               /* run level before implicit solving */
 };
 
-namespace mozilla {
-
-// Pseudo bidi embedding level indicating nonexistence.
-static const nsBidiLevel kBidiLevelNone = 0xff;
-
-struct FrameBidiData
-{
-  nsBidiLevel baseLevel;
-  nsBidiLevel embeddingLevel;
-  // The embedding level of virtual bidi formatting character before
-  // this frame if any. kBidiLevelNone is used to indicate nonexistence
-  // or unnecessity of such virtual character.
-  nsBidiLevel precedingControl;
-};
-
-} // namespace mozilla
-
 /**
  * This class holds information about a paragraph of text
  * with Bidi-algorithm-related details, or about one line of
  * such a paragraph.<p>
  * Reordering can be done on a line, or on a paragraph which is
  * then interpreted as one single line.<p>
  *
  * On construction, the class is initially empty. It is assigned
@@ -664,33 +581,16 @@ public:
    *
    * @param aOptions A bit set of options for the reordering that control
    *                how the reordered text is written.
    *
    * @param aDestSize will receive the number of characters that were written to <code>aDest</code>.
    */
   nsresult WriteReverse(const char16_t *aSrc, int32_t aSrcLength, char16_t *aDest, uint16_t aOptions, int32_t *aDestSize);
 
-  NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(BidiDataProperty, mozilla::FrameBidiData)
-
-  static mozilla::FrameBidiData GetBidiData(nsIFrame* aFrame)
-  {
-    return aFrame->Properties().Get(BidiDataProperty());
-  }
-
-  static nsBidiLevel GetBaseLevel(nsIFrame* aFrame)
-  {
-    return GetBidiData(aFrame).baseLevel;
-  }
-
-  static nsBidiLevel GetEmbeddingLevel(nsIFrame* aFrame)
-  {
-    return GetBidiData(aFrame).embeddingLevel;
-  }
-
 protected:
   friend class nsBidiPresUtils;
 
   class BracketData {
   public:
     explicit BracketData(const nsBidi* aBidi);
     ~BracketData();
 
@@ -801,9 +701,9 @@ private:
 
   void GetSingleRun(nsBidiLevel aLevel);
 
   void ReorderLine(nsBidiLevel aMinLevel, nsBidiLevel aMaxLevel);
 
   static bool PrepareReorder(const nsBidiLevel *aLevels, int32_t aLength, int32_t *aIndexMap, nsBidiLevel *aMinLevel, nsBidiLevel *aMaxLevel);
 };
 
-#endif // _nsBidi_h_
+#endif // _nsBidi_noICU_h_
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -3874,44 +3874,78 @@ nsCSSFrameConstructor::ConstructFrameFro
 
     // If we need to create a block formatting context to wrap our
     // kids, do it now.
     const nsStyleDisplay* maybeAbsoluteContainingBlockDisplay = display;
     nsIFrame* maybeAbsoluteContainingBlockStyleFrame = primaryFrame;
     nsIFrame* maybeAbsoluteContainingBlock = newFrame;
     nsIFrame* possiblyLeafFrame = newFrame;
     if (bits & FCDATA_CREATE_BLOCK_WRAPPER_FOR_ALL_KIDS) {
-      RefPtr<nsStyleContext> blockContext;
-      blockContext =
+      RefPtr<nsStyleContext> outerSC =
         mPresShell->StyleSet()->ResolveAnonymousBoxStyle(*data->mAnonBoxPseudo,
                                                          styleContext);
-      nsIFrame* blockFrame =
-        NS_NewBlockFormattingContext(mPresShell, blockContext);
-
 #ifdef DEBUG
       nsContainerFrame* containerFrame = do_QueryFrame(newFrame);
       MOZ_ASSERT(containerFrame);
 #endif
       nsContainerFrame* container = static_cast<nsContainerFrame*>(newFrame);
-      InitAndRestoreFrame(aState, content, container, blockFrame);
-
-      SetInitialSingleChild(container, blockFrame);
-
-      // Now figure out whether newFrame or blockFrame should be the
-      // absolute container.  It should be the latter if it's
-      // positioned, otherwise the former.
-      const nsStyleDisplay* blockDisplay = blockContext->StyleDisplay();
-      if (blockDisplay->IsAbsPosContainingBlock(blockFrame)) {
-        maybeAbsoluteContainingBlockDisplay = blockDisplay;
-        maybeAbsoluteContainingBlock = blockFrame;
-        maybeAbsoluteContainingBlockStyleFrame = blockFrame;
-      }
-
-      // Our kids should go into the blockFrame
-      newFrame = blockFrame;
+      nsContainerFrame* outerFrame;
+      nsContainerFrame* innerFrame;
+      switch (display->mDisplay) {
+        case StyleDisplay::Flex:
+        case StyleDisplay::InlineFlex:
+          outerFrame = NS_NewFlexContainerFrame(mPresShell, outerSC);
+          InitAndRestoreFrame(aState, content, container, outerFrame);
+          innerFrame = outerFrame;
+          break;
+        case StyleDisplay::Grid:
+        case StyleDisplay::InlineGrid:
+          outerFrame = NS_NewGridContainerFrame(mPresShell, outerSC);
+          InitAndRestoreFrame(aState, content, container, outerFrame);
+          innerFrame = outerFrame;
+          break;
+        default: {
+          nsContainerFrame* columnSetFrame = nullptr;
+          RefPtr<nsStyleContext> innerSC = outerSC;
+          const nsStyleColumn* columns = outerSC->StyleColumn();
+          if (columns->mColumnCount != NS_STYLE_COLUMN_COUNT_AUTO ||
+              columns->mColumnWidth.GetUnit() != eStyleUnit_Auto) {
+            columnSetFrame =
+              NS_NewColumnSetFrame(mPresShell, outerSC, nsFrameState(0));
+            InitAndRestoreFrame(aState, content, container, columnSetFrame);
+            innerSC = mPresShell->StyleSet()->ResolveAnonymousBoxStyle(
+              nsCSSAnonBoxes::columnContent, outerSC);
+          }
+          innerFrame = NS_NewBlockFormattingContext(mPresShell, innerSC);
+          if (columnSetFrame) {
+            InitAndRestoreFrame(aState, content, columnSetFrame, innerFrame);
+            SetInitialSingleChild(columnSetFrame, innerFrame);
+            outerFrame = columnSetFrame;
+          } else {
+            InitAndRestoreFrame(aState, content, container, innerFrame);
+            outerFrame = innerFrame;
+          }
+          break;
+        }
+      }
+
+      SetInitialSingleChild(container, outerFrame);
+
+      // Now figure out whether newFrame or outerFrame should be the
+      // absolute container.
+      auto outerDisplay = outerSC->StyleDisplay();
+      if (outerDisplay->IsAbsPosContainingBlock(outerFrame)) {
+        maybeAbsoluteContainingBlockDisplay = outerDisplay;
+        maybeAbsoluteContainingBlock = outerFrame;
+        maybeAbsoluteContainingBlockStyleFrame = outerFrame;
+        innerFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
+      }
+
+      // Our kids should go into the innerFrame.
+      newFrame = innerFrame;
     }
 
     aState.AddChild(frameToAddToList, aFrameItems, content, styleContext,
                     aParentFrame, allowOutOfFlow, allowOutOfFlow, isPopup);
 
     nsContainerFrame* newFrameAsContainer = do_QueryFrame(newFrame);
     if (newFrameAsContainer) {
 #ifdef MOZ_XUL
--- a/layout/base/nsCaret.cpp
+++ b/layout/base/nsCaret.cpp
@@ -666,17 +666,17 @@ nsCaret::GetCaretFrameForNodeOffset(nsFr
   // Direction Style from visibility->mDirection
   // ------------------
   // NS_STYLE_DIRECTION_LTR : LTR or Default
   // NS_STYLE_DIRECTION_RTL
   if (theFrame->PresContext()->BidiEnabled())
   {
     // If there has been a reflow, take the caret Bidi level to be the level of the current frame
     if (aBidiLevel & BIDI_LEVEL_UNDEFINED) {
-      aBidiLevel = nsBidi::GetEmbeddingLevel(theFrame);
+      aBidiLevel = theFrame->GetEmbeddingLevel();
     }
 
     int32_t start;
     int32_t end;
     nsIFrame* frameBefore;
     nsIFrame* frameAfter;
     nsBidiLevel levelBefore; // Bidi level of the character before the caret
     nsBidiLevel levelAfter;  // Bidi level of the character after the caret
@@ -714,17 +714,17 @@ nsCaret::GetCaretFrameForNodeOffset(nsFr
                 theFrameOffset = end;
               }
               else 
               {
                 // if there is no frameBefore, we must be at the beginning of the line
                 // so we stay with the current frame.
                 // Exception: when the first frame on the line has a different Bidi level from the paragraph level, there is no
                 // real frame for the caret to be in. We have to find the visually first frame on the line.
-                nsBidiLevel baseLevel = nsBidi::GetBaseLevel(frameAfter);
+                nsBidiLevel baseLevel = frameAfter->GetBaseLevel();
                 if (baseLevel != levelAfter)
                 {
                   nsPeekOffsetStruct pos(eSelectBeginLine, eDirPrevious, 0,
                                          nsPoint(0, 0), false, true, false,
                                          true, false);
                   if (NS_SUCCEEDED(frameAfter->PeekOffset(&pos))) {
                     theFrame = pos.mResultFrame;
                     theFrameOffset = pos.mContentOffset;
@@ -749,17 +749,17 @@ nsCaret::GetCaretFrameForNodeOffset(nsFr
                 theFrameOffset = start;
               }
               else 
               {
                 // if there is no frameAfter, we must be at the end of the line
                 // so we stay with the current frame.
                 // Exception: when the last frame on the line has a different Bidi level from the paragraph level, there is no
                 // real frame for the caret to be in. We have to find the visually last frame on the line.
-                nsBidiLevel baseLevel = nsBidi::GetBaseLevel(frameBefore);
+                nsBidiLevel baseLevel = frameBefore->GetBaseLevel();
                 if (baseLevel != levelBefore)
                 {
                   nsPeekOffsetStruct pos(eSelectEndLine, eDirNext, 0,
                                          nsPoint(0, 0), false, true, false,
                                          true, false);
                   if (NS_SUCCEEDED(frameBefore->PeekOffset(&pos))) {
                     theFrame = pos.mResultFrame;
                     theFrameOffset = pos.mContentOffset;
@@ -770,31 +770,31 @@ nsCaret::GetCaretFrameForNodeOffset(nsFr
           }
           else if (aBidiLevel > levelBefore && aBidiLevel < levelAfter  // rule c7/8
                    && IS_SAME_DIRECTION(levelBefore, levelAfter)        // before and after have the same parity
                    && !IS_SAME_DIRECTION(aBidiLevel, levelAfter))       // caret has different parity
           {
             if (NS_SUCCEEDED(aFrameSelection->GetFrameFromLevel(frameAfter, eDirNext, aBidiLevel, &theFrame)))
             {
               theFrame->GetOffsets(start, end);
-              levelAfter = nsBidi::GetEmbeddingLevel(theFrame);
+              levelAfter = theFrame->GetEmbeddingLevel();
               if (IS_LEVEL_RTL(aBidiLevel)) // c8: caret to the right of the rightmost character
                 theFrameOffset = IS_LEVEL_RTL(levelAfter) ? start : end;
               else               // c7: caret to the left of the leftmost character
                 theFrameOffset = IS_LEVEL_RTL(levelAfter) ? end : start;
             }
           }
           else if (aBidiLevel < levelBefore && aBidiLevel > levelAfter  // rule c11/12
                    && IS_SAME_DIRECTION(levelBefore, levelAfter)        // before and after have the same parity
                    && !IS_SAME_DIRECTION(aBidiLevel, levelAfter))       // caret has different parity
           {
             if (NS_SUCCEEDED(aFrameSelection->GetFrameFromLevel(frameBefore, eDirPrevious, aBidiLevel, &theFrame)))
             {
               theFrame->GetOffsets(start, end);
-              levelBefore = nsBidi::GetEmbeddingLevel(theFrame);
+              levelBefore = theFrame->GetEmbeddingLevel();
               if (IS_LEVEL_RTL(aBidiLevel)) // c12: caret to the left of the leftmost character
                 theFrameOffset = IS_LEVEL_RTL(levelBefore) ? end : start;
               else               // c11: caret to the right of the rightmost character
                 theFrameOffset = IS_LEVEL_RTL(levelBefore) ? start : end;
             }
           }
         }
       }
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -6164,17 +6164,19 @@ nsLayoutUtils::GetFirstLinePosition(Writ
                                     const nsIFrame* aFrame,
                                     LinePosition* aResult)
 {
   const nsBlockFrame* block = nsLayoutUtils::GetAsBlock(const_cast<nsIFrame*>(aFrame));
   if (!block) {
     // For the first-line baseline we also have to check for a table, and if
     // so, use the baseline of its first row.
     nsIAtom* fType = aFrame->GetType();
-    if (fType == nsGkAtoms::tableWrapperFrame) {
+    if (fType == nsGkAtoms::tableWrapperFrame  ||
+        fType == nsGkAtoms::flexContainerFrame ||
+        fType == nsGkAtoms::gridContainerFrame) {
       aResult->mBStart = 0;
       aResult->mBaseline = aFrame->GetLogicalBaseline(aWM);
       // This is what we want for the list bullet caller; not sure if
       // other future callers will want the same.
       aResult->mBEnd = aFrame->BSize(aWM);
       return true;
     }
 
--- a/layout/base/nsRefreshDriver.cpp
+++ b/layout/base/nsRefreshDriver.cpp
@@ -79,22 +79,26 @@ static mozilla::LazyLogModule sRefreshDr
 
 #define DEFAULT_THROTTLED_FRAME_RATE 1
 #define DEFAULT_RECOMPUTE_VISIBILITY_INTERVAL_MS 1000
 // after 10 minutes, stop firing off inactive timers
 #define DEFAULT_INACTIVE_TIMER_DISABLE_SECONDS 600
 
 // The number of seconds spent skipping frames because we are waiting for the compositor
 // before logging.
-#ifdef MOZ_VALGRIND
-#define REFRESH_WAIT_WARNING 10
-#elif defined(DEBUG) || defined(MOZ_ASAN)
-#define REFRESH_WAIT_WARNING 5
+#if defined(MOZ_ASAN)
+# define REFRESH_WAIT_WARNING 5
+#elif defined(DEBUG) && !defined(MOZ_VALGRIND)
+# define REFRESH_WAIT_WARNING 5
+#elif defined(DEBUG) && defined(MOZ_VALGRIND)
+# define REFRESH_WAIT_WARNING (RUNNING_ON_VALGRIND ? 20 : 5)
+#elif defined(MOZ_VALGRIND)
+# define REFRESH_WAIT_WARNING (RUNNING_ON_VALGRIND ? 10 : 1)
 #else
-#define REFRESH_WAIT_WARNING 1
+# define REFRESH_WAIT_WARNING 1
 #endif
 
 namespace {
   // `true` if we are currently in jank-critical mode.
   //
   // In jank-critical mode, any iteration of the event loop that takes
   // more than 16ms to compute will cause an ongoing animation to miss
   // frames.
--- a/layout/generic/nsFlexContainerFrame.cpp
+++ b/layout/generic/nsFlexContainerFrame.cpp
@@ -101,19 +101,21 @@ IsLegacyBox(const nsStyleDisplay* aStyle
   }
 
   // If this frame is for a scrollable element, then it will actually have
   // "display:block", and its *parent* will have the real flex-flavored display
   // value. So in that case, check the parent to find out if we're legacy.
   if (aStyleDisp->mDisplay == mozilla::StyleDisplay::Block) {
     nsStyleContext* parentStyleContext = aStyleContext->GetParent();
     NS_ASSERTION(parentStyleContext &&
-                 aStyleContext->GetPseudo() == nsCSSAnonBoxes::scrolledContent,
+                 (aStyleContext->GetPseudo() == nsCSSAnonBoxes::buttonContent ||
+                  aStyleContext->GetPseudo() == nsCSSAnonBoxes::scrolledContent),
                  "The only way a nsFlexContainerFrame can have 'display:block' "
-                 "should be if it's the inner part of a scrollable element");
+                 "should be if it's the inner part of a scrollable or button "
+                 "element");
     if (IsDisplayValueLegacyBox(parentStyleContext->StyleDisplay())) {
       return true;
     }
   }
 
   return false;
 }
 
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -144,17 +144,17 @@ struct nsContentAndOffset
 #define CALC_DEBUG             0
 
 // This is faster than nsBidiPresUtils::IsFrameInParagraphDirection,
 // because it uses the frame pointer passed in without drilling down to
 // the leaf frame.
 static bool
 IsReversedDirectionFrame(nsIFrame* aFrame)
 {
-  FrameBidiData bidiData = nsBidi::GetBidiData(aFrame);
+  FrameBidiData bidiData = aFrame->GetBidiData();
   return !IS_SAME_DIRECTION(bidiData.embeddingLevel, bidiData.baseLevel);
 }
 
 #include "nsILineIterator.h"
 
 //non Hack prototypes
 #if 0
 static void RefreshContentFrames(nsPresContext* aPresContext, nsIContent * aStartContent, nsIContent * aEndContent);
@@ -6600,17 +6600,17 @@ nsFrame::GetPointFromOffset(int32_t inOf
 
       // Find the direction of the frame from the EmbeddingLevelProperty,
       // which is the resolved bidi level set in
       // nsBidiPresUtils::ResolveParagraph (odd levels = right-to-left).
       // If the embedding level isn't set, just use the CSS direction
       // property.
       bool hasBidiData;
       FrameBidiData bidiData =
-        Properties().Get(nsBidi::BidiDataProperty(), &hasBidiData);
+        Properties().Get(BidiDataProperty(), &hasBidiData);
       bool isRTL = hasBidiData
         ? IS_LEVEL_RTL(bidiData.embeddingLevel)
         : StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL;
       if ((!isRTL && inOffset > newOffset) ||
           (isRTL && inOffset <= newOffset)) {
         pt = contentRect.TopRight();
       }
     }
--- a/layout/generic/nsGridContainerFrame.cpp
+++ b/layout/generic/nsGridContainerFrame.cpp
@@ -6448,45 +6448,51 @@ nsGridContainerFrame::SynthesizeBaseline
   const nsSize&        aCBPhysicalSize,
   nscoord              aCBSize,
   WritingMode          aCBWM)
 {
   if (MOZ_UNLIKELY(!aGridOrderItem.mItem)) {
     // No item in this fragment - synthesize a baseline from our border-box.
     return ::SynthesizeBaselineFromBorderBox(aGroup, aCBWM, aCBSize);
   }
+  auto GetBBaseline = [] (BaselineSharingGroup aGroup, WritingMode aWM,
+                          const nsIFrame* aFrame, nscoord* aBaseline) {
+    return aGroup == BaselineSharingGroup::eFirst ?
+      nsLayoutUtils::GetFirstLineBaseline(aWM, aFrame, aBaseline) :
+      nsLayoutUtils::GetLastLineBaseline(aWM, aFrame, aBaseline);
+  };
   nsIFrame* child = aGridOrderItem.mItem->mFrame;
   nsGridContainerFrame* grid = do_QueryFrame(child);
   auto childWM = child->GetWritingMode();
   bool isOrthogonal = aCBWM.IsOrthogonalTo(childWM);
   nscoord baseline;
   nscoord start;
   nscoord size;
   if (aAxis == eLogicalAxisBlock) {
     start = child->GetLogicalNormalPosition(aCBWM, aCBPhysicalSize).B(aCBWM);
     size = child->BSize(aCBWM);
     if (grid && aGridOrderItem.mIsInEdgeTrack) {
       isOrthogonal ? grid->GetIBaseline(aGroup, &baseline) :
                      grid->GetBBaseline(aGroup, &baseline);
     } else if (!isOrthogonal && aGridOrderItem.mIsInEdgeTrack &&
-               nsLayoutUtils::GetLastLineBaseline(childWM, child, &baseline)) {
+               GetBBaseline(aGroup, childWM, child, &baseline)) {
       if (aGroup == BaselineSharingGroup::eLast) {
         baseline = size - baseline; // convert to distance from border-box end
       }
     } else {
       baseline = ::SynthesizeBaselineFromBorderBox(aGroup, childWM, size);
     }
   } else {
     start = child->GetLogicalNormalPosition(aCBWM, aCBPhysicalSize).I(aCBWM);
     size = child->ISize(aCBWM);
     if (grid && aGridOrderItem.mIsInEdgeTrack) {
       isOrthogonal ? grid->GetBBaseline(aGroup, &baseline) :
                      grid->GetIBaseline(aGroup, &baseline);
     } else if (isOrthogonal && aGridOrderItem.mIsInEdgeTrack &&
-               nsLayoutUtils::GetLastLineBaseline(childWM, child, &baseline)) {
+               GetBBaseline(aGroup, childWM, child, &baseline)) {
       if (aGroup == BaselineSharingGroup::eLast) {
         baseline = size - baseline; // convert to distance from border-box end
       }
     } else {
       baseline = ::SynthesizeBaselineFromBorderBox(aGroup, childWM, size);
     }
   }
   return aGroup == BaselineSharingGroup::eFirst ? start + baseline :
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -345,16 +345,82 @@ enum class nsDidReflowStatus : uint32_t 
 
 #define NS_FRAME_OVERFLOW_NONE    0x00000000 // there are no overflow rects;
                                              // code relies on this being
                                              // the all-zero value
 
 #define NS_FRAME_OVERFLOW_LARGE   0x000000ff // overflow is stored as a
                                              // separate rect property
 
+/**
+ * nsBidiLevel is the type of the level values in our Unicode Bidi
+ * implementation.
+ * It holds an embedding level and indicates the visual direction
+ * by its bit 0 (even/odd value).<p>
+ *
+ * <li><code>aParaLevel</code> can be set to the
+ * pseudo-level values <code>NSBIDI_DEFAULT_LTR</code>
+ * and <code>NSBIDI_DEFAULT_RTL</code>.</li></ul>
+ *
+ * @see nsBidi::SetPara
+ *
+ * <p>The related constants are not real, valid level values.
+ * <code>NSBIDI_DEFAULT_XXX</code> can be used to specify
+ * a default for the paragraph level for
+ * when the <code>SetPara</code> function
+ * shall determine it but there is no
+ * strongly typed character in the input.<p>
+ *
+ * Note that the value for <code>NSBIDI_DEFAULT_LTR</code> is even
+ * and the one for <code>NSBIDI_DEFAULT_RTL</code> is odd,
+ * just like with normal LTR and RTL level values -
+ * these special values are designed that way. Also, the implementation
+ * assumes that NSBIDI_MAX_EXPLICIT_LEVEL is odd.
+ *
+ * @see NSBIDI_DEFAULT_LTR
+ * @see NSBIDI_DEFAULT_RTL
+ * @see NSBIDI_LEVEL_OVERRIDE
+ * @see NSBIDI_MAX_EXPLICIT_LEVEL
+ */
+typedef uint8_t nsBidiLevel;
+
+/** Paragraph level setting.
+ *  If there is no strong character, then set the paragraph level to 0 (left-to-right).
+ */
+#define NSBIDI_DEFAULT_LTR 0xfe
+
+/** Paragraph level setting.
+ *  If there is no strong character, then set the paragraph level to 1 (right-to-left).
+ */
+#define NSBIDI_DEFAULT_RTL 0xff
+
+/**
+ * Maximum explicit embedding level.
+ * (The maximum resolved level can be up to <code>NSBIDI_MAX_EXPLICIT_LEVEL+1</code>).
+ *
+ */
+#define NSBIDI_MAX_EXPLICIT_LEVEL 125
+
+/** Bit flag for level input.
+ *  Overrides directional properties.
+ */
+#define NSBIDI_LEVEL_OVERRIDE 0x80
+
+/**
+ * <code>nsBidiDirection</code> values indicate the text direction.
+ */
+enum nsBidiDirection {
+  /** All left-to-right text This is a 0 value. */
+  NSBIDI_LTR,
+  /** All right-to-left text This is a 1 value. */
+  NSBIDI_RTL,
+  /** Mixed-directional text. */
+  NSBIDI_MIXED
+};
+
 namespace mozilla {
 /*
  * For replaced elements only. Gets the intrinsic dimensions of this element.
  * The dimensions may only be one of the following two types:
  *
  *   eStyleUnit_Coord   - a length in app units
  *   eStyleUnit_None    - the element has no intrinsic size in this dimension
  */
@@ -373,16 +439,29 @@ struct IntrinsicSize {
   bool operator==(const IntrinsicSize& rhs) {
     return width == rhs.width && height == rhs.height;
   }
   bool operator!=(const IntrinsicSize& rhs) {
     return !(*this == rhs);
   }
 };
 
+// Pseudo bidi embedding level indicating nonexistence.
+static const nsBidiLevel kBidiLevelNone = 0xff;
+
+struct FrameBidiData
+{
+  nsBidiLevel baseLevel;
+  nsBidiLevel embeddingLevel;
+  // The embedding level of virtual bidi formatting character before
+  // this frame if any. kBidiLevelNone is used to indicate nonexistence
+  // or unnecessity of such virtual character.
+  nsBidiLevel precedingControl;
+};
+
 } // namespace mozilla
 
 /// Generic destructor for frame properties. Calls delete.
 template<typename T>
 static void DeleteValue(T* aPropertyValue)
 {
   delete aPropertyValue;
 }
@@ -948,16 +1027,33 @@ public:
   NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(FragStretchBSizeProperty, nscoord)
 
   NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(IBaselinePadProperty, nscoord)
   NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(BBaselinePadProperty, nscoord)
 
   NS_DECLARE_FRAME_PROPERTY_WITH_DTOR(GenConProperty, ContentArray,
                                       DestroyContentArray)
 
+  NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(BidiDataProperty, mozilla::FrameBidiData)
+
+  mozilla::FrameBidiData GetBidiData()
+  {
+    return Properties().Get(BidiDataProperty());
+  }
+
+  nsBidiLevel GetBaseLevel()
+  {
+    return GetBidiData().baseLevel;
+  }
+
+  nsBidiLevel GetEmbeddingLevel()
+  {
+    return GetBidiData().embeddingLevel;
+  }
+
   nsTArray<nsIContent*>* GetGenConPseudos() {
     return Properties().Get(GenConProperty());
   }
 
   /**
    * Return the distance between the border edge of the frame and the
    * margin edge of the frame.  Like GetRect(), returns the dimensions
    * as of the most recent reflow.
--- a/layout/generic/nsSelection.cpp
+++ b/layout/generic/nsSelection.cpp
@@ -1127,28 +1127,28 @@ nsFrameSelection::MoveCaret(nsDirection 
       switch (aAmount) {
         case eSelectBeginLine:
         case eSelectEndLine: {
           // In Bidi contexts, PeekOffset calculates pos.mContentOffset
           // differently depending on whether the movement is visual or logical.
           // For visual movement, pos.mContentOffset depends on the direction-
           // ality of the first/last frame on the line (theFrame), and the caret
           // directionality must correspond.
-          FrameBidiData bidiData = nsBidi::GetBidiData(theFrame);
+          FrameBidiData bidiData = theFrame->GetBidiData();
           SetCaretBidiLevel(visualMovement ? bidiData.embeddingLevel
                                            : bidiData.baseLevel);
           break;
         }
         default:
           // If the current position is not a frame boundary, it's enough just
           // to take the Bidi level of the current frame
           if ((pos.mContentOffset != frameStart &&
                pos.mContentOffset != frameEnd) ||
               eSelectLine == aAmount) {
-            SetCaretBidiLevel(nsBidi::GetEmbeddingLevel(theFrame));
+            SetCaretBidiLevel(theFrame->GetEmbeddingLevel());
           }
           else {
             BidiLevelFromMove(mShell, pos.mResultContent, pos.mContentOffset,
                               aAmount, tHint);
           }
       }
     }
     result = TakeFocus(pos.mResultContent, pos.mContentOffset, pos.mContentOffset,
@@ -1368,34 +1368,34 @@ nsFrameSelection::GetPrevNextBidiLevels(
   if (0 == frameStart && 0 == frameEnd)
     direction = eDirPrevious;
   else if (frameStart == currentOffset)
     direction = eDirPrevious;
   else if (frameEnd == currentOffset)
     direction = eDirNext;
   else {
     // we are neither at the beginning nor at the end of the frame, so we have no worries
-    nsBidiLevel currentLevel = nsBidi::GetEmbeddingLevel(currentFrame);
+    nsBidiLevel currentLevel = currentFrame->GetEmbeddingLevel();
     levels.SetData(currentFrame, currentFrame, currentLevel, currentLevel);
     return levels;
   }
 
   nsIFrame *newFrame;
   int32_t offset;
   bool jumpedLine, movedOverNonSelectableText;
   nsresult rv = currentFrame->GetFrameFromDirection(direction, false,
                                                     aJumpLines, true,
                                                     &newFrame, &offset, &jumpedLine,
                                                     &movedOverNonSelectableText);
   if (NS_FAILED(rv))
     newFrame = nullptr;
 
-  FrameBidiData currentBidi = nsBidi::GetBidiData(currentFrame);
+  FrameBidiData currentBidi = currentFrame->GetBidiData();
   nsBidiLevel currentLevel = currentBidi.embeddingLevel;
-  nsBidiLevel newLevel = newFrame ? nsBidi::GetEmbeddingLevel(newFrame)
+  nsBidiLevel newLevel = newFrame ? newFrame->GetEmbeddingLevel()
                                   : currentBidi.baseLevel;
   
   // If not jumping lines, disregard br frames, since they might be positioned incorrectly.
   // XXX This could be removed once bug 339786 is fixed.
   if (!aJumpLines) {
     if (currentFrame->GetType() == nsGkAtoms::brFrame) {
       currentFrame = nullptr;
       currentLevel = currentBidi.baseLevel;
@@ -1446,17 +1446,17 @@ nsFrameSelection::GetFrameFromLevel(nsIF
     if (aDirection == eDirNext)
       frameTraversal->Next();
     else
       frameTraversal->Prev();
 
     foundFrame = frameTraversal->CurrentItem();
     if (!foundFrame)
       return NS_ERROR_FAILURE;
-    foundLevel = nsBidi::GetEmbeddingLevel(foundFrame);
+    foundLevel = foundFrame->GetEmbeddingLevel();
 
   } while (foundLevel > aBidiLevel);
 
   return NS_OK;
 }
 
 
 nsresult
@@ -1546,17 +1546,17 @@ void nsFrameSelection::BidiLevelFromClic
 {
   nsIFrame* clickInFrame=nullptr;
   int32_t OffsetNotUsed;
 
   clickInFrame = GetFrameForNodeOffset(aNode, aContentOffset, mHint, &OffsetNotUsed);
   if (!clickInFrame)
     return;
 
-  SetCaretBidiLevel(nsBidi::GetEmbeddingLevel(clickInFrame));
+  SetCaretBidiLevel(clickInFrame->GetEmbeddingLevel());
 }
 
 
 bool
 nsFrameSelection::AdjustForMaintainedSelection(nsIContent *aContent,
                                                int32_t     aOffset)
 {
   if (!mMaintainRange)
@@ -6423,17 +6423,17 @@ Selection::SelectionLanguageChange(bool 
   int32_t frameStart, frameEnd;
   focusFrame->GetOffsets(frameStart, frameEnd);
   RefPtr<nsPresContext> context = GetPresContext();
   nsBidiLevel levelBefore, levelAfter;
   if (!context) {
     return NS_ERROR_FAILURE;
   }
 
-  nsBidiLevel level = nsBidi::GetEmbeddingLevel(focusFrame);
+  nsBidiLevel level = focusFrame->GetEmbeddingLevel();
   int32_t focusOffset = static_cast<int32_t>(FocusOffset());
   if ((focusOffset != frameStart) && (focusOffset != frameEnd))
     // the cursor is not at a frame boundary, so the level of both the characters (logically) before and after the cursor
     //  is equal to the frame level
     levelBefore = levelAfter = level;
   else {
     // the cursor is at a frame boundary, so use GetPrevNextBidiLevels to find the level of the characters
     //  before and after the cursor
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -1786,18 +1786,18 @@ BuildTextRunsScanner::ContinueTextRunAcr
 {
   // We don't need to check font size inflation, since
   // |FindLineContainer| above (via |nsIFrame::CanContinueTextRun|)
   // ensures that text runs never cross block boundaries.  This means
   // that the font size inflation on all text frames in the text run is
   // already guaranteed to be the same as each other (and for the line
   // container).
   if (mBidiEnabled) {
-    FrameBidiData data1 = nsBidi::GetBidiData(aFrame1);
-    FrameBidiData data2 = nsBidi::GetBidiData(aFrame2);
+    FrameBidiData data1 = aFrame1->GetBidiData();
+    FrameBidiData data2 = aFrame2->GetBidiData();
     if (data1.embeddingLevel != data2.embeddingLevel ||
         data2.precedingControl != kBidiLevelNone) {
       return false;
     }
   }
 
   nsStyleContext* sc1 = aFrame1->StyleContext();
   const nsStyleText* textStyle1 = sc1->StyleText();
@@ -2255,17 +2255,17 @@ BuildTextRunsScanner::BuildTextRunForFra
   }
 
   if (textFlags & nsTextFrameUtils::TEXT_HAS_TAB) {
     textFlags |= gfxTextRunFactory::TEXT_ENABLE_SPACING;
   }
   if (textFlags & nsTextFrameUtils::TEXT_HAS_SHY) {
     textFlags |= gfxTextRunFactory::TEXT_ENABLE_HYPHEN_BREAKS;
   }
-  if (mBidiEnabled && (IS_LEVEL_RTL(nsBidi::GetEmbeddingLevel(firstFrame)))) {
+  if (mBidiEnabled && (IS_LEVEL_RTL(firstFrame->GetEmbeddingLevel()))) {
     textFlags |= gfxTextRunFactory::TEXT_IS_RTL;
   }
   if (mNextRunContextInfo & nsTextFrameUtils::INCOMING_WHITESPACE) {
     textFlags |= nsTextFrameUtils::TEXT_TRAILING_WHITESPACE;
   }
   if (mNextRunContextInfo & nsTextFrameUtils::INCOMING_ARABICCHAR) {
     textFlags |= gfxTextRunFactory::TEXT_TRAILING_ARABICCHAR;
   }
@@ -4333,28 +4333,28 @@ nsContinuingTextFrame::Init(nsIContent* 
       gfxTextRun *uninflatedTextRun =
         prev->GetTextRun(nsTextFrame::eNotInflated);
       if (uninflatedTextRun) {
         SetTextRun(uninflatedTextRun, nsTextFrame::eNotInflated, 1.0f);
       }
     }
   }
   if (aPrevInFlow->GetStateBits() & NS_FRAME_IS_BIDI) {
-    FrameBidiData bidiData = nsBidi::GetBidiData(aPrevInFlow);
+    FrameBidiData bidiData = aPrevInFlow->GetBidiData();
     bidiData.precedingControl = kBidiLevelNone;
-    Properties().Set(nsBidi::BidiDataProperty(), bidiData);
+    Properties().Set(BidiDataProperty(), bidiData);
 
     if (nextContinuation) {
       SetNextContinuation(nextContinuation);
       nextContinuation->SetPrevContinuation(this);
       // Adjust next-continuations' content offset as needed.
       while (nextContinuation &&
              nextContinuation->GetContentOffset() < mContentOffset) {
 #ifdef DEBUG
-        FrameBidiData nextBidiData = nsBidi::GetBidiData(nextContinuation);
+        FrameBidiData nextBidiData = nextContinuation->GetBidiData();
         NS_ASSERTION(bidiData.embeddingLevel == nextBidiData.embeddingLevel &&
                      bidiData.baseLevel == nextBidiData.baseLevel,
                      "stealing text from different type of BIDI continuation");
         MOZ_ASSERT(nextBidiData.precedingControl == kBidiLevelNone,
                    "There shouldn't be any virtual bidi formatting character "
                    "between continuations");
 #endif
         nextContinuation->mContentOffset = mContentOffset;
--- a/layout/reftests/css-grid/grid-container-baselines-001-ref.html
+++ b/layout/reftests/css-grid/grid-container-baselines-001-ref.html
@@ -10,17 +10,17 @@
   <link rel="stylesheet" type="text/css" href="support/ahem.css">
   <style type="text/css">
 html,body {
     color:black; background-color:white; font:16px/1 Ahem; padding:0; margin:0;
 }
 
 span {
   background: lime;
-  display: inline-block;
+  display: inline-grid;
   border: 1px solid black;
   font-size:12px;
 }
 span:nth-child(1) {font-size:32px; }
 span:nth-child(2) {padding-block-end:20px; }
 
 .sfb { align-self:baseline; }
 .slb { align-self:last-baseline; }
@@ -34,41 +34,43 @@ span:nth-child(2) {padding-block-end:20p
 .hl { writing-mode: horizontal-tb; direction:ltr; }
 .hr { writing-mode: horizontal-tb; direction:rtl; }
 .vl { writing-mode: vertical-lr; }
 .vr { writing-mode: vertical-rl; }
 .vlr { writing-mode: vertical-lr; direction:rtl; }
 .vrl { writing-mode: vertical-rl; direction:ltr; }
 
 .ib {
-  display: inline-block;
+  display: inline-table;
+  align-items: start;
   border: 2px solid;
   height: 120px;
   margin: 1px;
   font-size:1px;
   position:relative;
 }
 .ib2 {
-  display: inline-block;
+  display: inline-table;
+  align-items: start;
   padding-bottom: 20px;
   font-size:1px;
 }
 
 </style>
 </head>
 <body>
 
 A<div class="ib"><span>B<br>B</span><span style="float:right">C<br>C</span></div>
 <div class="ib"><span style="float:left">B<br>B</span><span style="display:inline-flex">C<br>C</span></div>
-<div class="ib"><span>B<br>B</span><span style="float:right; visibility:hidden">C<br>C</span><span style="position:absolute; bottom:0; right:0; padding-bottom:20px;">C<br>C</span></div>
+<div class="ib"><span>B<br>B</span><span style="float:right; visibility:hidden">C<br>C</span><span style="position:absolute; bottom:2px; right:2px; padding-bottom:20px;">C<br>C</span></div>
 
 <!-- TODO: figure out a reference for the corresponding test -->
 <!-- TODO: figure out a reference for the corresponding test -->
 
-<div class="ib"><div style="display:inline-grid; height:120px; align-content:end;"><div><span>B<br>B</span><span>C<br>C</span></div></div></div>
+<div class="ib"><div style="display:inline-grid; align-self:baseline; grid: 120px / auto auto"><span class="slb">B<br>B</span><span class="slb">C<br>C</span></div></div>
 <div class="ib"><span style="float:right">B<br>B</span><span>C<br>C</span></div>
 <div class="ib"><span style="float:right">B<br>B</span><span style="display:inline-flex">C<br>C</span></div>
 <!--
 <div class="ib"><span style="vertical-align:bottom; margin-bottom:-1px">B<br>B</span><span style="float:right">C<br>C</span></div>
 <div class="ib"><span style="float:right">B<br>B</span><span style="vertical-align:bottom; margin-bottom:-21px">C<br>C</span></div>
 -->
 <div class="ib"><span>B<br>B</span><span style="float:right">C<br>C</span></div>
 <div class="ib"><span style="float:right">B<br>B</span><span>C<br>C</span></div>
--- a/layout/reftests/css-grid/grid-container-baselines-002-ref.html
+++ b/layout/reftests/css-grid/grid-container-baselines-002-ref.html
@@ -10,17 +10,17 @@
   <link rel="stylesheet" type="text/css" href="support/ahem.css">
   <style type="text/css">
 html,body {
     color:black; background-color:white; font:16px/1 Ahem; padding:0; margin:0;
 }
 
 span {
   background: lime;
-  display: inline-block;
+  display: inline-grid;
   border: 1px solid black;
   font-size: 12px;
 }
 span:nth-child(1) { font-size:32px; }
 span:nth-child(2) { padding-block-end:20px; }
 
 .sfb { align-self:baseline; }
 .slb { align-self:last-baseline; }
@@ -34,37 +34,37 @@ span:nth-child(2) { padding-block-end:20
 .hl { writing-mode: horizontal-tb; direction:ltr; }
 .hr { writing-mode: horizontal-tb; direction:rtl; }
 .vl { writing-mode: vertical-lr; }
 .vr { writing-mode: vertical-rl; }
 .vlr { writing-mode: vertical-lr; direction:rtl; }
 .vrl { writing-mode: vertical-rl; direction:ltr; }
 
 .ib {
-  display: inline-block;
+  display: inline-table;
   border: 2px solid;
-  height: 120px;
-  padding: 10px 0;
+  height: 130px;
+  padding-top: 10px;
   margin: 1px;
   font-size: 1px;
 }
-.ib2 { display: inline-block; padding-top:10px; }
-.ib3 { display: inline-block; padding-top:20px; }
+.ib2 { display: inline-table; padding-top:10px; }
+.ib3 { display: inline-table; padding-top:20px; }
 
 </style>
 </head>
 <body>
 
 A<div class="ib"><span>B<br>B</span><span style="float:right">C<br>C</span></div>
 <div class="ib"><span style="float:left">B<br>B</span><span style="display:inline-flex">C<br>C</span></div>
 
 <!-- TODO: figure out a reference for the corresponding test -->
 <!-- TODO: figure out a reference for the corresponding test -->
 
-<div class="ib"><div style="display:inline-grid; height:120px; align-content:end;"><div><span>B<br>B</span><span>C<br>C</span></div></div></div>
+<div class="ib"><div style="display:inline-grid; height:120px; align-content:end;"><div style="display:grid; align-self:baseline; grid: 120px / auto auto"><span class="slb">B<br>B</span><span class="slb">C<br>C</span></div></div></div>
 
 <div class="ib"><span style="float:right">B<br>B</span><span>C<br>C</span></div>
 <div class="ib"><span style="float:right">B<br>B</span><span style="display:inline-flex">C<br>C</span></div>
 
 <!--
 <div class="ib"><span style="vertical-align:bottom; margin-bottom:-1px">B<br>B</span><span style="float:right">C<br>C</span></div>
 <div class="ib"><span style="float:right">B<br>B</span><span style="vertical-align:bottom; margin-bottom:-21px">C<br>C</span></div>
 -->
--- a/layout/reftests/css-grid/grid-container-baselines-003-ref.html
+++ b/layout/reftests/css-grid/grid-container-baselines-003-ref.html
@@ -1,37 +1,37 @@
 <!DOCTYPE HTML>
 <!--
      Any copyright is dedicated to the Public Domain.
      http://creativecommons.org/publicdomain/zero/1.0/
 -->
 <html><head>
   <meta charset="utf-8">
-  <title>Reference: Grid container baselines, nested grids</title>
+  <title>Reference: Grid container baselines, nested flex/grid/table</title>
   <link rel="author" title="Mats Palmgren" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1151204">
   <link rel="stylesheet" type="text/css" href="support/ahem.css">
   <style type="text/css">
 html,body {
     color:black; background-color:white; font:16px/1 Ahem; padding:0; margin:0;
 }
 
 .ib {
-  display: inline-block;
+  display: inline-table;
   height: 163px;
   width: 120px;
   border: 1px solid;
   margin: 1px;
 }
 .ib2 {
   display: inline-flex;
   border: 1px solid;
   margin: 1px;
 }
 
-span { display: inline-block; }
+span { display: inline-table; }
 .a {
   border: 3px solid black;
   background:lime;
 }
 
 span:nth-of-type(1) { font-size:32px; }
 span:nth-of-type(2) { font-size:36px; }
 span:nth-of-type(3) { font-size:48px; }
@@ -73,46 +73,47 @@ A<div class="ib">
 
 <div class="ib" style="width:180px;">
   <div class="ib" style="height:120px; width:56px;"><span
      class="a" style="width:1em; margin-top:7px; padding-top:5px; padding-bottom:18px;">A<br>B</span>
   </div><span class="i a f" style="width:54px">C<br>D</span><span
      class="a f" style="width:54px; padding-top:20px">E<br>F</span>
 </div>
 
+
 <div class="ib" style="width:200px;">
   <div class="ib" style="height:120px; width:76px; white-space:nowrap"><span
      class="a" style="width:32px; margin-top:7px; padding-top:5px; padding-bottom:18px;">A<br>B</span><span
      class="a" style="float:right; width:36px; margin-right:-2px; padding-bottom:22px; margin-top:-100px">A<br>B</span>
   </div><span class="i a f" style="width:54px">C<br>D</span><span
      class="a f" style="width:54px; padding-top:20px;">E<br>F</span>
 </div>
 
 <br>
 
 <x style="position:relative; top:-33px">A</x><div class="ib" style="width:200px">
   <div class="ib vl" style="height:70px; width:196px;">
     <span class="a" style="display:block; padding-block-end:15px; height:-moz-min-content">A<br>B</span>
   </div>
 </div><div class="ib" style="width:200px">
   <div class="ib vl" style="height:70px; width:196px;">
-    <span class="a" style="display:block; padding-block-end:15px; width:calc(100% - 48px); height:-moz-min-content">A<br>B</span>
+    <span class="a" style="display:block; padding-block-end:15px; width:148px; height:-moz-min-content">A<br>B</span>
   </div>
 </div><div class="ib" style="width:200px">
   <div class="ib vl" style="height:70px; width:196px;">
-    <span class="a" style="display:block; height:-moz-min-content; padding-block-end:15px; width:calc(100% - 48px); height:-moz-min-content">A<br>B</span>
+    <span class="a" style="display:block; height:-moz-min-content; padding-block-end:15px; width:148px; height:-moz-min-content">A<br>B</span>
   </div>
 </div>
 
 <br>
 
 A<div class="igrid slb">
   <div class="igrid slb" style="grid: 7px 13px 100px / 36px 3px; grid-row: span 4;">
     <n></n>
     <n style="grid-row:2"></n>
-    <span class="slb a" style="margin-bottom:7px; padding-bottom:5px; grid-row:3">A<br>B</span>
+    <span class="slb a" style="margin-bottom:7px; padding-bottom:5px; grid-row:3; display:inline-block">A<br>B</span>
   </div>
-  <span class="slb i a" style="grid-row:4">C<br>D</span>
+  <span class="slb i a" style="grid-row:4; display:inline-block">C<br>D</span>
   <n></n>
 </div>
 
 </body>
 </html>
--- a/layout/reftests/css-grid/grid-container-baselines-003.html
+++ b/layout/reftests/css-grid/grid-container-baselines-003.html
@@ -112,17 +112,16 @@ A<div class="igrid sfb">
   <div class="igrid sfb" style="grid: 100px 7px 13px / 36px; grid-row: span 4;">
     <span class="sfb a" style="margin-top:7px; padding-top:5px;">A<br>B</span>
     <n></n>
   </div>
   <span class="sfb i a" style="grid-row:1">C<br>D</span>
   <span class="sfb a" style="grid-row:1; grid-column:3; padding-top:20px">E<br>F</span>
   <n></n>
 </div>
-
 <div class="igrid sfb" style="grid-template-columns:60px 60px 60px">
   <div class="igrid sfb" style="grid: 100px 7px 13px / 36px; grid-row: span 4;">
     <span class="a" style="margin-top:7px; padding-top:5px;">A<br>B</span>
     <n></n>
     <n></n>
   </div>
   <span class="sfb i a" style="grid-row:1">C<br>D</span>
   <span class="sfb a " style="grid-row:1; grid-column:3; padding-top:20px">E<br>F</span>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-grid/grid-container-baselines-004-ref.html
@@ -0,0 +1,45 @@
+<!DOCTYPE HTML>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html><head>
+  <meta charset="utf-8">
+  <title>CSS Grid Test: Grid container baselines, nested flex/grid/table</title>
+  <link rel="author" title="Mats Palmgren" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1306906">
+  <style type="text/css">
+
+* {
+    color:black; background-color:white; font-size:16px; font-family:monospace; padding:0; margin:0;
+}
+	
+grid, flex {
+  display: inline-flex;
+  border: 1px solid;
+  border-width: 10px 0 3px 0;
+  padding-top: 1px;
+}
+
+flex { display:inline-grid; }
+table { display:inline-table; }
+td { vertical-align: baseline; }
+
+    </style>
+</head>
+<body>
+
+A
+<flex>A<br>B</flex>
+<flex><span>A<br>B</span></flex>
+<grid>A<br>B</grid>
+<grid><span>A<br>B</span></grid>
+<table cellpadding="0" cellspacing="0"><td><flex>A<br>B</flex></td></table>
+<table cellpadding="0" cellspacing="0"><td><grid>A<br>B</grid></td></table>
+<!-- <flex><grid>A<br>B</grid></flex>   TODO: bug 1306894 -->
+<grid><flex>A<br>B</flex></grid>
+<flex><flex>A<br>B</flex></flex>
+<grid><grid>A<br>B</grid></grid>
+
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-grid/grid-container-baselines-004.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html><head>
+  <meta charset="utf-8">
+  <title>CSS Grid Test: Grid container baselines, nested flex/grid/table</title>
+  <link rel="author" title="Mats Palmgren" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1306906">
+  <link rel="help" href="https://drafts.csswg.org/css-grid/#grid-baselines">
+  <link rel="match" href="grid-container-baselines-004-ref.html">
+  <style type="text/css">
+
+* {
+    color:black; background-color:white; font-size:16px; font-family:monospace; padding:0; margin:0;
+}
+	
+grid, flex {
+  display: inline-grid;
+  border: 1px solid;
+  border-width: 10px 0 3px 0;
+  padding-top: 1px;
+}
+
+flex { display:inline-flex; }
+table { display:inline-table; }
+td { vertical-align: baseline; }
+
+    </style>
+</head>
+<body>
+
+A
+<flex>A<br>B</flex>
+<flex><span>A<br>B</span></flex>
+<grid>A<br>B</grid>
+<grid><span>A<br>B</span></grid>
+<table cellpadding="0" cellspacing="0"><td><flex>A<br>B</flex></td></table>
+<table cellpadding="0" cellspacing="0"><td><grid>A<br>B</grid></td></table>
+<flex><grid>A<br>B</grid></flex>
+<!-- <grid><flex>A<br>B</flex></grid>   TODO: bug 1306894 -->
+<flex><flex>A<br>B</flex></flex>
+<grid><grid>A<br>B</grid></grid>
+
+
+</body>
+</html>
--- a/layout/reftests/css-grid/reftest.list
+++ b/layout/reftests/css-grid/reftest.list
@@ -87,16 +87,17 @@ skip-if(!gtkWidget) == grid-item-mixed-b
 skip-if(!gtkWidget) == grid-item-mixed-baseline-004.html grid-item-mixed-baseline-004-ref.html # ditto
 == grid-align-content-001.html grid-align-content-001-ref.html
 == grid-justify-content-001.html grid-justify-content-001-ref.html
 skip-if(Android&&isDebugBuild) == grid-justify-content-002.html grid-justify-content-002-ref.html # Bug 1245884 - slow
 skip-if(Android&&isDebugBuild) == grid-justify-content-003.html grid-justify-content-003-ref.html # Bug 1245884 - slow
 skip-if(!gtkWidget) == grid-container-baselines-001.html grid-container-baselines-001-ref.html
 skip-if(!gtkWidget) == grid-container-baselines-002.html grid-container-baselines-002-ref.html
 skip-if(!gtkWidget) == grid-container-baselines-003.html grid-container-baselines-003-ref.html
+== grid-container-baselines-004.html grid-container-baselines-004-ref.html
 skip-if(Android&&isDebugBuild) == grid-column-gap-001.html grid-column-gap-001-ref.html # Bug 1245884 - slow
 == grid-column-gap-002.html grid-column-gap-002-ref.html
 == grid-column-gap-003.html grid-column-gap-003-ref.html
 == grid-column-gap-004.html grid-column-gap-004-ref.html
 == grid-row-gap-001.html grid-row-gap-001-ref.html
 == grid-percent-grid-gap-001.html grid-percent-grid-gap-001-ref.html
 skip-if(Android&&isDebugBuild) == grid-row-gap-002.html grid-row-gap-002-ref.html # Bug 1245884 - slow
 skip-if(Android&&isDebugBuild) == grid-row-gap-003.html grid-row-gap-003-ref.html # Bug 1245884 - slow
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/button/display-grid-flex-columnset-ref.html
@@ -0,0 +1,61 @@
+<!DOCTYPE HTML>
+<html><head>
+    <meta charset="utf-8">
+    <title>Testcase for bug 984869</title>
+    <style type="text/css">
+
+        html,body {
+            color:black; background-color:white; font-size:16px; padding:0; margin:0;
+        }
+
+.grid, .igrid {
+  display: grid;
+  grid: 20px / 20px 20px;
+}
+.igrid { display:inline-grid; }
+
+.flex, .iflex {
+  display: flex;
+}
+.iflex { display:inline-flex; }
+
+.columnset, .block-columnset {
+     -moz-columns: 2;
+      -ms-columns: 2;
+  -webkit-columns: 2;
+          columns: 2;
+}
+
+.block-columnset, .b { display: block; }
+
+a {
+  position: absolute;
+  right:0; bottom:0;
+  width:5px; height:5px;
+  background: lime;
+}
+.rel { position:relative; }
+
+button { vertical-align: bottom; }
+    </style>
+</head>
+<body>
+
+<button><wrap class="grid">x<span>y</span>z</wrap></button>
+<button><wrap class="iflex">x<span>y</span>z</wrap></button>
+<button><wrap class="b columnset">x<br><span>y</span>z</wrap></button>
+
+<button class="rel"><wrap class="grid">x<span>y</span>z<a></a></wrap></button>
+<button class="rel"><wrap class="iflex">x<span>y</span>z<a></a></wrap></button>
+<button class="rel"><wrap class="b columnset">x<br><span>y</span>z<a></a></wrap></button>
+
+<button class="b"><wrap class="grid">x<span>y</span>z</wrap></button>
+<button class="b"><wrap class="flex">x<span>y</span>z</wrap></button>
+<button class="b"><wrap class="b columnset">x<br><span>y</span>z</wrap></button>
+
+<button class="rel b"><wrap class="grid">x<span>y</span>z<a></a></wrap></button>
+<button class="rel b"><wrap class="flex">x<span>y</span>z<a></a></wrap></button>
+<button class="rel b"><wrap class="b columnset">x<br><span>y</span>z<a></a></wrap></button>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/button/display-grid-flex-columnset.html
@@ -0,0 +1,61 @@
+<!DOCTYPE HTML>
+<html><head>
+    <meta charset="utf-8">
+    <title>Testcase for bug 984869</title>
+    <style type="text/css">
+
+        html,body {
+            color:black; background-color:white; font-size:16px; padding:0; margin:0;
+        }
+
+.grid, .igrid {
+  display: grid;
+  grid: 20px / 20px 20px;
+}
+.igrid { display:inline-grid; }
+
+.flex, .iflex {
+  display: flex;
+}
+.iflex { display:inline-flex; }
+
+.columnset {
+     -moz-columns: 2;
+      -ms-columns: 2;
+  -webkit-columns: 2;
+          columns: 2;
+}
+
+.b { display: block; }
+
+a {
+  position: absolute;
+  right:0; bottom:0;
+  width:5px; height:5px;
+  background: lime;
+}
+.rel { position:relative; }
+
+button { vertical-align: bottom; }
+    </style>
+</head>
+<body>
+
+<button class="igrid">x<span>y</span>z</button>
+<button class="iflex">x<span>y</span>z</button>
+<button class="columnset">x<br><span>y</span>z</button>
+
+<button class="rel igrid">x<span>y</span>z<a></a></button>
+<button class="rel iflex">x<span>y</span>z<a></a></button>
+<button class="rel columnset">x<br><span>y</span>z<a></a></button>
+
+<button class="grid">x<span>y</span>z</button>
+<button class="flex">x<span>y</span>z</button>
+<button class="b columnset">x<br><span>y</span>z</button>
+
+<button class="rel grid">x<span>y</span>z<a></a></button>
+<button class="rel flex">x<span>y</span>z<a></a></button>
+<button class="rel b columnset">x<br><span>y</span>z<a></a></button>
+
+</body>
+</html>
--- a/layout/reftests/forms/button/reftest.list
+++ b/layout/reftests/forms/button/reftest.list
@@ -40,8 +40,9 @@ fails-if(B2G||Mulet) == width-auto-size-
 == width-erode-all-focuspadding-ltr.html width-erode-all-focuspadding-ltr-ref.html
 == width-erode-overflow-focuspadding-ltr.html width-erode-overflow-focuspadding-ltr-ref.html
 fails-if(B2G||Mulet) == width-auto-size-em-rtl.html width-auto-size-em-rtl-ref.html # Bug 1145672 # Bug 1150486
 fails-if(B2G||Mulet) == width-auto-size-rtl.html width-auto-size-rtl-ref.html # Bug 1145672 # Bug 1150486
 == width-exact-fit-rtl.html width-auto-size-rtl-ref.html
 == width-erode-part-focuspadding-rtl.html width-erode-part-focuspadding-rtl-ref.html
 == width-erode-all-focuspadding-rtl.html width-erode-all-focuspadding-rtl-ref.html
 == width-erode-overflow-focuspadding-rtl.html width-erode-overflow-focuspadding-rtl-ref.html
+== display-grid-flex-columnset.html display-grid-flex-columnset-ref.html
--- a/layout/style/res/forms.css
+++ b/layout/style/res/forms.css
@@ -14,17 +14,17 @@
   display: block; /* nsRuleNode::ComputeDisplayData overrules this in some cases */
   unicode-bidi: inherit;
   text-overflow: inherit;
   overflow: inherit;
   overflow-clip-box: inherit;
   padding: inherit;
   block-size: 100%; /* Need this so percentage block-sizes of kids work right */
   /* Please keep the Multicol/Flex/Grid/Align sections below in sync with
-     ::-moz-scrolled-content in ua.css */
+     ::-moz-scrolled-content in ua.css and ::-moz-button-content below. */
   /* Multicol container */
   -moz-column-count: inherit;
   -moz-column-width: inherit;
   -moz-column-gap: inherit;
   -moz-column-rule: inherit;
   -moz-column-fill: inherit;
   /* Flex container */
   flex-direction: inherit;
@@ -656,16 +656,41 @@ button {
   white-space: inherit;
   text-indent: 0;
   /* But no text-decoration reaching inside, by default */
   display: inline-block;
 }
 
 *|*::-moz-button-content {
   display: block;
+  /* Please keep the Multicol/Flex/Grid/Align sections below in sync with
+     ::-moz-scrolled-content in ua.css and ::-moz-fieldset-content above. */
+  /* Multicol container */
+  -moz-column-count: inherit;
+  -moz-column-width: inherit;
+  -moz-column-gap: inherit;
+  -moz-column-rule: inherit;
+  -moz-column-fill: inherit;
+  /* Flex container */
+  flex-direction: inherit;
+  flex-wrap: inherit;
+  /* Grid container */
+  grid-auto-columns: inherit;
+  grid-auto-rows: inherit;
+  grid-auto-flow: inherit;
+  grid-column-gap: inherit;
+  grid-row-gap: inherit;
+  grid-template-areas: inherit;
+  grid-template-columns: inherit;
+  grid-template-rows: inherit;
+  /* CSS Align */
+  align-content: inherit;
+  align-items: inherit;
+  justify-content: inherit;
+  justify-items: inherit;
 }
 
 button:hover,
 input[type="color"]:-moz-system-metric(color-picker-available):hover,
 input[type="reset"]:hover,
 input[type="button"]:hover,
 input[type="submit"]:hover {
   background-color: -moz-buttonhoverface;
--- a/layout/style/res/ua.css
+++ b/layout/style/res/ua.css
@@ -166,17 +166,17 @@
      affects auto-width sizing of the block we create. */
   display: block;
   -moz-box-orient: inherit;
   /* make unicode-bidi inherit, otherwise it has no effect on text inputs and
      blocks with overflow: scroll; */
   unicode-bidi: inherit;
   text-overflow: inherit;
   /* Please keep the Multicol/Flex/Grid/Align sections below in sync with
-     ::-moz-fieldset-content in forms.css */
+     ::-moz-fieldset-content/::-moz-button-content in forms.css */
   /* Multicol container */
   -moz-column-count: inherit;
   -moz-column-width: inherit;
   -moz-column-gap: inherit;
   -moz-column-rule: inherit;
   -moz-column-fill: inherit;
   /* Flex container */
   flex-direction: inherit;
--- a/media/ffvpx/README_MOZILLA
+++ b/media/ffvpx/README_MOZILLA
@@ -25,8 +25,15 @@ add to configure command: --disable-asm 
 replace: s/HAVE_SYSCTL 1/HAVE_SYSCTL 0/ and s/HAVE_MEMALIGN 1/HAVE_MEMALIGN 0/ and s/HAVE_POSIX_MEMALIGN 1/HAVE_POSIX_MEMALIGN 0/
 
 
 config_unix64.h/config_unix64.asm:
 replace: s/HAVE_SYSCTL 1/HAVE_SYSCTL 0
 
 config_win32/64.h/asm:
 add to configure command: --toolchain=msvc
+
+23 Sept 2016: libavcodec/pthread_frame.c has been patched so as to avoid
+generating large numbers of warnings from TSan (Thread Sanitizer) when
+decoding vp9 streams.  This will likely be fixed upstream sometime soon.
+When resyncing with upstream, first un-apply the patch shown at
+https://bugzilla.mozilla.org/show_bug.cgi?id=1274256#c60, then resync,
+then assess whether the patch is still necessary.
--- a/media/ffvpx/libavcodec/pthread_frame.c
+++ b/media/ffvpx/libavcodec/pthread_frame.c
@@ -38,19 +38,39 @@
 #include "libavutil/cpu.h"
 #include "libavutil/frame.h"
 #include "libavutil/internal.h"
 #include "libavutil/log.h"
 #include "libavutil/mem.h"
 #include "libavutil/opt.h"
 #include "libavutil/thread.h"
 
+#if defined(MOZ_TSAN)
+typedef  _Atomic(int)  atomic_int;
+#else
+typedef  volatile int  atomic_int;
+#endif
+
 /**
  * Context used by codec threads and stored in their AVCodecInternal thread_ctx.
  */
+typedef enum {
+    STATE_INPUT_READY,          ///< Set when the thread is awaiting a packet.
+    STATE_SETTING_UP,           ///< Set before the codec has called ff_thread_finish_setup().
+    STATE_GET_BUFFER,           /**<
+                                 * Set when the codec calls get_buffer().
+                                 * State is returned to STATE_SETTING_UP afterwards.
+                                 */
+    STATE_GET_FORMAT,           /**<
+                                 * Set when the codec calls get_format().
+                                 * State is returned to STATE_SETTING_UP afterwards.
+                                 */
+    STATE_SETUP_FINISHED        ///< Set after the codec has called ff_thread_finish_setup().
+} State;
+
 typedef struct PerThreadContext {
     struct FrameThreadContext *parent;
 
     pthread_t      thread;
     int            thread_init;
     pthread_cond_t input_cond;      ///< Used to wait for a new packet from the main thread.
     pthread_cond_t progress_cond;   ///< Used by child threads to wait for progress to change.
     pthread_cond_t output_cond;     ///< Used by the main thread to wait for frames to finish.
@@ -61,29 +81,17 @@ typedef struct PerThreadContext {
     AVCodecContext *avctx;          ///< Context used to decode packets passed to this thread.
 
     AVPacket       avpkt;           ///< Input packet (for decoding) or output (for encoding).
 
     AVFrame *frame;                 ///< Output frame (for decoding) or input (for encoding).
     int     got_frame;              ///< The output of got_picture_ptr from the last avcodec_decode_video() call.
     int     result;                 ///< The result of the last codec decode/encode() call.
 
-    enum {
-        STATE_INPUT_READY,          ///< Set when the thread is awaiting a packet.
-        STATE_SETTING_UP,           ///< Set before the codec has called ff_thread_finish_setup().
-        STATE_GET_BUFFER,           /**<
-                                     * Set when the codec calls get_buffer().
-                                     * State is returned to STATE_SETTING_UP afterwards.
-                                     */
-        STATE_GET_FORMAT,           /**<
-                                     * Set when the codec calls get_format().
-                                     * State is returned to STATE_SETTING_UP afterwards.
-                                     */
-        STATE_SETUP_FINISHED        ///< Set after the codec has called ff_thread_finish_setup().
-    } state;
+    atomic_int state;
 
     /**
      * Array of frames passed to ff_thread_release_buffer().
      * Frames are released after all threads referencing them are finished.
      */
     AVFrame *released_buffers;
     int  num_released_buffers;
     int      released_buffers_allocated;
@@ -353,17 +361,18 @@ static int submit_packet(PerThreadContex
          p->avctx->get_format != avcodec_default_get_format ||
          p->avctx->get_buffer2 != avcodec_default_get_buffer2)) {
         while (p->state != STATE_SETUP_FINISHED && p->state != STATE_INPUT_READY) {
             int call_done = 1;
             pthread_mutex_lock(&p->progress_mutex);
             while (p->state == STATE_SETTING_UP)
                 pthread_cond_wait(&p->progress_cond, &p->progress_mutex);
 
-            switch (p->state) {
+            State p_state = (State)p->state;
+            switch (p_state) {
             case STATE_GET_BUFFER:
                 p->result = ff_get_buffer(p->avctx, p->requested_frame, p->requested_flags);
                 break;
             case STATE_GET_FORMAT:
                 p->result_format = ff_get_format(p->avctx, p->available_formats);
                 break;
             default:
                 call_done = 0;
@@ -466,17 +475,17 @@ int ff_thread_decode_frame(AVCodecContex
 
     /* return the size of the consumed packet if no error occurred */
     return (p->result >= 0) ? avpkt->size : p->result;
 }
 
 void ff_thread_report_progress(ThreadFrame *f, int n, int field)
 {
     PerThreadContext *p;
-    volatile int *progress = f->progress ? (int*)f->progress->data : NULL;
+    atomic_int *progress = f->progress ? (atomic_int*)f->progress->data : NULL;
 
     if (!progress || progress[field] >= n) return;
 
     p = f->owner->internal->thread_ctx;
 
     if (f->owner->debug&FF_DEBUG_THREADS)
         av_log(f->owner, AV_LOG_DEBUG, "%p finished %d field %d\n", progress, n, field);
 
@@ -484,17 +493,17 @@ void ff_thread_report_progress(ThreadFra
     progress[field] = n;
     pthread_cond_broadcast(&p->progress_cond);
     pthread_mutex_unlock(&p->progress_mutex);
 }
 
 void ff_thread_await_progress(ThreadFrame *f, int n, int field)
 {
     PerThreadContext *p;
-    volatile int *progress = f->progress ? (int*)f->progress->data : NULL;
+    atomic_int *progress = f->progress ? (atomic_int*)f->progress->data : NULL;
 
     if (!progress || progress[field] >= n) return;
 
     p = f->owner->internal->thread_ctx;
 
     if (f->owner->debug&FF_DEBUG_THREADS)
         av_log(f->owner, AV_LOG_DEBUG, "thread awaiting %d field %d from %p\n", n, field, progress);
 
--- a/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp
+++ b/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp
@@ -18,16 +18,18 @@
 #include "mozilla/TemplateLib.h"
 
 #include "webrtc/common_types.h"
 #include "webrtc/common_video/interface/native_handle.h"
 #include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
 #include "webrtc/video_engine/include/vie_errors.h"
 #include "webrtc/video_engine/vie_defines.h"
 
+#include "mozilla/Unused.h"
+
 #ifdef MOZ_WIDGET_ANDROID
 #include "AndroidJNIWrapper.h"
 #endif
 
 // for ntohs
 #ifdef _MSC_VER
 #include "Winsock2.h"
 #else
@@ -272,46 +274,56 @@ WebrtcVideoConduit::InitMain()
   nsCOMPtr<nsIPrefService> prefs = do_GetService("@mozilla.org/preferences-service;1", &rv);
   if (!NS_WARN_IF(NS_FAILED(rv)))
   {
     nsCOMPtr<nsIPrefBranch> branch = do_QueryInterface(prefs);
 
     if (branch)
     {
       int32_t temp;
-      (void) NS_WARN_IF(NS_FAILED(branch->GetBoolPref("media.video.test_latency", &mVideoLatencyTestEnable)));
-      (void) NS_WARN_IF(NS_FAILED(branch->GetIntPref("media.peerconnection.video.min_bitrate", &temp)));
-      if (temp >= 0) {
-        mMinBitrate = temp;
+      Unused << NS_WARN_IF(NS_FAILED(branch->GetBoolPref("media.video.test_latency", &mVideoLatencyTestEnable)));
+      if (!NS_WARN_IF(NS_FAILED(branch->GetIntPref("media.peerconnection.video.min_bitrate", &temp))))
+      {
+         if (temp >= 0) {
+            mMinBitrate = temp;
+         }
       }
-      (void) NS_WARN_IF(NS_FAILED(branch->GetIntPref("media.peerconnection.video.start_bitrate", &temp)));
-      if (temp >= 0) {
-        mStartBitrate = temp;
+      if (!NS_WARN_IF(NS_FAILED(branch->GetIntPref("media.peerconnection.video.start_bitrate", &temp))))
+      {
+         if (temp >= 0) {
+         mStartBitrate = temp;
+         }
       }
-      (void) NS_WARN_IF(NS_FAILED(branch->GetIntPref("media.peerconnection.video.max_bitrate", &temp)));
-      if (temp >= 0) {
-        mMaxBitrate = temp;
+      if (!NS_WARN_IF(NS_FAILED(branch->GetIntPref("media.peerconnection.video.max_bitrate", &temp))))
+      {
+        if (temp >= 0) {
+          mMaxBitrate = temp;
+        }
       }
       if (mMinBitrate != 0 && mMinBitrate < webrtc::kViEMinCodecBitrate) {
         mMinBitrate = webrtc::kViEMinCodecBitrate;
       }
       if (mStartBitrate < mMinBitrate) {
         mStartBitrate = mMinBitrate;
       }
       if (mStartBitrate > mMaxBitrate) {
         mStartBitrate = mMaxBitrate;
       }
-      (void) NS_WARN_IF(NS_FAILED(branch->GetIntPref("media.peerconnection.video.min_bitrate_estimate", &temp)));
-      if (temp >= 0) {
-        mMinBitrateEstimate = temp;
+      if (!NS_WARN_IF(NS_FAILED(branch->GetIntPref("media.peerconnection.video.min_bitrate_estimate", &temp))))
+      {
+        if (temp >= 0) {
+          mMinBitrateEstimate = temp;
+        }
       }
       bool use_loadmanager = false;
-      (void) NS_WARN_IF(NS_FAILED(branch->GetBoolPref("media.navigator.load_adapt", &use_loadmanager)));
-      if (use_loadmanager) {
-        mLoadManager = LoadManagerBuild();
+      if (!NS_WARN_IF(NS_FAILED(branch->GetBoolPref("media.navigator.load_adapt", &use_loadmanager))))
+      {
+        if (use_loadmanager) {
+          mLoadManager = LoadManagerBuild();
+        }
       }
     }
   }
 
 #ifdef MOZ_WIDGET_ANDROID
   // get the JVM
   JavaVM *jvm = jsjni_GetVM();
 
--- a/media/webrtc/trunk/webrtc/modules/video_capture/windows/device_info_ds.cc
+++ b/media/webrtc/trunk/webrtc/modules/video_capture/windows/device_info_ds.cc
@@ -12,16 +12,18 @@
 
 #include "webrtc/modules/video_capture/video_capture_config.h"
 #include "webrtc/modules/video_capture/video_capture_delay.h"
 #include "webrtc/modules/video_capture/windows/help_functions_ds.h"
 #include "webrtc/system_wrappers/interface/ref_count.h"
 #include "webrtc/system_wrappers/interface/trace.h"
 
 #include <Dvdmedia.h>
+#include <dbt.h>
+#include <ks.h>
 
 namespace webrtc
 {
 namespace videocapturemodule
 {
 const int32_t NoWindowsCaptureDelays = 1;
 const DelayValues WindowsCaptureDelays[NoWindowsCaptureDelays] = {
   "Microsoft LifeCam Cinema",
@@ -36,16 +38,29 @@ const DelayValues WindowsCaptureDelays[N
     {160,120,109},
     {1280,720,166},
     {960,544,126},
     {800,448,120},
     {800,600,127}
   },
 };
 
+LRESULT CALLBACK WndProc(HWND hWnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)
+{
+    if (uiMsg == WM_DEVICECHANGE)
+    {
+        DeviceInfoDS* dsInfo = DeviceInfoDSSingleton::GetInfo();
+        if (dsInfo != NULL)
+        {
+            dsInfo->DeviceChange();
+        }
+        return 0;
+    }
+    return DefWindowProc(hWnd, uiMsg, wParam, lParam);
+}
 
 void _FreeMediaType(AM_MEDIA_TYPE& mt)
 {
     if (mt.cbFormat != 0)
     {
         CoTaskMemFree((PVOID)mt.pbFormat);
         mt.cbFormat = 0;
         mt.pbFormat = NULL;
@@ -56,23 +71,26 @@ void _FreeMediaType(AM_MEDIA_TYPE& mt)
         mt.pUnk->Release();
         mt.pUnk = NULL;
     }
 }
 
 // static
 DeviceInfoDS* DeviceInfoDS::Create(const int32_t id)
 {
-    DeviceInfoDS* dsInfo = new DeviceInfoDS(id);
-    if (!dsInfo || dsInfo->Init() != 0)
-    {
-        delete dsInfo;
-        dsInfo = NULL;
+    if (!DeviceInfoDSSingleton::GetInfo()) {
+        DeviceInfoDS* dsInfo = new DeviceInfoDS(id);
+        if (!dsInfo || dsInfo->Init() != 0)
+        {
+            delete dsInfo;
+            dsInfo = NULL;
+        }
+        DeviceInfoDSSingleton::GetInfo() = dsInfo;
     }
-    return dsInfo;
+    return DeviceInfoDSSingleton::GetInfo();
 }
 
 DeviceInfoDS::DeviceInfoDS(const int32_t id)
     : DeviceInfoImpl(id), _dsDevEnum(NULL),
       _CoUninitializeIsRequired(true)
 {
     // 1) Initialize the COM library (make Windows load the DLLs).
     //
@@ -107,25 +125,42 @@ DeviceInfoDS::DeviceInfoDS(const int32_t
             //
             WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCapture, _id,
                          "VideoCaptureWindowsDSInfo::VideoCaptureWindowsDSInfo "
                          "CoInitializeEx(NULL, COINIT_APARTMENTTHREADED) => "
                          "RPC_E_CHANGED_MODE, error 0x%x",
                          hr);
         }
     }
+
+    _hInstance = reinterpret_cast<HINSTANCE>(GetModuleHandle(NULL));
+    _wndClass = {0};
+    _wndClass.lpfnWndProc = &WndProc;
+    _wndClass.lpszClassName = TEXT("DeviceInfoDS");
+    _wndClass.hInstance = _hInstance;
+
+    if (RegisterClass(&_wndClass))
+    {
+        _hwnd = CreateWindow(_wndClass.lpszClassName, NULL, 0, CW_USEDEFAULT,
+            CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, _hInstance, NULL);
+    }
 }
 
 DeviceInfoDS::~DeviceInfoDS()
 {
     RELEASE_AND_CLEAR(_dsDevEnum);
     if (_CoUninitializeIsRequired)
     {
         CoUninitialize();
     }
+    if (_hwnd != NULL)
+    {
+        DestroyWindow(_hwnd);
+    }
+    UnregisterClass(_wndClass.lpszClassName, _hInstance);
 }
 
 int32_t DeviceInfoDS::Init()
 {
     HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC,
                                   IID_ICreateDevEnum, (void **) &_dsDevEnum);
     if (hr != NOERROR)
     {
--- a/media/webrtc/trunk/webrtc/modules/video_capture/windows/device_info_ds.h
+++ b/media/webrtc/trunk/webrtc/modules/video_capture/windows/device_info_ds.h
@@ -8,18 +8,20 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
 #ifndef WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_WINDOWS_DEVICE_INFO_DS_H_
 #define WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_WINDOWS_DEVICE_INFO_DS_H_
 
 #include "webrtc/modules/video_capture/device_info_impl.h"
 #include "webrtc/modules/video_capture/video_capture_impl.h"
+#include "base/singleton.h"
 
 #include <Dshow.h>
+#include <windows.h>
 
 namespace webrtc
 {
 namespace videocapturemodule
 {
 struct VideoCaptureCapabilityWindows: public VideoCaptureCapability
 {
     uint32_t directShowCapabilityIndex;
@@ -95,12 +97,30 @@ protected:
 
     virtual int32_t
         CreateCapabilityMap(const char* deviceUniqueIdUTF8);
 
 private:
     ICreateDevEnum* _dsDevEnum;
     bool _CoUninitializeIsRequired;
     std::vector<VideoCaptureCapabilityWindows> _captureCapabilitiesWindows;
+    HWND _hwnd;
+    WNDCLASS _wndClass;
+    HINSTANCE _hInstance;
 };
+
+class DeviceInfoDSSingleton {
+public:
+  DeviceInfoDSSingleton::DeviceInfoDSSingleton()
+    : mDeviceInfoDS(nullptr) {}
+
+  static DeviceInfoDS*& GetInfo() {
+    return gTheInstance.get()->mDeviceInfoDS;
+  }
+
+private:
+  static Singleton<DeviceInfoDSSingleton> gTheInstance;
+  DeviceInfoDS* mDeviceInfoDS;
+};
+
 }  // namespace videocapturemodule
 }  // namespace webrtc
 #endif // WEBRTC_MODULES_VIDEO_CAPTURE_MAIN_SOURCE_WINDOWS_DEVICE_INFO_DS_H_
--- a/mobile/android/app/build.gradle
+++ b/mobile/android/app/build.gradle
@@ -84,17 +84,16 @@ android {
         main {
             manifest.srcFile "${project.buildDir}/generated/source/preprocessed_manifest/AndroidManifest.xml"
 
             aidl {
                 srcDir "${topsrcdir}/mobile/android/base/aidl"
             }
 
             java {
-                srcDir "${topsrcdir}/mobile/android/geckoview/src/main/java"
                 srcDir "${topsrcdir}/mobile/android/base/java"
                 srcDir "${topsrcdir}/mobile/android/search/java"
                 srcDir "${topsrcdir}/mobile/android/javaaddons/java"
                 srcDir "${topsrcdir}/mobile/android/services/src/main/java"
 
                 if (mozconfig.substs.MOZ_ANDROID_MLS_STUMBLER) {
                     srcDir "${topsrcdir}/mobile/android/stumbler/java"
                 }
@@ -225,16 +224,17 @@ dependencies {
     // of this library.
     // It doesn't seem like there is a non-trivial way to be conditional on 'localOld', so instead we explicitly
     // define a version of leakcanary for every flavor:
     localCompile 'com.squareup.leakcanary:leakcanary-android:1.4-beta1'
     localOldCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.4-beta1'
     automationCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.4-beta1'
     testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.4-beta1'
 
+    compile project(':geckoview')
     compile project(':thirdparty')
 
     testCompile 'junit:junit:4.12'
     testCompile 'org.robolectric:robolectric:3.1.2'
     testCompile 'org.simpleframework:simple-http:6.0.1'
     testCompile 'org.mockito:mockito-core:1.10.19'
 
     // Including the Robotium JAR directly can cause issues with dexing.
@@ -248,50 +248,22 @@ task checkstyle(type: Checkstyle) {
     // TODO: should use sourceSets from project instead of hard-coded str.
     source '../base/java/'
     // TODO: This ignores our pre-processed resources.
     include '**/*.java'
     // TODO: classpath should probably be something.
     classpath = files()
 }
 
-task syncOmnijarFromDistDir(type: Sync) {
-    into("${project.buildDir}/generated/omnijar")
-    from("${topobjdir}/dist/fennec/assets") {
-        include 'omni.ja'
-    }
-}
-
-task checkLibsExistInDistDir<< {
-    if (syncLibsFromDistDir.source.empty) {
-        throw new GradleException("Required JNI libraries not found in ${topobjdir}/dist/fennec/lib.  Have you built and packaged?")
-    }
-}
-
-task syncLibsFromDistDir(type: Sync, dependsOn: checkLibsExistInDistDir) {
-    into("${project.buildDir}/generated/jniLibs")
-    from("${topobjdir}/dist/fennec/lib")
-}
-
-task checkAssetsExistInDistDir<< {
-    if (syncAssetsFromDistDir.source.empty) {
-        throw new GradleException("Required assets not found in ${topobjdir}/dist/fennec/assets.  Have you built and packaged?")
-    }
-}
-
-task syncAssetsFromDistDir(type: Sync, dependsOn: checkAssetsExistInDistDir) {
-    into("${project.buildDir}/generated/assets")
-    from("${topobjdir}/dist/fennec/assets") {
-        exclude 'omni.ja'
-    }
-}
-
 task syncPreprocessedCode(type: Sync, dependsOn: rootProject.generateCodeAndResources) {
     into("${project.buildDir}/generated/source/preprocessed_code")
-    from("${topobjdir}/mobile/android/base/generated/preprocessed")
+    from("${topobjdir}/mobile/android/base/generated/preprocessed") {
+        // All other preprocessed code is included in the geckoview project.
+        include '**/AdjustConstants.java'
+    }
 }
 
 // The localization system uses the moz.build preprocessor to interpolate a .dtd
 // file of XML entity definitions into an XML file of elements referencing those
 // entities.  (Each locale produces its own .dtd file, backstopped by the en-US
 // .dtd file in tree.)  Android Studio (and IntelliJ) don't handle these inline
 // entities smoothly.  This filter merely expands the entities in place, making
 // them appear properly throughout the IDE.  Be aware that this assumes that the
@@ -307,82 +279,48 @@ class ExpandXMLEntitiesFilter extends Fi
 task syncPreprocessedResources(type: Sync, dependsOn: rootProject.generateCodeAndResources) {
     into("${project.buildDir}/generated/source/preprocessed_resources")
     from("${topobjdir}/mobile/android/base/res")
     filesMatching('**/strings.xml') {
         filter(ExpandXMLEntitiesFilter)
     }
 }
 
-// The omnijar inputs are listed as resource directory inputs to a dummy JAR.
-// That arrangement labels them nicely in IntelliJ.  See the comment in the
-// :omnijar project for more context.
-evaluationDependsOn(':omnijar')
-
-task buildOmnijar(type:Exec) {
-    dependsOn rootProject.generateCodeAndResources
-
-    // See comment in :omnijar project regarding interface mismatches here.
-    inputs.source project(':omnijar').sourceSets.main.resources.srcDirs
-
-    // Produce a single output file.
-    outputs.file "${topobjdir}/dist/fennec/assets/omni.ja"
-
-    workingDir "${topobjdir}"
-
-    commandLine mozconfig.substs.GMAKE
-    args '-C'
-    args "${topobjdir}/mobile/android/base"
-    args 'gradle-omnijar'
-
-    // Only show the output if something went wrong.
-    ignoreExitValue = true
-    standardOutput = new ByteArrayOutputStream()
-    errorOutput = standardOutput
-    doLast {
-        if (execResult.exitValue != 0) {
-            throw new GradleException("Process '${commandLine}' finished with non-zero exit value ${execResult.exitValue}:\n\n${standardOutput.toString()}")
-        }
-    }
-}
-
 // It's not easy -- see the backout in Bug 1242213 -- to change the <manifest>
 // package for Fennec.  Gradle has grown a mechanism to achieve what we want for
 // Fennec, however, with applicationId.  To use the same manifest as moz.build,
 // we replace the package with org.mozilla.gecko (the eventual package) here.
 task rewriteManifestPackage(type: Copy, dependsOn: rootProject.generateCodeAndResources) {
     into("${project.buildDir}/generated/source/preprocessed_manifest")
     from("${topobjdir}/mobile/android/base/AndroidManifest.xml")
     filter { it.replaceFirst(/package=".*?"/, 'package="org.mozilla.gecko"') }
 }
 
+apply from: "${topsrcdir}/mobile/android/gradle/with_gecko_binaries.gradle"
+
 android.applicationVariants.all { variant ->
     variant.preBuild.dependsOn rewriteManifestPackage
     variant.preBuild.dependsOn syncPreprocessedCode
     variant.preBuild.dependsOn syncPreprocessedResources
 
+    // Automation builds don't include Gecko binaries, since those binaries are
+    // not produced until after build time (at package time).  Therefore,
+    // automation builds include the Gecko binaries into the APK at package
+    // time.  The "withGeckoBinaries" variant of the :geckoview project also
+    // does this.  (It does what it says on the tin!)  For notes on this
+    // approach, see mobile/android/gradle/with_gecko_binaries.gradle.
+
     // Like 'local' or 'localOld'.
     def productFlavor = variant.productFlavors[0].name
-    // Like 'debug' or 'release'.
-    def buildType = variant.buildType.name
 
-    // We insert omni.ja and the .so libraries into all local builds.
-    if (!productFlavor.startsWith('local')) {
-        return
+    // :app uses :geckoview:release and handles it's own Gecko binary inclusion,
+    // even though this would be most naturally done in the :geckoview project.
+    if (!productFlavor.equals('automation')) {
+        configureVariantWithGeckoBinaries(variant)
     }
-
-    syncOmnijarFromDistDir.dependsOn buildOmnijar
-    def generateAssetsTask = tasks.findByName("generate${productFlavor.capitalize()}${buildType.capitalize()}Assets")
-    generateAssetsTask.dependsOn syncOmnijarFromDistDir
-    generateAssetsTask.dependsOn syncLibsFromDistDir
-    generateAssetsTask.dependsOn syncAssetsFromDistDir
-
-    android.sourceSets."${productFlavor}${buildType.capitalize()}".assets.srcDir syncOmnijarFromDistDir.destinationDir
-    android.sourceSets."${productFlavor}${buildType.capitalize()}".assets.srcDir syncAssetsFromDistDir.destinationDir
-    android.sourceSets."${productFlavor}${buildType.capitalize()}".jniLibs.srcDir syncLibsFromDistDir.destinationDir
 }
 
 apply plugin: 'spoon'
 
 spoon {
     // For now, let's be verbose.
     debug = true
     // It's not helpful to pass when we don't have a device connected.
@@ -421,37 +359,37 @@ afterEvaluate {
 
 // Bug 1299015: Complain to treeherder if checkstyle, lint, or unittest fails.  It's not obvious
 // how to listen to individual errors in most cases, so we just link to the reports for now.
 def makeTaskExecutionListener(artifactRootUrl) {
     return new TaskExecutionListener() {
         void beforeExecute(Task task) {
             // Do nothing.
         }
-    
+
         void afterExecute(Task task, TaskState state) {
             if (!state.failure) {
                 return
             }
-    
+
             // Link to the failing report.  The task path and the report path
             // depend on the android-lint task in
             // taskcluster/ci/android-stuff/kind.yml.  It's not possible to link
             // directly, so for now consumers will need to copy-paste the URL.
             switch (task.path) {
             case ':app:checkstyle':
                 def url = "${artifactRootUrl}/public/android/checkstyle/checkstyle.xml"
                 println "TEST-UNEXPECTED-FAIL | android-checkstyle | Checkstyle rule violations were found. See the report at: $url"
                 break
-    
+
             case ':app:lintAutomationDebug':
                 def url = "${artifactRootUrl}/public/android/lint/lint-results-automationDebug.html"
                 println "TEST-UNEXPECTED-FAIL | android-lint | Lint found errors in the project; aborting build. See the report at: $url"
                 break
-    
+
             case ':app:testAutomationDebugUnitTest':
                 def url = "${artifactRootUrl}/public/android/unittest/automationDebug/index.html"
                 println "TEST-UNEXPECTED-FAIL | android-test | There were failing tests. See the report at: $url"
                 break
             }
         }
     }
 }
--- a/mobile/android/config/tooltool-manifests/android-frontend/releng.manifest
+++ b/mobile/android/config/tooltool-manifests/android-frontend/releng.manifest
@@ -30,26 +30,26 @@
 "filename": "java_home-1.7.0-openjdk-1.7.0.85.x86_64.tar.xz",
 "unpack": true
 },
 {
 "algorithm": "sha512",
 "visibility": "public",
 "filename": "jcentral.tar.xz",
 "unpack": true,
-"digest": "66640e3f77a0f9c0ea52f66c53bee8db3c1a27ea4a11526d15706b9da6a0302cd2d5b088f9addca84f4a962022cba3b76829cb878c90cf9bebb3aab050b4aaa4",
-"size": 47315996
+"digest": "8e50f0993e129d3447b228d7da77d661d4ae3d490d791630dabb73e7d8021920f765317a258fd6e819aca48daaa8d0d86ec07cb6c30736199bbf2c4f92270cb5",
+"size": 47164284
 },
 {
 "algorithm": "sha512",
 "visibility": "public",
 "filename": "gradle-dist.tar.xz",
 "unpack": true,
-"digest": "36f961f85b0be846cc9e72bfa0dd1f74e7da8ef785717ce4fd102fec977f21f8902c233b28a21c1ce3797eb2759c7a74c5f74e47bd8f13c1eec640f8d7bed4ac",
-"size": 51512016
+"digest": "e3cfe7f8259ad97722243d4e873d5a05c014bfc24d637427f89d804bf5073290229c778ea303142cf06c2dc79e0492f23521f57d3a73825f55b8db587317646f",
+"size": 51753660
 },
 {
 "algorithm": "sha512",
 "visibility": "public",
 "filename": "dotgradle.tar.xz",
 "unpack": true,
 "digest": "9f082ccd71ad18991eb71fcad355c6990f50a72a09ab9b79696521485656083a72faf5a8d4714de9c4b901ee2319b6786a51964846bb7075061642a8505501c2",
 "size": 512
--- a/mobile/android/config/tooltool-manifests/android-x86/releng.manifest
+++ b/mobile/android/config/tooltool-manifests/android-x86/releng.manifest
@@ -45,26 +45,26 @@
 "filename": "gcc.tar.xz",
 "unpack": true
 },
 {
 "algorithm": "sha512",
 "visibility": "public",
 "filename": "jcentral.tar.xz",
 "unpack": true,
-"digest": "66640e3f77a0f9c0ea52f66c53bee8db3c1a27ea4a11526d15706b9da6a0302cd2d5b088f9addca84f4a962022cba3b76829cb878c90cf9bebb3aab050b4aaa4",
-"size": 47315996
+"digest": "8e50f0993e129d3447b228d7da77d661d4ae3d490d791630dabb73e7d8021920f765317a258fd6e819aca48daaa8d0d86ec07cb6c30736199bbf2c4f92270cb5",
+"size": 47164284
 },
 {
 "algorithm": "sha512",
 "visibility": "public",
 "filename": "gradle-dist.tar.xz",
 "unpack": true,
-"digest": "36f961f85b0be846cc9e72bfa0dd1f74e7da8ef785717ce4fd102fec977f21f8902c233b28a21c1ce3797eb2759c7a74c5f74e47bd8f13c1eec640f8d7bed4ac",
-"size": 51512016
+"digest": "e3cfe7f8259ad97722243d4e873d5a05c014bfc24d637427f89d804bf5073290229c778ea303142cf06c2dc79e0492f23521f57d3a73825f55b8db587317646f",
+"size": 51753660
 },
 {
 "size": 30899096,
 "visibility": "public",
 "digest": "ac9f5f95d11580d3dbeff87e80a585fe4d324b270dabb91b1165686acab47d99fa6651074ab0be09420239a5d6af38bb2c539506962a7b44e0ed4d080bba2953",
 "algorithm": "sha512",
 "filename": "java_home-1.7.0-openjdk-1.7.0.85.x86_64.tar.xz",
 "unpack": true
--- a/mobile/android/config/tooltool-manifests/android/releng.manifest
+++ b/mobile/android/config/tooltool-manifests/android/releng.manifest
@@ -55,26 +55,26 @@
 "filename": "java_home-1.7.0-openjdk-1.7.0.85.x86_64.tar.xz",
 "unpack": true
 },
 {
 "algorithm": "sha512",
 "visibility": "public",
 "filename": "jcentral.tar.xz",
 "unpack": true,
-"digest": "66640e3f77a0f9c0ea52f66c53bee8db3c1a27ea4a11526d15706b9da6a0302cd2d5b088f9addca84f4a962022cba3b76829cb878c90cf9bebb3aab050b4aaa4",
-"size": 47315996
+"digest": "8e50f0993e129d3447b228d7da77d661d4ae3d490d791630dabb73e7d8021920f765317a258fd6e819aca48daaa8d0d86ec07cb6c30736199bbf2c4f92270cb5",
+"size": 47164284
 },
 {
 "algorithm": "sha512",
 "visibility": "public",
 "filename": "gradle-dist.tar.xz",
 "unpack": true,
-"digest": "36f961f85b0be846cc9e72bfa0dd1f74e7da8ef785717ce4fd102fec977f21f8902c233b28a21c1ce3797eb2759c7a74c5f74e47bd8f13c1eec640f8d7bed4ac",
-"size": 51512016
+"digest": "e3cfe7f8259ad97722243d4e873d5a05c014bfc24d637427f89d804bf5073290229c778ea303142cf06c2dc79e0492f23521f57d3a73825f55b8db587317646f",
+"size": 51753660
 },
 {
 "version": "rustc 1.11.0 (9b21dcd6a 2016-08-15) repack",
 "size": 97552448,
 "digest": "272438c1692a46998dc44f22bd1fe18da1be7af2e7fdcf6c52709366c80c73e30637f0c3864f45c64edf46ce6a905538c14b2313983be973f9f29a2f191ec89b",
 "algorithm": "sha512",
 "filename": "rustc.tar.xz",
 "unpack": true
new file mode 100644
--- /dev/null
+++ b/mobile/android/geckoview/build.gradle
@@ -0,0 +1,86 @@
+buildDir "${topobjdir}/gradle/build/mobile/android/geckoview"
+
+apply plugin: 'android-sdk-manager' // Must come before 'com.android.*'.
+apply plugin: 'com.android.library'
+
+android {
+    compileSdkVersion 23
+    buildToolsVersion mozconfig.substs.ANDROID_BUILD_TOOLS_VERSION
+
+    defaultConfig {
+        targetSdkVersion 23
+        minSdkVersion 15
+    }
+
+    buildTypes {
+        withGeckoBinaries {
+            initWith release
+        }
+        withoutGeckoBinaries { // For clarity and consistency throughout the tree.
+            initWith release
+        }
+    }
+
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_7
+        targetCompatibility JavaVersion.VERSION_1_7
+    }
+
+    dexOptions {
+        javaMaxHeapSize "2g"
+    }
+
+    lintOptions {
+        abortOnError false
+    }
+
+    sourceSets {
+        main {
+            java {
+                srcDir "${topsrcdir}/mobile/android/geckoview/src/thirdparty/java"
+
+                // TODO: support WebRTC.
+                // if (mozconfig.substs.MOZ_WEBRTC) {
+                //     srcDir "${topsrcdir}/media/webrtc/trunk/webrtc/modules/audio_device/android/java/src"
+                //     srcDir "${topsrcdir}/media/webrtc/trunk/webrtc/modules/video_capture/android/java/src"
+                //     srcDir "${topsrcdir}/media/webrtc/trunk/webrtc/modules/video_render/android/java/src"
+                // }
+
+                // TODO: don't use AppConstants.
+                srcDir "${project.buildDir}/generated/source/preprocessed_code" // See syncPreprocessedCode.
+            }
+
+            assets {
+            }
+        }
+    }
+}
+
+dependencies {
+    compile "com.android.support:support-v4:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
+}
+
+task syncPreprocessedCode(type: Sync, dependsOn: rootProject.generateCodeAndResources) {
+    into("${project.buildDir}/generated/source/preprocessed_code")
+    from("${topobjdir}/mobile/android/base/generated/preprocessed") {
+        // AdjustConstants is included in the main app project.
+        exclude '**/AdjustConstants.java'
+    }
+}
+
+apply from: "${topsrcdir}/mobile/android/gradle/with_gecko_binaries.gradle"
+
+android.libraryVariants.all { variant ->
+    variant.preBuild.dependsOn syncPreprocessedCode
+
+    // Like 'debug', 'release', or 'withGeckoBinaries'.
+    def buildType = variant.buildType.name
+
+    // It would be most natural for :geckoview to always include the Gecko
+    // binaries, but that's difficult; see the notes in
+    // mobile/android/gradle/with_gecko_binaries.gradle.  Instead :app uses
+    // :geckoview:release and handles it's own Gecko binary inclusion.
+    if (buildType.equals('withGeckoBinaries')) {
+        configureVariantWithGeckoBinaries(variant)
+    }
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/geckoview/src/main/AndroidManifest.xml
@@ -0,0 +1,39 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="org.mozilla.geckoview">
+
+    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
+    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
+    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+    <uses-permission android:name="android.permission.INTERNET"/>
+    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
+    <!-- READ_EXTERNAL_STORAGE was added in API 16, and is only enforced in API
+         19+.  We declare it so that the bouncer APK and the main APK have the
+         same set of permissions. -->
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
+    <uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT"/>
+    <uses-permission android:name="com.android.launcher.permission.UNINSTALL_SHORTCUT"/>
+
+    <uses-permission android:name="android.permission.WAKE_LOCK"/>
+    <uses-permission android:name="android.permission.VIBRATE"/>
+
+    <uses-feature android:name="android.hardware.location" android:required="false"/>
+    <uses-feature android:name="android.hardware.location.gps" android:required="false"/>
+    <uses-feature android:name="android.hardware.touchscreen"/>
+
+    <!--#ifdef MOZ_WEBRTC-->
+    <!--<uses-permission android:name="android.permission.RECORD_AUDIO"/>-->
+    <!--<uses-feature android:name="android.hardware.audio.low_latency" android:required="false"/>-->
+    <!--<uses-feature android:name="android.hardware.camera.any" android:required="false"/>-->
+    <!--<uses-feature android:name="android.hardware.microphone" android:required="false"/>-->
+    <!--#endif-->
+
+    <uses-permission android:name="android.permission.CAMERA" />
+    <uses-feature android:name="android.hardware.camera" android:required="false"/>
+    <uses-feature android:name="android.hardware.camera.autofocus" android:required="false"/>
+
+    <!-- App requires OpenGL ES 2.0 -->
+    <uses-feature android:glEsVersion="0x00020000" android:required="true" />
+
+</manifest>
new file mode 100644
--- /dev/null
+++ b/mobile/android/geckoview_example/build.gradle
@@ -0,0 +1,62 @@
+buildDir "${topobjdir}/gradle/build/mobile/android/geckoview_example"
+
+apply plugin: 'com.android.application'
+
+android {
+    compileSdkVersion 23
+    buildToolsVersion mozconfig.substs.ANDROID_BUILD_TOOLS_VERSION
+
+    defaultConfig {
+        applicationId "org.mozilla.geckoview_example"
+        minSdkVersion 15
+        targetSdkVersion 23
+        versionCode 1
+        versionName "1.0"
+        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+    }
+
+    // This is extremely frustrating, but the only way to do it automation for
+    // now.  Without this, we only get a "debugAndroidTest" configuration; we
+    // have no "withoutGeckoBinariesAndroidTest" configuration.
+    testBuildType "withoutGeckoBinaries"
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+        }
+        withGeckoBinaries { // For consistency with :geckoview project in Task Cluster invocations.
+            initWith debug
+        }
+        withoutGeckoBinaries { // Logical negation of withGeckoBinaries.
+            initWith debug
+        }
+    }
+}
+
+dependencies {
+    testCompile 'junit:junit:4.12'
+
+    compile 'com.android.support:support-annotations:23.0.1'
+
+    // Later versions (2.2.2, 0.5) requires newer support libraries, leading to
+    // "Conflict with dependency 'com.android.support:support-annotations'. Resolved versions for app (23.0.1) and test app (23.1.1) differ."
+    androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.1'
+    androidTestCompile 'com.android.support.test:runner:0.4.1'
+
+    compile project(':geckoview')
+}
+
+apply from: "${topsrcdir}/mobile/android/gradle/with_gecko_binaries.gradle"
+
+android.applicationVariants.all { variant ->
+    // Like 'debug', 'release', or 'withoutGeckoBinaries'.
+    def buildType = variant.buildType.name
+
+    // It would be most natural for :geckoview to always include the Gecko
+    // binaries, but that's difficult; see the notes in
+    // mobile/android/gradle/with_gecko_binaries.gradle.  Instead we handle our
+    // own Gecko binary inclusion.
+    if (!buildType.equals('withoutGeckoBinaries')) {
+        configureVariantWithGeckoBinaries(variant)
+    }
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/geckoview_example/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/nalexander/.mozbuild/android-sdk-macosx/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
new file mode 100644
--- /dev/null
+++ b/mobile/android/geckoview_example/src/androidTest/java/org/mozilla/geckoview_example/ApplicationTest.java
@@ -0,0 +1,13 @@
+package org.mozilla.geckoview_example;
+
+import android.app.Application;
+import android.test.ApplicationTestCase;
+
+/**
+ * <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
+ */
+public class ApplicationTest extends ApplicationTestCase<Application> {
+    public ApplicationTest() {
+        super(Application.class);
+    }
+}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/mobile/android/geckoview_example/src/androidTest/java/org/mozilla/geckoview_example/GeckoViewActivityTest.java
@@ -0,0 +1,32 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
+ * 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/. */
+
+package org.mozilla.geckoview_example;
+
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+
+@RunWith(AndroidJUnit4.class)
+public class GeckoViewActivityTest {
+
+    @Rule
+    public ActivityTestRule<GeckoViewActivity> mActivityRule = new ActivityTestRule(GeckoViewActivity.class);
+
+    @Test
+    public void testA() throws InterruptedException {
+        onView(withId(R.id.gecko_view))
+                .check(matches(isDisplayed()));
+    }
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/geckoview_example/src/main/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="org.mozilla.geckoview_example">
+
+    <application android:allowBackup="true"
+                 android:label="@string/app_name"
+                 android:supportsRtl="true">
+
+        <uses-library android:name="android.test.runner" />
+
+        <activity android:name="org.mozilla.geckoview_example.GeckoViewActivity"
+                  android:label="GeckoViewActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+    </application>
+
+</manifest>
new file mode 100644
--- /dev/null
+++ b/mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/GeckoViewActivity.java
@@ -0,0 +1,142 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
+ * 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/. */
+
+package org.mozilla.geckoview_example;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.util.Log;
+import android.widget.Toast;
+
+import org.mozilla.gecko.GeckoProfile;
+import org.mozilla.gecko.GeckoThread;
+import org.mozilla.gecko.GeckoView;
+import org.mozilla.gecko.PrefsHelper;
+
+public class GeckoViewActivity extends Activity {
+    private static final String LOGTAG = "GeckoViewActivity";
+
+    GeckoView mGeckoView;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.geckoview_activity);
+
+        mGeckoView = (GeckoView) findViewById(R.id.gecko_view);
+        mGeckoView.setChromeDelegate(new MyGeckoViewChrome());
+        mGeckoView.setContentDelegate(new MyGeckoViewContent());
+    }
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+
+        final GeckoProfile profile = GeckoProfile.get(getApplicationContext());
+
+        GeckoThread.init(profile, /* args */ null, /* action */ null, /* debugging */ false);
+        GeckoThread.launch();
+    }
+
+    private class MyGeckoViewChrome implements GeckoView.ChromeDelegate {
+        @Override
+        public void onReady(GeckoView view) {
+            Log.i(LOGTAG, "Gecko is ready");
+            // // Inject a script that adds some code to the content window
+            // mGeckoView.importScript("resource://android/assets/script.js");
+
+            // Set up remote debugging to a port number
+            PrefsHelper.setPref("layers.dump", true);
+            PrefsHelper.setPref("devtools.debugger.remote-port", 6000);
+            PrefsHelper.setPref("devtools.debugger.unix-domain-socket", "");
+            PrefsHelper.setPref("devtools.debugger.remote-enabled", true);
+
+            // The Gecko libraries have finished loading and we can use the rendering engine.
+            // Let's add a browser (required) and load a page into it.
+            // mGeckoView.addBrowser(getResources().getString(R.string.default_url));
+        }
+
+        @Override
+        public void onAlert(GeckoView view, GeckoView.Browser browser, String message, GeckoView.PromptResult result) {
+            Log.i(LOGTAG, "Alert!");
+            result.confirm();
+            Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show();
+        }
+
+        @Override
+        public void onConfirm(GeckoView view, GeckoView.Browser browser, String message, final GeckoView.PromptResult result) {
+            Log.i(LOGTAG, "Confirm!");
+            new AlertDialog.Builder(GeckoViewActivity.this)
+                .setTitle("javaScript dialog")
+                .setMessage(message)
+                .setPositiveButton(android.R.string.ok,
+                                   new DialogInterface.OnClickListener() {
+                                       public void onClick(DialogInterface dialog, int which) {
+                                           result.confirm();
+                                       }
+                                   })
+                .setNegativeButton(android.R.string.cancel,
+                                   new DialogInterface.OnClickListener() {
+                                       public void onClick(DialogInterface dialog, int which) {
+                                           result.cancel();
+                                       }
+                                   })
+                .create()
+                .show();
+        }
+
+        @Override
+        public void onPrompt(GeckoView view, GeckoView.Browser browser, String message, String defaultValue, GeckoView.PromptResult result) {
+            result.cancel();
+        }
+
+        @Override
+        public void onDebugRequest(GeckoView view, GeckoView.PromptResult result) {
+            Log.i(LOGTAG, "Remote Debug!");
+            result.confirm();
+        }
+
+        @Override
+        public void onScriptMessage(GeckoView view, Bundle data, GeckoView.MessageResult result) {
+            Log.i(LOGTAG, "Got Script Message: " + data.toString());
+            String type = data.getString("type");
+            if ("fetch".equals(type)) {
+                Bundle ret = new Bundle();
+                ret.putString("name", "Mozilla");
+                ret.putString("url", "https://mozilla.org");
+                result.success(ret);
+            }
+        }
+    }
+
+    private class MyGeckoViewContent implements GeckoView.ContentDelegate {
+        @Override
+        public void onPageStart(GeckoView view, GeckoView.Browser browser, String url) {
+
+        }
+
+        @Override
+        public void onPageStop(GeckoView view, GeckoView.Browser browser, boolean success) {
+
+        }
+
+        @Override
+        public void onPageShow(GeckoView view, GeckoView.Browser browser) {
+
+        }
+
+        @Override
+        public void onReceivedTitle(GeckoView view, GeckoView.Browser browser, String title) {
+            Log.i(LOGTAG, "Received a title: " + title);
+        }
+
+        @Override
+        public void onReceivedFavicon(GeckoView view, GeckoView.Browser browser, String url, int size) {
+            Log.i(LOGTAG, "Received a favicon URL: " + url);
+        }
+    }
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/geckoview_example/src/main/res/layout/geckoview_activity.xml
@@ -0,0 +1,13 @@
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:layout_width="fill_parent"
+              android:layout_height="fill_parent"
+              android:orientation="vertical">
+
+    <org.mozilla.gecko.GeckoView
+        android:id="@+id/gecko_view"
+        android:layout_width="fill_parent"
+        android:layout_height="match_parent"
+        android:scrollbars="none"
+        />
+
+</LinearLayout>
new file mode 100644
--- /dev/null
+++ b/mobile/android/geckoview_example/src/main/res/values/colors.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <color name="colorPrimary">#3F51B5</color>
+    <color name="colorPrimaryDark">#303F9F</color>
+    <color name="colorAccent">#FF4081</color>
+</resources>
new file mode 100644
--- /dev/null
+++ b/mobile/android/geckoview_example/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+<resources>
+    <string name="app_name">geckoview_example</string>
+</resources>
new file mode 100644
--- /dev/null
+++ b/mobile/android/geckoview_example/src/test/java/org/mozilla/geckoview_example/ExampleUnitTest.java
@@ -0,0 +1,15 @@
+package org.mozilla.geckoview_example;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * To work on unit tests, switch the Test Artifact in the Build Variants view.
+ */
+public class ExampleUnitTest {
+    @Test
+    public void addition_isCorrect() throws Exception {
+        assertEquals(4, 2 + 2);
+    }
+}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/mobile/android/gradle/with_gecko_binaries.gradle
@@ -0,0 +1,105 @@
+// We run fairly hard into a fundamental limitation of the Android Gradle
+// plugin.  There are many bugs filed about this, but
+// https://code.google.com/p/android/issues/detail?id=216978#c6 is a reason one.
+// The issue is that we need fine-grained control over when to include Gecko's
+// binary libraries into the GeckoView AAR and the Fennec APK, and that's hard
+// to achieve.  In particular:
+//
+// * :app:automation wants :geckoview to not include Gecko binaries (automation
+// * build, before package)
+//
+// * :geckoview:withLibraries wants :geckoview to include Gecko binaries
+// * (automation build, after package)
+//
+// * non-:app:automation wants :geckoview to include Gecko binaries (local
+// * build, always after package)
+//
+// publishNonDefault (see
+// http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Library-Publication)
+// is intended to address this, but doesn't handle our case.  That option always
+// builds *all* configurations, which fails when the required Gecko binaries
+// don't exist (automation build, before package).  So instead, we make both
+// :app and :geckoview both know how to include the Gecko binaries, and use a
+// non-default, non-published :geckoview:withGeckoBinaries configuration to
+// handle automation's needs.  Simple, right?
+
+// The omnijar inputs are listed as resource directory inputs to a dummy JAR.
+// That arrangement labels them nicely in IntelliJ.  See the comment in the
+// :omnijar project for more context.
+evaluationDependsOn(':omnijar')
+
+task buildOmnijar(type:Exec) {
+    dependsOn rootProject.generateCodeAndResources
+
+    // See comment in :omnijar project regarding interface mismatches here.
+    inputs.source project(':omnijar').sourceSets.main.resources.srcDirs
+
+    // Produce a single output file.
+    outputs.file "${topobjdir}/dist/fennec/assets/omni.ja"
+
+    workingDir "${topobjdir}"
+
+    commandLine mozconfig.substs.GMAKE
+    args '-C'
+    args "${topobjdir}/mobile/android/base"
+    args 'gradle-omnijar'
+
+    // Only show the output if something went wrong.
+    ignoreExitValue = true
+    standardOutput = new ByteArrayOutputStream()
+    errorOutput = standardOutput
+    doLast {
+        if (execResult.exitValue != 0) {
+            throw new GradleException("Process '${commandLine}' finished with non-zero exit value ${execResult.exitValue}:\n\n${standardOutput.toString()}")
+        }
+    }
+}
+
+task syncOmnijarFromDistDir(type: Sync) {
+    into("${project.buildDir}/generated/omnijar")
+    from("${topobjdir}/dist/fennec/assets") {
+        include 'omni.ja'
+    }
+}
+
+task checkLibsExistInDistDir<< {
+    if (syncLibsFromDistDir.source.empty) {
+        throw new GradleException("Required JNI libraries not found in ${topobjdir}/dist/fennec/lib.  Have you built and packaged?")
+    }
+}
+
+task syncLibsFromDistDir(type: Sync, dependsOn: checkLibsExistInDistDir) {
+    into("${project.buildDir}/generated/jniLibs")
+    from("${topobjdir}/dist/fennec/lib")
+}
+
+task checkAssetsExistInDistDir<< {
+    if (syncAssetsFromDistDir.source.empty) {
+        throw new GradleException("Required assets not found in ${topobjdir}/dist/fennec/assets.  Have you built and packaged?")
+    }
+}
+
+task syncAssetsFromDistDir(type: Sync, dependsOn: checkAssetsExistInDistDir) {
+    into("${project.buildDir}/generated/assets")
+    from("${topobjdir}/dist/fennec/assets") {
+        exclude 'omni.ja'
+    }
+}
+
+ext.configureVariantWithGeckoBinaries = { variant ->
+    // Like 'local' or 'localOld'; may be null.
+    def productFlavor = variant.productFlavors ? variant.productFlavors[0].name : ""
+    // Like 'debug' or 'release'.
+    def buildType = variant.buildType.name
+
+    syncOmnijarFromDistDir.dependsOn buildOmnijar
+    def generateAssetsTask = tasks.findByName("generate${productFlavor.capitalize()}${buildType.capitalize()}Assets")
+    generateAssetsTask.dependsOn syncOmnijarFromDistDir
+    generateAssetsTask.dependsOn syncLibsFromDistDir
+    generateAssetsTask.dependsOn syncAssetsFromDistDir
+
+    def sourceSet = productFlavor ? "${productFlavor}${buildType.capitalize()}" : buildType
+    android.sourceSets."${sourceSet}".assets.srcDir syncOmnijarFromDistDir.destinationDir
+    android.sourceSets."${sourceSet}".assets.srcDir syncAssetsFromDistDir.destinationDir
+    android.sourceSets."${sourceSet}".jniLibs.srcDir syncLibsFromDistDir.destinationDir
+}
--- a/mobile/android/thirdparty/build.gradle
+++ b/mobile/android/thirdparty/build.gradle
@@ -20,17 +20,16 @@ android {
         abortOnError false
     }
 
     sourceSets {
         main {
             manifest.srcFile 'AndroidManifest.xml'
             java {
                 srcDir '.'
-                srcDir "${topsrcdir}/mobile/android/geckoview/src/thirdparty/java"
 
                 if (!mozconfig.substs.MOZ_INSTALL_TRACKING) {
                     exclude 'com/adjust/**'
                 }
 
                 // Exclude LeakCanary: It will be added again via a gradle dependency. This version
                 // here is only the no-op library for mach-based builds.
                 exclude 'com/squareup/leakcanary/**'
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -4850,21 +4850,21 @@ pref("browser.dom.window.dump.enabled", 
 pref("dom.netinfo.enabled", true);
 #else
 pref("dom.netinfo.enabled", false);
 #endif
 
 #ifdef XP_WIN
 // On 32-bit Windows, fire a low-memory notification if we have less than this
 // many mb of virtual address space available.
-pref("memory.low_virtual_memory_threshold_mb", 256);
+pref("memory.low_virtual_memory_threshold_mb", 128);
 
 // On Windows 32-bit, fire a low-memory notification if we have less
 // than this many mb of commit space (physical memory plus page file) left.
-pref("memory.low_commit_space_threshold_mb", 256);
+pref("memory.low_commit_space_threshold_mb", 128);
 
 // On Windows 32-bit, fire a low-memory notification if we have less
 // than this many mb of physical memory available on the whole machine.
 pref("memory.low_physical_memory_threshold_mb", 0);
 
 // On Windows 32-bit, don't fire a low-memory notification because of
 // low available physical memory or low commit space more than once every
 // low_memory_notification_interval_ms.
--- a/netwerk/cache2/CacheIndex.cpp
+++ b/netwerk/cache2/CacheIndex.cpp
@@ -38,26 +38,39 @@ namespace mozilla {
 namespace net {
 
 namespace {
 
 class FrecencyComparator
 {
 public:
   bool Equals(CacheIndexRecord* a, CacheIndexRecord* b) const {
+    if (!a || !b) {
+      return false;
+    }
+
     return a->mFrecency == b->mFrecency;
   }
   bool LessThan(CacheIndexRecord* a, CacheIndexRecord* b) const {
-    // Place entries with frecency 0 at the end of the array.
+    // Removed (=null) entries must be at the end of the array.
+    if (!a) {
+      return false;
+    }
+    if (!b) {
+      return true;
+    }
+
+    // Place entries with frecency 0 at the end of the non-removed entries.
     if (a->mFrecency == 0) {
       return false;
     }
     if (b->mFrecency == 0) {
       return true;
     }
+
     return a->mFrecency < b->mFrecency;
   }
 };
 
 } // namespace
 
 /**
  * This helper class is responsible for keeping CacheIndex::mIndexStats and
@@ -90,29 +103,49 @@ public:
 
     const CacheIndexEntry *entry = FindEntry();
     mIndex->mIndexStats.AfterChange(entry);
     if (!entry || !entry->IsInitialized() || entry->IsRemoved()) {
       entry = nullptr;
     }
 
     if (entry && !mOldRecord) {
-      mIndex->InsertRecordToFrecencyArray(entry->mRec);
+      mIndex->mFrecencyArray.AppendRecord(entry->mRec);
       mIndex->AddRecordToIterators(entry->mRec);
     } else if (!entry && mOldRecord) {
-      mIndex->RemoveRecordFromFrecencyArray(mOldRecord);
+      mIndex->mFrecencyArray.RemoveRecord(mOldRecord);
       mIndex->RemoveRecordFromIterators(mOldRecord);
     } else if (entry && mOldRecord) {
       if (entry->mRec != mOldRecord) {
         // record has a different address, we have to replace it
         mIndex->ReplaceRecordInIterators(mOldRecord, entry->mRec);
-        mIndex->RemoveRecordFromFrecencyArray(mOldRecord);
-        mIndex->InsertRecordToFrecencyArray(entry->mRec);
+
+        if (entry->mRec->mFrecency == mOldFrecency) {
+          // If frecency hasn't changed simply replace the pointer
+          mIndex->mFrecencyArray.ReplaceRecord(mOldRecord, entry->mRec);
+        } else {
+          // It is expected that when the frecency is changed the new value is
+          // always bigger than the old one. There is also a special case when
+          // we zero the frecency if eviction of the entry fails. If the above
+          // isn't true, this algorithm might not work properly and needs to be
+          // changed.
+          MOZ_ASSERT(entry->mRec->mFrecency == 0 ||
+                     entry->mRec->mFrecency > mOldFrecency);
+
+          // Remove old pointer and insert the new one at the end of the array
+          mIndex->mFrecencyArray.RemoveRecord(mOldRecord);
+          mIndex->mFrecencyArray.AppendRecord(entry->mRec);
+        }
       } else if (entry->mRec->mFrecency != mOldFrecency) {
-        mIndex->mFrecencyArraySorted = false;
+        MOZ_ASSERT(entry->mRec->mFrecency == 0 ||
+                   entry->mRec->mFrecency > mOldFrecency);
+
+        // Move the element at the end of the array
+        mIndex->mFrecencyArray.RemoveRecord(entry->mRec);
+        mIndex->mFrecencyArray.AppendRecord(entry->mRec);
       }
     } else {
       // both entries were removed or not initialized, do nothing
     }
   }
 
   // We cannot rely on nsTHashtable::GetEntry() in case we are removing entries
   // while iterating. Destructor is called before the entry is removed. Caller
@@ -247,17 +280,16 @@ CacheIndex::CacheIndex()
   , mUpdateEventPending(false)
   , mSkipEntries(0)
   , mProcessEntries(0)
   , mRWBuf(nullptr)
   , mRWBufSize(0)
   , mRWBufPos(0)
   , mRWPending(false)
   , mJournalReadSuccessfully(false)
-  , mFrecencyArraySorted(false)
   , mAsyncGetDiskConsumptionBlocked(false)
 {
   sLock.AssertCurrentThreadOwns();
   LOG(("CacheIndex::CacheIndex [this=%p]", this));
   MOZ_COUNT_CTOR(CacheIndex);
   MOZ_ASSERT(!gInstance, "multiple CacheIndex instances!");
 }
 
@@ -1185,53 +1217,54 @@ CacheIndex::GetEntryForEviction(bool aIg
   if (!index)
     return NS_ERROR_NOT_INITIALIZED;
 
   if (!index->IsIndexUsable()) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   SHA1Sum::Hash hash;
-  bool foundEntry = false;
-  uint32_t i;
+  CacheIndexRecord *foundRecord = nullptr;
+  uint32_t skipped = 0;
 
   // find first non-forced valid and unpinned entry with the lowest frecency
-  if (!index->mFrecencyArraySorted) {
-    index->mFrecencyArray.Sort(FrecencyComparator());
-    index->mFrecencyArraySorted = true;
-  }
-
-  for (i = 0; i < index->mFrecencyArray.Length(); ++i) {
-    memcpy(&hash, &index->mFrecencyArray[i]->mHash, sizeof(SHA1Sum::Hash));
+  index->mFrecencyArray.SortIfNeeded();
+
+  for (auto iter = index->mFrecencyArray.Iter(); !iter.Done(); iter.Next()) {
+    CacheIndexRecord *rec = iter.Get();
+
+    memcpy(&hash, rec->mHash, sizeof(SHA1Sum::Hash));
+
+    ++skipped;
 
     if (IsForcedValidEntry(&hash)) {
       continue;
     }
 
-    if (CacheIndexEntry::IsPinned(index->mFrecencyArray[i])) {
+    if (CacheIndexEntry::IsPinned(rec)) {
       continue;
     }
 
-    if (aIgnoreEmptyEntries &&
-        !CacheIndexEntry::GetFileSize(index->mFrecencyArray[i])) {
+    if (aIgnoreEmptyEntries && !CacheIndexEntry::GetFileSize(rec)) {
       continue;
     }
 
-    foundEntry = true;
+    --skipped;
+    foundRecord = rec;
     break;
   }
 
-  if (!foundEntry)
+  if (!foundRecord)
     return NS_ERROR_NOT_AVAILABLE;
 
-  *aCnt = index->mFrecencyArray.Length() - i;
+  *aCnt = skipped;
 
   LOG(("CacheIndex::GetEntryForEviction() - returning entry from frecency "
         "array [hash=%08x%08x%08x%08x%08x, cnt=%u, frecency=%u]",
-        LOGSHA1(&hash), *aCnt, index->mFrecencyArray[i]->mFrecency));
+        LOGSHA1(&hash), *aCnt, foundRecord->mFrecency));
 
   memcpy(aHash, &hash, sizeof(SHA1Sum::Hash));
 
   return NS_OK;
 }
 
 
 // static
@@ -1315,18 +1348,18 @@ CacheIndex::GetCacheStats(nsILoadContext
 
   if (!aInfo) {
     return NS_ERROR_INVALID_ARG;
   }
 
   *aSize = 0;
   *aCount = 0;
 
-  for (uint32_t i = 0; i < index->mFrecencyArray.Length(); ++i) {
-    CacheIndexRecord* record = index->mFrecencyArray[i];
+  for (auto iter = index->mFrecencyArray.Iter(); !iter.Done(); iter.Next()) {
+    CacheIndexRecord *record = iter.Get();
     if (!CacheIndexEntry::RecordMatchesLoadContextInfo(record, aInfo))
       continue;
 
     *aSize += CacheIndexEntry::GetFileSize(record);
     ++*aCount;
   }
 
   return NS_OK;
@@ -1399,32 +1432,31 @@ CacheIndex::GetIterator(nsILoadContextIn
   if (!index) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
   if (!index->IsIndexUsable()) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
-  RefPtr<CacheIndexIterator> iter;
+  RefPtr<CacheIndexIterator> idxIter;
   if (aInfo) {
-    iter = new CacheIndexContextIterator(index, aAddNew, aInfo);
+    idxIter = new CacheIndexContextIterator(index, aAddNew, aInfo);
   } else {
-    iter = new CacheIndexIterator(index, aAddNew);
+    idxIter = new CacheIndexIterator(index, aAddNew);
   }
 
-  if (!index->mFrecencyArraySorted) {
-    index->mFrecencyArray.Sort(FrecencyComparator());
-    index->mFrecencyArraySorted = true;
+  index->mFrecencyArray.SortIfNeeded();
+
+  for (auto iter = index->mFrecencyArray.Iter(); !iter.Done(); iter.Next()) {
+    idxIter->AddRecord(iter.Get());
   }
 
-  iter->AddRe