Merge inbound to mozilla-central. a=merge
authorNoemi Erli <nerli@mozilla.com>
Fri, 19 Jan 2018 12:10:24 +0200
changeset 454280 6ffbba9ce0ef9ec77a63445f068f2e218ed4830f
parent 454278 3af31d84214f7cf4617a6798917ed1a0154f569d (current diff)
parent 454279 b572d99d0febdf94f140838c7c29867b873aa9e1 (diff)
child 454281 4c9965a3b8a0d495cfc39a2eee9756e81cf5f953
child 454282 55e8bfe2dc423584750bdd08eb469ef98f50b38e
child 454294 eac2d7ff2c83a0fddf16bb70f5c732ed7a369f62
child 454320 d502a8e0691ec8de2819d4c51f3b6d2ad7459f9b
push id1648
push usermtabara@mozilla.com
push dateThu, 01 Mar 2018 12:45:47 +0000
treeherdermozilla-release@cbb9688c2eeb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone59.0a1
first release with
nightly linux32
6ffbba9ce0ef / 59.0a1 / 20180119103852 / files
nightly linux64
6ffbba9ce0ef / 59.0a1 / 20180119103852 / files
nightly mac
6ffbba9ce0ef / 59.0a1 / 20180119103852 / files
nightly win32
6ffbba9ce0ef / 59.0a1 / 20180119103852 / files
nightly win64
6ffbba9ce0ef / 59.0a1 / 20180119103852 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to mozilla-central. a=merge
browser/extensions/shield-recipe-client/lib/ShieldPreferences.jsm
docshell/base/nsIContentViewerContainer.idl
layout/painting/RetainedDisplayListBuilder.cpp
layout/reftests/display-list/reftest.list
modules/libpref/init/all.js
--- a/browser/extensions/shield-recipe-client/lib/ShieldPreferences.jsm
+++ b/browser/extensions/shield-recipe-client/lib/ShieldPreferences.jsm
@@ -116,29 +116,24 @@ this.ShieldPreferences = {
     const viewStudies = doc.createElementNS(XUL_NS, "label");
     viewStudies.setAttribute("id", "viewShieldStudies");
     viewStudies.setAttribute("href", "about:studies");
     viewStudies.setAttribute("useoriginprincipal", true);
     viewStudies.textContent = "View Firefox Studies";
     viewStudies.classList.add("learnMore", "text-link");
     hContainer.appendChild(viewStudies);
 
-    const optOutPref = doc.createElementNS(XUL_NS, "preference");
-    optOutPref.setAttribute("id", OPT_OUT_STUDIES_ENABLED_PREF);
-    optOutPref.setAttribute("name", OPT_OUT_STUDIES_ENABLED_PREF);
-    optOutPref.setAttribute("type", "bool");
-
     // Preference instances for prefs that we need to monitor while the page is open.
     doc.defaultView.Preferences.add({ id: OPT_OUT_STUDIES_ENABLED_PREF, type: "bool" });
 
-    // Weirdly, FHR doesn't have a <preference> element on the page, so we create it.
+    // Weirdly, FHR doesn't have a Preference instance on the page, so we create it.
     const fhrPref = doc.defaultView.Preferences.add({ id: FHR_UPLOAD_ENABLED_PREF, type: "bool" });
-    fhrPref.on("change", function(event) {
-      // Avoid reference to the document directly, to avoid leaks.
-      const eventTargetCheckbox = event.target.ownerDocument.getElementById("optOutStudiesEnabled");
-      eventTargetCheckbox.disabled = !Services.prefs.getBoolPref(FHR_UPLOAD_ENABLED_PREF);
-    });
+    function onChangeFHRPref(event) {
+      checkbox.disabled = !Services.prefs.getBoolPref(FHR_UPLOAD_ENABLED_PREF);
+    }
+    fhrPref.on("change", onChangeFHRPref);
+    doc.defaultView.addEventListener("unload", () => fhrPref.off("change", onChangeFHRPref), { once: true });
 
     // Actually inject the elements we've created.
     const parent = doc.getElementById("submitHealthReportBox").closest("description");
     parent.appendChild(container);
   },
 };
--- a/browser/extensions/shield-recipe-client/test/browser/browser_about_preferences.js
+++ b/browser/extensions/shield-recipe-client/test/browser/browser_about_preferences.js
@@ -166,14 +166,16 @@ decorate_task(
 
 decorate_task(
   withPrivacyPrefs,
   async function testViewStudiesLink(browser) {
     browser.contentDocument.getElementById("viewShieldStudies").click();
     await BrowserTestUtils.waitForLocationChange(gBrowser);
 
     is(
-      browser.currentURI.spec,
+      gBrowser.currentURI.spec,
       "about:studies",
-      "Clicking the view studies link opens about:studies."
+      "Clicking the view studies link opens about:studies in a new tab."
     );
+
+    gBrowser.removeCurrentTab();
   }
 );
--- a/docshell/base/moz.build
+++ b/docshell/base/moz.build
@@ -34,17 +34,16 @@ with Files('nsIScrollObserver.*'):
 DIRS += [
     'timeline',
 ]
 
 XPIDL_SOURCES += [
     'nsCDefaultURIFixup.idl',
     'nsIClipboardCommands.idl',
     'nsIContentViewer.idl',
-    'nsIContentViewerContainer.idl',
     'nsIContentViewerEdit.idl',
     'nsIDocCharset.idl',
     'nsIDocShell.idl',
     'nsIDocShellLoadInfo.idl',
     'nsIDocShellTreeItem.idl',
     'nsIDocShellTreeOwner.idl',
     'nsIDocumentLoaderFactory.idl',
     'nsIDownloadHistory.idl',
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -508,17 +508,16 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   NS_INTERFACE_MAP_ENTRY(nsIWebNavigation)
   NS_INTERFACE_MAP_ENTRY(nsIBaseWindow)
   NS_INTERFACE_MAP_ENTRY(nsIScrollable)
   NS_INTERFACE_MAP_ENTRY(nsITextScroll)
   NS_INTERFACE_MAP_ENTRY(nsIDocCharset)
   NS_INTERFACE_MAP_ENTRY(nsIRefreshURI)
   NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener)
   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
-  NS_INTERFACE_MAP_ENTRY(nsIContentViewerContainer)
   NS_INTERFACE_MAP_ENTRY(nsIWebPageDescriptor)
   NS_INTERFACE_MAP_ENTRY(nsIAuthPromptProvider)
   NS_INTERFACE_MAP_ENTRY(nsILoadContext)
   NS_INTERFACE_MAP_ENTRY(nsIWebShellServices)
   NS_INTERFACE_MAP_ENTRY(nsILinkHandler)
   NS_INTERFACE_MAP_ENTRY(nsIClipboardCommands)
   NS_INTERFACE_MAP_ENTRY(nsIDOMStorageManager)
   NS_INTERFACE_MAP_ENTRY(nsINetworkInterceptController)
@@ -6851,21 +6850,17 @@ nsDocShell::RefreshURIFromQueue()
         }
       }
     }
   }
 
   return NS_OK;
 }
 
-//*****************************************************************************
-// nsDocShell::nsIContentViewerContainer
-//*****************************************************************************
-
-NS_IMETHODIMP
+nsresult
 nsDocShell::Embed(nsIContentViewer* aContentViewer,
                   const char* aCommand, nsISupports* aExtraInfo)
 {
   // Save the LayoutHistoryState of the previous document, before
   // setting up new document
   PersistLayoutHistoryState();
 
   nsresult rv = SetupNewViewer(aContentViewer);
@@ -6918,22 +6913,16 @@ nsDocShell::Embed(nsIContentViewer* aCon
 
   if (!updateHistory) {
     SetLayoutHistoryState(nullptr);
   }
 
   return NS_OK;
 }
 
-NS_IMETHODIMP
-nsDocShell::SetIsPrinting(bool aIsPrinting)
-{
-  mIsPrintingOrPP = aIsPrinting;
-  return NS_OK;
-}
 
 //*****************************************************************************
 // nsDocShell::nsIWebProgressListener
 //*****************************************************************************
 
 NS_IMETHODIMP
 nsDocShell::OnProgressChange(nsIWebProgress* aProgress,
                              nsIRequest* aRequest,
@@ -13853,16 +13842,23 @@ nsDocShell::StopDocumentLoad(void)
     Stop(nsIWebNavigation::STOP_ALL);
     return NS_OK;
   }
   // return failer if this request is not accepted due to mCharsetReloadState
   return NS_ERROR_DOCSHELL_REQUEST_REJECTED;
 }
 
 NS_IMETHODIMP
+nsDocShell::SetIsPrinting(bool aIsPrinting)
+{
+  mIsPrintingOrPP = aIsPrinting;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsDocShell::GetPrintPreview(nsIWebBrowserPrint** aPrintPreview)
 {
   *aPrintPreview = nullptr;
 #if NS_PRINT_PREVIEW
   nsCOMPtr<nsIDocumentViewerPrint> print = do_QueryInterface(mContentViewer);
   if (!print || !print->IsInitializedForPrintPreview()) {
     // XXX: Creating a brand new content viewer to host preview every
     // time we enter here seems overwork. We could skip ahead to where
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -15,17 +15,16 @@
 #include "mozilla/UniquePtr.h"
 #include "mozilla/WeakPtr.h"
 
 #include "mozilla/dom/ProfileTimelineMarkerBinding.h"
 
 #include "nsIAuthPromptProvider.h"
 #include "nsIBaseWindow.h"
 #include "nsIClipboardCommands.h"
-#include "nsIContentViewerContainer.h"
 #include "nsIDeprecationWarner.h"
 #include "nsIDocCharset.h"
 #include "nsIDocShell.h"
 #include "nsIDocShellLoadInfo.h"
 #include "nsIDocShellTreeItem.h"
 #include "nsIDOMStorageManager.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsILinkHandler.h"
@@ -122,17 +121,16 @@ enum eCharsetReloadState
 class nsDocShell final
   : public nsDocLoader
   , public nsIDocShell
   , public nsIWebNavigation
   , public nsIBaseWindow
   , public nsIScrollable
   , public nsITextScroll
   , public nsIDocCharset
-  , public nsIContentViewerContainer
   , public nsIRefreshURI
   , public nsIWebProgressListener
   , public nsIWebPageDescriptor
   , public nsIAuthPromptProvider
   , public nsILoadContext
   , public nsIWebShellServices
   , public nsILinkHandler
   , public nsIClipboardCommands
@@ -178,17 +176,16 @@ public:
   NS_DECL_NSIWEBNAVIGATION
   NS_DECL_NSIBASEWINDOW
   NS_DECL_NSISCROLLABLE
   NS_DECL_NSITEXTSCROLL
   NS_DECL_NSIDOCCHARSET
   NS_DECL_NSIINTERFACEREQUESTOR
   NS_DECL_NSIWEBPROGRESSLISTENER
   NS_DECL_NSIREFRESHURI
-  NS_DECL_NSICONTENTVIEWERCONTAINER
   NS_DECL_NSIWEBPAGEDESCRIPTOR
   NS_DECL_NSIAUTHPROMPTPROVIDER
   NS_DECL_NSICLIPBOARDCOMMANDS
   NS_DECL_NSIWEBSHELLSERVICES
   NS_DECL_NSINETWORKINTERCEPTCONTROLLER
   NS_DECL_NSIDEPRECATIONWARNER
 
   NS_FORWARD_SAFE_NSIDOMSTORAGEMANAGER(TopSessionStorageManager())
@@ -864,16 +861,18 @@ private: // member functions
   nsIDOMStorageManager* TopSessionStorageManager();
   nsIChannel* GetCurrentDocChannel();
   nsresult EnsureScriptEnvironment();
   nsresult EnsureEditorData();
   nsresult EnsureTransferableHookData();
   nsresult EnsureFind();
   nsresult EnsureCommandHandler();
   nsresult RefreshURIFromQueue();
+  nsresult Embed(nsIContentViewer* aContentViewer,
+                 const char* aCommand, nsISupports* aExtraInfo);
   nsresult GetEldestPresContext(nsPresContext** aPresContext);
   nsresult CheckLoadingPermissions();
   nsresult PersistLayoutHistoryState();
   nsresult LoadHistoryEntry(nsISHEntry* aEntry, uint32_t aLoadType);
   nsresult SetBaseUrlForWyciwyg(nsIContentViewer* aContentViewer);
   nsresult GetRootSessionHistory(nsISHistory** aReturn);
   nsresult GetHttpChannel(nsIChannel* aChannel, nsIHttpChannel** aReturn);
   nsresult ConfirmRepost(bool* aRepost);
deleted file mode 100644
--- a/docshell/base/nsIContentViewerContainer.idl
+++ /dev/null
@@ -1,20 +0,0 @@
-/* -*- Mode: IDL; tab-width: 4; 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 "nsISupports.idl"
-
-interface nsIContentViewer;
-
-[scriptable, uuid(ea2ce7a0-5c3d-11d4-90c2-0050041caf44)]
-interface nsIContentViewerContainer : nsISupports {
-	void embed(in nsIContentViewer aDocViewer, in string aCommand, in nsISupports aExtraInfo);
-
-  /**
-   * Allows nsPrintJob to make this call on an internal interface to the
-   * DocShell.
-   */
-  void setIsPrinting(in boolean aIsPrinting);
-};
--- a/docshell/base/nsIDocShell.idl
+++ b/docshell/base/nsIDocShell.idl
@@ -651,16 +651,22 @@ interface nsIDocShell : nsIDocShellTreeI
   /**
    * If true, this browser is not visible in the traditional sense, but
    * is actively being rendered to the screen (ex. painted on a canvas)
    * and should be treated accordingly.
    **/
   attribute boolean isOffScreenBrowser;
 
   /**
+   * Allows nsDocumentViewer to tell the top-level same-type docshell that
+   * one of the documents under it is printing.
+   */
+  void setIsPrinting(in boolean aIsPrinting);
+
+  /**
    * If the current content viewer isn't initialized for print preview,
    * it is replaced with one which is and to which an about:blank document
    * is loaded.
    */
   readonly attribute nsIWebBrowserPrint printPreview;
 
   /**
    * Whether this docshell can execute scripts based on its hierarchy.
--- a/dom/html/nsHTMLDocument.cpp
+++ b/dom/html/nsHTMLDocument.cpp
@@ -27,17 +27,16 @@
 #include "nsIDOMElement.h"
 #include "nsPIDOMWindow.h"
 #include "nsDOMString.h"
 #include "nsIStreamListener.h"
 #include "nsIURI.h"
 #include "nsIIOService.h"
 #include "nsNetUtil.h"
 #include "nsIPrivateBrowsingChannel.h"
-#include "nsIContentViewerContainer.h"
 #include "nsIContentViewer.h"
 #include "nsDocShell.h"
 #include "nsDocShellLoadTypes.h"
 #include "nsIWebNavigation.h"
 #include "nsIBaseWindow.h"
 #include "nsIWebShellServices.h"
 #include "nsIScriptContext.h"
 #include "nsIXPConnect.h"
--- a/dom/media/CubebUtils.cpp
+++ b/dom/media/CubebUtils.cpp
@@ -468,22 +468,16 @@ cubeb* GetCubebContextUnlocked()
     : cubeb_init(&sCubebContext, sBrandName, sCubebBackendName.get());
   sIPCConnection = nullptr;
 #else // !MOZ_CUBEB_REMOTING
   int rv = cubeb_init(&sCubebContext, sBrandName, sCubebBackendName.get());
 #endif // MOZ_CUBEB_REMOTING
   NS_WARNING_ASSERTION(rv == CUBEB_OK, "Could not get a cubeb context.");
   sCubebState = (rv == CUBEB_OK) ? CubebState::Initialized : CubebState::Uninitialized;
 
-  if (MOZ_LOG_TEST(gCubebLog, LogLevel::Verbose)) {
-    cubeb_set_log_callback(CUBEB_LOG_VERBOSE, CubebLogCallback);
-  } else if (MOZ_LOG_TEST(gCubebLog, LogLevel::Error)) {
-    cubeb_set_log_callback(CUBEB_LOG_NORMAL, CubebLogCallback);
-  }
-
   return sCubebContext;
 }
 
 void ReportCubebBackendUsed()
 {
   StaticMutexAutoLock lock(sMutex);
 
   sAudioStreamInitEverSucceeded = true;
@@ -545,20 +539,25 @@ Maybe<uint32_t> GetCubebMSGLatencyInFram
 
 void InitLibrary()
 {
   Preferences::RegisterCallbackAndCall(PrefChanged, PREF_VOLUME_SCALE);
   Preferences::RegisterCallbackAndCall(PrefChanged, PREF_CUBEB_LATENCY_PLAYBACK);
   Preferences::RegisterCallbackAndCall(PrefChanged, PREF_CUBEB_LATENCY_MSG);
   Preferences::RegisterCallbackAndCall(PrefChanged, PREF_CUBEB_BACKEND);
   Preferences::RegisterCallbackAndCall(PrefChanged, PREF_CUBEB_SANDBOX);
+  if (MOZ_LOG_TEST(gCubebLog, LogLevel::Verbose)) {
+    cubeb_set_log_callback(CUBEB_LOG_VERBOSE, CubebLogCallback);
+  } else if (MOZ_LOG_TEST(gCubebLog, LogLevel::Error)) {
+    cubeb_set_log_callback(CUBEB_LOG_NORMAL, CubebLogCallback);
+  }
   // We don't want to call the callback on startup, because the pref is the
   // empty string by default ("", which means "logging disabled"). Because the
   // logging can be enabled via environment variables (MOZ_LOG="module:5"),
-  // calling this callback on init would immediately re-disble the logging.
+  // calling this callback on init would immediately re-disable the logging.
   Preferences::RegisterCallback(PrefChanged, PREF_CUBEB_LOGGING_LEVEL);
 #ifndef MOZ_WIDGET_ANDROID
   AbstractThread::MainThread()->Dispatch(
     NS_NewRunnableFunction("CubebUtils::InitLibrary", &InitBrandName));
 #endif
 #ifdef MOZ_CUBEB_REMOTING
   if (sCubebSandbox && XRE_IsContentProcess()) {
     InitAudioIPCConnection();
--- a/dom/script/ScriptLoader.cpp
+++ b/dom/script/ScriptLoader.cpp
@@ -60,16 +60,18 @@
 #include "mozilla/Telemetry.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/Unused.h"
 #include "nsIScriptError.h"
 #include "nsIOutputStream.h"
 
 using JS::SourceBufferHolder;
 
+using mozilla::Telemetry::LABELS_DOM_SCRIPT_PRELOAD_RESULT;
+
 namespace mozilla {
 namespace dom {
 
 LazyLogModule ScriptLoader::gCspPRLog("CSP");
 LazyLogModule ScriptLoader::gScriptLoaderLog("ScriptLoader");
 
 #undef LOG
 #define LOG(args) \
@@ -175,31 +177,42 @@ ScriptLoader::~ScriptLoader()
     req->FireScriptAvailable(NS_ERROR_ABORT);
   }
 
   // Unblock the kids, in case any of them moved to a different document
   // subtree in the meantime and therefore aren't actually going away.
   for (uint32_t j = 0; j < mPendingChildLoaders.Length(); ++j) {
     mPendingChildLoaders[j]->RemoveParserBlockingScriptExecutionBlocker();
   }
+
+  for (size_t i = 0; i < mPreloads.Length(); i++) {
+    AccumulateCategorical(LABELS_DOM_SCRIPT_PRELOAD_RESULT::NotUsed);
+  }
 }
 
 // Collect telemtry data about the cache information, and the kind of source
 // which are being loaded, and where it is being loaded from.
 static void
 CollectScriptTelemetry(nsIIncrementalStreamLoader* aLoader,
                        ScriptLoadRequest* aRequest)
 {
   using namespace mozilla::Telemetry;
 
   // Skip this function if we are not running telemetry.
   if (!CanRecordExtended()) {
     return;
   }
 
+  // Report the script kind.
+  if (aRequest->IsModuleRequest()) {
+    AccumulateCategorical(LABELS_DOM_SCRIPT_KIND::ModuleScript);
+  } else {
+    AccumulateCategorical(LABELS_DOM_SCRIPT_KIND::ClassicScript);
+  }
+
   // Report the type of source, as well as the size of the source.
   if (aRequest->IsLoadingSource()) {
     if (aRequest->mIsInline) {
       AccumulateCategorical(LABELS_DOM_SCRIPT_LOADING_SOURCE::Inline);
       nsAutoString inlineData;
       aRequest->mElement->GetScriptText(inlineData);
       Accumulate(DOM_SCRIPT_INLINE_SIZE, inlineData.Length());
     } else {
@@ -1311,26 +1324,29 @@ ScriptLoader::ProcessExternalScript(nsIS
 
   RefPtr<ScriptLoadRequest> request = LookupPreloadRequest(aElement, aScriptKind);
 
   if (request && NS_FAILED(CheckContentPolicy(mDocument, aElement, request->mURI,
                                               aTypeAttr, false))) {
     // Probably plans have changed; even though the preload was allowed seems
     // like the actual load is not; let's cancel the preload request.
     request->Cancel();
+    AccumulateCategorical(LABELS_DOM_SCRIPT_PRELOAD_RESULT::RejectedByPolicy);
     return false;
   }
 
   if (request) {
     // Use the preload request.
 
     // It's possible these attributes changed since we started the preload so
     // update them here.
     request->SetScriptMode(aElement->GetScriptDeferred(),
                            aElement->GetScriptAsync());
+
+    AccumulateCategorical(LABELS_DOM_SCRIPT_PRELOAD_RESULT::Used);
   } else {
     // No usable preload found.
 
     SRIMetadata sriMetadata;
     {
       nsAutoString integrity;
       aScriptContent->AsElement()->GetAttr(kNameSpaceID_None,
                                           nsGkAtoms::integrity,
@@ -1576,16 +1592,17 @@ ScriptLoader::LookupPreloadRequest(nsISc
   // we have now.
   nsAutoString elementCharset;
   aElement->GetScriptCharset(elementCharset);
   if (!elementCharset.Equals(preloadCharset) ||
       aElement->GetCORSMode() != request->mCORSMode ||
       mDocument->GetReferrerPolicy() != request->mReferrerPolicy ||
       aScriptKind != request->mKind) {
     // Drop the preload.
+    AccumulateCategorical(LABELS_DOM_SCRIPT_PRELOAD_RESULT::RequestMismatch);
     return nullptr;
   }
 
   return request;
 }
 
 void
 ScriptLoader::GetSRIMetadata(const nsAString& aIntegrityAttr,
@@ -2952,16 +2969,17 @@ ScriptLoader::HandleLoadError(ScriptLoad
     nsCOMPtr<nsIScriptElement> oldParserInsertedScript =
       mCurrentParserInsertedScript;
     mCurrentParserInsertedScript = aRequest->mElement;
     FireScriptAvailable(aResult, aRequest);
     ContinueParserAsync(aRequest);
     mCurrentParserInsertedScript = oldParserInsertedScript;
   } else {
     mPreloads.RemoveElement(aRequest, PreloadRequestComparator());
+    AccumulateCategorical(LABELS_DOM_SCRIPT_PRELOAD_RESULT::LoadError);
   }
 }
 
 void
 ScriptLoader::UnblockParser(ScriptLoadRequest* aParserBlockingRequest)
 {
   aParserBlockingRequest->mElement->UnblockParser();
 }
--- a/dom/xml/XMLDocument.cpp
+++ b/dom/xml/XMLDocument.cpp
@@ -6,17 +6,16 @@
 
 
 #include "mozilla/dom/XMLDocument.h"
 #include "nsParserCIID.h"
 #include "nsCharsetSource.h"
 #include "nsIXMLContentSink.h"
 #include "nsPresContext.h"
 #include "nsIContent.h"
-#include "nsIContentViewerContainer.h"
 #include "nsIContentViewer.h"
 #include "nsIDocShell.h"
 #include "nsHTMLParts.h"
 #include "nsIComponentManager.h"
 #include "nsIDOMElement.h"
 #include "nsIBaseWindow.h"
 #include "nsIDOMWindow.h"
 #include "nsIDOMDocumentType.h"
--- a/gfx/layers/client/SingleTiledContentClient.cpp
+++ b/gfx/layers/client/SingleTiledContentClient.cpp
@@ -237,19 +237,19 @@ ClientSingleTiledLayerBuffer::PaintThebe
                   frontBufferOnWhite, dtOnWhite, rect, dest
                 };
                 if (asyncPaint) {
                   paintCopies.push_back(copy);
                 } else {
                   copy.CopyBuffer();
                 }
               }
+            } else {
+              gfxWarning() << "[Tiling:Client] Failed to aquire the discarded front buffer's draw target";
             }
-          } else {
-            gfxWarning() << "[Tiling:Client] Failed to aquire the discarded front buffer's draw target";
           }
         } else {
           gfxWarning() << "[Tiling:Client] Failed to aquire the discarded front buffer's draw target";
         }
 
         TILING_LOG("TILING %p: Region copied from discarded frontbuffer %s\n", &mPaintedLayer, Stringify(copyableRegion).c_str());
 
         // We don't need to repaint valid content that was just copied.
@@ -331,21 +331,15 @@ ClientSingleTiledLayerBuffer::PaintThebe
   // The new buffer is now validated, remove the dirty region from it.
   mTile.mInvalidBack.SubOut(tileDirtyRegion);
 
   dt = nullptr;
 
   mTile.Flip();
   UnlockTile(mTile);
 
-  if (backBuffer->HasIntermediateBuffer()) {
-    // If our new buffer has an internal buffer, we don't want to keep another
-    // TextureClient around unnecessarily, so discard the back-buffer.
-    mTile.DiscardBackBuffer();
-  }
-
   mValidRegion = aNewValidRegion;
   mLastPaintSurfaceMode = mode;
   mLastPaintContentType = content;
 }
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/thebes/gfxFont.cpp
+++ b/gfx/thebes/gfxFont.cpp
@@ -1990,27 +1990,37 @@ gfxFont::DrawMissingGlyph(const TextRunD
                           const gfxShapedText::DetailedGlyph* aDetails,
                           const gfx::Point&                   aPt)
 {
     // Default-ignorable chars will have zero advance width;
     // we don't have to draw the hexbox for them.
     float advance = aDetails->mAdvance;
     if (aRunParams.drawMode != DrawMode::GLYPH_PATH && advance > 0) {
         auto* textDrawer = aRunParams.context->GetTextDrawer();
+        const Matrix* matPtr = nullptr;
+        Matrix mat;
         if (textDrawer) {
-            textDrawer->FoundUnsupportedFeature();
-            return false;
+            // Generate an orientation matrix for the current writing mode
+            wr::FontInstanceFlags flags = textDrawer->GetWRGlyphFlags();
+            if (flags.bits & wr::FontInstanceFlags::TRANSPOSE) {
+                std::swap(mat._11, mat._12);
+                std::swap(mat._21, mat._22);
+            }
+            mat.PostScale(flags.bits & wr::FontInstanceFlags::FLIP_X ? -1.0f : 1.0f,
+                          flags.bits & wr::FontInstanceFlags::FLIP_Y ? -1.0f : 1.0f);
+            matPtr = &mat;
         }
 
         Point pt(Float(ToDeviceUnits(aPt.x, aRunParams.devPerApp)),
                  Float(ToDeviceUnits(aPt.y, aRunParams.devPerApp)));
         Float advanceDevUnits =
             Float(ToDeviceUnits(advance, aRunParams.devPerApp));
         Float height = GetMetrics(eHorizontal).maxAscent;
-        Rect glyphRect = aFontParams.isVerticalFont ?
+        // Horizontally center if drawing vertically upright with no sideways transform.
+        Rect glyphRect = aFontParams.isVerticalFont && !mat.HasNonAxisAlignedTransform() ?
             Rect(pt.x - height / 2, pt.y,
                  height, advanceDevUnits) :
             Rect(pt.x, pt.y - height,
                  advanceDevUnits, height);
 
         // If there's a fake-italic skew in effect as part
         // of the drawTarget's transform, we need to undo
         // this before drawing the hexbox. (Bug 983985)
@@ -2023,17 +2033,17 @@ gfxFont::DrawMissingGlyph(const TextRunD
                 PreMultiply(gfx::Matrix(1, 0, OBLIQUE_SKEW_FACTOR, 1, 0, 0)).
                 PreTranslate(-pt);
             aRunParams.context->SetMatrix(mat);
         }
 
         gfxFontMissingGlyphs::DrawMissingGlyph(
             aDetails->mGlyphID, glyphRect, *aRunParams.dt,
             PatternFromState(aRunParams.context),
-            1.0 / aRunParams.devPerApp);
+            1.0 / aRunParams.devPerApp, matPtr);
     }
     return true;
 }
 
 // This method is mostly parallel to DrawGlyphs.
 void
 gfxFont::DrawEmphasisMarks(const gfxTextRun* aShapedText, gfx::Point* aPt,
                            uint32_t aOffset, uint32_t aCount,
--- a/gfx/thebes/gfxFontMissingGlyphs.cpp
+++ b/gfx/thebes/gfxFontMissingGlyphs.cpp
@@ -153,23 +153,49 @@ static const Float BOX_BORDER_OPACITY = 
  * would be to fill in an A8 image surface and then use it as a mask
  * to paint the current color. Tragically this doesn't currently work with the
  * Quartz cairo backend which doesn't generally support masking with surfaces.
  * So for now we just paint a bunch of rectangles...
  */
 #ifndef MOZ_GFX_OPTIMIZE_MOBILE
 static void
 DrawHexChar(uint32_t aDigit, const Point& aPt, DrawTarget& aDrawTarget,
-            const Pattern &aPattern)
+            const Pattern &aPattern, const Matrix* aMat)
 {
+    uint32_t glyphBits = glyphMicroFont[aDigit];
+
+    if (aMat) {
+        // If using an orientation matrix instead of a DT transform, step
+        // with the matrix basis vectors, filling individual rectangles of
+        // the size indicated by the matrix.
+        Point stepX(aMat->_11, aMat->_12);
+        Point stepY(aMat->_21, aMat->_22);
+        Point corner = stepX + stepY;
+        // Get the rectangle at the origin that will be stepped into place.
+        Rect startRect(std::min(corner.x, 0.0f), std::min(corner.y, 0.0f),
+                       fabs(corner.x), fabs(corner.y));
+        startRect.MoveBy(aMat->TransformPoint(aPt));
+        for (int y = 0; y < MINIFONT_HEIGHT; ++y) {
+            Rect curRect = startRect;
+            for (int x = 0; x < MINIFONT_WIDTH; ++x) {
+                if (glyphBits & 1) {
+                    aDrawTarget.FillRect(curRect, aPattern);
+                }
+                glyphBits >>= 1;
+                curRect.MoveBy(stepX);
+            }
+            startRect.MoveBy(stepY);
+        }
+        return;
+    }
+
     // To avoid the potential for seams showing between rects when we're under
     // a transform we concat all the rects into a PathBuilder and fill the
     // resulting Path (rather than using DrawTarget::FillRect).
     RefPtr<PathBuilder> builder = aDrawTarget.CreatePathBuilder();
-    uint32_t glyphBits = glyphMicroFont[aDigit];
     for (int y = 0; y < MINIFONT_HEIGHT; ++y) {
         for (int x = 0; x < MINIFONT_WIDTH; ++x) {
             if (glyphBits & 1) {
                 Rect r(aPt.x + x, aPt.y + y, 1, 1);
                 MaybeSnapToDevicePixels(r, aDrawTarget, true);
                 builder->MoveTo(r.TopLeft());
                 builder->LineTo(r.TopRight());
                 builder->LineTo(r.BottomRight());
@@ -184,93 +210,121 @@ DrawHexChar(uint32_t aDigit, const Point
 }
 #endif // MOZ_GFX_OPTIMIZE_MOBILE
 
 void
 gfxFontMissingGlyphs::DrawMissingGlyph(uint32_t aChar,
                                        const Rect& aRect,
                                        DrawTarget& aDrawTarget,
                                        const Pattern& aPattern,
-                                       uint32_t aAppUnitsPerDevPixel)
+                                       uint32_t aAppUnitsPerDevPixel,
+                                       const Matrix* aMat)
 {
+    Rect rect(aRect);
+    // If there is an orientation transform, reorient the bounding rect.
+    if (aMat) {
+        rect.MoveBy(-aRect.BottomLeft());
+        rect = aMat->TransformBounds(rect);
+        rect.MoveBy(aRect.BottomLeft());
+    }
+
     // If we're currently drawing with some kind of pattern, we just draw the
     // missing-glyph data in black.
     ColorPattern color = aPattern.GetType() == PatternType::COLOR ?
         static_cast<const ColorPattern&>(aPattern) :
         ColorPattern(ToDeviceColor(Color(0.f, 0.f, 0.f, 1.f)));
 
     // Stroke a rectangle so that the stroke's left edge is inset one pixel
     // from the left edge of the glyph box and the stroke's right edge
     // is inset one pixel from the right edge of the glyph box.
     Float halfBorderWidth = BOX_BORDER_WIDTH / 2.0;
-    Float borderLeft = aRect.X() + BOX_HORIZONTAL_INSET + halfBorderWidth;
-    Float borderRight = aRect.XMost() - BOX_HORIZONTAL_INSET - halfBorderWidth;
-    Rect borderStrokeRect(borderLeft, aRect.Y() + halfBorderWidth,
+    Float borderLeft = rect.X() + BOX_HORIZONTAL_INSET + halfBorderWidth;
+    Float borderRight = rect.XMost() - BOX_HORIZONTAL_INSET - halfBorderWidth;
+    Rect borderStrokeRect(borderLeft, rect.Y() + halfBorderWidth,
                           borderRight - borderLeft,
-                          aRect.Height() - 2.0 * halfBorderWidth);
+                          rect.Height() - 2.0 * halfBorderWidth);
     if (!borderStrokeRect.IsEmpty()) {
         ColorPattern adjustedColor = color;
         color.mColor.a *= BOX_BORDER_OPACITY;
 #ifdef MOZ_GFX_OPTIMIZE_MOBILE
         aDrawTarget.FillRect(borderStrokeRect, adjustedColor);
 #else
         StrokeOptions strokeOptions(BOX_BORDER_WIDTH);
         aDrawTarget.StrokeRect(borderStrokeRect, adjustedColor, strokeOptions);
 #endif
     }
 
 #ifndef MOZ_GFX_OPTIMIZE_MOBILE
-    Point center = aRect.Center();
+    Point center = rect.Center();
     Float halfGap = HEX_CHAR_GAP / 2.f;
     Float top = -(MINIFONT_HEIGHT + halfGap);
     // We always want integer scaling, otherwise the "bitmap" glyphs will look
     // even uglier than usual when zoomed
     int32_t devPixelsPerCSSPx =
         std::max<int32_t>(1, nsDeviceContext::AppUnitsPerCSSPixel() /
                              aAppUnitsPerDevPixel);
-    AutoRestoreTransform autoRestoreTransform(&aDrawTarget);
-    aDrawTarget.SetTransform(
-      aDrawTarget.GetTransform().PreTranslate(center).
-                                 PreScale(devPixelsPerCSSPx,
-                                          devPixelsPerCSSPx));
+
+    Matrix tempMat;
+    if (aMat) {
+        // If there is an orientation transform, since draw target transforms may
+        // not be supported, scale and translate it so that it can be directly used
+        // for rendering the mini font without changing the draw target transform.
+        tempMat = Matrix(*aMat).PostScale(devPixelsPerCSSPx, devPixelsPerCSSPx)
+                               .PostTranslate(center);
+        aMat = &tempMat;
+    } else {
+        // Otherwise, scale and translate the draw target transform assuming it
+        // supports that.
+        tempMat = aDrawTarget.GetTransform();
+        aDrawTarget.SetTransform(
+            Matrix(tempMat).PreTranslate(center)
+                           .PreScale(devPixelsPerCSSPx, devPixelsPerCSSPx));
+    }
+
     if (aChar < 0x10000) {
-        if (aRect.Width() >= 2 * (MINIFONT_WIDTH + HEX_CHAR_GAP) &&
-            aRect.Height() >= 2 * MINIFONT_HEIGHT + HEX_CHAR_GAP) {
+        if (rect.Width() >= 2 * (MINIFONT_WIDTH + HEX_CHAR_GAP) &&
+            rect.Height() >= 2 * MINIFONT_HEIGHT + HEX_CHAR_GAP) {
             // Draw 4 digits for BMP
             Float left = -(MINIFONT_WIDTH + halfGap);
             DrawHexChar((aChar >> 12) & 0xF,
-                        Point(left, top), aDrawTarget, color);
+                        Point(left, top), aDrawTarget, color, aMat);
             DrawHexChar((aChar >> 8) & 0xF,
-                        Point(halfGap, top), aDrawTarget, color);
+                        Point(halfGap, top), aDrawTarget, color, aMat);
             DrawHexChar((aChar >> 4) & 0xF,
-                        Point(left, halfGap), aDrawTarget, color);
+                        Point(left, halfGap), aDrawTarget, color, aMat);
             DrawHexChar(aChar & 0xF,
-                        Point(halfGap, halfGap), aDrawTarget, color);
+                        Point(halfGap, halfGap), aDrawTarget, color, aMat);
         }
     } else {
-        if (aRect.Width() >= 3 * (MINIFONT_WIDTH + HEX_CHAR_GAP) &&
-            aRect.Height() >= 2 * MINIFONT_HEIGHT + HEX_CHAR_GAP) {
+        if (rect.Width() >= 3 * (MINIFONT_WIDTH + HEX_CHAR_GAP) &&
+            rect.Height() >= 2 * MINIFONT_HEIGHT + HEX_CHAR_GAP) {
             // Draw 6 digits for non-BMP
             Float first = -(MINIFONT_WIDTH * 1.5 + HEX_CHAR_GAP);
             Float second = -(MINIFONT_WIDTH / 2.0);
             Float third = (MINIFONT_WIDTH / 2.0 + HEX_CHAR_GAP);
             DrawHexChar((aChar >> 20) & 0xF,
-                        Point(first, top), aDrawTarget, color);
+                        Point(first, top), aDrawTarget, color, aMat);
             DrawHexChar((aChar >> 16) & 0xF,
-                        Point(second, top), aDrawTarget, color);
+                        Point(second, top), aDrawTarget, color, aMat);
             DrawHexChar((aChar >> 12) & 0xF,
-                        Point(third, top), aDrawTarget, color);
+                        Point(third, top), aDrawTarget, color, aMat);
             DrawHexChar((aChar >> 8) & 0xF,
-                        Point(first, halfGap), aDrawTarget, color);
+                        Point(first, halfGap), aDrawTarget, color, aMat);
             DrawHexChar((aChar >> 4) & 0xF,
-                        Point(second, halfGap), aDrawTarget, color);
+                        Point(second, halfGap), aDrawTarget, color, aMat);
             DrawHexChar(aChar & 0xF,
-                        Point(third, halfGap), aDrawTarget, color);
+                        Point(third, halfGap), aDrawTarget, color, aMat);
         }
     }
+
+    if (!aMat) {
+        // The draw target transform was changed, so it must be restored to
+        // the original value.
+        aDrawTarget.SetTransform(tempMat);
+    }
 #endif
 }
 
 Float
 gfxFontMissingGlyphs::GetDesiredMinWidth(uint32_t aChar,
                                          uint32_t aAppUnitsPerDevPixel)
 {
 /**
--- a/gfx/thebes/gfxFontMissingGlyphs.h
+++ b/gfx/thebes/gfxFontMissingGlyphs.h
@@ -33,22 +33,24 @@ public:
     /**
      * Draw hexboxes for a missing glyph.
      * @param aChar the UTF16 codepoint for the character
      * @param aRect the glyph-box for the glyph that is missing
      * @param aDrawTarget the DrawTarget to draw to
      * @param aPattern the pattern currently being used to paint
      * @param aAppUnitsPerDevPixel the appUnits to devPixel ratio we're using,
      *                             (so we can scale glyphs to a sensible size)
+     * @param aMat optional local-space orientation matrix
      */
     static void DrawMissingGlyph(uint32_t aChar,
                                  const Rect& aRect,
                                  DrawTarget& aDrawTarget,
                                  const Pattern& aPattern,
-                                 uint32_t aAppUnitsPerDevPixel);
+                                 uint32_t aAppUnitsPerDevPixel,
+                                 const Matrix* aMat = nullptr);
     /**
      * @return the desired minimum width for a glyph-box that will allow
      * the hexboxes to be drawn reasonably.
      */
     static Float GetDesiredMinWidth(uint32_t aChar,
                                     uint32_t aAppUnitsPerDevUnit);
 };
 
--- a/gfx/webrender_bindings/RenderCompositor.cpp
+++ b/gfx/webrender_bindings/RenderCompositor.cpp
@@ -2,26 +2,36 @@
 /* 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 "RenderCompositor.h"
 
 #include "GLContext.h"
+#include "mozilla/gfx/gfxVars.h"
 #include "mozilla/layers/SyncObject.h"
 #include "mozilla/webrender/RenderCompositorOGL.h"
 #include "mozilla/widget/CompositorWidget.h"
 
+#ifdef XP_WIN
+#include "mozilla/webrender/RenderCompositorANGLE.h"
+#endif
+
 namespace mozilla {
 namespace wr {
 
 /* static */ UniquePtr<RenderCompositor>
 RenderCompositor::Create(RefPtr<widget::CompositorWidget>&& aWidget)
 {
+#ifdef XP_WIN
+  if (gfx::gfxVars::UseWebRenderANGLE()) {
+    return RenderCompositorANGLE::Create(Move(aWidget));
+  }
+#endif
   return RenderCompositorOGL::Create(Move(aWidget));
 }
 
 RenderCompositor::RenderCompositor(RefPtr<widget::CompositorWidget>&& aWidget)
   : mWidget(aWidget)
 {
 }
 
new file mode 100644
--- /dev/null
+++ b/gfx/webrender_bindings/RenderCompositorANGLE.cpp
@@ -0,0 +1,162 @@
+/* -*- 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 "RenderCompositorANGLE.h"
+
+#include "GLContext.h"
+#include "GLContextEGL.h"
+#include "GLContextProvider.h"
+#include "mozilla/gfx/DeviceManagerDx.h"
+#include "mozilla/layers/HelpersD3D11.h"
+#include "mozilla/layers/SyncObject.h"
+#include "mozilla/widget/CompositorWidget.h"
+
+#include <d3d11.h>
+
+namespace mozilla {
+namespace wr {
+
+/* static */ UniquePtr<RenderCompositor>
+RenderCompositorANGLE::Create(RefPtr<widget::CompositorWidget>&& aWidget)
+{
+  UniquePtr<RenderCompositorANGLE> compositor = MakeUnique<RenderCompositorANGLE>(Move(aWidget));
+  if (!compositor->Initialize()) {
+    return nullptr;
+  }
+  return compositor;
+}
+
+RenderCompositorANGLE::RenderCompositorANGLE(RefPtr<widget::CompositorWidget>&& aWidget)
+  : RenderCompositor(Move(aWidget))
+{
+}
+
+RenderCompositorANGLE::~RenderCompositorANGLE()
+{
+}
+
+bool
+RenderCompositorANGLE::Initialize()
+{
+  mDevice = gfx::DeviceManagerDx::Get()->GetCompositorDevice();
+  if (!mDevice) {
+    gfxCriticalNote << "[D3D11] failed to get compositor device.";
+    return false;
+  }
+
+  mDevice->GetImmediateContext(getter_AddRefs(mCtx));
+  if (!mCtx) {
+    gfxCriticalNote << "[D3D11] failed to get immediate context.";
+    return false;
+  }
+
+  mSyncObject = layers::SyncObjectHost::CreateSyncObjectHost(mDevice);
+  if (!mSyncObject->Init()) {
+    // Some errors occur. Clear the mSyncObject here.
+    // Then, there will be no texture synchronization.
+    return false;
+  }
+
+  mGL = gl::GLContextProviderEGL::CreateForCompositorWidget(mWidget, true);
+  if (!mGL || !mGL->IsANGLE()) {
+    gfxCriticalNote << "Failed ANGLE GL context creation for WebRender: " << gfx::hexa(mGL.get());
+    return false;
+  }
+  if (!mGL->MakeCurrent()) {
+    gfxCriticalNote << "Failed GL context creation for WebRender: " << gfx::hexa(mGL.get());
+    return false;
+  }
+
+  return true;
+}
+
+bool
+RenderCompositorANGLE::Destroy()
+{
+  return true;
+}
+
+bool
+RenderCompositorANGLE::BeginFrame()
+{
+  if (!mGL->MakeCurrent()) {
+    gfxCriticalNote << "Failed to make render context current, can't draw.";
+    return false;
+  }
+
+  if (mSyncObject) {
+    // XXX: if the synchronization is failed, we should handle the device reset.
+    mSyncObject->Synchronize();
+  }
+  return true;
+}
+
+void
+RenderCompositorANGLE::EndFrame()
+{
+  InsertPresentWaitQuery();
+
+  mGL->SwapBuffers();
+
+  // Note: this waits on the query we inserted in the previous frame,
+  // not the one we just inserted now. Example:
+  //   Insert query #1
+  //   Present #1
+  //   (first frame, no wait)
+  //   Insert query #2
+  //   Present #2
+  //   Wait for query #1.
+  //   Insert query #3
+  //   Present #3
+  //   Wait for query #2.
+  //
+  // This ensures we're done reading textures before swapping buffers.
+  WaitForPreviousPresentQuery();
+}
+
+void
+RenderCompositorANGLE::Pause()
+{
+}
+
+bool
+RenderCompositorANGLE::Resume()
+{
+  return true;
+}
+
+LayoutDeviceIntSize
+RenderCompositorANGLE::GetClientSize()
+{
+  return mWidget->GetClientSize();
+}
+
+void
+RenderCompositorANGLE::InsertPresentWaitQuery()
+{
+  CD3D11_QUERY_DESC desc(D3D11_QUERY_EVENT);
+  HRESULT hr = mDevice->CreateQuery(&desc, getter_AddRefs(mNextWaitForPresentQuery));
+  if (FAILED(hr) || !mNextWaitForPresentQuery) {
+    gfxWarning() << "Could not create D3D11_QUERY_EVENT: " << hexa(hr);
+    return;
+  }
+
+  mCtx->End(mNextWaitForPresentQuery);
+}
+
+void
+RenderCompositorANGLE::WaitForPreviousPresentQuery()
+{
+  if (mWaitForPresentQuery) {
+    BOOL result;
+    layers::WaitForGPUQuery(mDevice, mCtx, mWaitForPresentQuery, &result);
+  }
+  mWaitForPresentQuery = mNextWaitForPresentQuery.forget();
+}
+
+
+} // namespace wr
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/webrender_bindings/RenderCompositorANGLE.h
@@ -0,0 +1,58 @@
+/* -*- 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_GFX_RENDERCOMPOSITOR_ANGLE_H
+#define MOZILLA_GFX_RENDERCOMPOSITOR_ANGLE_H
+
+#include "mozilla/webrender/RenderCompositor.h"
+
+struct ID3D11DeviceContext;
+struct ID3D11Device;
+struct ID3D11Query;
+
+namespace mozilla {
+
+namespace wr {
+
+class RenderCompositorANGLE : public RenderCompositor
+{
+public:
+  static UniquePtr<RenderCompositor> Create(RefPtr<widget::CompositorWidget>&& aWidget);
+
+  explicit RenderCompositorANGLE(RefPtr<widget::CompositorWidget>&& aWidget);
+  virtual ~RenderCompositorANGLE();
+  bool Initialize();
+
+  bool Destroy() override;
+  bool BeginFrame() override;
+  void EndFrame() override;
+  void Pause() override;
+  bool Resume() override;
+
+  gl::GLContext* gl() const override { return mGL; }
+
+  bool UseANGLE() const override { return true; }
+
+  LayoutDeviceIntSize GetClientSize() override;
+
+protected:
+  void InsertPresentWaitQuery();
+  void WaitForPreviousPresentQuery();
+
+  RefPtr<gl::GLContext> mGL;
+
+  RefPtr<ID3D11Device> mDevice;
+  RefPtr<ID3D11DeviceContext> mCtx;
+
+  RefPtr<ID3D11Query> mWaitForPresentQuery;
+  RefPtr<ID3D11Query> mNextWaitForPresentQuery;
+
+};
+
+} // namespace wr
+} // namespace mozilla
+
+#endif
--- a/gfx/webrender_bindings/RenderCompositorOGL.cpp
+++ b/gfx/webrender_bindings/RenderCompositorOGL.cpp
@@ -3,77 +3,39 @@
 /* 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 "RenderCompositorOGL.h"
 
 #include "GLContext.h"
 #include "GLContextProvider.h"
-#include "mozilla/gfx/gfxVars.h"
-#include "mozilla/layers/SyncObject.h"
 #include "mozilla/widget/CompositorWidget.h"
 
-#ifdef XP_WIN
-#include "GLContextEGL.h"
-#endif
-
 namespace mozilla {
 namespace wr {
 
 /* static */ UniquePtr<RenderCompositor>
 RenderCompositorOGL::Create(RefPtr<widget::CompositorWidget>&& aWidget)
 {
   RefPtr<gl::GLContext> gl;
-  if (gfx::gfxVars::UseWebRenderANGLE()) {
-    gl = gl::GLContextProviderEGL::CreateForCompositorWidget(aWidget, true);
-    if (!gl || !gl->IsANGLE()) {
-      gfxCriticalNote << "Failed ANGLE GL context creation for WebRender: " << gfx::hexa(gl.get());
-      return nullptr;
-    }
-  }
-  if (!gl) {
-    gl = gl::GLContextProvider::CreateForCompositorWidget(aWidget, true);
-  }
+  gl = gl::GLContextProvider::CreateForCompositorWidget(aWidget, true);
   if (!gl || !gl->MakeCurrent()) {
     gfxCriticalNote << "Failed GL context creation for WebRender: " << gfx::hexa(gl.get());
     return nullptr;
   }
   return MakeUnique<RenderCompositorOGL>(Move(gl), Move(aWidget));
 }
 
 RenderCompositorOGL::RenderCompositorOGL(RefPtr<gl::GLContext>&& aGL,
-                                             RefPtr<widget::CompositorWidget>&& aWidget)
+                                         RefPtr<widget::CompositorWidget>&& aWidget)
   : RenderCompositor(Move(aWidget))
   , mGL(aGL)
 {
   MOZ_ASSERT(mGL);
-
-#ifdef XP_WIN
-  if (mGL->IsANGLE()) {
-    gl::GLLibraryEGL* egl = &gl::sEGLLibrary;
-
-    // Fetch the D3D11 device.
-    EGLDeviceEXT eglDevice = nullptr;
-    egl->fQueryDisplayAttribEXT(egl->Display(), LOCAL_EGL_DEVICE_EXT, (EGLAttrib*)&eglDevice);
-    MOZ_ASSERT(eglDevice);
-    ID3D11Device* device = nullptr;
-    egl->fQueryDeviceAttribEXT(eglDevice, LOCAL_EGL_D3D11_DEVICE_ANGLE, (EGLAttrib*)&device);
-    MOZ_ASSERT(device);
-
-    mSyncObject = layers::SyncObjectHost::CreateSyncObjectHost(device);
-    if (mSyncObject) {
-      if (!mSyncObject->Init()) {
-        // Some errors occur. Clear the mSyncObject here.
-        // Then, there will be no texture synchronization.
-        mSyncObject = nullptr;
-      }
-    }
-  }
-#endif
 }
 
 RenderCompositorOGL::~RenderCompositorOGL()
 {
 }
 
 bool
 RenderCompositorOGL::Destroy()
@@ -83,21 +45,16 @@ RenderCompositorOGL::Destroy()
 
 bool
 RenderCompositorOGL::BeginFrame()
 {
   if (!mGL->MakeCurrent()) {
     gfxCriticalNote << "Failed to make render context current, can't draw.";
     return false;
   }
-
-  if (mSyncObject) {
-    // XXX: if the synchronization is failed, we should handle the device reset.
-    mSyncObject->Synchronize();
-  }
   return true;
 }
 
 void
 RenderCompositorOGL::EndFrame()
 {
   mGL->SwapBuffers();
 }
@@ -123,22 +80,16 @@ RenderCompositorOGL::Resume()
   }
   // RenewSurface internally calls MakeCurrent.
   return mGL->RenewSurface(mWidget);
 #else
   return true;
 #endif
 }
 
-bool
-RenderCompositorOGL::UseANGLE() const
-{
-  return mGL->IsANGLE();
-}
-
 LayoutDeviceIntSize
 RenderCompositorOGL::GetClientSize()
 {
   return mWidget->GetClientSize();
 }
 
 
 } // namespace wr
--- a/gfx/webrender_bindings/RenderCompositorOGL.h
+++ b/gfx/webrender_bindings/RenderCompositorOGL.h
@@ -5,49 +5,37 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MOZILLA_GFX_RENDERCOMPOSITOR_OGL_H
 #define MOZILLA_GFX_RENDERCOMPOSITOR_OGL_H
 
 #include "mozilla/webrender/RenderCompositor.h"
 
 namespace mozilla {
-/*
-namespace gl {
-class GLContext;
-}
 
-namespace layers {
-class SyncObjectHost;
-}
-
-namespace widget {
-class CompositorWidget;
-}
-*/
 namespace wr {
 
 class RenderCompositorOGL : public RenderCompositor
 {
 public:
   static UniquePtr<RenderCompositor> Create(RefPtr<widget::CompositorWidget>&& aWidget);
 
   RenderCompositorOGL(RefPtr<gl::GLContext>&& aGL,
-                        RefPtr<widget::CompositorWidget>&& aWidget);
+                      RefPtr<widget::CompositorWidget>&& aWidget);
   virtual ~RenderCompositorOGL();
 
   bool Destroy() override;
   bool BeginFrame() override;
   void EndFrame() override;
   void Pause() override;
   bool Resume() override;
 
   gl::GLContext* gl() const override { return mGL; }
 
-  bool UseANGLE() const override;
+  bool UseANGLE() const override { return false; }
 
   LayoutDeviceIntSize GetClientSize() override;
 
 protected:
   RefPtr<gl::GLContext> mGL;
 };
 
 } // namespace wr
--- a/gfx/webrender_bindings/moz.build
+++ b/gfx/webrender_bindings/moz.build
@@ -42,19 +42,21 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'coco
     ]
     UNIFIED_SOURCES += [
         'RenderMacIOSurfaceTextureHostOGL.cpp',
     ]
 
 if CONFIG['MOZ_ENABLE_D3D10_LAYER']:
     DEFINES['MOZ_ENABLE_D3D10_LAYER'] = True
     EXPORTS.mozilla.webrender += [
+        'RenderCompositorANGLE.h',
         'RenderD3D11TextureHostOGL.h',
     ]
     UNIFIED_SOURCES += [
+        'RenderCompositorANGLE.cpp',
         'RenderD3D11TextureHostOGL.cpp',
     ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('android', 'gtk3'):
     CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS']
     CXXFLAGS += CONFIG['CAIRO_FT_CFLAGS']
 
 include('/ipc/chromium/chromium-config.mozbuild')
--- a/js/src/builtin/Array.js
+++ b/js/src/builtin/Array.js
@@ -1107,16 +1107,115 @@ function ArrayConcat(arg1) {
 
     // Step 6.
     A.length = n;
 
     // Step 7.
     return A;
 }
 
+// https://tc39.github.io/proposal-flatMap/
+// January 16, 2018
+function ArrayFlatMap(mapperFunction/*, thisArg*/) {
+    // Step 1.
+    var O = ToObject(this);
+
+    // Step 2.
+    var sourceLen = ToLength(O.length);
+
+    // Step 3.
+    if (!IsCallable(mapperFunction))
+        ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, mapperFunction));
+
+    // Step 4.
+    var T = arguments.length > 1 ? arguments[1] : undefined;
+
+    // Step 5.
+    var A = ArraySpeciesCreate(O, 0);
+
+    // Step 6.
+    FlattenIntoArray(A, O, sourceLen, 0, 1, mapperFunction, T);
+
+    // Step 7.
+    return A;
+}
+
+// https://tc39.github.io/proposal-flatMap/
+// January 16, 2018
+function ArrayFlatten(/* depth */) {
+     // Step 1.
+    var O = ToObject(this);
+
+    // Step 2.
+    var sourceLen = ToLength(O.length);
+
+    // Step 3.
+    var depthNum = 1;
+
+    // Step 4.
+    if (arguments.length > 0 && arguments[0] !== undefined)
+        depthNum = ToInteger(arguments[0]);
+
+    // Step 5.
+    var A = ArraySpeciesCreate(O, 0);
+
+    // Step 6.
+    FlattenIntoArray(A, O, sourceLen, 0, depthNum);
+
+    // Step 7.
+    return A;
+}
+
+function FlattenIntoArray(target, source, sourceLen, start, depth, mapperFunction, thisArg) {
+    // Step 1.
+    var targetIndex = start;
+
+    // Steps 2-3.
+    for (var sourceIndex = 0; sourceIndex < sourceLen; sourceIndex++) {
+        // Steps 3.a-c.
+        if (sourceIndex in source) {
+            // Step 3.c.i.
+            var element = source[sourceIndex];
+
+            if (mapperFunction) {
+                // Step 3.c.ii.1.
+                assert(arguments.length === 7, "thisArg is present");
+
+                // Step 3.c.ii.2.
+                element = callContentFunction(mapperFunction, thisArg, element, sourceIndex, source);
+            }
+
+            // Step 3.c.iii.
+            var flattenable = IsArray(element);
+
+            // Step 3.c.iv.
+            if (flattenable && depth > 0) {
+                // Step 3.c.iv.1.
+                var elementLen = ToLength(element.length);
+
+                // Step 3.c.iv.2.
+                targetIndex = FlattenIntoArray(target, element, elementLen, targetIndex, depth - 1);
+            } else {
+                // Step 3.c.v.1.
+                if (targetIndex >= MAX_NUMERIC_INDEX)
+                    ThrowTypeError(JSMSG_TOO_LONG_ARRAY);
+
+                // Step 3.c.v.2.
+                _DefineDataProperty(target, targetIndex, element);
+
+                // Step 3.c.v.3.
+                targetIndex++;
+            }
+        }
+    }
+
+    // Step 4.
+    return targetIndex;
+}
+
 function ArrayStaticConcat(arr, arg1) {
     if (arguments.length < 1)
         ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, "Array.concat");
     var args = callFunction(std_Array_slice, arguments, 1);
     return callFunction(std_Function_apply, ArrayConcat, arr, args);
 }
 
 function ArrayStaticJoin(arr, separator) {
--- a/js/src/builtin/Utilities.js
+++ b/js/src/builtin/Utilities.js
@@ -35,18 +35,18 @@
     } while (false)
 #define dbg(msg) \
     do { \
         DumpMessage(callFunction(std_Array_pop, \
                                  StringSplitString(__FILE__, '/')) + \
                     '#' + __LINE__ + ': ' + msg) \
     } while (false)
 #else
-#define assert(b, info) do {} while (false) // Elided assertion.
-#define dbg(msg) do {} while (false) // Elided debugging output.
+#define assert(b, info) ; // Elided assertion.
+#define dbg(msg) ; // Elided debugging output.
 #endif
 
 // All C++-implemented standard builtins library functions used in self-hosted
 // code are installed via the std_functions JSFunctionSpec[] in
 // SelfHosting.cpp.
 //
 // Do not create an alias to a self-hosted builtin, otherwise it will be cloned
 // twice.
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -8530,16 +8530,41 @@ CodeGenerator::visitBoundsCheckRange(LBo
 void
 CodeGenerator::visitBoundsCheckLower(LBoundsCheckLower* lir)
 {
     int32_t min = lir->mir()->minimum();
     bailoutCmp32(Assembler::LessThan, ToRegister(lir->index()), Imm32(min),
                  lir->snapshot());
 }
 
+void
+CodeGenerator::visitSpectreMaskIndex(LSpectreMaskIndex* lir)
+{
+    MOZ_ASSERT(JitOptions.spectreIndexMasking);
+
+    const LAllocation* index = lir->index();
+    const LAllocation* length = lir->length();
+    Register output = ToRegister(lir->output());
+
+    if (index->isConstant()) {
+        int32_t idx = ToInt32(index);
+        if (length->isRegister())
+            masm.spectreMaskIndex(idx, ToRegister(length), output);
+        else
+            masm.spectreMaskIndex(idx, ToAddress(length), output);
+        return;
+    }
+
+    Register indexReg = ToRegister(index);
+    if (length->isRegister())
+        masm.spectreMaskIndex(indexReg, ToRegister(length), output);
+    else
+        masm.spectreMaskIndex(indexReg, ToAddress(length), output);
+}
+
 class OutOfLineStoreElementHole : public OutOfLineCodeBase<CodeGenerator>
 {
     LInstruction* ins_;
     Label rejoinStore_;
     bool strict_;
 
   public:
     explicit OutOfLineStoreElementHole(LInstruction* ins, bool strict)
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -246,16 +246,17 @@ class CodeGenerator final : public CodeG
     void visitSubstr(LSubstr* lir) override;
     void visitInitializedLength(LInitializedLength* lir) override;
     void visitSetInitializedLength(LSetInitializedLength* lir) override;
     void visitNotO(LNotO* ins) override;
     void visitNotV(LNotV* ins) override;
     void visitBoundsCheck(LBoundsCheck* lir) override;
     void visitBoundsCheckRange(LBoundsCheckRange* lir) override;
     void visitBoundsCheckLower(LBoundsCheckLower* lir) override;
+    void visitSpectreMaskIndex(LSpectreMaskIndex* lir) override;
     void visitLoadFixedSlotV(LLoadFixedSlotV* ins) override;
     void visitLoadFixedSlotAndUnbox(LLoadFixedSlotAndUnbox* lir) override;
     void visitLoadFixedSlotT(LLoadFixedSlotT* ins) override;
     void visitStoreFixedSlotV(LStoreFixedSlotV* ins) override;
     void visitStoreFixedSlotT(LStoreFixedSlotT* ins) override;
     void emitGetPropertyPolymorphic(LInstruction* lir, Register obj,
                                     Register scratch, const TypedOrValueRegister& output);
     void visitGetPropertyPolymorphicV(LGetPropertyPolymorphicV* ins) override;
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -7830,17 +7830,18 @@ IonBuilder::getElemTryTypedObject(bool* 
     MOZ_CRASH("Bad kind");
 }
 
 bool
 IonBuilder::checkTypedObjectIndexInBounds(uint32_t elemSize,
                                           MDefinition* obj,
                                           MDefinition* index,
                                           TypedObjectPrediction objPrediction,
-                                          LinearSum* indexAsByteOffset)
+                                          LinearSum* indexAsByteOffset,
+                                          BoundsCheckKind kind)
 {
     // Ensure index is an integer.
     MInstruction* idInt32 = MToInt32::New(alloc(), index);
     current->add(idInt32);
 
     // If we know the length statically from the type, just embed it.
     // Otherwise, load it from the appropriate reserved slot on the
     // typed object.  We know it's an int32, so we can convert from
@@ -7857,17 +7858,17 @@ IonBuilder::checkTypedObjectIndexInBound
             trackOptimizationOutcome(TrackedOutcome::TypedObjectHasDetachedBuffer);
             return false;
         }
     } else {
         trackOptimizationOutcome(TrackedOutcome::TypedObjectArrayRange);
         return false;
     }
 
-    index = addBoundsCheck(idInt32, length);
+    index = addBoundsCheck(idInt32, length, kind);
 
     return indexAsByteOffset->add(index, AssertedCast<int32_t>(elemSize));
 }
 
 AbortReasonOr<Ok>
 IonBuilder::getElemTryScalarElemOfTypedObject(bool* emitted,
                                               MDefinition* obj,
                                               MDefinition* index,
@@ -7877,18 +7878,21 @@ IonBuilder::getElemTryScalarElemOfTypedO
 {
     MOZ_ASSERT(objPrediction.ofArrayKind());
 
     // Must always be loading the same scalar type
     ScalarTypeDescr::Type elemType = elemPrediction.scalarType();
     MOZ_ASSERT(elemSize == ScalarTypeDescr::alignment(elemType));
 
     LinearSum indexAsByteOffset(alloc());
-    if (!checkTypedObjectIndexInBounds(elemSize, obj, index, objPrediction, &indexAsByteOffset))
-        return Ok();
+    if (!checkTypedObjectIndexInBounds(elemSize, obj, index, objPrediction, &indexAsByteOffset,
+                                       BoundsCheckKind::IsLoad))
+    {
+        return Ok();
+    }
 
     trackOptimizationSuccess();
     *emitted = true;
 
     return pushScalarLoadFromTypedObject(obj, indexAsByteOffset, elemType);
 }
 
 AbortReasonOr<Ok>
@@ -7899,18 +7903,21 @@ IonBuilder::getElemTryReferenceElemOfTyp
                                                  TypedObjectPrediction elemPrediction)
 {
     MOZ_ASSERT(objPrediction.ofArrayKind());
 
     ReferenceTypeDescr::Type elemType = elemPrediction.referenceType();
     uint32_t elemSize = ReferenceTypeDescr::size(elemType);
 
     LinearSum indexAsByteOffset(alloc());
-    if (!checkTypedObjectIndexInBounds(elemSize, obj, index, objPrediction, &indexAsByteOffset))
-        return Ok();
+    if (!checkTypedObjectIndexInBounds(elemSize, obj, index, objPrediction, &indexAsByteOffset,
+                                       BoundsCheckKind::IsLoad))
+    {
+        return Ok();
+    }
 
     trackOptimizationSuccess();
     *emitted = true;
 
     return pushReferenceLoadFromTypedObject(obj, indexAsByteOffset, elemType, nullptr);
 }
 
 AbortReasonOr<Ok>
@@ -8020,18 +8027,21 @@ IonBuilder::getElemTryComplexElemOfTyped
                                                uint32_t elemSize)
 {
     MOZ_ASSERT(objPrediction.ofArrayKind());
 
     MDefinition* type = loadTypedObjectType(obj);
     MDefinition* elemTypeObj = typeObjectForElementFromArrayStructType(type);
 
     LinearSum indexAsByteOffset(alloc());
-    if (!checkTypedObjectIndexInBounds(elemSize, obj, index, objPrediction, &indexAsByteOffset))
-        return Ok();
+    if (!checkTypedObjectIndexInBounds(elemSize, obj, index, objPrediction, &indexAsByteOffset,
+                                       BoundsCheckKind::IsLoad))
+    {
+        return Ok();
+    }
 
     return pushDerivedTypedObject(emitted, obj, indexAsByteOffset,
                                   elemPrediction, elemTypeObj);
 }
 
 AbortReasonOr<Ok>
 IonBuilder::pushDerivedTypedObject(bool* emitted,
                                    MDefinition* obj,
@@ -8312,17 +8322,17 @@ IonBuilder::getElemTryString(bool* emitt
     // Emit fast path for string[index].
     MInstruction* idInt32 = MToInt32::New(alloc(), index);
     current->add(idInt32);
     index = idInt32;
 
     MStringLength* length = MStringLength::New(alloc(), obj);
     current->add(length);
 
-    index = addBoundsCheck(index, length);
+    index = addBoundsCheck(index, length, BoundsCheckKind::IsLoad);
 
     MCharCodeAt* charCode = MCharCodeAt::New(alloc(), obj, index);
     current->add(charCode);
 
     MFromCharCode* result = MFromCharCode::New(alloc(), charCode);
     current->add(result);
     current->push(result);
 
@@ -8354,17 +8364,17 @@ IonBuilder::getElemTryArguments(bool* em
     current->add(length);
 
     // Ensure index is an integer.
     MInstruction* idInt32 = MToInt32::New(alloc(), index);
     current->add(idInt32);
     index = idInt32;
 
     // Bailouts if we read more than the number of actual arguments.
-    index = addBoundsCheck(index, length);
+    index = addBoundsCheck(index, length, BoundsCheckKind::IsLoad);
 
     // Load the argument from the actual arguments.
     bool modifiesArgs = script()->baselineScript()->modifiesArguments();
     MGetFrameArgument* load = MGetFrameArgument::New(alloc(), index, modifiesArgs);
     current->add(load);
     current->push(load);
 
     TemporaryTypeSet* types = bytecodeTypes(pc);
@@ -8442,17 +8452,18 @@ IonBuilder::getElemTryArgumentsInlinedIn
     MInstruction* idInt32 = MToInt32::New(alloc(), index);
     current->add(idInt32);
     index = idInt32;
 
     // Bailout if we read more than the number of actual arguments. This bailout
     // cannot re-enter because reading out of bounds arguments will disable the
     // lazy arguments optimization for this script, when this code would be
     // executed in Baseline. (see GetElemOptimizedArguments)
-    index = addBoundsCheck(index, constantInt(inlineCallInfo_->argc()));
+    index = addBoundsCheck(index, constantInt(inlineCallInfo_->argc()),
+                           BoundsCheckKind::IsLoad);
 
     // Get an instruction to represent the state of the argument vector.
     MInstruction* args = MArgumentState::New(alloc().fallible(), inlineCallInfo_->argv());
     if (!args)
         return abort(AbortReason::Alloc);
     current->add(args);
 
     // Select a value to pick from a vector.
@@ -8641,17 +8652,17 @@ IonBuilder::jsop_getelem_dense(MDefiniti
 
     MInstruction* load;
 
     if (!readOutOfBounds) {
         // This load should not return undefined, so likely we're reading
         // in-bounds elements, and the array is packed or its holes are not
         // read. This is the best case: we can separate the bounds check for
         // hoisting.
-        index = addBoundsCheck(index, initLength);
+        index = addBoundsCheck(index, initLength, BoundsCheckKind::IsLoad);
 
         load = MLoadElement::New(alloc(), elements, index, needsHoleCheck, loadDouble);
         current->add(load);
     } else {
         // This load may return undefined, so assume that we *can* read holes,
         // or that we can read out-of-bounds accesses. In this case, the bounds
         // check is part of the opcode.
         load = MLoadElementHole::New(alloc(), elements, index, initLength, needsHoleCheck);
@@ -8680,17 +8691,18 @@ IonBuilder::addArrayBufferByteLength(MDe
     ins->setResultType(MIRType::Int32);
     return ins;
 }
 
 void
 IonBuilder::addTypedArrayLengthAndData(MDefinition* obj,
                                        BoundsChecking checking,
                                        MDefinition** index,
-                                       MInstruction** length, MInstruction** elements)
+                                       MInstruction** length, MInstruction** elements,
+                                       BoundsCheckKind boundsCheckKind)
 {
     MOZ_ASSERT((index != nullptr) == (elements != nullptr));
 
     JSObject* tarr = nullptr;
 
     if (MConstant* objConst = obj->maybeConstantValue()) {
         if (objConst->type() == MIRType::Object)
             tarr = &objConst->toObject();
@@ -8714,32 +8726,32 @@ IonBuilder::addTypedArrayLengthAndData(M
                 obj->setImplicitlyUsedUnchecked();
 
                 int32_t len = AssertedCast<int32_t>(tarr->as<TypedArrayObject>().length());
                 *length = MConstant::New(alloc(), Int32Value(len));
                 current->add(*length);
 
                 if (index) {
                     if (checking == DoBoundsCheck)
-                        *index = addBoundsCheck(*index, *length);
+                        *index = addBoundsCheck(*index, *length, boundsCheckKind);
 
                     *elements = MConstantElements::New(alloc(), data);
                     current->add(*elements);
                 }
                 return;
             }
         }
     }
 
     *length = MTypedArrayLength::New(alloc(), obj);
     current->add(*length);
 
     if (index) {
         if (checking == DoBoundsCheck)
-            *index = addBoundsCheck(*index, *length);
+            *index = addBoundsCheck(*index, *length, boundsCheckKind);
 
         *elements = MTypedArrayElements::New(alloc(), obj);
         current->add(*elements);
     }
 }
 
 MDefinition*
 IonBuilder::convertShiftToMaskForStaticTypedArray(MDefinition* id,
@@ -8811,17 +8823,18 @@ IonBuilder::jsop_getelem_typed(MDefiniti
         // the array type to determine the result type, even if the opcode has
         // never executed. The known pushed type is only used to distinguish
         // uint32 reads that may produce either doubles or integers.
         MIRType knownType = MIRTypeForTypedArrayRead(arrayType, allowDouble);
 
         // Get length, bounds-check, then get elements, and add all instructions.
         MInstruction* length;
         MInstruction* elements;
-        addTypedArrayLengthAndData(obj, DoBoundsCheck, &index, &length, &elements);
+        addTypedArrayLengthAndData(obj, DoBoundsCheck, &index, &length, &elements,
+                                   BoundsCheckKind::IsLoad);
 
         // Load the element.
         MLoadUnboxedScalar* load = MLoadUnboxedScalar::New(alloc(), elements, index, arrayType);
         current->add(load);
         current->push(load);
 
         // Note: we can ignore the type barrier here, we know the type must
         // be valid and unbarriered.
@@ -8996,18 +9009,21 @@ IonBuilder::setElemTryReferenceElemOfTyp
                                                  TypedObjectPrediction objPrediction,
                                                  MDefinition* value,
                                                  TypedObjectPrediction elemPrediction)
 {
     ReferenceTypeDescr::Type elemType = elemPrediction.referenceType();
     uint32_t elemSize = ReferenceTypeDescr::size(elemType);
 
     LinearSum indexAsByteOffset(alloc());
-    if (!checkTypedObjectIndexInBounds(elemSize, obj, index, objPrediction, &indexAsByteOffset))
-        return Ok();
+    if (!checkTypedObjectIndexInBounds(elemSize, obj, index, objPrediction, &indexAsByteOffset,
+                                       BoundsCheckKind::IsStore))
+    {
+        return Ok();
+    }
 
     return setPropTryReferenceTypedObjectValue(emitted, obj, indexAsByteOffset,
                                                elemType, value, nullptr);
 }
 
 AbortReasonOr<Ok>
 IonBuilder::setElemTryScalarElemOfTypedObject(bool* emitted,
                                               MDefinition* obj,
@@ -9017,18 +9033,21 @@ IonBuilder::setElemTryScalarElemOfTypedO
                                               TypedObjectPrediction elemPrediction,
                                               uint32_t elemSize)
 {
     // Must always be loading the same scalar type
     ScalarTypeDescr::Type elemType = elemPrediction.scalarType();
     MOZ_ASSERT(elemSize == ScalarTypeDescr::alignment(elemType));
 
     LinearSum indexAsByteOffset(alloc());
-    if (!checkTypedObjectIndexInBounds(elemSize, obj, index, objPrediction, &indexAsByteOffset))
-        return Ok();
+    if (!checkTypedObjectIndexInBounds(elemSize, obj, index, objPrediction, &indexAsByteOffset,
+                                       BoundsCheckKind::IsStore))
+    {
+        return Ok();
+    }
 
     return setPropTryScalarTypedObjectValue(emitted, obj, indexAsByteOffset, elemType, value);
 }
 
 AbortReasonOr<Ok>
 IonBuilder::setElemTryTypedStatic(bool* emitted, MDefinition* object,
                                   MDefinition* index, MDefinition* value)
 {
@@ -9314,17 +9333,17 @@ IonBuilder::initOrSetElemDense(Temporary
                                                                 newValue, strict);
         store = ins;
         common = ins;
 
         current->add(ins);
     } else {
         MInstruction* initLength = initializedLength(obj, elements);
 
-        id = addBoundsCheck(id, initLength);
+        id = addBoundsCheck(id, initLength, BoundsCheckKind::IsStore);
         bool needsHoleCheck = !packed && hasExtraIndexedProperty;
 
         MStoreElement* ins = MStoreElement::New(alloc(), elements, id, newValue, needsHoleCheck);
         store = ins;
         common = ins;
 
         current->add(store);
     }
@@ -9362,17 +9381,18 @@ IonBuilder::jsop_setelem_typed(Scalar::T
     MInstruction* idInt32 = MToInt32::New(alloc(), id);
     current->add(idInt32);
     id = idInt32;
 
     // Get length, bounds-check, then get elements, and add all instructions.
     MInstruction* length;
     MInstruction* elements;
     BoundsChecking checking = expectOOB ? SkipBoundsCheck : DoBoundsCheck;
-    addTypedArrayLengthAndData(obj, checking, &id, &length, &elements);
+    addTypedArrayLengthAndData(obj, checking, &id, &length, &elements,
+                               BoundsCheckKind::IsStore);
 
     // Clamp value to [0, 255] for Uint8ClampedArray.
     MDefinition* toWrite = value;
     if (arrayType == Scalar::Uint8Clamped) {
         toWrite = MClampToUint8::New(alloc(), value);
         current->add(toWrite->toInstruction());
     }
 
@@ -12896,17 +12916,17 @@ IonBuilder::inTryDense(bool* emitted, MD
     // Get the elements vector.
     MElements* elements = MElements::New(alloc(), obj);
     current->add(elements);
 
     MInstruction* initLength = initializedLength(obj, elements);
 
     // If there are no holes, speculate the InArray check will not fail.
     if (!needsHoleCheck && !failedBoundsCheck_) {
-        addBoundsCheck(idInt32, initLength);
+        addBoundsCheck(idInt32, initLength, BoundsCheckKind::UnusedIndex);
         pushConstant(BooleanValue(true));
         return Ok();
     }
 
     // Check if id < initLength and elem[id] not a hole.
     MInArray* ins = MInArray::New(alloc(), elements, id, initLength, obj, needsHoleCheck);
 
     current->add(ins);
@@ -13241,25 +13261,30 @@ IonBuilder::addMaybeCopyElementsForWrite
     if (!ElementAccessMightBeCopyOnWrite(constraints(), object))
         return object;
     MInstruction* copy = MMaybeCopyElementsForWrite::New(alloc(), object, checkNative);
     current->add(copy);
     return copy;
 }
 
 MInstruction*
-IonBuilder::addBoundsCheck(MDefinition* index, MDefinition* length)
+IonBuilder::addBoundsCheck(MDefinition* index, MDefinition* length, BoundsCheckKind kind)
 {
     MInstruction* check = MBoundsCheck::New(alloc(), index, length);
     current->add(check);
 
     // If a bounds check failed in the past, don't optimize bounds checks.
     if (failedBoundsCheck_)
         check->setNotMovable();
 
+    if (kind == BoundsCheckKind::IsLoad && JitOptions.spectreIndexMasking) {
+        check = MSpectreMaskIndex::New(alloc(), check, length);
+        current->add(check);
+    }
+
     return check;
 }
 
 MInstruction*
 IonBuilder::addShapeGuard(MDefinition* obj, Shape* const shape, BailoutKind bailoutKind)
 {
     MGuardShape* guard = MGuardShape::New(alloc(), obj, shape, bailoutKind);
     current->add(guard);
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -190,17 +190,20 @@ class IonBuilder
     MDefinition* createThis(JSFunction* target, MDefinition* callee, MDefinition* newTarget);
     MInstruction* createNamedLambdaObject(MDefinition* callee, MDefinition* envObj);
     AbortReasonOr<MInstruction*> createCallObject(MDefinition* callee, MDefinition* envObj);
 
     MDefinition* walkEnvironmentChain(unsigned hops);
 
     MInstruction* addConvertElementsToDoubles(MDefinition* elements);
     MDefinition* addMaybeCopyElementsForWrite(MDefinition* object, bool checkNative);
-    MInstruction* addBoundsCheck(MDefinition* index, MDefinition* length);
+
+    enum class BoundsCheckKind { IsLoad, IsStore, UnusedIndex };
+    MInstruction* addBoundsCheck(MDefinition* index, MDefinition* length, BoundsCheckKind kind);
+
     MInstruction* addShapeGuard(MDefinition* obj, Shape* const shape, BailoutKind bailoutKind);
     MInstruction* addGroupGuard(MDefinition* obj, ObjectGroup* group, BailoutKind bailoutKind);
     MInstruction* addUnboxedExpandoGuard(MDefinition* obj, bool hasExpando, BailoutKind bailoutKind);
     MInstruction* addSharedTypedArrayGuard(MDefinition* obj);
 
     MInstruction*
     addGuardReceiverPolymorphic(MDefinition* obj, const BaselineInspector::ReceiverVector& receivers);
 
@@ -370,17 +373,18 @@ class IonBuilder
                                               int32_t* ownerByteAdjustment);
     MDefinition* typeObjectForElementFromArrayStructType(MDefinition* typedObj);
     MDefinition* typeObjectForFieldFromStructType(MDefinition* type,
                                                   size_t fieldIndex);
     bool checkTypedObjectIndexInBounds(uint32_t elemSize,
                                        MDefinition* obj,
                                        MDefinition* index,
                                        TypedObjectPrediction objTypeDescrs,
-                                       LinearSum* indexAsByteOffset);
+                                       LinearSum* indexAsByteOffset,
+                                       BoundsCheckKind kind);
     AbortReasonOr<Ok> pushDerivedTypedObject(bool* emitted,
                                              MDefinition* obj,
                                              const LinearSum& byteOffset,
                                              TypedObjectPrediction derivedTypeDescrs,
                                              MDefinition* derivedTypeObj);
     AbortReasonOr<Ok> pushScalarLoadFromTypedObject(MDefinition* obj,
                                                     const LinearSum& byteoffset,
                                                     ScalarTypeDescr::Type type);
@@ -459,24 +463,26 @@ class IonBuilder
     // Add instructions to compute a typed array's length and data.  Also
     // optionally convert |*index| into a bounds-checked definition, if
     // requested.
     //
     // If you only need the array's length, use addTypedArrayLength below.
     void addTypedArrayLengthAndData(MDefinition* obj,
                                     BoundsChecking checking,
                                     MDefinition** index,
-                                    MInstruction** length, MInstruction** elements);
+                                    MInstruction** length, MInstruction** elements,
+                                    BoundsCheckKind boundsCheckKind);
 
     // Add an instruction to compute a typed array's length to the current
     // block.  If you also need the typed array's data, use the above method
     // instead.
     MInstruction* addTypedArrayLength(MDefinition* obj) {
         MInstruction* length;
-        addTypedArrayLengthAndData(obj, SkipBoundsCheck, nullptr, &length, nullptr);
+        addTypedArrayLengthAndData(obj, SkipBoundsCheck, nullptr, &length, nullptr,
+                                   BoundsCheckKind::UnusedIndex);
         return length;
     }
 
     AbortReasonOr<Ok> improveThisTypesForCall();
 
     MDefinition* getCallee();
     MDefinition* getAliasedVar(EnvironmentCoordinate ec);
     AbortReasonOr<MDefinition*> addLexicalCheck(MDefinition* input);
@@ -761,17 +767,17 @@ class IonBuilder
                                      unsigned numVectors);
     InliningResult inlineSimdCheck(CallInfo& callInfo, JSNative native, SimdType type);
     InliningResult inlineSimdConvert(CallInfo& callInfo, JSNative native, bool isCast,
                                      SimdType from, SimdType to);
     InliningResult inlineSimdSelect(CallInfo& callInfo, JSNative native, SimdType type);
 
     bool prepareForSimdLoadStore(CallInfo& callInfo, Scalar::Type simdType,
                                  MInstruction** elements, MDefinition** index,
-                                 Scalar::Type* arrayType);
+                                 Scalar::Type* arrayType, BoundsCheckKind boundsCheckKind);
     InliningResult inlineSimdLoad(CallInfo& callInfo, JSNative native, SimdType type,
                                   unsigned numElems);
     InliningResult inlineSimdStore(CallInfo& callInfo, JSNative native, SimdType type,
                                    unsigned numElems);
 
     InliningResult inlineSimdAnyAllTrue(CallInfo& callInfo, bool IsAllTrue, JSNative native,
                                         SimdType type);
 
@@ -825,17 +831,18 @@ class IonBuilder
     enum AtomicCheckResult {
         DontCheckAtomicResult,
         DoCheckAtomicResult
     };
 
     bool atomicsMeetsPreconditions(CallInfo& callInfo, Scalar::Type* arrayElementType,
                                    bool* requiresDynamicCheck,
                                    AtomicCheckResult checkResult=DoCheckAtomicResult);
-    void atomicsCheckBounds(CallInfo& callInfo, MInstruction** elements, MDefinition** index);
+    void atomicsCheckBounds(CallInfo& callInfo, MInstruction** elements, MDefinition** index,
+                            BoundsCheckKind kind);
 
     bool testNeedsArgumentCheck(JSFunction* target, CallInfo& callInfo);
 
     AbortReasonOr<MCall*> makeCallHelper(JSFunction* target, CallInfo& callInfo);
     AbortReasonOr<Ok> makeCall(JSFunction* target, CallInfo& callInfo);
 
     MDefinition* patchInlinedReturn(CallInfo& callInfo, MBasicBlock* exit, MBasicBlock* bottom);
     MDefinition* patchInlinedReturns(CallInfo& callInfo, MIRGraphReturns& returns,
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -3125,16 +3125,36 @@ LIRGenerator::visitBoundsCheck(MBoundsCh
         check = new(alloc()) LBoundsCheck(useRegisterOrConstant(ins->index()),
                                           useAnyOrConstant(ins->length()));
     }
     assignSnapshot(check, Bailout_BoundsCheck);
     add(check, ins);
 }
 
 void
+LIRGenerator::visitSpectreMaskIndex(MSpectreMaskIndex* ins)
+{
+    MOZ_ASSERT(ins->index()->type() == MIRType::Int32);
+    MOZ_ASSERT(ins->length()->type() == MIRType::Int32);
+    MOZ_ASSERT(ins->type() == MIRType::Int32);
+
+    // On 64-bit platforms, the length must be in a register, so
+    // MacroAssembler::maskIndex can emit more efficient code.
+#if JS_BITS_PER_WORD == 64
+    LAllocation lengthUse = useRegister(ins->length());
+#else
+    LAllocation lengthUse = useAny(ins->length());
+#endif
+
+    LSpectreMaskIndex* lir =
+        new(alloc()) LSpectreMaskIndex(useRegisterOrConstant(ins->index()), lengthUse);
+    define(lir, ins);
+}
+
+void
 LIRGenerator::visitBoundsCheckLower(MBoundsCheckLower* ins)
 {
     MOZ_ASSERT(ins->index()->type() == MIRType::Int32);
 
     if (!ins->fallible())
         return;
 
     LInstruction* check = new(alloc()) LBoundsCheckLower(useRegister(ins->index()));
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -224,16 +224,17 @@ class LIRGenerator : public LIRGenerator
     void visitTypedObjectElements(MTypedObjectElements* ins) override;
     void visitSetTypedObjectOffset(MSetTypedObjectOffset* ins) override;
     void visitTypedObjectDescr(MTypedObjectDescr* ins) override;
     void visitInitializedLength(MInitializedLength* ins) override;
     void visitSetInitializedLength(MSetInitializedLength* ins) override;
     void visitNot(MNot* ins) override;
     void visitBoundsCheck(MBoundsCheck* ins) override;
     void visitBoundsCheckLower(MBoundsCheckLower* ins) override;
+    void visitSpectreMaskIndex(MSpectreMaskIndex* ins) override;
     void visitLoadElement(MLoadElement* ins) override;
     void visitLoadElementHole(MLoadElementHole* ins) override;
     void visitLoadUnboxedObjectOrNull(MLoadUnboxedObjectOrNull* ins) override;
     void visitLoadUnboxedString(MLoadUnboxedString* ins) override;
     void visitLoadElementFromState(MLoadElementFromState* ins) override;
     void visitStoreElement(MStoreElement* ins) override;
     void visitStoreElementHole(MStoreElementHole* ins) override;
     void visitFallibleStoreElement(MFallibleStoreElement* ins) override;
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -1863,17 +1863,17 @@ IonBuilder::inlineStrCharCodeAt(CallInfo
     callInfo.setImplicitlyUsedUnchecked();
 
     MInstruction* index = MToInt32::New(alloc(), callInfo.getArg(0));
     current->add(index);
 
     MStringLength* length = MStringLength::New(alloc(), callInfo.thisArg());
     current->add(length);
 
-    index = addBoundsCheck(index, length);
+    index = addBoundsCheck(index, length, BoundsCheckKind::IsLoad);
 
     MCharCodeAt* charCode = MCharCodeAt::New(alloc(), callInfo.thisArg(), index);
     current->add(charCode);
     current->push(charCode);
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningResult
@@ -1973,17 +1973,17 @@ IonBuilder::inlineStrCharAt(CallInfo& ca
     callInfo.setImplicitlyUsedUnchecked();
 
     MInstruction* index = MToInt32::New(alloc(), callInfo.getArg(0));
     current->add(index);
 
     MStringLength* length = MStringLength::New(alloc(), callInfo.thisArg());
     current->add(length);
 
-    index = addBoundsCheck(index, length);
+    index = addBoundsCheck(index, length, BoundsCheckKind::IsLoad);
 
     // String.charAt(x) = String.fromCharCode(String.charCodeAt(x))
     MCharCodeAt* charCode = MCharCodeAt::New(alloc(), callInfo.thisArg(), index);
     current->add(charCode);
 
     MFromCharCode* string = MFromCharCode::New(alloc(), charCode);
     current->add(string);
     current->push(string);
@@ -3264,17 +3264,17 @@ IonBuilder::inlineAtomicsCompareExchange
     bool requiresCheck = false;
     if (!atomicsMeetsPreconditions(callInfo, &arrayType, &requiresCheck))
         return InliningStatus_NotInlined;
 
     callInfo.setImplicitlyUsedUnchecked();
 
     MInstruction* elements;
     MDefinition* index;
-    atomicsCheckBounds(callInfo, &elements, &index);
+    atomicsCheckBounds(callInfo, &elements, &index, BoundsCheckKind::IsLoad);
 
     if (requiresCheck)
         addSharedTypedArrayGuard(callInfo.getArg(0));
 
     MCompareExchangeTypedArrayElement* cas =
         MCompareExchangeTypedArrayElement::New(alloc(), elements, index, arrayType, oldval, newval);
     cas->setResultType(getInlineReturnType());
     current->add(cas);
@@ -3300,17 +3300,17 @@ IonBuilder::inlineAtomicsExchange(CallIn
     bool requiresCheck = false;
     if (!atomicsMeetsPreconditions(callInfo, &arrayType, &requiresCheck))
         return InliningStatus_NotInlined;
 
     callInfo.setImplicitlyUsedUnchecked();
 
     MInstruction* elements;
     MDefinition* index;
-    atomicsCheckBounds(callInfo, &elements, &index);
+    atomicsCheckBounds(callInfo, &elements, &index, BoundsCheckKind::IsLoad);
 
     if (requiresCheck)
         addSharedTypedArrayGuard(callInfo.getArg(0));
 
     MInstruction* exchange =
         MAtomicExchangeTypedArrayElement::New(alloc(), elements, index, value, arrayType);
     exchange->setResultType(getInlineReturnType());
     current->add(exchange);
@@ -3332,17 +3332,17 @@ IonBuilder::inlineAtomicsLoad(CallInfo& 
     bool requiresCheck = false;
     if (!atomicsMeetsPreconditions(callInfo, &arrayType, &requiresCheck))
         return InliningStatus_NotInlined;
 
     callInfo.setImplicitlyUsedUnchecked();
 
     MInstruction* elements;
     MDefinition* index;
-    atomicsCheckBounds(callInfo, &elements, &index);
+    atomicsCheckBounds(callInfo, &elements, &index, BoundsCheckKind::IsLoad);
 
     if (requiresCheck)
         addSharedTypedArrayGuard(callInfo.getArg(0));
 
     MLoadUnboxedScalar* load =
         MLoadUnboxedScalar::New(alloc(), elements, index, arrayType,
                                 DoesRequireMemoryBarrier);
     load->setResultType(getInlineReturnType());
@@ -3384,17 +3384,17 @@ IonBuilder::inlineAtomicsStore(CallInfo&
     bool requiresCheck = false;
     if (!atomicsMeetsPreconditions(callInfo, &arrayType, &requiresCheck, DontCheckAtomicResult))
         return InliningStatus_NotInlined;
 
     callInfo.setImplicitlyUsedUnchecked();
 
     MInstruction* elements;
     MDefinition* index;
-    atomicsCheckBounds(callInfo, &elements, &index);
+    atomicsCheckBounds(callInfo, &elements, &index, BoundsCheckKind::IsStore);
 
     if (requiresCheck)
         addSharedTypedArrayGuard(callInfo.getArg(0));
 
     MDefinition* toWrite = value;
     if (toWrite->type() != MIRType::Int32) {
         toWrite = MTruncateToInt32::New(alloc(), toWrite);
         current->add(toWrite->toInstruction());
@@ -3428,17 +3428,17 @@ IonBuilder::inlineAtomicsBinop(CallInfo&
 
     callInfo.setImplicitlyUsedUnchecked();
 
     if (requiresCheck)
         addSharedTypedArrayGuard(callInfo.getArg(0));
 
     MInstruction* elements;
     MDefinition* index;
-    atomicsCheckBounds(callInfo, &elements, &index);
+    atomicsCheckBounds(callInfo, &elements, &index, BoundsCheckKind::IsLoad);
 
     AtomicOp k = AtomicFetchAddOp;
     switch (target) {
       case InlinableNative::AtomicsAdd:
         k = AtomicFetchAddOp;
         break;
       case InlinableNative::AtomicsSub:
         k = AtomicFetchSubOp;
@@ -3525,24 +3525,25 @@ IonBuilder::atomicsMeetsPreconditions(Ca
         return checkResult == DontCheckAtomicResult || getInlineReturnType() == MIRType::Double;
       default:
         // Excludes floating types and Uint8Clamped.
         return false;
     }
 }
 
 void
-IonBuilder::atomicsCheckBounds(CallInfo& callInfo, MInstruction** elements, MDefinition** index)
+IonBuilder::atomicsCheckBounds(CallInfo& callInfo, MInstruction** elements, MDefinition** index,
+                               BoundsCheckKind kind)
 {
     // Perform bounds checking and extract the elements vector.
     MDefinition* obj = callInfo.getArg(0);
     MInstruction* length = nullptr;
     *index = callInfo.getArg(1);
     *elements = nullptr;
-    addTypedArrayLengthAndData(obj, DoBoundsCheck, index, &length, elements);
+    addTypedArrayLengthAndData(obj, DoBoundsCheck, index, &length, elements, kind);
 }
 
 IonBuilder::InliningResult
 IonBuilder::inlineIsConstructing(CallInfo& callInfo)
 {
     MOZ_ASSERT(!callInfo.constructing());
     MOZ_ASSERT(callInfo.argc() == 0);
     MOZ_ASSERT(script()->functionNonDelazifying(),
@@ -4246,17 +4247,18 @@ SimdTypeToArrayElementType(SimdType type
       case SimdType::Int32x4:
       case SimdType::Uint32x4:  return Scalar::Int32x4;
       default:                MOZ_CRASH("unexpected simd type");
     }
 }
 
 bool
 IonBuilder::prepareForSimdLoadStore(CallInfo& callInfo, Scalar::Type simdType, MInstruction** elements,
-                                    MDefinition** index, Scalar::Type* arrayType)
+                                    MDefinition** index, Scalar::Type* arrayType,
+                                    BoundsCheckKind boundsCheckKind)
 {
     MDefinition* array = callInfo.getArg(0);
     *index = callInfo.getArg(1);
 
     if (!ElementAccessIsTypedArray(constraints(), array, *index, arrayType))
         return false;
 
     MInstruction* indexAsInt32 = MToInt32::New(alloc(), *index);
@@ -4276,42 +4278,43 @@ IonBuilder::prepareForSimdLoadStore(Call
         // We're fine even with the add overflows, as long as the generated code
         // for the bounds check uses an unsigned comparison.
         addedIndex->setInt32Specialization();
         current->add(addedIndex);
         indexForBoundsCheck = addedIndex;
     }
 
     MInstruction* length;
-    addTypedArrayLengthAndData(array, SkipBoundsCheck, index, &length, elements);
+    addTypedArrayLengthAndData(array, SkipBoundsCheck, index, &length, elements, boundsCheckKind);
 
     // It can be that the index is out of bounds, while the added index for the
     // bounds check is in bounds, so we actually need two bounds checks here.
-    MInstruction* positiveCheck = MBoundsCheck::New(alloc(), *index, length);
-    current->add(positiveCheck);
-
-    MInstruction* fullCheck = MBoundsCheck::New(alloc(), indexForBoundsCheck, length);
-    current->add(fullCheck);
+    *index = addBoundsCheck(*index, length, boundsCheckKind);
+
+    addBoundsCheck(indexForBoundsCheck, length, BoundsCheckKind::UnusedIndex);
     return true;
 }
 
 IonBuilder::InliningResult
 IonBuilder::inlineSimdLoad(CallInfo& callInfo, JSNative native, SimdType type, unsigned numElems)
 {
     InlineTypedObject* templateObj = nullptr;
     if (!canInlineSimd(callInfo, native, 2, &templateObj))
         return InliningStatus_NotInlined;
 
     Scalar::Type elemType = SimdTypeToArrayElementType(type);
 
     MDefinition* index = nullptr;
     MInstruction* elements = nullptr;
     Scalar::Type arrayType;
-    if (!prepareForSimdLoadStore(callInfo, elemType, &elements, &index, &arrayType))
-        return InliningStatus_NotInlined;
+    if (!prepareForSimdLoadStore(callInfo, elemType, &elements, &index, &arrayType,
+                                 BoundsCheckKind::IsLoad))
+    {
+        return InliningStatus_NotInlined;
+    }
 
     MLoadUnboxedScalar* load = MLoadUnboxedScalar::New(alloc(), elements, index, arrayType);
     load->setResultType(SimdTypeToMIRType(type));
     load->setSimdRead(elemType, numElems);
 
     return boxSimd(callInfo, load, templateObj);
 }
 
@@ -4322,18 +4325,21 @@ IonBuilder::inlineSimdStore(CallInfo& ca
     if (!canInlineSimd(callInfo, native, 3, &templateObj))
         return InliningStatus_NotInlined;
 
     Scalar::Type elemType = SimdTypeToArrayElementType(type);
 
     MDefinition* index = nullptr;
     MInstruction* elements = nullptr;
     Scalar::Type arrayType;
-    if (!prepareForSimdLoadStore(callInfo, elemType, &elements, &index, &arrayType))
-        return InliningStatus_NotInlined;
+    if (!prepareForSimdLoadStore(callInfo, elemType, &elements, &index, &arrayType,
+                                 BoundsCheckKind::IsStore))
+    {
+        return InliningStatus_NotInlined;
+    }
 
     MDefinition* valueToWrite = unboxSimd(callInfo.getArg(2), type);
     MStoreUnboxedScalar* store = MStoreUnboxedScalar::New(alloc(), elements, index,
                                                           valueToWrite, arrayType,
                                                           MStoreUnboxedScalar::TruncateInput);
     store->setSimdWrite(elemType, numElems);
 
     current->add(store);
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -9544,16 +9544,48 @@ class MBoundsCheckLower
         return AliasSet::None();
     }
     bool fallible() const {
         return fallible_;
     }
     void collectRangeInfoPreTrunc() override;
 };
 
+class MSpectreMaskIndex
+  : public MBinaryInstruction,
+    public MixPolicy<IntPolicy<0>, IntPolicy<1>>::Data
+{
+    MSpectreMaskIndex(MDefinition* index, MDefinition* length)
+      : MBinaryInstruction(classOpcode, index, length)
+    {
+        setGuard();
+        setMovable();
+        MOZ_ASSERT(index->type() == MIRType::Int32);
+        MOZ_ASSERT(length->type() == MIRType::Int32);
+
+        // Returns the masked index.
+        setResultType(MIRType::Int32);
+    }
+
+  public:
+    INSTRUCTION_HEADER(SpectreMaskIndex)
+    TRIVIAL_NEW_WRAPPERS
+    NAMED_OPERANDS((0, index), (1, length))
+
+    bool congruentTo(const MDefinition* ins) const override {
+        return congruentIfOperandsEqual(ins);
+    }
+    virtual AliasSet getAliasSet() const override {
+        return AliasSet::None();
+    }
+    void computeRange(TempAllocator& alloc) override;
+
+    ALLOW_CLONE(MSpectreMaskIndex)
+};
+
 // Instructions which access an object's elements can either do so on a
 // definition accessing that elements pointer, or on the object itself, if its
 // elements are inline. In the latter case there must be an offset associated
 // with the access.
 static inline bool
 IsValidElementsType(MDefinition* elements, int32_t offsetAdjustment)
 {
     return elements->type() == MIRType::Elements ||
--- a/js/src/jit/MOpcodes.h
+++ b/js/src/jit/MOpcodes.h
@@ -210,16 +210,17 @@ namespace jit {
     _(TypedObjectDescr)                                                     \
     _(TypedObjectElements)                                                  \
     _(SetTypedObjectOffset)                                                 \
     _(InitializedLength)                                                    \
     _(SetInitializedLength)                                                 \
     _(Not)                                                                  \
     _(BoundsCheck)                                                          \
     _(BoundsCheckLower)                                                     \
+    _(SpectreMaskIndex)                                                     \
     _(InArray)                                                              \
     _(LoadElement)                                                          \
     _(LoadElementHole)                                                      \
     _(LoadUnboxedScalar)                                                    \
     _(LoadUnboxedObjectOrNull)                                              \
     _(LoadUnboxedString)                                                    \
     _(LoadElementFromState)                                                 \
     _(StoreElement)                                                         \
--- a/js/src/jit/MacroAssembler.cpp
+++ b/js/src/jit/MacroAssembler.cpp
@@ -3408,16 +3408,83 @@ MacroAssembler::debugAssertIsObject(cons
 #ifdef DEBUG
     Label ok;
     branchTestObject(Assembler::Equal, val, &ok);
     assumeUnreachable("Expected an object!");
     bind(&ok);
 #endif
 }
 
+template <typename T>
+void
+MacroAssembler::spectreMaskIndexImpl(Register index, const T& length, Register output)
+{
+    // mask := ((index - length) & ~index) >> 31
+    // output := index & mask
+    mov(index, output);
+    sub32(length, output);
+    not32(index);
+    and32(index, output);
+    not32(index); // Restore index register to its original value.
+    rshift32Arithmetic(Imm32(31), output);
+    and32(index, output);
+}
+
+template <typename T>
+void
+MacroAssembler::spectreMaskIndexImpl(int32_t index, const T& length, Register output)
+{
+    // mask := ((index - length) & ~index) >> 31
+    // output := index & mask
+    move32(Imm32(index), output);
+    if (index == 0)
+        return;
+    sub32(length, output);
+    and32(Imm32(~index), output);
+    rshift32Arithmetic(Imm32(31), output);
+    and32(Imm32(index), output);
+}
+
+void
+MacroAssembler::spectreMaskIndex(int32_t index, Register length, Register output)
+{
+    spectreMaskIndexImpl(index, length, output);
+}
+
+void
+MacroAssembler::spectreMaskIndex(int32_t index, const Address& length, Register output)
+{
+    spectreMaskIndexImpl(index, length, output);
+}
+
+void
+MacroAssembler::spectreMaskIndex(Register index, Register length, Register output)
+{
+#if JS_BITS_PER_WORD == 64
+    // On 64-bit platforms, we can use a faster algorithm:
+    //
+    //   mask := (uint64_t(index) - uint64_t(length)) >> 32
+    //   output := index & mask
+    //
+    // mask is 0x11…11 if index < length, 0 otherwise.
+    move32(index, output);
+    subPtr(length, output);
+    rshiftPtr(Imm32(32), output);
+    and32(index, output);
+#else
+    spectreMaskIndexImpl(index, length, output);
+#endif
+}
+
+void
+MacroAssembler::spectreMaskIndex(Register index, const Address& length, Register output)
+{
+    spectreMaskIndexImpl(index, length, output);
+}
+
 namespace js {
 namespace jit {
 
 #ifdef DEBUG
 template <class RegisterType>
 AutoGenericRegisterScope<RegisterType>::AutoGenericRegisterScope(MacroAssembler& masm, RegisterType reg)
   : RegisterType(reg), masm_(masm)
 {
--- a/js/src/jit/MacroAssembler.h
+++ b/js/src/jit/MacroAssembler.h
@@ -1889,16 +1889,29 @@ class MacroAssembler : public MacroAssem
     using MacroAssemblerSpecific::store32;
     void store32(const RegisterOrInt32Constant& key, const Address& dest) {
         if (key.isRegister())
             store32(key.reg(), dest);
         else
             store32(Imm32(key.constant()), dest);
     }
 
+  private:
+    template <typename T>
+    void spectreMaskIndexImpl(Register index, const T& length, Register output);
+
+    template <typename T>
+    void spectreMaskIndexImpl(int32_t index, const T& length, Register output);
+
+  public:
+    void spectreMaskIndex(int32_t index, Register length, Register output);
+    void spectreMaskIndex(int32_t index, const Address& length, Register output);
+    void spectreMaskIndex(Register index, Register length, Register output);
+    void spectreMaskIndex(Register index, const Address& length, Register output);
+
     template <typename T>
     void guardedCallPreBarrier(const T& address, MIRType type) {
         Label done;
 
         branchTestNeedsIncrementalBarrier(Assembler::Zero, &done);
 
         if (type == MIRType::Value)
             branchTestGCThing(Assembler::NotEqual, address, &done);
@@ -2320,128 +2333,71 @@ class MacroAssembler : public MacroAssem
                                                      FloatRegister temp, Register output,
                                                      Label* fail, IntConversionBehavior behavior);
     void convertTypedOrValueToInt(TypedOrValueRegister src, FloatRegister temp, Register output,
                                   Label* fail, IntConversionBehavior behavior);
 
     //
     // Convenience functions for converting values to int32.
     //
-    void convertValueToInt32(ValueOperand value, FloatRegister temp, Register output, Label* fail,
-                             bool negativeZeroCheck)
-    {
-        convertValueToInt(value, temp, output, fail, negativeZeroCheck
-                          ? IntConversion_NegativeZeroCheck
-                          : IntConversion_Normal);
-    }
     void convertValueToInt32(ValueOperand value, MDefinition* input,
                              FloatRegister temp, Register output, Label* fail,
-                             bool negativeZeroCheck, IntConversionInputKind conversion = IntConversion_Any)
+                             bool negativeZeroCheck,
+                             IntConversionInputKind conversion = IntConversion_Any)
     {
         convertValueToInt(value, input, nullptr, nullptr, nullptr, InvalidReg, temp, output, fail,
                           negativeZeroCheck
                           ? IntConversion_NegativeZeroCheck
                           : IntConversion_Normal,
                           conversion);
     }
-    MOZ_MUST_USE bool convertValueToInt32(JSContext* cx, const Value& v, Register output,
-                                          Label* fail, bool negativeZeroCheck)
-    {
-        return convertValueToInt(cx, v, output, fail, negativeZeroCheck
-                                 ? IntConversion_NegativeZeroCheck
-                                 : IntConversion_Normal);
-    }
-    MOZ_MUST_USE bool convertConstantOrRegisterToInt32(JSContext* cx,
-                                                       const ConstantOrRegister& src,
-                                                       FloatRegister temp, Register output,
-                                                       Label* fail, bool negativeZeroCheck)
-    {
-        return convertConstantOrRegisterToInt(cx, src, temp, output, fail, negativeZeroCheck
-                                              ? IntConversion_NegativeZeroCheck
-                                              : IntConversion_Normal);
-    }
-    void convertTypedOrValueToInt32(TypedOrValueRegister src, FloatRegister temp, Register output,
-                                    Label* fail, bool negativeZeroCheck)
-    {
-        convertTypedOrValueToInt(src, temp, output, fail, negativeZeroCheck
-                                 ? IntConversion_NegativeZeroCheck
-                                 : IntConversion_Normal);
-    }
 
     //
     // Convenience functions for truncating values to int32.
     //
-    void truncateValueToInt32(ValueOperand value, FloatRegister temp, Register output, Label* fail) {
-        convertValueToInt(value, temp, output, fail, IntConversion_Truncate);
-    }
     void truncateValueToInt32(ValueOperand value, MDefinition* input,
                               Label* handleStringEntry, Label* handleStringRejoin,
                               Label* truncateDoubleSlow,
                               Register stringReg, FloatRegister temp, Register output, Label* fail)
     {
         convertValueToInt(value, input, handleStringEntry, handleStringRejoin, truncateDoubleSlow,
                           stringReg, temp, output, fail, IntConversion_Truncate);
     }
-    void truncateValueToInt32(ValueOperand value, MDefinition* input,
-                              FloatRegister temp, Register output, Label* fail)
+
+    void truncateValueToInt32(ValueOperand value, FloatRegister temp, Register output, Label* fail)
     {
-        convertValueToInt(value, input, nullptr, nullptr, nullptr, InvalidReg, temp, output, fail,
-                          IntConversion_Truncate);
+        truncateValueToInt32(value, nullptr, nullptr, nullptr, nullptr, InvalidReg, temp, output,
+                             fail);
     }
-    MOZ_MUST_USE bool truncateValueToInt32(JSContext* cx, const Value& v, Register output,
-                                           Label* fail) {
-        return convertValueToInt(cx, v, output, fail, IntConversion_Truncate);
-    }
+
     MOZ_MUST_USE bool truncateConstantOrRegisterToInt32(JSContext* cx,
                                                         const ConstantOrRegister& src,
                                                         FloatRegister temp, Register output,
                                                         Label* fail)
     {
         return convertConstantOrRegisterToInt(cx, src, temp, output, fail, IntConversion_Truncate);
     }
-    void truncateTypedOrValueToInt32(TypedOrValueRegister src, FloatRegister temp, Register output,
-                                     Label* fail)
-    {
-        convertTypedOrValueToInt(src, temp, output, fail, IntConversion_Truncate);
-    }
 
     // Convenience functions for clamping values to uint8.
-    void clampValueToUint8(ValueOperand value, FloatRegister temp, Register output, Label* fail) {
-        convertValueToInt(value, temp, output, fail, IntConversion_ClampToUint8);
-    }
     void clampValueToUint8(ValueOperand value, MDefinition* input,
                            Label* handleStringEntry, Label* handleStringRejoin,
                            Register stringReg, FloatRegister temp, Register output, Label* fail)
     {
         convertValueToInt(value, input, handleStringEntry, handleStringRejoin, nullptr,
                           stringReg, temp, output, fail, IntConversion_ClampToUint8);
     }
-    void clampValueToUint8(ValueOperand value, MDefinition* input,
-                           FloatRegister temp, Register output, Label* fail)
-    {
-        convertValueToInt(value, input, nullptr, nullptr, nullptr, InvalidReg, temp, output, fail,
-                          IntConversion_ClampToUint8);
-    }
-    MOZ_MUST_USE bool clampValueToUint8(JSContext* cx, const Value& v, Register output,
-                                        Label* fail) {
-        return convertValueToInt(cx, v, output, fail, IntConversion_ClampToUint8);
-    }
+
     MOZ_MUST_USE bool clampConstantOrRegisterToUint8(JSContext* cx,
                                                      const ConstantOrRegister& src,
                                                      FloatRegister temp, Register output,
                                                      Label* fail)
     {
         return convertConstantOrRegisterToInt(cx, src, temp, output, fail,
                                               IntConversion_ClampToUint8);
     }
-    void clampTypedOrValueToUint8(TypedOrValueRegister src, FloatRegister temp, Register output,
-                                  Label* fail)
-    {
-        convertTypedOrValueToInt(src, temp, output, fail, IntConversion_ClampToUint8);
-    }
 
   public:
     class AfterICSaveLive {
         friend class MacroAssembler;
         explicit AfterICSaveLive(uint32_t initialStack)
 #ifdef JS_DEBUG
           : initialStack(initialStack)
 #endif
--- a/js/src/jit/RangeAnalysis.cpp
+++ b/js/src/jit/RangeAnalysis.cpp
@@ -1856,16 +1856,23 @@ MBoundsCheck::computeRange(TempAllocator
 {
     // Just transfer the incoming index range to the output. The length() is
     // also interesting, but it is handled as a bailout check, and we're
     // computing a pre-bailout range here.
     setRange(new(alloc) Range(index()));
 }
 
 void
+MSpectreMaskIndex::computeRange(TempAllocator& alloc)
+{
+    // Just transfer the incoming index range to the output for now.
+    setRange(new(alloc) Range(index()));
+}
+
+void
 MArrayPush::computeRange(TempAllocator& alloc)
 {
     // MArrayPush returns the new array length.
     setRange(Range::NewUInt32Range(alloc, 0, UINT32_MAX));
 }
 
 void
 MMathFunction::computeRange(TempAllocator& alloc)
--- a/js/src/jit/ScalarReplacement.cpp
+++ b/js/src/jit/ScalarReplacement.cpp
@@ -856,16 +856,18 @@ ObjectMemoryView::visitLoadUnboxedString
     loadOffset(ins, offset);
 }
 
 static bool
 IndexOf(MDefinition* ins, int32_t* res)
 {
     MOZ_ASSERT(ins->isLoadElement() || ins->isStoreElement());
     MDefinition* indexDef = ins->getOperand(1); // ins->index();
+    if (indexDef->isSpectreMaskIndex())
+        indexDef = indexDef->toSpectreMaskIndex()->index();
     if (indexDef->isBoundsCheck())
         indexDef = indexDef->toBoundsCheck()->index();
     if (indexDef->isToInt32())
         indexDef = indexDef->toToInt32()->getOperand(0);
     MConstant* indexDefConst = indexDef->maybeConstantValue();
     if (!indexDefConst || indexDefConst->type() != MIRType::Int32)
         return false;
     *res = indexDefConst->toInt32();
--- a/js/src/jit/shared/LIR-shared.h
+++ b/js/src/jit/shared/LIR-shared.h
@@ -5545,16 +5545,33 @@ class LBoundsCheckLower : public LInstru
     MBoundsCheckLower* mir() const {
         return mir_->toBoundsCheckLower();
     }
     const LAllocation* index() {
         return getOperand(0);
     }
 };
 
+class LSpectreMaskIndex : public LInstructionHelper<1, 2, 0>
+{
+  public:
+    LIR_HEADER(SpectreMaskIndex)
+
+    LSpectreMaskIndex(const LAllocation& index, const LAllocation& length) {
+        setOperand(0, index);
+        setOperand(1, length);
+    }
+    const LAllocation* index() {
+        return getOperand(0);
+    }
+    const LAllocation* length() {
+        return getOperand(1);
+    }
+};
+
 // Load a value from a dense array's elements vector. Bail out if it's the hole value.
 class LLoadElementV : public LInstructionHelper<BOX_PIECES, 2, 0>
 {
   public:
     LIR_HEADER(LoadElementV)
 
     LLoadElementV(const LAllocation& elements, const LAllocation& index) {
         setOperand(0, elements);
--- a/js/src/jit/shared/LOpcodes-shared.h
+++ b/js/src/jit/shared/LOpcodes-shared.h
@@ -272,16 +272,17 @@
     _(PostWriteBarrierV)            \
     _(PostWriteElementBarrierO)     \
     _(PostWriteElementBarrierV)     \
     _(InitializedLength)            \
     _(SetInitializedLength)         \
     _(BoundsCheck)                  \
     _(BoundsCheckRange)             \
     _(BoundsCheckLower)             \
+    _(SpectreMaskIndex)             \
     _(LoadElementV)                 \
     _(LoadElementT)                 \
     _(LoadElementHole)              \
     _(LoadUnboxedScalar)            \
     _(LoadUnboxedPointerV)          \
     _(LoadUnboxedPointerT)          \
     _(LoadElementFromStateV)        \
     _(UnboxObjectOrNull)            \
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -3544,16 +3544,22 @@ static const JSFunctionSpec array_method
     JS_SELF_HOSTED_FN("entries",     "ArrayEntries",     0,0),
     JS_SELF_HOSTED_FN("keys",        "ArrayKeys",        0,0),
 #ifdef NIGHTLY_BUILD
     JS_SELF_HOSTED_FN("values",      "ArrayValues",      0,0),
 #endif
 
     /* ES7 additions */
     JS_SELF_HOSTED_FN("includes",    "ArrayIncludes",    2,0),
+
+#ifdef NIGHTLY_BUILD
+    JS_SELF_HOSTED_FN("flatMap",     "ArrayFlatMap",     1,0),
+    JS_SELF_HOSTED_FN("flatten",     "ArrayFlatten",     0,0),
+#endif
+
     JS_FS_END
 };
 
 static const JSFunctionSpec array_static_methods[] = {
     JS_INLINABLE_FN("isArray",       array_isArray,        1,0, ArrayIsArray),
     JS_SELF_HOSTED_FN("concat",      "ArrayStaticConcat", 2,0),
     JS_SELF_HOSTED_FN("lastIndexOf", "ArrayStaticLastIndexOf", 2,0),
     JS_SELF_HOSTED_FN("indexOf",     "ArrayStaticIndexOf", 2,0),
--- a/js/src/tests/jstests.list
+++ b/js/src/tests/jstests.list
@@ -44,16 +44,19 @@ skip-if(!this.hasOwnProperty("SIMD")) in
 # https://bugzilla.mozilla.org/show_bug.cgi?id=1415303
 skip-if(!this.hasOwnProperty("SharedArrayBuffer")) script non262/SIMD/load-sab-buffer-compat.js
 skip-if(!this.hasOwnProperty("Atomics")) include test262/built-ins/Atomics/jstests.list
 skip-if(!this.hasOwnProperty("SharedArrayBuffer")) include test262/built-ins/SharedArrayBuffer/jstests.list
 skip-if(!this.hasOwnProperty("SharedArrayBuffer")) script test262/built-ins/ArrayBuffer/prototype/byteLength/this-is-sharedarraybuffer.js
 skip-if(!this.hasOwnProperty("SharedArrayBuffer")) script test262/built-ins/ArrayBuffer/prototype/slice/this-is-sharedarraybuffer.js
 skip-if(!this.hasOwnProperty("SharedArrayBuffer")) script test262/built-ins/TypedArrays/internals/Get/indexed-value-sab.js
 
+# flatMap and flatten are Nightly-only
+skip-if(!Array.prototype.flatMap) include test262/built-ins/Array/prototype/flatMap/jstests.list
+skip-if(!Array.prototype.flatten) include test262/built-ins/Array/prototype/flatten/jstests.list
 
 #####################################
 # Test262 tests disabled on browser #
 #####################################
 
 # Defines a non-configurable property on the WindowProxy object.
 skip-if(!xulRuntime.shell) script test262/annexB/language/eval-code/direct/global-block-decl-eval-global-existing-global-update.js
 skip-if(!xulRuntime.shell) script test262/annexB/language/eval-code/direct/global-if-decl-else-decl-a-eval-global-existing-global-update.js
--- a/js/src/tests/test262-update.py
+++ b/js/src/tests/test262-update.py
@@ -15,18 +15,16 @@ import shutil
 import sys
 
 from functools import partial
 from itertools import chain, imap
 
 # Skip all tests which use features not supported in SpiderMonkey.
 UNSUPPORTED_FEATURES = set([
                             "tail-call-optimization",
-                            "Array.prototype.flatMap",
-                            "Array.prototype.flatten",
                             "BigInt",
                             "class-fields-public",
                             "class-fields-private",
                             "regexp-dotall",
                             "regexp-lookbehind",
                             "regexp-named-groups",
                             "regexp-unicode-property-escapes",
                        ])
--- a/js/src/tests/test262/built-ins/Array/prototype/flatMap/array-like-objects.js
+++ b/js/src/tests/test262/built-ins/Array/prototype/flatMap/array-like-objects.js
@@ -1,9 +1,8 @@
-// |reftest| skip -- Array.prototype.flatMap is not supported
 // Copyright (C) 2018 Shilpi Jain and Michael Ficarra. All rights reserved.
 // This code is governed by the BSD license found in the LICENSE file.
 /*---
 esid: sec-array.prototype.flatMap
 description: >
     array-like objects can be flattened
 includes: [compareArray.js]
 features: [Array.prototype.flatMap]
--- a/js/src/tests/test262/built-ins/Array/prototype/flatMap/bound-function-argument.js
+++ b/js/src/tests/test262/built-ins/Array/prototype/flatMap/bound-function-argument.js
@@ -1,9 +1,8 @@
-// |reftest| skip -- Array.prototype.flatMap is not supported
 // Copyright (C) 2018 Shilpi Jain and Michael Ficarra. All rights reserved.
 // This code is governed by the BSD license found in the LICENSE file.
 /*---
 esid: sec-array.prototype.flatMap
 description: >
     Behavior when given a bound function
 includes: [compareArray.js]
 features: [Array.prototype.flatMap]
--- a/js/src/tests/test262/built-ins/Array/prototype/flatMap/depth-always-one.js
+++ b/js/src/tests/test262/built-ins/Array/prototype/flatMap/depth-always-one.js
@@ -1,9 +1,8 @@
-// |reftest| skip -- Array.prototype.flatMap is not supported
 // Copyright (C) 2018 Shilpi Jain and Michael Ficarra. All rights reserved.
 // This code is governed by the BSD license found in the LICENSE file.
 /*---
 esid: sec-array.prototype.flatMap
 description: >
     Behavior when array is depth more than 1
 includes: [compareArray.js]
 features: [Array.prototype.flatMap]
--- a/js/src/tests/test262/built-ins/Array/prototype/flatMap/length.js
+++ b/js/src/tests/test262/built-ins/Array/prototype/flatMap/length.js
@@ -1,9 +1,8 @@
-// |reftest| skip -- Array.prototype.flatMap is not supported
 // Copyright (C) 2018 Shilpi Jain and Michael Ficarra. All rights reserved.
 // This code is governed by the BSD license found in the LICENSE file.
 /*---
 esid: sec-array.prototype.flatMap
 description: Array.prototype.flatMap.length value and descriptor.
 info: >
   17 ECMAScript Standard Built-in Objects
 includes: [propertyHelper.js]
--- a/js/src/tests/test262/built-ins/Array/prototype/flatMap/name.js
+++ b/js/src/tests/test262/built-ins/Array/prototype/flatMap/name.js
@@ -1,9 +1,8 @@
-// |reftest| skip -- Array.prototype.flatMap is not supported
 // Copyright (C) 2018 Shilpi Jain and Michael Ficarra. All rights reserved.
 // This code is governed by the BSD license found in the LICENSE file.
 /*---
 esid: sec-array.prototype.flatmap
 description: Array.prototype.flatmap name value and descriptor.
 info: >
   17 ECMAScript Standard Built-in Objects
 includes: [propertyHelper.js]
--- a/js/src/tests/test262/built-ins/Array/prototype/flatMap/non-callable-argument-throws.js
+++ b/js/src/tests/test262/built-ins/Array/prototype/flatMap/non-callable-argument-throws.js
@@ -1,9 +1,8 @@
-// |reftest| skip -- Array.prototype.flatMap is not supported
 // Copyright (C) 2018 Shilpi Jain and Michael Ficarra. All rights reserved.
 // This code is governed by the BSD license found in the LICENSE file.
 /*---
 esid: sec-array.prototype.flatMap
 description: >
     non callable argument should throw TypeError Exception
 features: [Array.prototype.flatMap]
 ---*/
--- a/js/src/tests/test262/built-ins/Array/prototype/flatMap/non-object-ctor-throws.js
+++ b/js/src/tests/test262/built-ins/Array/prototype/flatMap/non-object-ctor-throws.js
@@ -1,9 +1,8 @@
-// |reftest| skip -- Array.prototype.flatMap is not supported
 // Copyright (C) 2018 Shilpi Jain and Michael Ficarra. All rights reserved.
 // This code is governed by the BSD license found in the LICENSE file.
 /*---
 esid: sec-array.prototype.flatMap
 description: >
     Behavior when `constructor` property is neither an Object nor undefined
     - if IsConstructor(C) is false, throw a TypeError exception.
 features: [Array.prototype.flatMap]
--- a/js/src/tests/test262/built-ins/Array/prototype/flatMap/null-undefined-input-throws.js
+++ b/js/src/tests/test262/built-ins/Array/prototype/flatMap/null-undefined-input-throws.js
@@ -1,9 +1,8 @@
-// |reftest| skip -- Array.prototype.flatMap is not supported
 // Copyright (C) 2018 Shilpi Jain and Michael Ficarra. All rights reserved.
 // This code is governed by the BSD license found in the LICENSE file.
 /*---
 esid: sec-array.prototype.flatMap
 description: >
     null or undefined should throw TypeError Exception
 features: [Array.prototype.flatMap]
 ---*/
--- a/js/src/tests/test262/built-ins/Array/prototype/flatMap/thisArg-argument-strict.js
+++ b/js/src/tests/test262/built-ins/Array/prototype/flatMap/thisArg-argument-strict.js
@@ -1,9 +1,8 @@
-// |reftest| skip -- Array.prototype.flatMap is not supported
 'use strict';
 // Copyright (C) 2018 Shilpi Jain and Michael Ficarra. All rights reserved.
 // This code is governed by the BSD license found in the LICENSE file.
 /*---
 esid: sec-array.prototype.flatMap
 description: >
     Behavior when thisArg is provided
     Array.prototype.flatMap ( mapperFunction [ , thisArg ] )
--- a/js/src/tests/test262/built-ins/Array/prototype/flatten/array-like-objects.js
+++ b/js/src/tests/test262/built-ins/Array/prototype/flatten/array-like-objects.js
@@ -1,9 +1,8 @@
-// |reftest| skip -- Array.prototype.flatten is not supported
 // Copyright (C) 2018 Shilpi Jain and Michael Ficarra. All rights reserved.
 // This code is governed by the BSD license found in the LICENSE file.
 /*---
 esid: sec-array.prototype.flatten
 description: >
     array-like objects can be flattened
 includes: [compareArray.js]
 features: [Array.prototype.flatten]
--- a/js/src/tests/test262/built-ins/Array/prototype/flatten/bound-function-call.js
+++ b/js/src/tests/test262/built-ins/Array/prototype/flatten/bound-function-call.js
@@ -1,9 +1,8 @@
-// |reftest| skip -- Array.prototype.flatten is not supported
 // Copyright (C) 2018 Shilpi Jain and Michael Ficarra. All rights reserved.
 // This code is governed by the BSD license found in the LICENSE file.
 /*---
 esid: sec-array.prototype.flatten
 description: >
     using bound functions
 includes: [compareArray.js]
 features: [Array.prototype.flatten]
--- a/js/src/tests/test262/built-ins/Array/prototype/flatten/empty-array-elements.js
+++ b/js/src/tests/test262/built-ins/Array/prototype/flatten/empty-array-elements.js
@@ -1,9 +1,8 @@
-// |reftest| skip -- Array.prototype.flatten is not supported
 // Copyright (C) 2018 Shilpi Jain and Michael Ficarra. All rights reserved.
 // This code is governed by the BSD license found in the LICENSE file.
 /*---
 esid: sec-array.prototype.flatten
 description: >
     arrays with empty arrays elements
 includes: [compareArray.js]
 features: [Array.prototype.flatten]
--- a/js/src/tests/test262/built-ins/Array/prototype/flatten/empty-object-elements.js
+++ b/js/src/tests/test262/built-ins/Array/prototype/flatten/empty-object-elements.js
@@ -1,9 +1,8 @@
-// |reftest| skip -- Array.prototype.flatten is not supported
 // Copyright (C) 2018 Shilpi Jain and Michael Ficarra. All rights reserved.
 // This code is governed by the BSD license found in the LICENSE file.
 /*---
 esid: sec-array.prototype.flatten
 description: >
     arrays with empty object elements
 includes: [compareArray.js]
 features: [Array.prototype.flatten]
--- a/js/src/tests/test262/built-ins/Array/prototype/flatten/length.js
+++ b/js/src/tests/test262/built-ins/Array/prototype/flatten/length.js
@@ -1,9 +1,8 @@
-// |reftest| skip -- Array.prototype.flatten is not supported
 // Copyright (C) 2018 Shilpi Jain and Michael Ficarra. All rights reserved.
 // This code is governed by the BSD license found in the LICENSE file.
 /*---
 esid: sec-array.prototype.flatten
 description: Array.prototype.flatten.length value and descriptor.
 info: >
   17 ECMAScript Standard Built-in Objects
 includes: [propertyHelper.js]
--- a/js/src/tests/test262/built-ins/Array/prototype/flatten/name.js
+++ b/js/src/tests/test262/built-ins/Array/prototype/flatten/name.js
@@ -1,9 +1,8 @@
-// |reftest| skip -- Array.prototype.flatten is not supported
 // Copyright (C) 2018 Shilpi Jain and Michael Ficarra. All rights reserved.
 // This code is governed by the BSD license found in the LICENSE file.
 /*---
 esid: sec-array.prototype.flatten
 description: >
   Array.prototype.flatten.name value and descriptor.
 info: >
   17 ECMAScript Standard Built-in Objects
--- a/js/src/tests/test262/built-ins/Array/prototype/flatten/non-numeric-depth-should-not-throw.js
+++ b/js/src/tests/test262/built-ins/Array/prototype/flatten/non-numeric-depth-should-not-throw.js
@@ -1,9 +1,8 @@
-// |reftest| skip -- Array.prototype.flatten is not supported
 // Copyright (C) 2018 Shilpi Jain and Michael Ficarra. All rights reserved.
 // This code is governed by the BSD license found in the LICENSE file.
 /*---
 esid: sec-array.prototype.flatten
 description: >
     if the argument is a string or object, the depthNum is 0
 includes: [compareArray.js]
 features: [Array.prototype.flatten]
--- a/js/src/tests/test262/built-ins/Array/prototype/flatten/non-object-ctor-throws.js
+++ b/js/src/tests/test262/built-ins/Array/prototype/flatten/non-object-ctor-throws.js
@@ -1,9 +1,8 @@
-// |reftest| skip -- Array.prototype.flatten is not supported
 // Copyright (C) 2018 Shilpi Jain and Michael Ficarra. All rights reserved.
 // This code is governed by the BSD license found in the LICENSE file.
 /*---
 esid: sec-array.prototype.flatten
 description: >
     Behavior when `constructor` property is neither an Object nor undefined
     - if IsConstructor(C) is false, throw a TypeError exception.
 features: [Array.prototype.flatten]
--- a/js/src/tests/test262/built-ins/Array/prototype/flatten/null-undefined-elements.js
+++ b/js/src/tests/test262/built-ins/Array/prototype/flatten/null-undefined-elements.js
@@ -1,9 +1,8 @@
-// |reftest| skip -- Array.prototype.flatten is not supported
 // Copyright (C) 2018 Shilpi Jain and Michael Ficarra. All rights reserved.
 // This code is governed by the BSD license found in the LICENSE file.
 /*---
 esid: sec-array.prototype.flatten
 description: >
     arrays with null, and undefined
 includes: [compareArray.js]
 features: [Array.prototype.flatten]
--- a/js/src/tests/test262/built-ins/Array/prototype/flatten/null-undefined-input-throws.js
+++ b/js/src/tests/test262/built-ins/Array/prototype/flatten/null-undefined-input-throws.js
@@ -1,9 +1,8 @@
-// |reftest| skip -- Array.prototype.flatten is not supported
 // Copyright (C) 2018 Shilpi Jain and Michael Ficarra. All rights reserved.
 // This code is governed by the BSD license found in the LICENSE file.
 /*---
 esid: sec-array.prototype.flatten
 description: >
     null or undefined should throw TypeError Exception
 features: [Array.prototype.flatten]
 ---*/
--- a/js/src/tests/test262/built-ins/Array/prototype/flatten/positive-infinity.js
+++ b/js/src/tests/test262/built-ins/Array/prototype/flatten/positive-infinity.js
@@ -1,9 +1,8 @@
-// |reftest| skip -- Array.prototype.flatten is not supported
 // Copyright (C) 2018 Shilpi Jain and Michael Ficarra. All rights reserved.
 // This code is governed by the BSD license found in the LICENSE file.
 /*---
 esid: sec-array.prototype.flatten
 description: >
     if the argument is a positive infinity, the depthNum is max depth of the array
 includes: [compareArray.js]
 features: [Array.prototype.flatten]
--- a/js/src/tests/test262/built-ins/Array/prototype/flatten/prop-desc.js
+++ b/js/src/tests/test262/built-ins/Array/prototype/flatten/prop-desc.js
@@ -1,9 +1,8 @@
-// |reftest| skip -- Array.prototype.flatten is not supported
 // Copyright (C) 2018 Shilpi Jain and Michael Ficarra. All rights reserved.
 // This code is governed by the BSD license found in the LICENSE file.
 /*---
 esid: sec-array.prototype.flatten
 es6id: 22.1.3
 description: Property type and descriptor.
 info: >
   17 ECMAScript Standard Built-in Objects
--- a/js/src/tests/test262/built-ins/Array/prototype/flatten/symbol-object-create-null-depth-throws.js
+++ b/js/src/tests/test262/built-ins/Array/prototype/flatten/symbol-object-create-null-depth-throws.js
@@ -1,9 +1,8 @@
-// |reftest| skip -- Array.prototype.flatten is not supported
 // Copyright (C) 2018 Shilpi Jain and Michael Ficarra. All rights reserved.
 // This code is governed by the BSD license found in the LICENSE file.
 /*---
 esid: sec-array.prototype.flatten
 description: >
     if the argument is a Symbol or Object null, it throws exception
 features: [Array.prototype.flatten]
 ---*/
--- a/js/src/wasm/WasmStubs.cpp
+++ b/js/src/wasm/WasmStubs.cpp
@@ -802,18 +802,17 @@ GenerateImportJitExit(MacroAssembler& ma
     }
 #endif
 
     Label oolConvert;
     switch (fi.sig().ret()) {
       case ExprType::Void:
         break;
       case ExprType::I32:
-        masm.convertValueToInt32(JSReturnOperand, ReturnDoubleReg, ReturnReg, &oolConvert,
-                                 /* -0 check */ false);
+        masm.truncateValueToInt32(JSReturnOperand, ReturnDoubleReg, ReturnReg, &oolConvert);
         break;
       case ExprType::I64:
         masm.breakpoint();
         break;
       case ExprType::F32:
         masm.convertValueToFloat(JSReturnOperand, ReturnFloat32Reg, &oolConvert);
         break;
       case ExprType::F64:
--- a/js/xpconnect/tests/chrome/test_xrayToJS.xul
+++ b/js/xpconnect/tests/chrome/test_xrayToJS.xul
@@ -202,16 +202,17 @@ https://bugzilla.mozilla.org/show_bug.cg
   gPrototypeProperties['Array'] =
     ["length", "toSource", "toString", "toLocaleString", "join", "reverse", "sort", "push",
       "pop", "shift", "unshift", "splice", "concat", "slice", "lastIndexOf", "indexOf",
       "includes", "forEach", "map", "reduce", "reduceRight", "filter", "some", "every", "find",
       "findIndex", "copyWithin", "fill", Symbol.iterator, Symbol.unscopables, "entries", "keys",
       "constructor"];
   if (isNightlyBuild) {
     gPrototypeProperties['Array'].push("values");
+    gPrototypeProperties['Array'].push("flatten", "flatMap");
   }
   gConstructorProperties['Array'] =
     constructorProps(["join", "reverse", "sort", "push", "pop", "shift",
                       "unshift", "splice", "concat", "slice", "isArray",
                       "lastIndexOf", "indexOf", "forEach", "map", "filter",
                       "every", "some", "reduce", "reduceRight", "from", "of",
                       Symbol.species]);
   for (var c of typedArrayClasses) {
--- a/layout/base/nsDocumentViewer.cpp
+++ b/layout/base/nsDocumentViewer.cpp
@@ -11,17 +11,16 @@
 #include "mozilla/ServoStyleSet.h"
 #include "nsAutoPtr.h"
 #include "nscore.h"
 #include "nsCOMPtr.h"
 #include "nsCRT.h"
 #include "nsString.h"
 #include "nsReadableUtils.h"
 #include "nsIContent.h"
-#include "nsIContentViewerContainer.h"
 #include "nsIContentViewer.h"
 #include "nsIDocumentViewerPrint.h"
 #include "mozilla/dom/BeforeUnloadEvent.h"
 #include "nsIDocument.h"
 #include "nsPresContext.h"
 #include "nsIPresShell.h"
 #include "mozilla/StyleSetHandle.h"
 #include "mozilla/StyleSetHandleInlines.h"
@@ -4322,17 +4321,17 @@ nsDocumentViewer::SetIsPrintingInDocShel
       }
       mTopContainerWhilePrinting = do_GetWeakReference(parentItem);
     } else {
       parentItem = do_QueryReferent(mTopContainerWhilePrinting);
     }
   }
 
   // Check to see if the DocShell's ContentViewer is printing/PP
-  nsCOMPtr<nsIContentViewerContainer> viewerContainer(do_QueryInterface(parentItem));
+  nsCOMPtr<nsIDocShell> viewerContainer = do_QueryInterface(parentItem);
   if (viewerContainer) {
     viewerContainer->SetIsPrinting(aIsPrintingOrPP);
   }
 
   if (!aParentNode) {
     return;
   }
 
--- a/layout/generic/TextDrawTarget.h
+++ b/layout/generic/TextDrawTarget.h
@@ -394,24 +394,58 @@ public:
                    const IntRect &aSourceRect,
                    const IntPoint &aDestination) override {
     MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
   }
 
   void FillRect(const Rect &aRect,
                 const Pattern &aPattern,
                 const DrawOptions &aOptions = DrawOptions()) override {
-    MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
+    MOZ_RELEASE_ASSERT(aPattern.GetType() == PatternType::COLOR);
+
+    auto rect = mSc.ToRelativeLayoutRect(LayoutDeviceRect::FromUnknownRect(aRect));
+    auto color = wr::ToColorF(static_cast<const ColorPattern&>(aPattern).mColor);
+    mBuilder.PushRect(rect, mClipRect, mBackfaceVisible, color);
   }
 
   void StrokeRect(const Rect &aRect,
                   const Pattern &aPattern,
                   const StrokeOptions &aStrokeOptions,
                   const DrawOptions &aOptions) override {
-    MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
+    MOZ_RELEASE_ASSERT(aPattern.GetType() == PatternType::COLOR &&
+                       aStrokeOptions.mDashLength == 0);
+
+    wr::Line line;
+    line.wavyLineThickness = 0; // dummy value, unused
+    line.color = wr::ToColorF(static_cast<const ColorPattern&>(aPattern).mColor);
+    line.style = wr::LineStyle::Solid;
+
+    // Top horizontal line
+    LayoutDevicePoint top(aRect.x, aRect.y - aStrokeOptions.mLineWidth / 2);
+    LayoutDeviceSize horiSize(aRect.width, aStrokeOptions.mLineWidth);
+    line.bounds = mSc.ToRelativeLayoutRect(LayoutDeviceRect(top, horiSize));
+    line.orientation = wr::LineOrientation::Horizontal;
+    mBuilder.PushLine(mClipRect, mBackfaceVisible, line);
+
+    // Bottom horizontal line
+    LayoutDevicePoint bottom(aRect.x, aRect.YMost() - aStrokeOptions.mLineWidth / 2);
+    line.bounds = mSc.ToRelativeLayoutRect(LayoutDeviceRect(bottom, horiSize));
+    mBuilder.PushLine(mClipRect, mBackfaceVisible, line);
+
+    // Left vertical line
+    LayoutDevicePoint left(aRect.x + aStrokeOptions.mLineWidth / 2, aRect.y + aStrokeOptions.mLineWidth / 2);
+    LayoutDeviceSize vertSize(aStrokeOptions.mLineWidth, aRect.height - aStrokeOptions.mLineWidth);
+    line.bounds = mSc.ToRelativeLayoutRect(LayoutDeviceRect(left, vertSize));
+    line.orientation = wr::LineOrientation::Vertical;
+    mBuilder.PushLine(mClipRect, mBackfaceVisible, line);
+
+    // Right vertical line
+    LayoutDevicePoint right(aRect.XMost() - aStrokeOptions.mLineWidth / 2, aRect.y + aStrokeOptions.mLineWidth / 2);
+    line.bounds = mSc.ToRelativeLayoutRect(LayoutDeviceRect(right, vertSize));
+    mBuilder.PushLine(mClipRect, mBackfaceVisible, line);
   }
 
   void StrokeLine(const Point &aStart,
                   const Point &aEnd,
                   const Pattern &aPattern,
                   const StrokeOptions &aStrokeOptions,
                   const DrawOptions &aOptions) override {
     MOZ_CRASH("TextDrawTarget: Method shouldn't be called");
--- a/layout/generic/nsGridContainerFrame.cpp
+++ b/layout/generic/nsGridContainerFrame.cpp
@@ -4527,22 +4527,27 @@ nsGridContainerFrame::Tracks::FindUsedFl
   // a flex track with its max-content contribution as 'space to fill'
   for (; !iter.AtEnd(); iter.Next()) {
     const GridItemInfo& item = aGridItems[iter.ItemIndex()];
     if (item.mState[mAxis] & ItemState::eIsFlexing) {
       // XXX optimize: bug 1194446
       auto pb = Some(aState.PercentageBasisFor(mAxis, item));
       nscoord spaceToFill = ContentContribution(item, aState, rc, wm, mAxis, pb,
                                                 nsLayoutUtils::PREF_ISIZE);
+      const LineRange& range =
+        mAxis == eLogicalAxisInline ? item.mArea.mCols : item.mArea.mRows;
+      MOZ_ASSERT(range.Extent() >= 1);
+      const auto spannedGaps = range.Extent() - 1;
+      if (spannedGaps > 0) {
+        spaceToFill -= mGridGap * spannedGaps;
+      }
       if (spaceToFill <= 0) {
         continue;
       }
       // ... and all its spanned tracks as input.
-      const LineRange& range =
-        mAxis == eLogicalAxisInline ? item.mArea.mCols : item.mArea.mRows;
       nsTArray<uint32_t> itemFlexTracks;
       for (uint32_t i = range.mStart, end = range.mEnd; i < end; ++i) {
         if (mSizes[i].mState & TrackSize::eFlexMaxSizing) {
           itemFlexTracks.AppendElement(i);
         }
       }
       float itemFr =
         FindFrUnitSize(range, itemFlexTracks, aFunctions, spaceToFill);
--- a/layout/painting/RetainedDisplayListBuilder.cpp
+++ b/layout/painting/RetainedDisplayListBuilder.cpp
@@ -308,53 +308,88 @@ void UpdateASR(nsDisplayItem* aItem,
                                      aContainerASR.value()));
 }
 
 /**
  * Takes two display lists and merges them into an output list.
  *
  * The basic algorithm is:
  *
- * For-each item in the new list:
+ * For-each item i in the new list:
  *     If the item has a matching item in the old list:
- *         Remove items from the bottom of the old list until we reach the matching item:
+ *         Remove items from the start of the old list up until we reach an item that also exists in the new list (leaving the matched item in place):
  *             Add valid items to the merged list, destroy invalid items.
- *         Destroy the matching item from the old list.
- *     Add the item from the new list into the merged list.
- * Add all remaining valid items from the old list into the merged list.
+ *     Add i into the merged list.
+ *     If the start of the old list matches i, remove and destroy it, otherwise mark the old version of i as used.
+ * Add all remaining valid items from the old list into the merged list, skipping over (and destroying) any that are marked as used.
  *
  * If any item has a child display list, then we recurse into the merge
  * algorithm once we match up the new/old versions (if present).
  *
  * Example 1:
  *
  * Old List: A,B,C,D
- * New List: A,D
+ * Modified List: A,D
  * Invalidations: C,D
  *
  * We first match the A items, and add the new one to the merged list.
  * We then match the D items, copy B into the merged list, but not C
  * (since it's invalid). We then add the new D to the list and we're
  * finished.
  *
  * Merged List: A,B,D
  *
- * Example 2:
+ * Example 2 (layout/reftests/retained-dl-zindex-1.html):
  *
  * Old List: A, B
- * New List, B, A
+ * Modified List: B, A
+ * Invalidations: A
+ *
+ * In this example A has been explicitly moved to the back.
+ *
+ * We match the B items, but don't copy A since it's invalid, and then add the
+ * new B into the merged list. We then add A, and we're done.
+ *
+ * Merged List: B, A
+ *
+ * Example 3:
+ *
+ * Old List: A, B
+ * Modified List: B, A
  * Invalidations: -
  *
  * This can happen because a prior merge might have changed the ordering
  * for non-intersecting items.
  *
  * We match the B items, but don't copy A since it's also present in the new list
  * and then add the new B into the merged list. We then add A, and we're done.
  *
  * Merged List: B, A
+ *
+ * Example 4 (layout/reftests/retained-dl-zindex-2.html):
+ *
+ * Element A has two elements covering it (B and C), that don't intersect each
+ * other. We then move C to the back.
+ *
+ * The correct initial ordering has B and C after A, in any order.
+ *
+ * Old List: A, B, C
+ * Modified List: C, A
+ * Invalidations: C
+ *
+ * We match the C items, but don't add anything from the old list because A is present
+ * in both lists. We add C to the merged list, and mark the old version of C as reused.
+ *
+ * We then match A, add the new version the merged list and delete the old version.
+ *
+ * We then process the remainder of the old list, B is added (since it is valid,
+ * and hasn't been mark as reused), C is destroyed since it's marked as reused and
+ * is already present in the merged list.
+ *
+ * Merged List: C, A, B
  */
 void
 RetainedDisplayListBuilder::MergeDisplayLists(nsDisplayList* aNewList,
                                               nsDisplayList* aOldList,
                                               nsDisplayList* aOutList,
                                               Maybe<const ActiveScrolledRoot*>& aOutContainerASR)
 {
   nsDisplayList merged;
@@ -409,49 +444,59 @@ RetainedDisplayListBuilder::MergeDisplay
     newListLookup.Put({ i->Frame(), i->GetPerFrameKey() }, i);
   }
 
   while (nsDisplayItem* newItem = aNewList->RemoveBottom()) {
     if (nsDisplayItem* oldItem = oldListLookup.Get({ newItem->Frame(), newItem->GetPerFrameKey() })) {
       // The new item has a matching counterpart in the old list that we haven't yet reached,
       // so copy all valid items from the old list into the merged list until we get to the
       // matched item.
-      if (!oldItem->IsReused()) {
-        nsDisplayItem* old = nullptr;
-        while ((old = aOldList->RemoveBottom()) && !IsSameItem(newItem, old)) {
-          if (IsAnyAncestorModified(old->FrameForInvalidation())) {
-            // The old item is invalid, discard it.
-            oldListLookup.Remove({ old->Frame(), old->GetPerFrameKey() });
-            old->Destroy(&mBuilder);
-          } else if (newListLookup.Get({ old->Frame(), old->GetPerFrameKey() })) {
-            // The old item is also in the new list, but we haven't got to it yet.
-            // Mark that we've found it, and we'll deal with it when we get to the new
-            // entry.
-            old->SetReused(true);
-          } else {
-            // Recurse into the child list (without a matching new list) to
-            // ensure that we find and remove any invalidated items.
-            if (old->GetChildren()) {
-              nsDisplayList empty;
-              Maybe<const ActiveScrolledRoot*> containerASRForChildren;
-              MergeDisplayLists(&empty, old->GetChildren(),
-                                old->GetChildren(), containerASRForChildren);
-              UpdateASR(old, containerASRForChildren);
-              old->UpdateBounds(&mBuilder);
-            }
-            ReuseItem(old);
+      nsDisplayItem* old = nullptr;
+      while ((old = aOldList->GetBottom()) && old != oldItem) {
+        if (IsAnyAncestorModified(old->FrameForInvalidation())) {
+          // The old item is invalid, discard it.
+          oldListLookup.Remove({ old->Frame(), old->GetPerFrameKey() });
+          aOldList->RemoveBottom();
+          old->Destroy(&mBuilder);
+        } else if (newListLookup.Get({ old->Frame(), old->GetPerFrameKey() })) {
+          // This old item is also in the new list, but we haven't got to it yet.
+          // Stop now, and we'll deal with it when we get to the new entry.
+          break;
+        } else {
+          // Recurse into the child list (without a matching new list) to
+          // ensure that we find and remove any invalidated items.
+          if (old->GetChildren()) {
+            nsDisplayList empty;
+            Maybe<const ActiveScrolledRoot*> containerASRForChildren;
+            MergeDisplayLists(&empty, old->GetChildren(),
+                              old->GetChildren(), containerASRForChildren);
+            UpdateASR(old, containerASRForChildren);
+            old->UpdateBounds(&mBuilder);
           }
+          aOldList->RemoveBottom();
+          ReuseItem(old);
         }
-        MOZ_ASSERT(old && IsSameItem(newItem, old));
-        MOZ_ASSERT(old == oldItem);
+      }
+      bool destroy = false;
+      if (old == oldItem) {
+        // If we advanced the old list until the matching item then we can pop
+        // the matching item off the old list and make sure we clean it up.
+        aOldList->RemoveBottom();
+        destroy = true;
+      } else {
+        // If we didn't get to the matching item, then mark the old item
+        // as being reused (since we're adding the new version to the new
+        // list now) so that we don't add it twice at the end.
+        oldItem->SetReused(true);
       }
 
       // Recursively merge any child lists, destroy the old item and add
       // the new one to the list.
-      if (oldItem->GetType() == DisplayItemType::TYPE_LAYER_EVENT_REGIONS &&
+      if (destroy &&
+          oldItem->GetType() == DisplayItemType::TYPE_LAYER_EVENT_REGIONS &&
           !IsAnyAncestorModified(oldItem->FrameForInvalidation())) {
         // Event regions items don't have anything interesting other than
         // the lists of regions and frames, so we have no need to use the
         // newer item. Always use the old item instead since we assume it's
         // likely to have the bigger lists and merging will be quicker.
         MergeLayerEventRegions(oldItem, newItem);
         ReuseItem(oldItem);
         newItem->Destroy(&mBuilder);
@@ -461,29 +506,32 @@ RetainedDisplayListBuilder::MergeDisplay
           MOZ_ASSERT(newItem->GetChildren());
           Maybe<const ActiveScrolledRoot*> containerASRForChildren;
           MergeDisplayLists(newItem->GetChildren(), oldItem->GetChildren(),
                             newItem->GetChildren(), containerASRForChildren);
           UpdateASR(newItem, containerASRForChildren);
           newItem->UpdateBounds(&mBuilder);
         }
 
-        oldItem->Destroy(&mBuilder);
+        if (destroy) {
+          oldItem->Destroy(&mBuilder);
+        }
         UseItem(newItem);
       }
     } else {
       // If there was no matching item in the old list, then we only need to
       // add the new item to the merged list.
       UseItem(newItem);
     }
   }
 
   // Reuse the remaining valid items from the old display list.
   while (nsDisplayItem* old = aOldList->RemoveBottom()) {
-    if (!IsAnyAncestorModified(old->FrameForInvalidation())) {
+    if (!IsAnyAncestorModified(old->FrameForInvalidation()) &&
+        !old->IsReused()) {
       if (old->GetChildren()) {
         // We are calling MergeDisplayLists() to ensure that the display items
         // with modified or deleted children will be correctly handled.
         // Passing an empty new display list as an argument skips the merging
         // loop above and jumps back here.
         nsDisplayList empty;
         Maybe<const ActiveScrolledRoot*> containerASRForChildren;
 
--- a/layout/printing/nsPrintJob.cpp
+++ b/layout/printing/nsPrintJob.cpp
@@ -101,17 +101,16 @@ static const char kPrintingPromptService
 #include "nsIInterfaceRequestor.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIDocShellTreeOwner.h"
 #include "nsIWebBrowserChrome.h"
 #include "nsIBaseWindow.h"
 #include "nsILayoutHistoryState.h"
 #include "nsFrameManager.h"
 #include "mozilla/ReflowInput.h"
-#include "nsIContentViewerContainer.h"
 #include "nsIContentViewer.h"
 #include "nsIDocumentViewerPrint.h"
 
 #include "nsFocusManager.h"
 #include "nsRange.h"
 #include "nsCDefaultURIFixup.h"
 #include "nsIURIFixup.h"
 #include "mozilla/dom/Element.h"
--- a/layout/reftests/css-grid/grid-column-gap-002-ref.html
+++ b/layout/reftests/css-grid/grid-column-gap-002-ref.html
@@ -13,17 +13,17 @@ body,html { color:black; background:whit
 .grid {
   display: block;
   border: dashed blue;
   float: left;
   clear: left;
 }
 
 .c1 { grid-column: 1 / span 2; min-width:40px; }
-.r1 { grid-column: 2 / span 3; min-width:70px; margin-left:41px; }
+.r1 { grid-column: 2 / span 3; min-width:70px; margin-left:40px; }
 .c3 { grid-column: 3 / span 1; min-width:0; margin-left:138px; }
 
 span {
   display: block;
   float: left;
   clear: left;
   background: gray;
   border-style: solid;
@@ -34,31 +34,31 @@ span {
 }
 
 x { display:inline-block; width:10px; height:18px; }
   </style>
 </head>
 <body>
 
 <div class="grid">
-<span class="c1" style="width:43px"><x></x></span>
-<span class="r1" style="width:79px"><x></x></span>
+<span class="c1" style="width:40px"><x></x></span>
+<span class="r1" style="width:74px"><x></x></span>
 </div>
 
 <div class="grid">
 <span class="c1" style="width:49px"><x></x></span>
 <span class="r1" style="margin-left:44px; width:88px"><x></x></span>
 <span class="c3" style="margin-left:83px; "><x></x></span>
 </div>
 
 <div class="grid">
-<span class="c1" style="width:43px"><x></x></span>
-<span class="r1" style="width:79px"><x></x></span>
-<span class="r1" style="width:79px"><x></x></span>
-<span class="r1" style="width:79px"><x></x></span>
+<span class="c1" style="width:40px"><x></x></span>
+<span class="r1" style="width:74px"><x></x></span>
+<span class="r1" style="width:74px"><x></x></span>
+<span class="r1" style="width:74px"><x></x></span>
 </div>
 <div class="grid" style="">
 <span class="c1" style="width:413px"><x></x></span>
 <span class="r1" style="margin-left:44px; width:452px"><x></x></span>
 <span class="c3" style="margin-left:447px;"><x></x></span>
 </div>
 
 <div class="grid" style="width:500px;">
@@ -71,20 +71,20 @@ x { display:inline-block; width:10px; he
 <div class="grid" style="">
 <span class="c1" style="width:513px"><x></x></span>
 <span class="r1" style="margin-left:44px; width:552px"><x></x></span>
 <span class="c3" style="margin-left:547px;"><x></x></span>
 </div>
 
 <div class="grid">
 <span class="c1" style="width:100px"><x></x></span>
-<span class="r1" style="margin-left:117px; margin-right:11px; width:300px"><x></x></span>
-<span class="c3" style="margin-left:228px; width:83px"><x></x></span>
+<span class="r1" style="margin-left:115px; margin-right:5px; width:300px"><x></x></span>
+<span class="c3" style="margin-left:224px; width:81px"><x></x></span>
 </div>
 
 <div class="grid">
 <span class="c1" style="width:100px; border-sizing:border-box"><x></x></span>
-<span class="r1" style="margin-left:117px; margin-right:11px; width:300px; border-sizing:border-box"><x></x></span>
-<span class="c3" style="margin-left:228px; width:83px"><x></x></span>
+<span class="r1" style="margin-left:115px; margin-right:5px; width:300px; border-sizing:border-box"><x></x></span>
+<span class="c3" style="margin-left:224px; width:81px"><x></x></span>
 </div>
 
 </body>
 </html>
--- a/layout/reftests/display-list/reftest.list
+++ b/layout/reftests/display-list/reftest.list
@@ -3,14 +3,16 @@ skip-if(!retainedDisplayList) == retaine
 skip-if(!retainedDisplayList) == retained-dl-frame-created-1.html retained-dl-style-change-1-ref.html
 skip-if(!retainedDisplayList) == retained-dl-style-change-stacking-context-1.html retained-dl-style-change-stacking-context-1-ref.html
 skip-if(!retainedDisplayList||!asyncPan) == retained-dl-async-scrolled-1.html retained-dl-async-scrolled-1-ref.html
 skip-if(!retainedDisplayList) == retained-dl-remove-for-ancestor-change-1.html retained-dl-remove-for-ancestor-change-1-ref.html
 skip-if(!retainedDisplayList) == retained-dl-scroll-out-of-view-1.html retained-dl-scroll-out-of-view-1-ref.html
 skip-if(!retainedDisplayList) == retained-dl-displayport-1.html retained-dl-displayport-1-ref.html
 skip-if(!retainedDisplayList) == retained-dl-prerender-transform-1.html retained-dl-prerender-transform-1-ref.html
 == retained-dl-wrap-list.html retained-dl-wrap-list-ref.html
+== retained-dl-zindex-1.html retained-dl-zindex-1-ref.html
+== retained-dl-zindex-2.html retained-dl-zindex-2-ref.html
 fuzzy(1,235200) == 1413073.html 1413073-ref.html
 == 1416291.html 1416291-ref.html
 == 1417601-1.html 1417601-1-ref.html
 == 1418945-1.html 1418945-1-ref.html
 skip-if(Android) == 1428993-1.html 1428993-1-ref.html
 == 1428993-2.html 1428993-2-ref.html
new file mode 100644
--- /dev/null
+++ b/layout/reftests/display-list/retained-dl-zindex-1-ref.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+div {
+  width: 200px;
+  height: 200px;
+  position:relative;
+  will-change: transform;
+}
+#one {
+  top:120px;
+  background-color:blue;
+}
+#two {
+  top: -200px;
+  left: 100px;
+  background-color:green;
+  z-index: -1;
+}
+</style>
+</head>
+<body>
+  <div id="one"></div>
+  <div id="two"></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/display-list/retained-dl-zindex-1.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+<style>
+div {
+  width: 200px;
+  height: 200px;
+  position:relative;
+  will-change: transform;
+}
+#one {
+  top:120px;
+  background-color:blue;
+}
+#two {
+  top: -200px;
+  left: 100px;
+  background-color:green;
+  z-index: 1;
+}
+</style>
+</head>
+<body>
+  <div id="one"></div>
+  <div id="two"></div>
+</body>
+<script>
+function doTest() {
+  document.getElementById("two").style.zIndex = -1;
+  document.documentElement.removeAttribute("class");
+}
+
+window.addEventListener("MozReftestInvalidate", doTest);
+</script>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/display-list/retained-dl-zindex-2-ref.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+div {
+  width: 200px;
+  height: 200px;
+  position:relative;
+  will-change:transform;
+}
+#one {
+  top:120px;
+  background-color:blue;
+}
+#two {
+  top: -200px;
+  left: 100px;
+  background-color:green;
+}
+#three {
+  top: -180px;
+  left: 100px;
+  background-color:red;
+  z-index: -1;
+}
+</style>
+</head>
+<body>
+  <div id="one"></div>
+  <div id="two"></div>
+  <div id="three"></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/display-list/retained-dl-zindex-2.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+<style>
+div {
+  width: 200px;
+  height: 200px;
+  position:relative;
+  will-change:transform;
+}
+#one {
+  top:120px;
+  background-color:blue;
+}
+#two {
+  top: -200px;
+  left: 100px;
+  background-color:green;
+}
+#three {
+  top: -180px;
+  left: 100px;
+  background-color:red;
+  z-index: 1;
+}
+</style>
+</head>
+<body>
+  <div id="one"></div>
+  <div id="two"></div>
+  <div id="three"></div>
+</body>
+<script>
+function doTest() {
+  document.getElementById("three").style.zIndex = -1;
+  document.documentElement.removeAttribute("class");
+}
+
+window.addEventListener("MozReftestInvalidate", doTest);
+</script>
+</html>
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -1284,24 +1284,20 @@ pref("dom.storage.testing", false);
 pref("dom.send_after_paint_to_content", false);
 
 // Timeout clamp in ms for timeouts we clamp
 pref("dom.min_timeout_value", 4);
 // And for background windows
 pref("dom.min_background_timeout_value", 1000);
 // Timeout clamp in ms for tracking timeouts we clamp
 // Note that this requires the privacy.trackingprotection.annotate_channels pref to be on in order to have any effect.
-#ifdef NIGHTLY_BUILD
-pref("dom.min_tracking_timeout_value", 10000);
-#else
 pref("dom.min_tracking_timeout_value", 4);
-#endif
 // And for background windows
 // Note that this requires the privacy.trackingprotection.annotate_channels pref to be on in order to have any effect.
-pref("dom.min_tracking_background_timeout_value", 10000);
+pref("dom.min_tracking_background_timeout_value", 4);
 // Delay in ms from document load until we start throttling background timeouts.
 pref("dom.timeout.throttling_delay", 30000);
 
 // Time (in ms) that it takes to regenerate 1ms.
 pref("dom.timeout.background_budget_regeneration_rate", 100);
 // Maximum value (in ms) for the background budget. Only valid for
 // values greater than 0.
 pref("dom.timeout.background_throttling_max_budget", 50);
--- a/netwerk/cache2/CacheFileMetadata.cpp
+++ b/netwerk/cache2/CacheFileMetadata.cpp
@@ -268,18 +268,20 @@ CacheFileMetadata::WriteMetadata(uint32_
     p += mHashCount * sizeof(CacheHash::Hash16_t);
   }
   mMetaHdr.WriteToBuf(p);
   p += sizeof(CacheFileMetadataHeader);
   memcpy(p, mKey.get(), mKey.Length());
   p += mKey.Length();
   *p = 0;
   p++;
-  memcpy(p, mBuf, mElementsSize);
-  p += mElementsSize;
+  if (mElementsSize) {
+    memcpy(p, mBuf, mElementsSize);
+    p += mElementsSize;
+  }
 
   CacheHash::Hash32_t hash;
   hash = CacheHash::Hash(mWriteBuf + sizeof(uint32_t),
                          p - mWriteBuf - sizeof(uint32_t));
   NetworkEndian::writeUint32(mWriteBuf, hash);
 
   NetworkEndian::writeUint32(p, aOffset);
   p += sizeof(uint32_t);
--- a/security/manager/ssl/tests/unit/tlsserver/lib/OCSPCommon.cpp
+++ b/security/manager/ssl/tests/unit/tlsserver/lib/OCSPCommon.cpp
@@ -2,46 +2,38 @@
  * 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 "OCSPCommon.h"
 
 #include <stdio.h>
 
 #include "pkixtestutil.h"
+#include "pkixtestnss.h"
 #include "TLSServer.h"
 #include "secder.h"
 #include "secerr.h"
 
-namespace mozilla { namespace pkix { namespace test {
-
-// Ownership of privateKey is transfered.
-TestKeyPair* CreateTestKeyPair(const TestPublicKeyAlgorithm publicKeyAlg,
-                               const SECKEYPublicKey& publicKey,
-                               SECKEYPrivateKey* privateKey);
-
-} } } // namespace mozilla::pkix::test
-
 using namespace mozilla;
 using namespace mozilla::pkix;
 using namespace mozilla::pkix::test;
 using namespace mozilla::test;
 
 static TestKeyPair*
 CreateTestKeyPairFromCert(const UniqueCERTCertificate& cert)
 {
-  UniqueSECKEYPrivateKey privateKey(PK11_FindKeyByAnyCert(cert.get(), nullptr));
+  ScopedSECKEYPrivateKey privateKey(PK11_FindKeyByAnyCert(cert.get(), nullptr));
   if (!privateKey) {
     return nullptr;
   }
-  UniqueSECKEYPublicKey publicKey(CERT_ExtractPublicKey(cert.get()));
+  ScopedSECKEYPublicKey publicKey(CERT_ExtractPublicKey(cert.get()));
   if (!publicKey) {
     return nullptr;
   }
-  return CreateTestKeyPair(RSA_PKCS1(), *publicKey.get(), privateKey.release());
+  return CreateTestKeyPair(RSA_PKCS1(), publicKey, privateKey);
 }
 
 SECItemArray*
 GetOCSPResponseForType(OCSPResponseType aORT, const UniqueCERTCertificate& aCert,
                        const UniquePLArenaPool& aArena,
                        const char* aAdditionalCertName, time_t aThisUpdateSkew)
 {
   MOZ_ASSERT(aArena);
--- a/security/nss.symbols
+++ b/security/nss.symbols
@@ -385,16 +385,17 @@ PK11_GetTokenName
 PK11_GetTokenURI
 PK11_HasAttributeSet
 PK11_HashBuf
 PK11_HasRootCerts
 PK11_ImportCert
 PK11_ImportCertForKey
 PK11_ImportCRL
 PK11_ImportDERPrivateKeyInfoAndReturnKey
+PK11_ImportEncryptedPrivateKeyInfoAndReturnKey
 PK11_ImportPublicKey
 PK11_ImportSymKey
 PK11_InitPin
 PK11_IsDisabled
 PK11_IsFIPS
 PK11_IsFriendly
 PK11_IsHW
 PK11_IsInternal
--- a/security/pkix/test/lib/pkixtestnss.cpp
+++ b/security/pkix/test/lib/pkixtestnss.cpp
@@ -18,16 +18,17 @@
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #include "pkixtestutil.h"
+#include "pkixtestnss.h"
 
 #include <limits>
 
 #include "cryptohi.h"
 #include "keyhi.h"
 #include "nss.h"
 #include "pk11pqg.h"
 #include "pk11pub.h"
@@ -37,27 +38,28 @@
 #include "prinit.h"
 #include "secerr.h"
 #include "secitem.h"
 
 namespace mozilla { namespace pkix { namespace test {
 
 namespace {
 
-typedef ScopedPtr<SECKEYPublicKey, SECKEY_DestroyPublicKey>
-  ScopedSECKEYPublicKey;
-typedef ScopedPtr<SECKEYPrivateKey, SECKEY_DestroyPrivateKey>
-  ScopedSECKEYPrivateKey;
-
 inline void
 SECITEM_FreeItem_true(SECItem* item)
 {
   SECITEM_FreeItem(item, true);
 }
 
+inline void
+SECKEY_DestroyEncryptedPrivateKeyInfo_true(SECKEYEncryptedPrivateKeyInfo* e)
+{
+  SECKEY_DestroyEncryptedPrivateKeyInfo(e, true);
+}
+
 typedef mozilla::pkix::ScopedPtr<SECItem, SECITEM_FreeItem_true> ScopedSECItem;
 
 TestKeyPair* GenerateKeyPairInner();
 
 void
 InitNSSIfNeeded()
 {
   if (NSS_NoDB_Init(nullptr) != SECSuccess) {
@@ -73,22 +75,25 @@ InitReusedKeyPair()
   InitNSSIfNeeded();
   reusedKeyPair.reset(GenerateKeyPairInner());
   return reusedKeyPair ? PR_SUCCESS : PR_FAILURE;
 }
 
 class NSSTestKeyPair final : public TestKeyPair
 {
 public:
-  // NSSTestKeyPair takes ownership of privateKey.
   NSSTestKeyPair(const TestPublicKeyAlgorithm& publicKeyAlg,
                  const ByteString& spk,
-                 SECKEYPrivateKey* privateKey)
+                 const ByteString& encryptedPrivateKey,
+                 const ByteString& encryptionAlgorithm,
+                 const ByteString& encryptionParams)
     : TestKeyPair(publicKeyAlg, spk)
-    , privateKey(privateKey)
+    , encryptedPrivateKey(encryptedPrivateKey)
+    , encryptionAlgorithm(encryptionAlgorithm)
+    , encryptionParams(encryptionParams)
   {
   }
 
   Result SignData(const ByteString& tbs,
                   const TestSignatureAlgorithm& signatureAlgorithm,
                   /*out*/ ByteString& signature) const override
   {
     SECOidTag oidTag;
@@ -116,63 +121,127 @@ public:
           oidTag = SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION;
           break;
         MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM
       }
     } else {
       abort();
     }
 
+    ScopedPtr<PK11SlotInfo, PK11_FreeSlot> slot(PK11_GetInternalSlot());
+    if (!slot) {
+      return MapPRErrorCodeToResult(PR_GetError());
+    }
+    SECItem encryptedPrivateKeyInfoItem = {
+      siBuffer,
+      const_cast<uint8_t*>(encryptedPrivateKey.data()),
+      static_cast<unsigned int>(encryptedPrivateKey.length())
+    };
+    SECItem encryptionAlgorithmItem = {
+      siBuffer,
+      const_cast<uint8_t*>(encryptionAlgorithm.data()),
+      static_cast<unsigned int>(encryptionAlgorithm.length())
+    };
+    SECItem encryptionParamsItem = {
+      siBuffer,
+      const_cast<uint8_t*>(encryptionParams.data()),
+      static_cast<unsigned int>(encryptionParams.length())
+    };
+    SECKEYEncryptedPrivateKeyInfo encryptedPrivateKeyInfo = {
+      nullptr,
+      { encryptionAlgorithmItem, encryptionParamsItem },
+      encryptedPrivateKeyInfoItem
+    };
+    SECItem passwordItem = { siBuffer, nullptr, 0 };
+    SECItem publicValueItem = {
+      siBuffer,
+      const_cast<uint8_t*>(subjectPublicKey.data()),
+      static_cast<unsigned int>(subjectPublicKey.length())
+    };
+    SECKEYPrivateKey* privateKey;
+    // This should always be an RSA key (we'll have aborted above if we're not
+    // doing an RSA signature).
+    if (PK11_ImportEncryptedPrivateKeyInfoAndReturnKey(
+          slot.get(), &encryptedPrivateKeyInfo, &passwordItem, nullptr,
+          &publicValueItem, false, false, rsaKey, KU_ALL, &privateKey,
+          nullptr) != SECSuccess) {
+      return MapPRErrorCodeToResult(PR_GetError());
+    }
+    ScopedSECKEYPrivateKey scopedPrivateKey(privateKey);
     SECItem signatureItem;
     if (SEC_SignData(&signatureItem, tbs.data(),
                      static_cast<int>(tbs.length()),
-                     privateKey.get(), oidTag) != SECSuccess) {
+                     scopedPrivateKey.get(), oidTag) != SECSuccess) {
       return MapPRErrorCodeToResult(PR_GetError());
     }
     signature.assign(signatureItem.data, signatureItem.len);
     SECITEM_FreeItem(&signatureItem, false);
     return Success;
   }
 
   TestKeyPair* Clone() const override
   {
-    ScopedSECKEYPrivateKey
-      privateKeyCopy(SECKEY_CopyPrivateKey(privateKey.get()));
-    if (!privateKeyCopy) {
-      return nullptr;
-    }
     return new (std::nothrow) NSSTestKeyPair(publicKeyAlg,
                                              subjectPublicKey,
-                                             privateKeyCopy.release());
+                                             encryptedPrivateKey,
+                                             encryptionAlgorithm,
+                                             encryptionParams);
   }
 
 private:
-  ScopedSECKEYPrivateKey privateKey;
+  const ByteString encryptedPrivateKey;
+  const ByteString encryptionAlgorithm;
+  const ByteString encryptionParams;
 };
 
 } // namespace
 
 // This private function is also used by Gecko's PSM test framework
 // (OCSPCommon.cpp).
-//
-// Ownership of privateKey is transfered.
 TestKeyPair* CreateTestKeyPair(const TestPublicKeyAlgorithm publicKeyAlg,
-                               const SECKEYPublicKey& publicKey,
-                               SECKEYPrivateKey* privateKey)
+                               const ScopedSECKEYPublicKey& publicKey,
+                               const ScopedSECKEYPrivateKey& privateKey)
 {
   ScopedPtr<CERTSubjectPublicKeyInfo, SECKEY_DestroySubjectPublicKeyInfo>
-    spki(SECKEY_CreateSubjectPublicKeyInfo(&publicKey));
+    spki(SECKEY_CreateSubjectPublicKeyInfo(publicKey.get()));
   if (!spki) {
     return nullptr;
   }
   SECItem spkDER = spki->subjectPublicKey;
   DER_ConvertBitString(&spkDER); // bits to bytes
-  return new (std::nothrow) NSSTestKeyPair(publicKeyAlg,
-                                           ByteString(spkDER.data, spkDER.len),
-                                           privateKey);
+  ScopedPtr<PK11SlotInfo, PK11_FreeSlot> slot(PK11_GetInternalSlot());
+  if (!slot) {
+    return nullptr;
+  }
+  // Because NSSTestKeyPair isn't tracked by XPCOM and won't otherwise be aware
+  // of shutdown, we don't have a way to release NSS resources at the
+  // appropriate time. To work around this, NSSTestKeyPair doesn't hold on to
+  // NSS resources. Instead, we export the generated private key part as an
+  // encrypted blob (with an empty password and fairly lame encryption). When we
+  // need to use it (e.g. to sign something), we decrypt it and create a
+  // temporary key object.
+  SECItem passwordItem = { siBuffer, nullptr, 0 };
+  ScopedPtr<SECKEYEncryptedPrivateKeyInfo,
+            SECKEY_DestroyEncryptedPrivateKeyInfo_true> encryptedPrivateKey(
+    PK11_ExportEncryptedPrivKeyInfo(
+      slot.get(), SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_3KEY_TRIPLE_DES_CBC,
+      &passwordItem, privateKey.get(), 1, nullptr));
+  if (!encryptedPrivateKey) {
+    return nullptr;
+  }
+
+  return new (std::nothrow) NSSTestKeyPair(
+    publicKeyAlg,
+    ByteString(spkDER.data, spkDER.len),
+    ByteString(encryptedPrivateKey->encryptedData.data,
+               encryptedPrivateKey->encryptedData.len),
+    ByteString(encryptedPrivateKey->algorithm.algorithm.data,
+               encryptedPrivateKey->algorithm.algorithm.len),
+    ByteString(encryptedPrivateKey->algorithm.parameters.data,
+               encryptedPrivateKey->algorithm.parameters.len));
 }
 
 namespace {
 
 TestKeyPair*
 GenerateKeyPairInner()
 {
   ScopedPtr<PK11SlotInfo, PK11_FreeSlot> slot(PK11_GetInternalSlot());
@@ -189,17 +258,17 @@ GenerateKeyPairInner()
     params.pe = 3;
     SECKEYPublicKey* publicKeyTemp = nullptr;
     ScopedSECKEYPrivateKey
       privateKey(PK11_GenerateKeyPair(slot.get(), CKM_RSA_PKCS_KEY_PAIR_GEN,
                                       &params, &publicKeyTemp, false, true,
                                       nullptr));
     ScopedSECKEYPublicKey publicKey(publicKeyTemp);
     if (privateKey) {
-      return CreateTestKeyPair(RSA_PKCS1(), *publicKey, privateKey.release());
+      return CreateTestKeyPair(RSA_PKCS1(), publicKey, privateKey);
     }
 
     assert(!publicKeyTemp);
 
     if (PR_GetError() != SEC_ERROR_PKCS11_FUNCTION_FAILED) {
       break;
     }
 
@@ -270,17 +339,17 @@ GenerateDSSKeyPair()
   ScopedSECKEYPrivateKey
     privateKey(PK11_GenerateKeyPair(slot.get(), CKM_DSA_KEY_PAIR_GEN,
                                     const_cast<PQGParams*>(&PARAMS),
                                     &publicKeyTemp, false, true, nullptr));
   if (!privateKey) {
     return nullptr;
   }
   ScopedSECKEYPublicKey publicKey(publicKeyTemp);
-  return CreateTestKeyPair(DSS(), *publicKey, privateKey.release());
+  return CreateTestKeyPair(DSS(), publicKey, privateKey);
 }
 
 Result
 TestVerifyECDSASignedDigest(const SignedDigest& signedDigest,
                             Input subjectPublicKeyInfo)
 {
   InitNSSIfNeeded();
   return VerifyECDSASignedDigestNSS(signedDigest, subjectPublicKeyInfo,
new file mode 100644
--- /dev/null
+++ b/security/pkix/test/lib/pkixtestnss.h
@@ -0,0 +1,49 @@
+/* -*- 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 code is made available to you under your choice of the following sets
+ * of licensing terms:
+ */
+/* 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/.
+ */
+/* Copyright 2018 Mozilla Contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// This file provides some implementation-specific test utilities. This is only
+// necessary because some PSM xpcshell test utilities overlap in functionality
+// with these test utilities, so the underlying implementation is shared.
+
+#ifndef mozilla_pkix_test_pkixtestnss_h
+#define mozilla_pkix_test_pkixtestnss_h
+
+#include "keyhi.h"
+#include "keythi.h"
+#include "pkixtestutil.h"
+
+namespace mozilla { namespace pkix { namespace test {
+
+typedef ScopedPtr<SECKEYPublicKey, SECKEY_DestroyPublicKey>
+  ScopedSECKEYPublicKey;
+typedef ScopedPtr<SECKEYPrivateKey, SECKEY_DestroyPrivateKey>
+  ScopedSECKEYPrivateKey;
+
+TestKeyPair* CreateTestKeyPair(const TestPublicKeyAlgorithm publicKeyAlg,
+                               const ScopedSECKEYPublicKey& publicKey,
+                               const ScopedSECKEYPrivateKey& privateKey);
+
+} } } // namespace mozilla::pkix::test
+
+#endif // mozilla_pkix_test_pkixtestnss_h
--- a/security/pkix/test/lib/pkixtestutil.h
+++ b/security/pkix/test/lib/pkixtestutil.h
@@ -17,18 +17,18 @@
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
-#ifndef mozilla_pkix_test_pkixtestutils_h
-#define mozilla_pkix_test_pkixtestutils_h
+#ifndef mozilla_pkix_test_pkixtestutil_h
+#define mozilla_pkix_test_pkixtestutil_h
 
 #include <ctime>
 #include <stdint.h> // Some Mozilla-supported compilers lack <cstdint>
 #include <string>
 #include <cstring>
 
 #include "pkix/pkixtypes.h"
 #include "../../lib/ScopedPtr.h"
@@ -440,9 +440,9 @@ public:
   std::time_t nextUpdate;
   bool includeNextUpdate;
 };
 
 ByteString CreateEncodedOCSPResponse(OCSPResponseContext& context);
 
 } } } // namespace mozilla::pkix::test
 
-#endif // mozilla_pkix_test_pkixtestutils_h
+#endif // mozilla_pkix_test_pkixtestutil_h
--- a/security/sandbox/linux/moz.build
+++ b/security/sandbox/linux/moz.build
@@ -73,16 +73,19 @@ SOURCES += [
     'SandboxUtil.cpp',
 ]
 
 if CONFIG['MOZ_GMP_SANDBOX']:
     SOURCES += [
         'SandboxOpenedFiles.cpp',
     ]
 
+if CONFIG['MOZ_ALSA']:
+    DEFINES['MOZ_ALSA'] = True
+
 # This copy of SafeSPrintf doesn't need to avoid the Chromium logging
 # dependency like the one in libxul does, but this way the behavior is
 # consistent.  See also the comment in SandboxLogging.h.
 SOURCES['../chromium/base/strings/safe_sprintf.cc'].flags += ['-DNDEBUG']
 
 if CONFIG['CC_TYPE'] in ('clang', 'gcc'):
     # Keep clang from warning about intentional 'switch' fallthrough in icu_utf.cc:
     SOURCES['../chromium/base/third_party/icu/icu_utf.cc'].flags += ['-Wno-implicit-fallthrough']
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -12445,16 +12445,25 @@
     "record_in_processes": ["main", "content"],
     "alert_emails": ["gfx-telemetry-alerts@mozilla.com","msreckovic@mozilla.com"],
     "expires_in_version": "never",
     "kind": "enumerated",
     "bug_numbers": [1229961],
     "n_values": 12,
     "description": "Plugin drawing model. 0 when windowed, otherwise NPDrawingModel + 1."
   },
+  "DOM_SCRIPT_KIND": {
+    "record_in_processes": ["main", "content"],
+    "alert_emails": ["jcoppeard@mozilla.com"],
+    "expires_in_version": "63",
+    "kind": "categorical",
+    "bug_numbers": [1430145],
+    "labels": ["ClassicScript", "ModuleScript"],
+    "description": "Record the kind of every script loaded in a document."
+  },
   "DOM_SCRIPT_SRC_ENCODING": {
     "record_in_processes": ["main", "content"],
     "alert_emails": ["dteller@mozilla.com"],
     "expires_in_version": "61",
     "kind": "count",
     "keyed": true,
     "bug_numbers": [1344152],
     "description": "Note the encoding (e.g. 'UTF-8', 'windows-1252', 'ASCII') of each external <script> element that is successfully loaded and decoded."
@@ -12597,16 +12606,25 @@
     "expires_in_version": "62",
     "kind": "linear",
     "low": 1,
     "high": 200,
     "n_buckets": 50,
     "bug_numbers": [1362114],
     "description": "Number of script tags evaluated per document."
   },
+  "DOM_SCRIPT_PRELOAD_RESULT": {
+    "record_in_processes": ["main", "content"],
+    "alert_emails": ["jcoppeard@mozilla.com"],
+    "expires_in_version": "63",
+    "kind": "categorical",
+    "bug_numbers": [1430145],
+    "labels": ["Used", "RejectedByPolicy", "RequestMismatch", "LoadError", "NotUsed"],
+    "description": "Whether a preloaded script was used or the reason it was not used."
+  },
   "VIDEO_FASTSEEK_USED": {
     "record_in_processes": ["main", "content"],
     "alert_emails": ["cpearce@mozilla.com", "tkuo@mozilla.com"],
     "expires_in_version": "60",
     "bug_numbers": [1245982],
     "kind": "count",
     "description": "Uses of HTMLMediaElement.fastSeek"
   },
--- a/toolkit/content/preferencesBindings.js
+++ b/toolkit/content/preferencesBindings.js
@@ -355,20 +355,31 @@ const Preferences = window.Preferences =
       /**
        * Initialize a UI element property with a value. Handles the case
        * where an element has not yet had a XBL binding attached for it and
        * the property setter does not yet exist by setting the same attribute
        * on the XUL element using DOM apis and assuming the element's
        * constructor or property getters appropriately handle this state.
        */
       function setValue(element, attribute, value) {
-        if (attribute in element)
+        if (attribute in element) {
           element[attribute] = value;
-        else
+        } else if (attribute === "checked") {
+          // The "checked" attribute can't simply be set to the specified value;
+          // it has to be set if the value is true and removed if the value
+          // is false in order to be interpreted correctly by the element.
+          if (value) {
+            // We can set it to anything; convention is to set it to itself.
+            element.setAttribute(attribute, attribute);
+          } else {
+            element.removeAttribute(attribute);
+          }
+        } else {
           element.setAttribute(attribute, value);
+        }
       }
       if (aElement.localName == "checkbox" ||
           aElement.localName == "listitem")
         setValue(aElement, "checked", val);
       else if (aElement.localName == "colorpicker")
         setValue(aElement, "color", val);
       else if (aElement.localName == "textbox") {
         // XXXmano Bug 303998: Avoid a caret placement issue if either the
--- a/xpcom/string/nsTDependentString.h
+++ b/xpcom/string/nsTDependentString.h
@@ -100,21 +100,31 @@ public:
   }
 
   // Create a nsTDependentSubstring to be bound later
   nsTDependentString()
     : string_type()
   {
   }
 
-  // XXX are you sure??
-  // auto-generated copy-constructor OK
-  // auto-generated copy-assignment operator OK
   // auto-generated destructor OK
 
+  nsTDependentString(self_type&& aStr)
+    : string_type()
+  {
+    Rebind(aStr, /* aStartPos = */ 0);
+    aStr.SetToEmptyBuffer();
+  }
+
+  explicit
+  nsTDependentString(const self_type& aStr)
+    : string_type()
+  {
+    Rebind(aStr, /* aStartPos = */ 0);
+  }
 
   /**
    * allow this class to be bound to a different string...
    */
 
   using nsTString<T>::Rebind;
   void Rebind(const char_type* aData)
   {
@@ -123,14 +133,15 @@ public:
 
   void Rebind(const char_type* aStart, const char_type* aEnd);
   void Rebind(const string_type&, uint32_t aStartPos);
 
 private:
 
   // NOT USED
   nsTDependentString(const substring_tuple_type&) = delete;
+  self_type& operator=(const self_type& aStr) = delete;
 };
 
 extern template class nsTDependentString<char>;
 extern template class nsTDependentString<char16_t>;
 
 #endif
--- a/xpcom/tests/gtest/TestStrings.cpp
+++ b/xpcom/tests/gtest/TestStrings.cpp
@@ -63,16 +63,62 @@ TEST(Strings, IsChar)
   a_ctest.AssignLiteral("hello");
   // This should cause a compilation failure.
   a_ctest.AssignLiteral(u"hello");
   a_test.AssignLiteral(u"hello");
   a_test.AssignLiteral("hello");
 #endif
 }
 
+TEST(Strings, DependentStrings)
+{
+  // A few tests that make sure copying nsTDependentStrings behaves properly.
+  using DataFlags = mozilla::detail::StringDataFlags;
+
+  {
+    // Test copy ctor.
+    nsDependentCString tmp("foo");
+    auto data = tmp.Data();
+    nsDependentCString foo(tmp);
+    // Neither string should be using a shared buffer.
+    EXPECT_FALSE(tmp.GetDataFlags() & DataFlags::SHARED);
+    EXPECT_FALSE(foo.GetDataFlags() & DataFlags::SHARED);
+    // Both strings should be pointing to the original buffer.
+    EXPECT_EQ(data, tmp.Data());
+    EXPECT_EQ(data, foo.Data());
+  }
+  {
+    // Test move ctor.
+    nsDependentCString tmp("foo");
+    auto data = tmp.Data();
+    nsDependentCString foo(mozilla::Move(tmp));
+    // Neither string should be using a shared buffer.
+    EXPECT_FALSE(tmp.GetDataFlags() & DataFlags::SHARED);
+    EXPECT_FALSE(foo.GetDataFlags() & DataFlags::SHARED);
+    // First string should be reset, the second should be pointing to the
+    // original buffer.
+    EXPECT_NE(data, tmp.Data());
+    EXPECT_EQ(data, foo.Data());
+    EXPECT_TRUE(tmp.IsEmpty());
+  }
+  {
+    // Test copying to a nsCString.
+    nsDependentCString tmp("foo");
+    auto data = tmp.Data();
+    nsCString foo(tmp);
+    // Original string should not be shared, copy should be shared.
+    EXPECT_FALSE(tmp.GetDataFlags() & DataFlags::SHARED);
+    EXPECT_TRUE(foo.GetDataFlags() & DataFlags::SHARED);
+    // First string should remain the same, the second should be pointing to
+    // a new buffer.
+    EXPECT_EQ(data, tmp.Data());
+    EXPECT_NE(data, foo.Data());
+  }
+}
+
 TEST(Strings, assign)
 {
   nsCString result;
   test_assign_helper(NS_LITERAL_CSTRING("a") + NS_LITERAL_CSTRING("b"), result);
   EXPECT_STREQ(result.get(), "ab");
 }
 
 TEST(Strings, assign_c)