Merge mozilla-central to mozilla-autoland. r=merge a=merge on a CLOSED TREE
authorCoroiu Cristina <ccoroiu@mozilla.com>
Tue, 07 Nov 2017 01:13:57 +0200
changeset 443682 ab8fb74b86d40aff7a8f8fcd35d3c49e382da583
parent 443680 9d1a657e4d83dc9c2a3e3a304dc08054f4b7ed6b (current diff)
parent 443681 c2fe4b3b1b930b3e7fdb84eae44cec165394f322 (diff)
child 443687 d44fe4ae02fe3dd94080347722cdde0c5d44be02
push id1618
push userCallek@gmail.com
push dateThu, 11 Jan 2018 17:45:48 +0000
treeherdermozilla-release@882ca853e05a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge, merge
milestone58.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to mozilla-autoland. r=merge a=merge on a CLOSED TREE
docshell/base/nsContextMenuInfo.cpp
docshell/base/nsContextMenuInfo.h
docshell/base/nsIContextMenuListener.idl
docshell/base/nsIContextMenuListener2.idl
layout/painting/nsDisplayList.cpp
--- a/docshell/base/moz.build
+++ b/docshell/base/moz.build
@@ -36,18 +36,16 @@ DIRS += [
 ]
 
 XPIDL_SOURCES += [
     'nsCDefaultURIFixup.idl',
     'nsIClipboardCommands.idl',
     'nsIContentViewer.idl',
     'nsIContentViewerContainer.idl',
     'nsIContentViewerEdit.idl',
-    'nsIContextMenuListener.idl',
-    'nsIContextMenuListener2.idl',
     'nsIDocCharset.idl',
     'nsIDocShell.idl',
     'nsIDocShellLoadInfo.idl',
     'nsIDocShellTreeItem.idl',
     'nsIDocShellTreeOwner.idl',
     'nsIDocumentLoaderFactory.idl',
     'nsIDownloadHistory.idl',
     'nsIGlobalHistory2.idl',
@@ -84,17 +82,16 @@ EXPORTS.mozilla += [
 
 EXPORTS.mozilla.dom += [
     'PendingGlobalHistoryEntry.h',
 ]
 
 UNIFIED_SOURCES += [
     'LoadContext.cpp',
     'nsAboutRedirector.cpp',
-    'nsContextMenuInfo.cpp',
     'nsDefaultURIFixup.cpp',
     'nsDocShell.cpp',
     'nsDocShellEditorData.cpp',
     'nsDocShellEnumerator.cpp',
     'nsDocShellLoadInfo.cpp',
     'nsDocShellTransferableHooks.cpp',
     'nsDocShellTreeOwner.cpp',
     'nsDSURIContentListener.cpp',
deleted file mode 100644
--- a/docshell/base/nsContextMenuInfo.cpp
+++ /dev/null
@@ -1,320 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "nsContextMenuInfo.h"
-
-#include "nsIImageLoadingContent.h"
-#include "imgLoader.h"
-#include "nsIDOMDocument.h"
-#include "nsIDOMHTMLDocument.h"
-#include "nsIDOMHTMLElement.h"
-#include "nsIDOMHTMLHtmlElement.h"
-#include "nsIDOMWindow.h"
-#include "nsICSSDeclaration.h"
-#include "nsIDOMCSSValue.h"
-#include "nsIDOMCSSPrimitiveValue.h"
-#include "nsNetUtil.h"
-#include "nsUnicharUtils.h"
-#include "nsIDocument.h"
-#include "nsIPrincipal.h"
-#include "nsIContentSecurityPolicy.h"
-#include "nsIContentPolicy.h"
-#include "imgRequestProxy.h"
-#include "mozilla/dom/HTMLAnchorElement.h"
-#include "mozilla/dom/HTMLAreaElement.h"
-#include "mozilla/dom/HTMLLinkElement.h"
-
-using mozilla::dom::HTMLAnchorElement;
-using mozilla::dom::HTMLAreaElement;
-using mozilla::dom::HTMLLinkElement;
-using mozilla::dom::Element;
-using mozilla::ErrorResult;
-
-NS_IMPL_ISUPPORTS(nsContextMenuInfo, nsIContextMenuInfo)
-
-nsContextMenuInfo::nsContextMenuInfo()
-{
-}
-
-nsContextMenuInfo::~nsContextMenuInfo()
-{
-}
-
-NS_IMETHODIMP
-nsContextMenuInfo::GetMouseEvent(nsIDOMEvent** aEvent)
-{
-  NS_ENSURE_ARG_POINTER(aEvent);
-  NS_IF_ADDREF(*aEvent = mMouseEvent);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsContextMenuInfo::GetTargetNode(nsIDOMNode** aNode)
-{
-  NS_ENSURE_ARG_POINTER(aNode);
-  NS_IF_ADDREF(*aNode = mDOMNode);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsContextMenuInfo::GetAssociatedLink(nsAString& aHRef)
-{
-  NS_ENSURE_STATE(mAssociatedLink);
-  aHRef.Truncate(0);
-
-  nsCOMPtr<nsIContent> content(do_QueryInterface(mAssociatedLink));
-  nsCOMPtr<nsIContent> linkContent;
-  if (content &&
-      content->IsAnyOfHTMLElements(nsGkAtoms::a,
-                                   nsGkAtoms::area,
-                                   nsGkAtoms::link)) {
-    bool hasAttr = content->HasAttr(kNameSpaceID_None, nsGkAtoms::href);
-    if (hasAttr) {
-      linkContent = content;
-      RefPtr<HTMLAnchorElement> anchor = HTMLAnchorElement::FromContent(linkContent);
-      if (anchor) {
-        anchor->GetHref(aHRef);
-      } else {
-        RefPtr<HTMLAreaElement> area = HTMLAreaElement::FromContent(linkContent);
-        if (area) {
-          area->GetHref(aHRef);
-        } else {
-          RefPtr<HTMLLinkElement> link = HTMLLinkElement::FromContent(linkContent);
-          if (link) {
-            link->GetHref(aHRef);
-          }
-        }
-      }
-    }
-  } else {
-    nsCOMPtr<nsIDOMNode> curr;
-    mAssociatedLink->GetParentNode(getter_AddRefs(curr));
-    while (curr) {
-      content = do_QueryInterface(curr);
-      if (!content) {
-        break;
-      }
-      if (content->IsHTMLElement(nsGkAtoms::a)) {
-        bool hasAttr;
-        hasAttr = content->HasAttr(kNameSpaceID_None, nsGkAtoms::href);
-        if (hasAttr) {
-          linkContent = content;
-          RefPtr<HTMLAnchorElement> anchor = HTMLAnchorElement::FromContent(linkContent);
-          if (anchor) {
-            anchor->GetHref(aHRef);
-          }
-        } else {
-          linkContent = nullptr; // Links can't be nested.
-        }
-        break;
-      }
-
-      nsCOMPtr<nsIDOMNode> temp = curr;
-      temp->GetParentNode(getter_AddRefs(curr));
-    }
-  }
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsContextMenuInfo::GetImageContainer(imgIContainer** aImageContainer)
-{
-  NS_ENSURE_ARG_POINTER(aImageContainer);
-  NS_ENSURE_STATE(mDOMNode);
-
-  nsCOMPtr<imgIRequest> request;
-  GetImageRequest(mDOMNode, getter_AddRefs(request));
-  if (request) {
-    return request->GetImage(aImageContainer);
-  }
-
-  return NS_ERROR_FAILURE;
-}
-
-NS_IMETHODIMP
-nsContextMenuInfo::GetImageSrc(nsIURI** aURI)
-{
-  NS_ENSURE_ARG_POINTER(aURI);
-  NS_ENSURE_STATE(mDOMNode);
-
-  nsCOMPtr<nsIImageLoadingContent> content(do_QueryInterface(mDOMNode));
-  NS_ENSURE_TRUE(content, NS_ERROR_FAILURE);
-  return content->GetCurrentURI(aURI);
-}
-
-NS_IMETHODIMP
-nsContextMenuInfo::GetBackgroundImageContainer(imgIContainer** aImageContainer)
-{
-  NS_ENSURE_ARG_POINTER(aImageContainer);
-  NS_ENSURE_STATE(mDOMNode);
-
-  RefPtr<imgRequestProxy> request;
-  GetBackgroundImageRequest(mDOMNode, getter_AddRefs(request));
-  if (request) {
-    return request->GetImage(aImageContainer);
-  }
-
-  return NS_ERROR_FAILURE;
-}
-
-NS_IMETHODIMP
-nsContextMenuInfo::GetBackgroundImageSrc(nsIURI** aURI)
-{
-  NS_ENSURE_ARG_POINTER(aURI);
-  NS_ENSURE_STATE(mDOMNode);
-
-  RefPtr<imgRequestProxy> request;
-  GetBackgroundImageRequest(mDOMNode, getter_AddRefs(request));
-  if (request) {
-    return request->GetURI(aURI);
-  }
-
-  return NS_ERROR_FAILURE;
-}
-
-nsresult
-nsContextMenuInfo::GetImageRequest(nsIDOMNode* aDOMNode, imgIRequest** aRequest)
-{
-  NS_ENSURE_ARG(aDOMNode);
-  NS_ENSURE_ARG_POINTER(aRequest);
-
-  // Get content
-  nsCOMPtr<nsIImageLoadingContent> content(do_QueryInterface(aDOMNode));
-  NS_ENSURE_TRUE(content, NS_ERROR_FAILURE);
-
-  return content->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST, aRequest);
-}
-
-bool
-nsContextMenuInfo::HasBackgroundImage(nsIDOMNode* aDOMNode)
-{
-  NS_ENSURE_TRUE(aDOMNode, false);
-
-  RefPtr<imgRequestProxy> request;
-  GetBackgroundImageRequest(aDOMNode, getter_AddRefs(request));
-
-  return (request != nullptr);
-}
-
-nsresult
-nsContextMenuInfo::GetBackgroundImageRequest(nsIDOMNode* aDOMNode,
-                                             imgRequestProxy** aRequest)
-{
-
-  NS_ENSURE_ARG(aDOMNode);
-  NS_ENSURE_ARG_POINTER(aRequest);
-
-  nsCOMPtr<nsIDOMNode> domNode = aDOMNode;
-
-  // special case for the <html> element: if it has no background-image
-  // we'll defer to <body>
-  nsCOMPtr<nsIDOMHTMLHtmlElement> htmlElement = do_QueryInterface(domNode);
-  if (htmlElement) {
-    nsCOMPtr<nsIDOMHTMLElement> element = do_QueryInterface(domNode);
-    nsAutoString nameSpace;
-    element->GetNamespaceURI(nameSpace);
-    if (nameSpace.IsEmpty()) {
-      nsresult rv = GetBackgroundImageRequestInternal(domNode, aRequest);
-      if (NS_SUCCEEDED(rv) && *aRequest) {
-        return NS_OK;
-      }
-
-      // no background-image found
-      nsCOMPtr<nsIDOMDocument> document;
-      domNode->GetOwnerDocument(getter_AddRefs(document));
-      nsCOMPtr<nsIDOMHTMLDocument> htmlDocument(do_QueryInterface(document));
-      NS_ENSURE_TRUE(htmlDocument, NS_ERROR_FAILURE);
-
-      nsCOMPtr<nsIDOMHTMLElement> body;
-      htmlDocument->GetBody(getter_AddRefs(body));
-      domNode = do_QueryInterface(body);
-      NS_ENSURE_TRUE(domNode, NS_ERROR_FAILURE);
-    }
-  }
-  return GetBackgroundImageRequestInternal(domNode, aRequest);
-}
-
-nsresult
-nsContextMenuInfo::GetBackgroundImageRequestInternal(nsIDOMNode* aDOMNode,
-                                                     imgRequestProxy** aRequest)
-{
-  NS_ENSURE_ARG_POINTER(aDOMNode);
-
-  nsCOMPtr<nsIDOMNode> domNode = aDOMNode;
-  nsCOMPtr<nsIDOMNode> parentNode;
-
-  nsCOMPtr<nsIDOMDocument> document;
-  domNode->GetOwnerDocument(getter_AddRefs(document));
-  NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
-
-  nsCOMPtr<mozIDOMWindowProxy> window;
-  document->GetDefaultView(getter_AddRefs(window));
-  NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
-
-  auto* piWindow = nsPIDOMWindowOuter::From(window);
-  nsPIDOMWindowInner* innerWindow = piWindow->GetCurrentInnerWindow();
-  MOZ_ASSERT(innerWindow);
-
-  nsCOMPtr<nsIDOMCSSPrimitiveValue> primitiveValue;
-  nsAutoString bgStringValue;
-
-  nsCOMPtr<nsIDocument> doc(do_QueryInterface(document));
-  nsCOMPtr<nsIPrincipal> principal = doc ? doc->NodePrincipal() : nullptr;
-
-  while (true) {
-    nsCOMPtr<Element> domElement(do_QueryInterface(domNode));
-    // bail for the parent node of the root element or null argument
-    if (!domElement) {
-      break;
-    }
-
-    ErrorResult dummy;
-    nsCOMPtr<nsICSSDeclaration> computedStyle =
-      innerWindow->GetComputedStyle(*domElement, EmptyString(), dummy);
-    dummy.SuppressException();
-    if (computedStyle) {
-      nsCOMPtr<nsIDOMCSSValue> cssValue;
-      computedStyle->GetPropertyCSSValue(NS_LITERAL_STRING("background-image"),
-                                         getter_AddRefs(cssValue));
-      primitiveValue = do_QueryInterface(cssValue);
-      if (primitiveValue) {
-        primitiveValue->GetStringValue(bgStringValue);
-        if (!bgStringValue.EqualsLiteral("none")) {
-          nsCOMPtr<nsIURI> bgUri;
-          NS_NewURI(getter_AddRefs(bgUri), bgStringValue);
-          NS_ENSURE_TRUE(bgUri, NS_ERROR_FAILURE);
-
-          imgLoader* il = imgLoader::NormalLoader();
-          NS_ENSURE_TRUE(il, NS_ERROR_FAILURE);
-
-          return il->LoadImage(bgUri, nullptr, nullptr,
-                               doc->GetReferrerPolicy(), principal, 0, nullptr,
-                               nullptr, nullptr, nullptr, nsIRequest::LOAD_NORMAL,
-                               nullptr, nsIContentPolicy::TYPE_INTERNAL_IMAGE,
-                               EmptyString(),
-                               /* aUseUrgentStartForChannel */ false, aRequest);
-        }
-      }
-
-      // bail if we encounter non-transparent background-color
-      computedStyle->GetPropertyCSSValue(NS_LITERAL_STRING("background-color"),
-                                         getter_AddRefs(cssValue));
-      primitiveValue = do_QueryInterface(cssValue);
-      if (primitiveValue) {
-        primitiveValue->GetStringValue(bgStringValue);
-        if (!bgStringValue.EqualsLiteral("transparent")) {
-          return NS_ERROR_FAILURE;
-        }
-      }
-    }
-
-    domNode->GetParentNode(getter_AddRefs(parentNode));
-    domNode = parentNode;
-  }
-
-  return NS_ERROR_FAILURE;
-}
deleted file mode 100644
--- a/docshell/base/nsContextMenuInfo.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef nsContextMenuInfo_h__
-#define nsContextMenuInfo_h__
-
-#include "nsCOMPtr.h"
-#include "nsIContextMenuListener2.h"
-#include "nsIDOMNode.h"
-#include "nsIDOMEvent.h"
-#include "imgIContainer.h"
-#include "imgIRequest.h"
-
-class ChromeContextMenuListener;
-class imgRequestProxy;
-
-// Helper class for implementors of nsIContextMenuListener2
-class nsContextMenuInfo : public nsIContextMenuInfo
-{
-  friend class ChromeContextMenuListener;
-
-public:
-  nsContextMenuInfo();
-
-  NS_DECL_ISUPPORTS
-  NS_DECL_NSICONTEXTMENUINFO
-
-private:
-  virtual ~nsContextMenuInfo();
-
-  void SetMouseEvent(nsIDOMEvent* aEvent) { mMouseEvent = aEvent; }
-  void SetDOMNode(nsIDOMNode* aNode) { mDOMNode = aNode; }
-  void SetAssociatedLink(nsIDOMNode* aLink) { mAssociatedLink = aLink; }
-
-  nsresult GetImageRequest(nsIDOMNode* aDOMNode, imgIRequest** aRequest);
-
-  bool HasBackgroundImage(nsIDOMNode* aDOMNode);
-
-  nsresult GetBackgroundImageRequest(nsIDOMNode* aDOMNode,
-                                     imgRequestProxy** aRequest);
-
-  nsresult GetBackgroundImageRequestInternal(nsIDOMNode* aDOMNode,
-                                             imgRequestProxy** aRequest);
-
-private:
-  nsCOMPtr<nsIDOMEvent> mMouseEvent;
-  nsCOMPtr<nsIDOMNode> mDOMNode;
-  nsCOMPtr<nsIDOMNode> mAssociatedLink;
-};
-
-#endif // nsContextMenuInfo_h__
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -10,16 +10,17 @@
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/AutoRestore.h"
 #include "mozilla/BasePrincipal.h"
 #include "mozilla/Casting.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/Element.h"
+#include "mozilla/dom/HTMLAnchorElement.h"
 #include "mozilla/dom/PendingGlobalHistoryEntry.h"
 #include "mozilla/dom/TabChild.h"
 #include "mozilla/dom/ProfileTimelineMarkerBinding.h"
 #include "mozilla/dom/ScreenOrientation.h"
 #include "mozilla/dom/ToJSValue.h"
 #include "mozilla/dom/PermissionMessageUtils.h"
 #include "mozilla/dom/workers/ServiceWorkerManager.h"
 #include "mozilla/EventStateManager.h"
--- a/docshell/base/nsDocShellTreeOwner.cpp
+++ b/docshell/base/nsDocShellTreeOwner.cpp
@@ -19,18 +19,16 @@
 #include "nsAtom.h"
 #include "nsReadableUtils.h"
 #include "nsUnicharUtils.h"
 #include "nsISimpleEnumerator.h"
 #include "mozilla/LookAndFeel.h"
 
 // Interfaces needed to be included
 #include "nsPresContext.h"
-#include "nsIContextMenuListener.h"
-#include "nsIContextMenuListener2.h"
 #include "nsITooltipListener.h"
 #include "nsIDOMNode.h"
 #include "nsIDOMNodeList.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMDocumentType.h"
 #include "nsIDOMElement.h"
 #include "Link.h"
 #include "mozilla/dom/Element.h"
@@ -54,17 +52,16 @@
 #include "nsPIWindowWatcher.h"
 #include "nsIPrompt.h"
 #include "nsITabParent.h"
 #include "nsITabChild.h"
 #include "nsRect.h"
 #include "nsIWebBrowserChromeFocus.h"
 #include "nsIContent.h"
 #include "imgIContainer.h"
-#include "nsContextMenuInfo.h"
 #include "nsPresContext.h"
 #include "nsViewManager.h"
 #include "nsView.h"
 #include "nsIDOMDragEvent.h"
 #include "nsIConstraintValidation.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/dom/Event.h" // for nsIDOMEvent::InternalDOMEvent()
@@ -893,29 +890,16 @@ nsDocShellTreeOwner::AddChromeListeners(
       do_QueryInterface(webBrowserChrome));
     if (tooltipListener) {
       mChromeTooltipListener = new ChromeTooltipListener(mWebBrowser,
                                                          webBrowserChrome);
       rv = mChromeTooltipListener->AddChromeListeners();
     }
   }
 
-  // install context menus
-  if (!mChromeContextMenuListener) {
-    nsCOMPtr<nsIContextMenuListener2> contextListener2(
-      do_QueryInterface(webBrowserChrome));
-    nsCOMPtr<nsIContextMenuListener> contextListener(
-      do_QueryInterface(webBrowserChrome));
-    if (contextListener2 || contextListener) {
-      mChromeContextMenuListener =
-        new ChromeContextMenuListener(mWebBrowser, webBrowserChrome);
-      rv = mChromeContextMenuListener->AddChromeListeners();
-    }
-  }
-
   // register dragover and drop event listeners with the listener manager
   nsCOMPtr<EventTarget> target;
   GetDOMEventTarget(mWebBrowser, getter_AddRefs(target));
 
   EventListenerManager* elmP = target->GetOrCreateListenerManager();
   if (elmP) {
     elmP->AddEventListenerByType(this, NS_LITERAL_STRING("dragover"),
                                  TrustedEventsAtSystemGroupBubble());
@@ -928,20 +912,16 @@ nsDocShellTreeOwner::AddChromeListeners(
 
 NS_IMETHODIMP
 nsDocShellTreeOwner::RemoveChromeListeners()
 {
   if (mChromeTooltipListener) {
     mChromeTooltipListener->RemoveChromeListeners();
     mChromeTooltipListener = nullptr;
   }
-  if (mChromeContextMenuListener) {
-    mChromeContextMenuListener->RemoveChromeListeners();
-    mChromeContextMenuListener = nullptr;
-  }
 
   nsCOMPtr<EventTarget> piTarget;
   GetDOMEventTarget(mWebBrowser, getter_AddRefs(piTarget));
   if (!piTarget) {
     return NS_OK;
   }
 
   EventListenerManager* elmP = piTarget->GetOrCreateListenerManager();
@@ -1401,281 +1381,8 @@ ChromeTooltipListener::sTooltipCallback(
                           tooltipText, directionText);
       }
     }
 
     // release tooltip target if there is one, NO MATTER WHAT
     self->mPossibleTooltipNode = nullptr;
   }
 }
-
-NS_IMPL_ISUPPORTS(ChromeContextMenuListener, nsIDOMEventListener)
-
-ChromeContextMenuListener::ChromeContextMenuListener(
-      nsWebBrowser* aInBrowser,
-      nsIWebBrowserChrome* aInChrome)
-  : mContextMenuListenerInstalled(false)
-  , mWebBrowser(aInBrowser)
-  , mWebBrowserChrome(aInChrome)
-{
-}
-
-ChromeContextMenuListener::~ChromeContextMenuListener()
-{
-}
-
-// Subscribe to the events that will allow us to track context menus. Bascially,
-// this is just the context-menu DOM event.
-NS_IMETHODIMP
-ChromeContextMenuListener::AddContextMenuListener()
-{
-  if (mEventTarget) {
-    nsresult rv = mEventTarget->AddEventListener(
-      NS_LITERAL_STRING("contextmenu"), this, false, false);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    mContextMenuListenerInstalled = true;
-  }
-
-  return NS_OK;
-}
-
-// Unsubscribe from all the various context menu events that we were listening
-// to.
-NS_IMETHODIMP
-ChromeContextMenuListener::RemoveContextMenuListener()
-{
-  if (mEventTarget) {
-    nsresult rv = mEventTarget->RemoveEventListener(
-      NS_LITERAL_STRING("contextmenu"), this, false);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    mContextMenuListenerInstalled = false;
-  }
-
-  return NS_OK;
-}
-
-// Hook up things to the chrome like context menus and tooltips, if the chrome
-// has implemented the right interfaces.
-NS_IMETHODIMP
-ChromeContextMenuListener::AddChromeListeners()
-{
-  if (!mEventTarget) {
-    GetDOMEventTarget(mWebBrowser, getter_AddRefs(mEventTarget));
-  }
-
-  // Register the appropriate events for context menus, but only if
-  // the embedding chrome cares.
-  nsresult rv = NS_OK;
-
-  nsCOMPtr<nsIContextMenuListener2> contextListener2(
-    do_QueryInterface(mWebBrowserChrome));
-  nsCOMPtr<nsIContextMenuListener> contextListener(
-    do_QueryInterface(mWebBrowserChrome));
-  if ((contextListener || contextListener2) && !mContextMenuListenerInstalled) {
-    rv = AddContextMenuListener();
-  }
-
-  return rv;
-}
-
-// Unsubscribe from the various things we've hooked up to the window root.
-NS_IMETHODIMP
-ChromeContextMenuListener::RemoveChromeListeners()
-{
-  if (mContextMenuListenerInstalled) {
-    RemoveContextMenuListener();
-  }
-
-  mEventTarget = nullptr;
-
-  // it really doesn't matter if these fail...
-  return NS_OK;
-}
-
-// We're on call to show the context menu. Dig around in the DOM to find the
-// type of object we're dealing with and notify the front end chrome.
-NS_IMETHODIMP
-ChromeContextMenuListener::HandleEvent(nsIDOMEvent* aMouseEvent)
-{
-  nsCOMPtr<nsIDOMMouseEvent> mouseEvent = do_QueryInterface(aMouseEvent);
-  NS_ENSURE_TRUE(mouseEvent, NS_ERROR_UNEXPECTED);
-
-  bool isDefaultPrevented = false;
-  aMouseEvent->GetDefaultPrevented(&isDefaultPrevented);
-  if (isDefaultPrevented) {
-    return NS_OK;
-  }
-
-  nsCOMPtr<EventTarget> targetNode =
-    aMouseEvent->InternalDOMEvent()->GetTarget();
-  if (!targetNode) {
-    return NS_ERROR_NULL_POINTER;
-  }
-
-  nsCOMPtr<nsIDOMNode> targetDOMnode;
-  nsCOMPtr<nsIDOMNode> node = do_QueryInterface(targetNode);
-  if (!node) {
-    return NS_OK;
-  }
-
-  // Stop the context menu event going to other windows (bug 78396)
-  aMouseEvent->PreventDefault();
-
-  // If the listener is a nsIContextMenuListener2, create the info object
-  nsCOMPtr<nsIContextMenuListener2> menuListener2(
-    do_QueryInterface(mWebBrowserChrome));
-  nsContextMenuInfo* menuInfoImpl = nullptr;
-  nsCOMPtr<nsIContextMenuInfo> menuInfo;
-  if (menuListener2) {
-    menuInfoImpl = new nsContextMenuInfo;
-    menuInfo = menuInfoImpl;
-  }
-
-  uint32_t flags = nsIContextMenuListener::CONTEXT_NONE;
-  uint32_t flags2 = nsIContextMenuListener2::CONTEXT_NONE;
-
-  // XXX test for selected text
-
-  uint16_t nodeType;
-  nsresult res = node->GetNodeType(&nodeType);
-  NS_ENSURE_SUCCESS(res, res);
-
-  // First, checks for nodes that never have children.
-  if (nodeType == nsIDOMNode::ELEMENT_NODE) {
-    nsCOMPtr<nsIImageLoadingContent> imageContent(do_QueryInterface(node));
-    if (imageContent) {
-      nsCOMPtr<nsIURI> imgUri;
-      imageContent->GetCurrentURI(getter_AddRefs(imgUri));
-      if (imgUri) {
-        flags |= nsIContextMenuListener::CONTEXT_IMAGE;
-        flags2 |= nsIContextMenuListener2::CONTEXT_IMAGE;
-        targetDOMnode = node;
-      }
-    }
-
-    nsCOMPtr<nsIFormControl> formControl(do_QueryInterface(node));
-    if (formControl) {
-      if (formControl->ControlType() == NS_FORM_TEXTAREA) {
-        flags |= nsIContextMenuListener::CONTEXT_TEXT;
-        flags2 |= nsIContextMenuListener2::CONTEXT_TEXT;
-        targetDOMnode = node;
-      } else {
-        nsCOMPtr<nsIDOMHTMLInputElement> inputElement(
-          do_QueryInterface(formControl));
-        if (inputElement) {
-          flags |= nsIContextMenuListener::CONTEXT_INPUT;
-          flags2 |= nsIContextMenuListener2::CONTEXT_INPUT;
-
-          if (menuListener2) {
-            if (formControl->IsSingleLineTextControl(false)) {
-              flags2 |= nsIContextMenuListener2::CONTEXT_TEXT;
-            }
-          }
-
-          targetDOMnode = node;
-        }
-      }
-    }
-
-    // always consume events for plugins who may throw their own context menus
-    // but not for image objects. Document objects will never be targets or
-    // ancestors of targets, so that's OK.
-    nsCOMPtr<nsIContent> content = do_QueryInterface(node);
-    if (content &&
-        (content->IsHTMLElement(nsGkAtoms::embed) ||
-         (!(flags & nsIContextMenuListener::CONTEXT_IMAGE) &&
-          content->IsHTMLElement(nsGkAtoms::object)))) {
-      return NS_OK;
-    }
-  }
-
-  // Bubble out, looking for items of interest
-  do {
-    uint16_t nodeType;
-    res = node->GetNodeType(&nodeType);
-    NS_ENSURE_SUCCESS(res, res);
-
-    if (nodeType == nsIDOMNode::ELEMENT_NODE) {
-
-      // Test if the element has an associated link
-      nsCOMPtr<nsIDOMElement> element(do_QueryInterface(node));
-
-      bool hasAttr = false;
-      res = element->HasAttribute(NS_LITERAL_STRING("href"), &hasAttr);
-
-      if (NS_SUCCEEDED(res) && hasAttr) {
-        flags |= nsIContextMenuListener::CONTEXT_LINK;
-        flags2 |= nsIContextMenuListener2::CONTEXT_LINK;
-        if (!targetDOMnode) {
-          targetDOMnode = node;
-        }
-        if (menuInfoImpl) {
-          menuInfoImpl->SetAssociatedLink(node);
-        }
-        break; // exit do-while
-      }
-    }
-
-    // walk-up-the-tree
-    nsCOMPtr<nsIDOMNode> parentNode;
-    node->GetParentNode(getter_AddRefs(parentNode));
-    node = parentNode;
-  } while (node);
-
-  if (!flags && !flags2) {
-    // We found nothing of interest so far, check if we
-    // have at least an html document.
-    nsCOMPtr<nsIDOMDocument> document;
-    node = do_QueryInterface(targetNode);
-    node->GetOwnerDocument(getter_AddRefs(document));
-    nsCOMPtr<nsIDOMHTMLDocument> htmlDocument(do_QueryInterface(document));
-    if (htmlDocument) {
-      flags |= nsIContextMenuListener::CONTEXT_DOCUMENT;
-      flags2 |= nsIContextMenuListener2::CONTEXT_DOCUMENT;
-      targetDOMnode = node;
-      if (!(flags & nsIContextMenuListener::CONTEXT_IMAGE)) {
-        // check if this is a background image that the user was trying to click
-        // on and if the listener is ready for that (only
-        // nsIContextMenuListener2 and up)
-        if (menuInfoImpl && menuInfoImpl->HasBackgroundImage(targetDOMnode)) {
-          flags2 |= nsIContextMenuListener2::CONTEXT_BACKGROUND_IMAGE;
-          // For the embedder to get the correct background image
-          // targetDOMnode must point to the original node.
-          targetDOMnode = do_QueryInterface(targetNode);
-        }
-      }
-    }
-  }
-
-  // we need to cache the event target into the focus controller's popupNode
-  // so we can get at it later from command code, etc.:
-
-  // get the dom window
-  nsCOMPtr<mozIDOMWindowProxy> win;
-  res = mWebBrowser->GetContentDOMWindow(getter_AddRefs(win));
-  NS_ENSURE_SUCCESS(res, res);
-  NS_ENSURE_TRUE(win, NS_ERROR_FAILURE);
-
-  auto* window = nsPIDOMWindowOuter::From(win);
-  nsCOMPtr<nsPIWindowRoot> root = window->GetTopWindowRoot();
-  NS_ENSURE_TRUE(root, NS_ERROR_FAILURE);
-  if (root) {
-    // set the window root's popup node to the event target
-    root->SetPopupNode(targetDOMnode);
-  }
-
-  // Tell the listener all about the event
-  if (menuListener2) {
-    menuInfoImpl->SetMouseEvent(aMouseEvent);
-    menuInfoImpl->SetDOMNode(targetDOMnode);
-    menuListener2->OnShowContextMenu(flags2, menuInfo);
-  } else {
-    nsCOMPtr<nsIContextMenuListener> menuListener(
-      do_QueryInterface(mWebBrowserChrome));
-    if (menuListener) {
-      menuListener->OnShowContextMenu(flags, aMouseEvent, targetDOMnode);
-    }
-  }
-
-  return NS_OK;
-}
--- a/docshell/base/nsDocShellTreeOwner.h
+++ b/docshell/base/nsDocShellTreeOwner.h
@@ -32,17 +32,16 @@
 namespace mozilla {
 namespace dom {
 class EventTarget;
 } // namespace dom
 } // namespace mozilla
 
 class nsWebBrowser;
 class ChromeTooltipListener;
-class ChromeContextMenuListener;
 
 // {6D10C180-6888-11d4-952B-0020183BF181}
 #define NS_ICDOCSHELLTREEOWNER_IID \
   { 0x6d10c180, 0x6888, 0x11d4, { 0x95, 0x2b, 0x0, 0x20, 0x18, 0x3b, 0xf1, 0x81 } }
 
 // This is a fake 'hidden' interface that nsDocShellTreeOwner implements.
 // Classes can QI for this interface to be sure that
 // they're dealing with a valid nsDocShellTreeOwner and not some other object
@@ -115,17 +114,16 @@ protected:
   nsIInterfaceRequestor* mOwnerRequestor;
 
   nsWeakPtr mWebBrowserChromeWeak; // nsIWebBrowserChrome
 
   // the objects that listen for chrome events like context menus and tooltips.
   // They are separate objects to avoid circular references between |this|
   // and the DOM.
   RefPtr<ChromeTooltipListener> mChromeTooltipListener;
-  RefPtr<ChromeContextMenuListener> mChromeContextMenuListener;
 
   RefPtr<nsDocShellTreeOwner> mContentTreeOwner;
 
   nsCOMPtr<nsIPrompt> mPrompter;
   nsCOMPtr<nsIAuthPrompt> mAuthPrompter;
   nsCOMPtr<nsITabParent> mPrimaryTabParent;
 };
 
@@ -198,42 +196,9 @@ private:
   // firing. This is a strong reference, because the tooltip content can be
   // destroyed while we're waiting for the tooltip to pup up, and we need to
   // detect that. It's set only when the tooltip timer is created and launched.
   // The timer must either fire or be cancelled (or possibly released?), and we
   // release this reference in each of those cases. So we don't leak.
   nsCOMPtr<nsIDOMNode> mPossibleTooltipNode;
 };
 
-// The class that listens to the chrome events and tells the embedding chrome to
-// show context menus, as appropriate. Handles registering itself with the DOM
-// with AddChromeListeners() and removing itself with RemoveChromeListeners().
-class ChromeContextMenuListener : public nsIDOMEventListener
-{
-protected:
-  virtual ~ChromeContextMenuListener();
-
-public:
-  NS_DECL_ISUPPORTS
-
-  ChromeContextMenuListener(nsWebBrowser* aInBrowser,
-                            nsIWebBrowserChrome* aInChrome);
-
-  // nsIDOMContextMenuListener
-  NS_IMETHOD HandleEvent(nsIDOMEvent* aEvent) override;
-
-  // Add/remove the relevant listeners, based on what interfaces
-  // the embedding chrome implements.
-  NS_IMETHOD AddChromeListeners();
-  NS_IMETHOD RemoveChromeListeners();
-
-private:
-  NS_IMETHOD AddContextMenuListener();
-  NS_IMETHOD RemoveContextMenuListener();
-
-  bool mContextMenuListenerInstalled;
-
-  nsWebBrowser* mWebBrowser;
-  nsCOMPtr<mozilla::dom::EventTarget> mEventTarget;
-  nsCOMPtr<nsIWebBrowserChrome> mWebBrowserChrome;
-};
-
 #endif /* nsDocShellTreeOwner_h__ */
deleted file mode 100644
--- a/docshell/base/nsIContextMenuListener.idl
+++ /dev/null
@@ -1,65 +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 nsIDOMEvent;
-interface nsIDOMNode;
-
-/**
- * An optional interface for embedding clients wishing to receive
- * notifications for context menu events (e.g. generated by
- * a user right-mouse clicking on a link). The embedder implements
- * this interface on the web browser chrome object associated
- * with the window that notifications are required for. When a context
- * menu event, the browser will call this interface if present.
- * 
- * @see nsIDOMNode
- * @see nsIDOMEvent
- */
-[scriptable, uuid(3478b6b0-3875-11d4-94ef-0020183bf181)]
-interface nsIContextMenuListener : nsISupports
-{
-    /** Flag. No context. */
-    const unsigned long CONTEXT_NONE     = 0;
-    /** Flag. Context is a link element. */
-    const unsigned long CONTEXT_LINK     = 1;
-    /** Flag. Context is an image element. */
-    const unsigned long CONTEXT_IMAGE    = 2;
-    /** Flag. Context is the whole document. */
-    const unsigned long CONTEXT_DOCUMENT = 4;
-    /** Flag. Context is a text area element. */
-    const unsigned long CONTEXT_TEXT     = 8;
-    /** Flag. Context is an input element. */
-    const unsigned long CONTEXT_INPUT    = 16;
-
-    /**
-     * Called when the browser receives a context menu event (e.g. user is right-mouse
-     * clicking somewhere on the document). The combination of flags, event and node
-     * provided in the call indicate where and what was clicked on.
-     *
-     * The following table describes what context flags and node combinations are
-     * possible.
-     *
-     * <TABLE>
-     * <TR><TD><B>aContextFlag</B></TD><TD>aNode</TD></TR>
-     * <TR><TD>CONTEXT_LINK</TD><TD>&lt;A&gt;</TD></TR>
-     * <TR><TD>CONTEXT_IMAGE</TD><TD>&lt;IMG&gt;</TD></TR>
-     * <TR><TD>CONTEXT_IMAGE | CONTEXT_LINK</TD><TD>&lt;IMG&gt;
-     *       with an &lt;A&gt; as an ancestor</TD></TR>
-     * <TR><TD>CONTEXT_INPUT</TD><TD>&lt;INPUT&gt;</TD></TR>
-     * <TR><TD>CONTEXT_TEXT</TD><TD>&lt;TEXTAREA&gt;</TD></TR>
-     * <TR><TD>CONTEXT_DOCUMENT</TD><TD>&lt;HTML&gt;</TD></TR>
-     * </TABLE>
-     *
-     * @param aContextFlags Flags indicating the kind of context.
-     * @param aEvent The DOM context menu event.
-     * @param aNode The DOM node most relevant to the context.
-     *
-     * @return <CODE>NS_OK</CODE> always.
-     */
-    void onShowContextMenu(in unsigned long aContextFlags, in nsIDOMEvent aEvent, in nsIDOMNode aNode);
-};
-
deleted file mode 100644
--- a/docshell/base/nsIContextMenuListener2.idl
+++ /dev/null
@@ -1,121 +0,0 @@
-/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "nsISupports.idl"
-
-interface nsIDOMEvent;
-interface nsIDOMNode;
-interface imgIContainer;
-interface nsIURI;
-interface nsIContextMenuInfo;
-
-/* THIS IS A PUBLIC EMBEDDING API */
-
-/**
- * nsIContextMenuListener2
- *
- * This is an extended version of nsIContextMenuListener
- * It provides a helper class, nsIContextMenuInfo, to allow access to
- * background images as well as various utilities.
- *
- * @see nsIContextMenuListener
- * @see nsIContextMenuInfo
- */
- 
-[scriptable, uuid(7fb719b3-d804-4964-9596-77cf924ee314)]
-interface nsIContextMenuListener2 : nsISupports
-{
-  /** Flag. No context. */
-  const unsigned long CONTEXT_NONE        = 0;
-  /** Flag. Context is a link element. */
-  const unsigned long CONTEXT_LINK        = 1;
-  /** Flag. Context is an image element. */
-  const unsigned long CONTEXT_IMAGE       = 2;
-  /** Flag. Context is the whole document. */
-  const unsigned long CONTEXT_DOCUMENT    = 4;
-  /** Flag. Context is a text area element. */
-  const unsigned long CONTEXT_TEXT        = 8;
-  /** Flag. Context is an input element. */
-  const unsigned long CONTEXT_INPUT       = 16;  
-  /** Flag. Context is a background image. */
-  const unsigned long CONTEXT_BACKGROUND_IMAGE  = 32;
-
-  /**
-   * Called when the browser receives a context menu event (e.g. user is right-mouse
-   * clicking somewhere on the document). The combination of flags, along with the
-   * attributes of <CODE>aUtils</CODE>, indicate where and what was clicked on.
-   *
-   * The following table describes what context flags and node combinations are
-   * possible.
-   *
-   * aContextFlags                  aUtils.targetNode
-   *
-   * CONTEXT_LINK                   <A>
-   * CONTEXT_IMAGE                  <IMG>
-   * CONTEXT_IMAGE | CONTEXT_LINK   <IMG> with <A> as an ancestor
-   * CONTEXT_INPUT                  <INPUT>
-   * CONTEXT_INPUT | CONTEXT_IMAGE  <INPUT> with type=image
-   * CONTEXT_TEXT                   <TEXTAREA>
-   * CONTEXT_DOCUMENT               <HTML>
-   * CONTEXT_BACKGROUND_IMAGE       <HTML> with background image
-   *
-   * @param aContextFlags           Flags indicating the kind of context.
-   * @param aUtils                  Context information and helper utilities.
-   *
-   * @see nsIContextMenuInfo
-   */ 
-  void onShowContextMenu(in unsigned long aContextFlags, in nsIContextMenuInfo aUtils);
-};
-
-/**
- * nsIContextMenuInfo
- *
- * A helper object for implementors of nsIContextMenuListener2.
- */
- 
-[scriptable, uuid(2f977d56-5485-11d4-87e2-0010a4e75ef2)]
-interface nsIContextMenuInfo : nsISupports
-{
-  /**
-   * The DOM context menu event.
-   */
-  readonly attribute nsIDOMEvent mouseEvent;
-
-  /**
-   * The DOM node most relevant to the context.
-   */
-  readonly attribute nsIDOMNode targetNode;
-
-  /**
-   * Given the <CODE>CONTEXT_LINK</CODE> flag, <CODE>targetNode</CODE> may not
-   * nescesarily be a link. This returns the anchor from <CODE>targetNode</CODE>
-   * if it has one or that of its nearest ancestor if it does not.
-   */
-  readonly attribute AString associatedLink;
-
-  /**
-   * Given the <CODE>CONTEXT_IMAGE</CODE> flag, these methods can be
-   * used in order to get the image for viewing, saving, or for the clipboard.
-   *
-   * @return <CODE>NS_OK</CODE> if successful, otherwise <CODE>NS_ERROR_FAILURE</CODE> if no
-   * image was found, or NS_ERROR_NULL_POINTER if an internal error occurs where we think there 
-   * is an image, but for some reason it cannot be returned.
-   */
-
-  readonly attribute imgIContainer imageContainer;
-  readonly attribute nsIURI imageSrc;
-
-  /**
-   * Given the <CODE>CONTEXT_BACKGROUND_IMAGE</CODE> flag, these methods can be
-   * used in order to get the image for viewing, saving, or for the clipboard.
-   *
-   * @return <CODE>NS_OK</CODE> if successful, otherwise <CODE>NS_ERROR_FAILURE</CODE> if no background
-   * image was found, or NS_ERROR_NULL_POINTER if an internal error occurs where we think there is a 
-   * background image, but for some reason it cannot be returned.
-   */
-
-  readonly attribute imgIContainer backgroundImageContainer;
-  readonly attribute nsIURI backgroundImageSrc;
-};
--- a/gfx/layers/opengl/TextureHostOGL.cpp
+++ b/gfx/layers/opengl/TextureHostOGL.cpp
@@ -448,45 +448,62 @@ SurfaceTextureHost::~SurfaceTextureHost(
     mSurfTex->DecrementUse();
     mSurfTex = nullptr;
   }
 }
 
 void
 SurfaceTextureHost::PrepareTextureSource(CompositableTextureSourceRef& aTexture)
 {
-  GLContext* gl = this->gl();
-  if (!gl || !gl->MakeCurrent()) {
-    return;
-  }
+  if (!mContinuousUpdate && mSurfTex) {
+    if (!EnsureAttached()) {
+      return;
+    }
 
-  if (!mContinuousUpdate && mSurfTex) {
     // UpdateTexImage() advances the internal buffer queue, so we only want to call this
     // once per transactionwhen we are not in continuous mode (as we are here). Otherwise,
     // the SurfaceTexture content will be de-synced from the rest of the page in subsequent
     // compositor passes.
     mSurfTex->UpdateTexImage();
   }
 }
 
 gl::GLContext*
 SurfaceTextureHost::gl() const
 {
   return mProvider ? mProvider->GetGLContext() : nullptr;
 }
 
 bool
-SurfaceTextureHost::Lock()
+SurfaceTextureHost::EnsureAttached()
 {
+  GLContext* gl = this->gl();
+  if (!gl || !gl->MakeCurrent()) {
+    return false;
+  }
+
   if (!mSurfTex) {
     return false;
   }
 
-  GLContext* gl = this->gl();
-  if (!gl || !gl->MakeCurrent()) {
+  if (!mSurfTex->IsAttachedToGLContext((int64_t)gl)) {
+    GLuint texName;
+    gl->fGenTextures(1, &texName);
+    if (NS_FAILED(mSurfTex->AttachToGLContext((int64_t)gl, texName))) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+bool
+SurfaceTextureHost::Lock()
+{
+  if (!EnsureAttached()) {
     return false;
   }
 
   if (mContinuousUpdate) {
     mSurfTex->UpdateTexImage();
   }
 
   if (!mTextureSource) {
@@ -496,24 +513,16 @@ SurfaceTextureHost::Lock()
                                               mSurfTex,
                                               mFormat,
                                               target,
                                               wrapMode,
                                               mSize,
                                               mIgnoreTransform);
   }
 
-  if (!mSurfTex->IsAttachedToGLContext((int64_t)gl)) {
-    GLuint texName;
-    gl->fGenTextures(1, &texName);
-    if (NS_FAILED(mSurfTex->AttachToGLContext((int64_t)gl, texName))) {
-      return false;
-    }
-  }
-
   return true;
 }
 
 void
 SurfaceTextureHost::SetTextureSourceProvider(TextureSourceProvider* aProvider)
 {
   if (mProvider != aProvider) {
     if (!aProvider || !aProvider->GetGLContext()) {
--- a/gfx/layers/opengl/TextureHostOGL.h
+++ b/gfx/layers/opengl/TextureHostOGL.h
@@ -424,16 +424,18 @@ public:
 
   gl::GLContext* gl() const;
 
   virtual gfx::IntSize GetSize() const override { return mSize; }
 
   virtual const char* Name() override { return "SurfaceTextureHost"; }
 
 protected:
+  bool EnsureAttached();
+
   mozilla::java::GeckoSurfaceTexture::GlobalRef mSurfTex;
   const gfx::IntSize mSize;
   const gfx::SurfaceFormat mFormat;
   bool mContinuousUpdate;
   const bool mIgnoreTransform;
   RefPtr<CompositorOGL> mCompositor;
   RefPtr<SurfaceTextureSource> mTextureSource;
 };
--- a/gfx/layers/wr/WebRenderCommandBuilder.cpp
+++ b/gfx/layers/wr/WebRenderCommandBuilder.cpp
@@ -454,34 +454,37 @@ WebRenderCommandBuilder::GenerateFallbac
                                               const StackingContextHelper& aSc,
                                               nsDisplayListBuilder* aDisplayListBuilder,
                                               LayoutDeviceRect& aImageRect)
 {
   RefPtr<WebRenderFallbackData> fallbackData = CreateOrRecycleWebRenderUserData<WebRenderFallbackData>(aItem);
 
   bool snap;
   nsRect itemBounds = aItem->GetBounds(aDisplayListBuilder, &snap);
-  nsRect clippedBounds = itemBounds;
 
-  const DisplayItemClip& clip = aItem->GetClip();
   // Blob images will only draw the visible area of the blob so we don't need to clip
   // them here and can just rely on the webrender clipping.
-  if (clip.HasClip() && !gfxPrefs::WebRenderBlobImages()) {
-    clippedBounds = itemBounds.Intersect(clip.GetClipRect());
+  bool useClipBounds = true;
+  nsRect paintBounds = itemBounds;
+  if (gfxPrefs::WebRenderBlobImages()) {
+    paintBounds = itemBounds;
+    useClipBounds = false;
+  } else {
+    paintBounds = aItem->GetClippedBounds(aDisplayListBuilder);
   }
 
   // nsDisplayItem::Paint() may refer the variables that come from ComputeVisibility().
   // So we should call RecomputeVisibility() before painting. e.g.: nsDisplayBoxShadowInner
   // uses mVisibleRegion in Paint() and mVisibleRegion is computed in
   // nsDisplayBoxShadowInner::ComputeVisibility().
-  nsRegion visibleRegion(clippedBounds);
-  aItem->RecomputeVisibility(aDisplayListBuilder, &visibleRegion);
+  nsRegion visibleRegion(itemBounds);
+  aItem->RecomputeVisibility(aDisplayListBuilder, &visibleRegion, useClipBounds);
 
   const int32_t appUnitsPerDevPixel = aItem->Frame()->PresContext()->AppUnitsPerDevPixel();
-  LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits(clippedBounds, appUnitsPerDevPixel);
+  LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits(paintBounds, appUnitsPerDevPixel);
 
   gfx::Size scale = aSc.GetInheritedScale();
   // XXX not sure if paintSize should be in layer or layoutdevice pixels, it
   // has some sort of scaling applied.
   LayerIntSize paintSize = RoundedToInt(LayerSize(bounds.width * scale.width, bounds.height * scale.height));
   if (paintSize.width == 0 || paintSize.height == 0) {
     return nullptr;
   }
@@ -497,39 +500,39 @@ WebRenderCommandBuilder::GenerateFallbac
   if (geometry && !fallbackData->IsInvalid() &&
       aItem->GetType() != DisplayItemType::TYPE_FILTER &&
       aItem->GetType() != DisplayItemType::TYPE_SVG_WRAPPER &&
       scale == fallbackData->GetScale()) {
     nsRect invalid;
     nsRegion invalidRegion;
 
     if (aItem->IsInvalid(invalid)) {
-      invalidRegion.OrWith(clippedBounds);
+      invalidRegion.OrWith(paintBounds);
     } else {
       nsPoint shift = itemBounds.TopLeft() - geometry->mBounds.TopLeft();
       geometry->MoveBy(shift);
       aItem->ComputeInvalidationRegion(aDisplayListBuilder, geometry, &invalidRegion);
 
       nsRect lastBounds = fallbackData->GetBounds();
       lastBounds.MoveBy(shift);
 
-      if (!lastBounds.IsEqualInterior(clippedBounds)) {
+      if (!lastBounds.IsEqualInterior(paintBounds)) {
         invalidRegion.OrWith(lastBounds);
-        invalidRegion.OrWith(clippedBounds);
+        invalidRegion.OrWith(paintBounds);
       }
     }
     needPaint = !invalidRegion.IsEmpty();
   }
 
   if (needPaint || !fallbackData->GetKey()) {
     gfx::SurfaceFormat format = aItem->GetType() == DisplayItemType::TYPE_MASK ?
                                                       gfx::SurfaceFormat::A8 : gfx::SurfaceFormat::B8G8R8A8;
     if (gfxPrefs::WebRenderBlobImages()) {
       bool snapped;
-      bool isOpaque = aItem->GetOpaqueRegion(aDisplayListBuilder, &snapped).Contains(clippedBounds);
+      bool isOpaque = aItem->GetOpaqueRegion(aDisplayListBuilder, &snapped).Contains(paintBounds);
 
       RefPtr<gfx::DrawEventRecorderMemory> recorder = MakeAndAddRef<gfx::DrawEventRecorderMemory>([&] (MemStream &aStream, std::vector<RefPtr<UnscaledFont>> &aUnscaledFonts) {
           size_t count = aUnscaledFonts.size();
           aStream.write((const char*)&count, sizeof(count));
           for (auto unscaled : aUnscaledFonts) {
             wr::FontKey key = mManager->WrBridge()->GetFontKeyForUnscaledFont(unscaled);
             aStream.write((const char*)&key, sizeof(key));
           }
@@ -580,17 +583,17 @@ WebRenderCommandBuilder::GenerateFallbac
 
     geometry = aItem->AllocateGeometry(aDisplayListBuilder);
     fallbackData->SetScale(scale);
     fallbackData->SetInvalid(false);
   }
 
   // Update current bounds to fallback data
   fallbackData->SetGeometry(Move(geometry));
-  fallbackData->SetBounds(clippedBounds);
+  fallbackData->SetBounds(paintBounds);
 
   MOZ_ASSERT(fallbackData->GetKey());
 
   return fallbackData.forget();
 }
 
 Maybe<wr::WrImageMask>
 WebRenderCommandBuilder::BuildWrMaskImage(nsDisplayItem* aItem,
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -2771,18 +2771,21 @@ class CloneBufferObject : public NativeO
         }
 
         if (nbytes == 0 || (nbytes % sizeof(uint64_t) != 0)) {
             JS_ReportErrorASCII(cx, "Invalid length for clonebuffer data");
             return false;
         }
 
         auto buf = js::MakeUnique<JSStructuredCloneData>(0, 0, nbytes);
-        if (!buf->Init(nbytes, nbytes))
+        if (!buf || !buf->Init(nbytes, nbytes)) {
+            ReportOutOfMemory(cx);
             return false;
+        }
+
         js_memcpy(buf->Start(), data, nbytes);
         obj->discard();
         obj->setData(buf.release(), true);
 
         args.rval().setUndefined();
         return true;
     }
 
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug1411294.js
@@ -0,0 +1,13 @@
+if (!('oomTest' in this))
+    quit();
+oomTest(function() {
+  eval(`var clonebuffer = serialize("abc");
+  clonebuffer.clonebuffer = "\
+\\x00\\x00\\x00\\x00\\b\\x00\\xFF\\xFF\\f\
+\\x00\\x00\\x00\\x03\\x00\\xFF\\xFF\\x00\\x00\\x00\\x00\\x00\\x00\\x00\
+\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\xF0?\\x00\\x00\\x00\\\x00\\x00\
+\\x00\\xFF\\xFF"
+  var obj = deserialize(clonebuffer)
+  assertEq(new ({ get }).keys(obj).toString(), "12,ab");
+`);
+});
--- a/js/src/jit-test/tests/debug/bug1385843.js
+++ b/js/src/jit-test/tests/debug/bug1385843.js
@@ -3,17 +3,17 @@ g.parent = this;
 g.count = 0;
 g.eval("(" + function() {
     var dbg = new Debugger(parent);
     dbg.onEnterFrame = function(frame) {
         if (count === 5)
             dbg.onEnterFrame = undefined;
         count++;
         var ex = frame.eval("this").throw.unsafeDereference();
-        assertEq(ex.message.includes("uninitialized"), true);
+        assertEq(ex.message.includes("call super constructor"), true);
         assertEq(ex.message.includes("Foo2"), true);
     }
 } + ")()");
 class Foo1 {};
 class Foo2 extends Foo1 {
     constructor() {
         super();
     }
--- a/js/src/jit/BaselineBailouts.cpp
+++ b/js/src/jit/BaselineBailouts.cpp
@@ -1022,17 +1022,17 @@ InitFromBailout(JSContext* cx, HandleScr
             pc = GetNextNonLoopEntryPc(pc);
             fasterPc = GetNextNonLoopEntryPc(GetNextNonLoopEntryPc(fasterPc));
             if (fasterPc == pc)
                 break;
         }
         op = JSOp(*pc);
     }
 
-    uint32_t pcOff = script->pcToOffset(pc);
+    const uint32_t pcOff = script->pcToOffset(pc);
     BaselineScript* baselineScript = script->baselineScript();
 
 #ifdef DEBUG
     uint32_t expectedDepth;
     bool reachablePC;
     if (!ReconstructStackDepth(cx, script, resumeAfter ? GetNextPc(pc) : pc, &expectedDepth, &reachablePC))
         return false;
 
@@ -1102,18 +1102,23 @@ InitFromBailout(JSContext* cx, HandleScr
         builder.setResumePC(pc);
         builder.setResumeFramePtr(prevFramePtr);
 
         if (enterMonitorChain) {
             BaselineICEntry& icEntry = baselineScript->icEntryFromPCOffset(pcOff);
             ICFallbackStub* fallbackStub = icEntry.firstStub()->getChainFallback();
             MOZ_ASSERT(fallbackStub->isMonitoredFallback());
             JitSpew(JitSpew_BaselineBailouts, "      [TYPE-MONITOR CHAIN]");
-            ICMonitoredFallbackStub* monFallbackStub = fallbackStub->toMonitoredFallbackStub();
-            ICStub* firstMonStub = monFallbackStub->fallbackMonitorStub()->firstMonitorStub();
+
+            ICTypeMonitor_Fallback* typeMonitorFallback =
+                fallbackStub->toMonitoredFallbackStub()->getFallbackMonitorStub(cx, script);
+            if (!typeMonitorFallback)
+                return false;
+
+            ICStub* firstMonStub = typeMonitorFallback->firstMonitorStub();
 
             // To enter a monitoring chain, we load the top stack value into R0
             JitSpew(JitSpew_BaselineBailouts, "      Popping top stack value into R0.");
             builder.popValueInto(PCMappingSlotInfo::SlotInR0);
             frameSize -= sizeof(Value);
 
             if (JSOp(*pc) == JSOP_GETELEM_SUPER) {
                 // Push a fake value so that the stack stays balanced.
@@ -1382,16 +1387,24 @@ InitFromBailout(JSContext* cx, HandleScr
     if (!builder.writePtr(CalleeToToken(calleeFun, pushedNewTarget), "CalleeToken"))
         return false;
     nextCallee.set(calleeFun);
 
     // Push BaselineStub frame descriptor
     if (!builder.writeWord(baselineStubFrameDescr, "Descriptor"))
         return false;
 
+    // Ensure we have a TypeMonitor fallback stub so we don't crash in JIT code
+    // when we try to enter it. See callers of offsetOfFallbackMonitorStub.
+    if (CodeSpec[*pc].format & JOF_TYPESET) {
+        ICFallbackStub* fallbackStub = icEntry.firstStub()->getChainFallback();
+        if (!fallbackStub->toMonitoredFallbackStub()->getFallbackMonitorStub(cx, script))
+            return false;
+    }
+
     // Push return address into ICCall_Scripted stub, immediately after the call.
     void* baselineCallReturnAddr = GetStubReturnAddress(cx, pc);
     MOZ_ASSERT(baselineCallReturnAddr);
     if (!builder.writePtr(baselineCallReturnAddr, "ReturnAddr"))
         return false;
     MOZ_ASSERT(builder.framePushed() % JitStackAlignment == 0);
 
     // If actualArgc >= fun->nargs, then we are done.  Otherwise, we need to push on
--- a/js/src/jit/BaselineCacheIRCompiler.cpp
+++ b/js/src/jit/BaselineCacheIRCompiler.cpp
@@ -2257,18 +2257,23 @@ js::jit::AttachBaselineCacheIRStub(JSCon
       case BaselineCacheIRStubKind::Regular: {
         auto newStub = new(newStubMem) ICCacheIR_Regular(code, stubInfo);
         writer.copyStubData(newStub->stubDataStart());
         stub->addNewStub(newStub);
         *attached = true;
         return newStub;
       }
       case BaselineCacheIRStubKind::Monitored: {
-        ICStub* monitorStub =
-            stub->toMonitoredFallbackStub()->fallbackMonitorStub()->firstMonitorStub();
+        ICTypeMonitor_Fallback* typeMonitorFallback =
+            stub->toMonitoredFallbackStub()->getFallbackMonitorStub(cx, outerScript);
+        if (!typeMonitorFallback) {
+            cx->recoverFromOutOfMemory();
+            return nullptr;
+        }
+        ICStub* monitorStub = typeMonitorFallback->firstMonitorStub();
         auto newStub = new(newStubMem) ICCacheIR_Monitored(code, monitorStub, stubInfo);
         writer.copyStubData(newStub->stubDataStart());
         stub->addNewStub(newStub);
         *attached = true;
         return newStub;
       }
       case BaselineCacheIRStubKind::Updated: {
         auto newStub = new(newStubMem) ICCacheIR_Updated(code, stubInfo);
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -977,48 +977,46 @@ BaselineCompiler::emitBody()
             if (pc >= script->codeEnd())
                 break;
 
             lastOpUnreachable = true;
             prevpc = pc;
             continue;
         }
 
-        // Fully sync the stack if there are incoming jumps.
         if (info->jumpTarget) {
+            // Fully sync the stack if there are incoming jumps.
             frame.syncStack(0);
             frame.setStackDepth(info->stackDepth);
-        }
-
-        // Always sync in debug mode.
-        if (compileDebugInstrumentation_)
+            masm.bind(labelOf(pc));
+        } else if (MOZ_UNLIKELY(compileDebugInstrumentation_)) {
+            // Also fully sync the stack if the debugger is enabled.
             frame.syncStack(0);
-
-        // At the beginning of any op, at most the top 2 stack-values are unsynced.
-        if (frame.stackDepth() > 2)
-            frame.syncStack(2);
+        } else {
+            // At the beginning of any op, at most the top 2 stack-values are unsynced.
+            if (frame.stackDepth() > 2)
+                frame.syncStack(2);
+        }
 
         frame.assertValidState(*info);
 
-        masm.bind(labelOf(pc));
-
         // Add a PC -> native mapping entry for the current op. These entries are
         // used when we need the native code address for a given pc, for instance
         // for bailouts from Ion, the debugger and exception handling. See
         // PCMappingIndexEntry for more information.
         bool addIndexEntry = (pc == script->code() || lastOpUnreachable || emittedOps > 100);
         if (addIndexEntry)
             emittedOps = 0;
-        if (!addPCMappingEntry(addIndexEntry)) {
+        if (MOZ_UNLIKELY(!addPCMappingEntry(addIndexEntry))) {
             ReportOutOfMemory(cx);
             return Method_Error;
         }
 
         // Emit traps for breakpoints and step mode.
-        if (compileDebugInstrumentation_ && !emitDebugTrap())
+        if (MOZ_UNLIKELY(compileDebugInstrumentation_) && !emitDebugTrap())
             return Method_Error;
 
         switch (op) {
           // ===== NOT Yet Implemented =====
           case JSOP_FORCEINTERPRETER:
             // Intentionally not implemented.
           case JSOP_SETINTRINSIC:
             // Run-once opcode during self-hosting initialization.
@@ -1028,17 +1026,17 @@ BaselineCompiler::emitBody()
             // === !! WARNING WARNING WARNING !! ===
             // Do you really want to sacrifice performance by not implementing
             // this operation in the BaselineCompiler?
             JitSpew(JitSpew_BaselineAbort, "Unhandled op: %s", CodeName[op]);
             return Method_CantCompile;
 
 #define EMIT_OP(OP)                            \
           case OP:                             \
-            if (!this->emit_##OP())            \
+            if (MOZ_UNLIKELY(!this->emit_##OP())) \
                 return Method_Error;           \
             break;
 OPCODE_LIST(EMIT_OP)
 #undef EMIT_OP
         }
 
         // If the main instruction is not a jump target, then we emit the
         //  corresponding code coverage counter.
--- a/js/src/jit/BaselineDebugModeOSR.cpp
+++ b/js/src/jit/BaselineDebugModeOSR.cpp
@@ -752,17 +752,20 @@ CloneOldBaselineStub(JSContext* cx, Debu
         }
     }
 
     // Some stubs are monitored, get the first stub in the monitor chain from
     // the new fallback stub if so.
     ICStub* firstMonitorStub;
     if (fallbackStub->isMonitoredFallback()) {
         ICMonitoredFallbackStub* monitored = fallbackStub->toMonitoredFallbackStub();
-        firstMonitorStub = monitored->fallbackMonitorStub()->firstMonitorStub();
+        ICTypeMonitor_Fallback* fallback = monitored->getFallbackMonitorStub(cx, entry.script);
+        if (!fallback)
+            return false;
+        firstMonitorStub = fallback->firstMonitorStub();
     } else {
         firstMonitorStub = nullptr;
     }
     ICStubSpace* stubSpace = ICStubCompiler::StubSpaceForStub(oldStub->makesGCCalls(), entry.script,
                                                               ICStubCompiler::Engine::Baseline);
 
     // Clone the existing stub into the recompiled IC.
     //
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -1836,34 +1836,35 @@ ICSetProp_Fallback::Compiler::postGenera
 }
 
 //
 // Call_Fallback
 //
 
 static bool
 TryAttachFunApplyStub(JSContext* cx, ICCall_Fallback* stub, HandleScript script, jsbytecode* pc,
-                      HandleValue thisv, uint32_t argc, Value* argv, bool* attached)
+                      HandleValue thisv, uint32_t argc, Value* argv,
+                      ICTypeMonitor_Fallback* typeMonitorFallback, bool* attached)
 {
     if (argc != 2)
         return true;
 
     if (!thisv.isObject() || !thisv.toObject().is<JSFunction>())
         return true;
     RootedFunction target(cx, &thisv.toObject().as<JSFunction>());
 
     bool isScripted = target->hasJITCode();
 
     // right now, only handle situation where second argument is |arguments|
     if (argv[1].isMagic(JS_OPTIMIZED_ARGUMENTS) && !script->needsArgsObj()) {
         if (isScripted && !stub->hasStub(ICStub::Call_ScriptedApplyArguments)) {
             JitSpew(JitSpew_BaselineIC, "  Generating Call_ScriptedApplyArguments stub");
 
             ICCall_ScriptedApplyArguments::Compiler compiler(
-                cx, stub->fallbackMonitorStub()->firstMonitorStub(), script->pcToOffset(pc));
+                cx, typeMonitorFallback->firstMonitorStub(), script->pcToOffset(pc));
             ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
             if (!newStub)
                 return false;
 
             stub->addNewStub(newStub);
             *attached = true;
             return true;
         }
@@ -1871,49 +1872,50 @@ TryAttachFunApplyStub(JSContext* cx, ICC
         // TODO: handle FUNAPPLY for native targets.
     }
 
     if (argv[1].isObject() && argv[1].toObject().is<ArrayObject>()) {
         if (isScripted && !stub->hasStub(ICStub::Call_ScriptedApplyArray)) {
             JitSpew(JitSpew_BaselineIC, "  Generating Call_ScriptedApplyArray stub");
 
             ICCall_ScriptedApplyArray::Compiler compiler(
-                cx, stub->fallbackMonitorStub()->firstMonitorStub(), script->pcToOffset(pc));
+                cx, typeMonitorFallback->firstMonitorStub(), script->pcToOffset(pc));
             ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
             if (!newStub)
                 return false;
 
             stub->addNewStub(newStub);
             *attached = true;
             return true;
         }
     }
     return true;
 }
 
 static bool
 TryAttachFunCallStub(JSContext* cx, ICCall_Fallback* stub, HandleScript script, jsbytecode* pc,
-                     HandleValue thisv, bool* attached)
+                     HandleValue thisv, ICTypeMonitor_Fallback* typeMonitorFallback,
+                     bool* attached)
 {
     // Try to attach a stub for Function.prototype.call with scripted |this|.
 
     *attached = false;
     if (!thisv.isObject() || !thisv.toObject().is<JSFunction>())
         return true;
     RootedFunction target(cx, &thisv.toObject().as<JSFunction>());
 
     // Attach a stub if the script can be Baseline-compiled. We do this also
     // if the script is not yet compiled to avoid attaching a CallNative stub
     // that handles everything, even after the callee becomes hot.
     if (target->hasScript() && target->nonLazyScript()->canBaselineCompile() &&
         !stub->hasStub(ICStub::Call_ScriptedFunCall))
     {
         JitSpew(JitSpew_BaselineIC, "  Generating Call_ScriptedFunCall stub");
 
-        ICCall_ScriptedFunCall::Compiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub(),
+        ICCall_ScriptedFunCall::Compiler compiler(cx, typeMonitorFallback->firstMonitorStub(),
                                                   script->pcToOffset(pc));
         ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
         if (!newStub)
             return false;
 
         *attached = true;
         stub->addNewStub(newStub);
         return true;
@@ -2150,31 +2152,35 @@ TryAttachCallStub(JSContext* cx, ICCall_
     if (stub->numOptimizedStubs() == 0 && IsOptimizableConstStringSplit(callee, argc, vp + 2))
         return true;
 
     stub->unlinkStubsWithKind(cx, ICStub::Call_ConstStringSplit);
 
     if (!callee.isObject())
         return true;
 
+    ICTypeMonitor_Fallback* typeMonitorFallback = stub->getFallbackMonitorStub(cx, script);
+    if (!typeMonitorFallback)
+        return false;
+
     RootedObject obj(cx, &callee.toObject());
     if (!obj->is<JSFunction>()) {
         // Try to attach a stub for a call/construct hook on the object.
         // Ignore proxies, which are special cased by callHook/constructHook.
         if (obj->is<ProxyObject>())
             return true;
         if (JSNative hook = constructing ? obj->constructHook() : obj->callHook()) {
             if (op != JSOP_FUNAPPLY && !isSpread && !createSingleton) {
                 RootedObject templateObject(cx);
                 CallArgs args = CallArgsFromVp(argc, vp);
                 if (!GetTemplateObjectForClassHook(cx, hook, args, &templateObject))
                     return false;
 
                 JitSpew(JitSpew_BaselineIC, "  Generating Call_ClassHook stub");
-                ICCall_ClassHook::Compiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub(),
+                ICCall_ClassHook::Compiler compiler(cx, typeMonitorFallback->firstMonitorStub(),
                                                     obj->getClass(), hook, templateObject,
                                                     script->pcToOffset(pc), constructing);
                 ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
                 if (!newStub)
                     return false;
 
                 stub->addNewStub(newStub);
                 *handled = true;
@@ -2212,17 +2218,17 @@ TryAttachCallStub(JSContext* cx, ICCall_
             JitSpew(JitSpew_BaselineIC, "  Chain already has generalized scripted call stub!");
             return true;
         }
 
         if (stub->scriptedStubCount() >= ICCall_Fallback::MAX_SCRIPTED_STUBS) {
             // Create a Call_AnyScripted stub.
             JitSpew(JitSpew_BaselineIC, "  Generating Call_AnyScripted stub (cons=%s, spread=%s)",
                     constructing ? "yes" : "no", isSpread ? "yes" : "no");
-            ICCallScriptedCompiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub(),
+            ICCallScriptedCompiler compiler(cx, typeMonitorFallback->firstMonitorStub(),
                                             constructing, isSpread, script->pcToOffset(pc));
             ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
             if (!newStub)
                 return false;
 
             // Before adding new stub, unlink all previous Call_Scripted.
             stub->unlinkStubsWithKind(cx, ICStub::Call_Scripted);
 
@@ -2281,17 +2287,17 @@ TryAttachCallStub(JSContext* cx, ICCall_
             if (thisObject->is<PlainObject>() || thisObject->is<UnboxedPlainObject>())
                 templateObject = thisObject;
         }
 
         JitSpew(JitSpew_BaselineIC,
                 "  Generating Call_Scripted stub (fun=%p, %s:%zu, cons=%s, spread=%s)",
                 fun.get(), fun->nonLazyScript()->filename(), fun->nonLazyScript()->lineno(),
                 constructing ? "yes" : "no", isSpread ? "yes" : "no");
-        ICCallScriptedCompiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub(),
+        ICCallScriptedCompiler compiler(cx, typeMonitorFallback->firstMonitorStub(),
                                         fun, templateObject,
                                         constructing, isSpread, script->pcToOffset(pc));
         ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
         if (!newStub)
             return false;
 
         stub->addNewStub(newStub);
         *handled = true;
@@ -2299,26 +2305,28 @@ TryAttachCallStub(JSContext* cx, ICCall_
     }
 
     if (fun->isNative() && (!constructing || (constructing && fun->isConstructor()))) {
         // Generalized native call stubs are not here yet!
         MOZ_ASSERT(!stub->nativeStubsAreGeneralized());
 
         // Check for JSOP_FUNAPPLY
         if (op == JSOP_FUNAPPLY) {
-            if (fun->native() == fun_apply)
-                return TryAttachFunApplyStub(cx, stub, script, pc, thisv, argc, vp + 2, handled);
+            if (fun->native() == fun_apply) {
+                return TryAttachFunApplyStub(cx, stub, script, pc, thisv, argc, vp + 2,
+                                             typeMonitorFallback, handled);
+            }
 
             // Don't try to attach a "regular" optimized call stubs for FUNAPPLY ops,
             // since MagicArguments may escape through them.
             return true;
         }
 
         if (op == JSOP_FUNCALL && fun->native() == fun_call) {
-            if (!TryAttachFunCallStub(cx, stub, script, pc, thisv, handled))
+            if (!TryAttachFunCallStub(cx, stub, script, pc, thisv, typeMonitorFallback, handled))
                 return false;
             if (*handled)
                 return true;
         }
 
         if (stub->nativeStubCount() >= ICCall_Fallback::MAX_NATIVE_STUBS) {
             JitSpew(JitSpew_BaselineIC,
                     "  Too many Call_Native stubs. TODO: add Call_AnyNative!");
@@ -2357,17 +2365,17 @@ TryAttachCallStub(JSContext* cx, ICCall_
         bool ignoresReturnValue = false;
         if (op == JSOP_CALL_IGNORES_RV && fun->isNative()) {
             const JSJitInfo* jitInfo = fun->jitInfo();
             ignoresReturnValue = jitInfo && jitInfo->type() == JSJitInfo::IgnoresReturnValueNative;
         }
 
         JitSpew(JitSpew_BaselineIC, "  Generating Call_Native stub (fun=%p, cons=%s, spread=%s)",
                 fun.get(), constructing ? "yes" : "no", isSpread ? "yes" : "no");
-        ICCall_Native::Compiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub(),
+        ICCall_Native::Compiler compiler(cx, typeMonitorFallback->firstMonitorStub(),
                                          fun, templateObject, constructing, ignoresReturnValue,
                                          isSpread, script->pcToOffset(pc));
         ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
         if (!newStub)
             return false;
 
         stub->addNewStub(newStub);
         *handled = true;
@@ -2425,17 +2433,21 @@ TryAttachConstStringSplit(JSContext* cx,
     for (uint32_t i = 0; i < initLength; i++) {
         JSAtom* str = js::AtomizeString(cx, arrObj->getDenseElement(i).toString());
         if (!str)
             return false;
 
         arrObj->setDenseElementWithType(cx, i, StringValue(str));
     }
 
-    ICCall_ConstStringSplit::Compiler compiler(cx, stub->fallbackMonitorStub()->firstMonitorStub(),
+    ICTypeMonitor_Fallback* typeMonitorFallback = stub->getFallbackMonitorStub(cx, script);
+    if (!typeMonitorFallback)
+        return false;
+
+    ICCall_ConstStringSplit::Compiler compiler(cx, typeMonitorFallback->firstMonitorStub(),
                                                script->pcToOffset(pc), str, sep, arrObj);
     ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
     if (!newStub)
         return false;
 
     stub->addNewStub(newStub);
     *attached = true;
     return true;
@@ -3022,17 +3034,19 @@ ICCall_Fallback::Compiler::generateStubC
         masm.assumeUnreachable("Failed to return object in constructing call.");
 #endif
         masm.bind(&skipThisReplace);
     }
 
     // At this point, ICStubReg points to the ICCall_Fallback stub, which is NOT
     // a MonitoredStub, but rather a MonitoredFallbackStub.  To use EmitEnterTypeMonitorIC,
     // first load the ICTypeMonitor_Fallback stub into ICStubReg.  Then, use
-    // EmitEnterTypeMonitorIC with a custom struct offset.
+    // EmitEnterTypeMonitorIC with a custom struct offset. Note that we must
+    // have a non-null fallbackMonitorStub here because InitFromBailout
+    // delazifies.
     masm.loadPtr(Address(ICStubReg, ICMonitoredFallbackStub::offsetOfFallbackMonitorStub()),
                  ICStubReg);
     EmitEnterTypeMonitorIC(masm, ICTypeMonitor_Fallback::offsetOfFirstMonitorStub());
 
     return true;
 }
 
 void
--- a/js/src/jit/BaselineIC.h
+++ b/js/src/jit/BaselineIC.h
@@ -426,22 +426,17 @@ class ICGetElem_Fallback : public ICMoni
 
       public:
         explicit Compiler(JSContext* cx, bool hasReceiver = false)
           : ICStubCompiler(cx, ICStub::GetElem_Fallback, Engine::Baseline),
             hasReceiver_(hasReceiver)
         { }
 
         ICStub* getStub(ICStubSpace* space) {
-            ICGetElem_Fallback* stub = newStub<ICGetElem_Fallback>(space, getStubCode());
-            if (!stub)
-                return nullptr;
-            if (!stub->initMonitoringChain(cx, space))
-                return nullptr;
-            return stub;
+            return newStub<ICGetElem_Fallback>(space, getStubCode());
         }
     };
 };
 
 // SetElem
 //      JSOP_SETELEM
 //      JSOP_INITELEM
 
@@ -557,20 +552,17 @@ class ICGetName_Fallback : public ICMoni
         MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm);
 
       public:
         explicit Compiler(JSContext* cx)
           : ICStubCompiler(cx, ICStub::GetName_Fallback, Engine::Baseline)
         { }
 
         ICStub* getStub(ICStubSpace* space) {
-            ICGetName_Fallback* stub = newStub<ICGetName_Fallback>(space, getStubCode());
-            if (!stub || !stub->initMonitoringChain(cx, space))
-                return nullptr;
-            return stub;
+            return newStub<ICGetName_Fallback>(space, getStubCode());
         }
     };
 };
 
 // BindName
 //      JSOP_BINDNAME
 class ICBindName_Fallback : public ICFallbackStub
 {
@@ -612,21 +604,17 @@ class ICGetIntrinsic_Fallback : public I
         MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm);
 
       public:
         explicit Compiler(JSContext* cx)
           : ICStubCompiler(cx, ICStub::GetIntrinsic_Fallback, Engine::Baseline)
         { }
 
         ICStub* getStub(ICStubSpace* space) {
-            ICGetIntrinsic_Fallback* stub =
-                newStub<ICGetIntrinsic_Fallback>(space, getStubCode());
-            if (!stub || !stub->initMonitoringChain(cx, space))
-                return nullptr;
-            return stub;
+            return newStub<ICGetIntrinsic_Fallback>(space, getStubCode());
         }
     };
 };
 
 // Stub that loads the constant result of a GETINTRINSIC operation.
 class ICGetIntrinsic_Constant : public ICStub
 {
     friend class ICStubSpace;
@@ -794,20 +782,17 @@ class ICCall_Fallback : public ICMonitor
       public:
         Compiler(JSContext* cx, bool isConstructing, bool isSpread)
           : ICCallStubCompiler(cx, ICStub::Call_Fallback),
             isConstructing_(isConstructing),
             isSpread_(isSpread)
         { }
 
         ICStub* getStub(ICStubSpace* space) {
-            ICCall_Fallback* stub = newStub<ICCall_Fallback>(space, getStubCode());
-            if (!stub || !stub->initMonitoringChain(cx, space))
-                return nullptr;
-            return stub;
+            return newStub<ICCall_Fallback>(space, getStubCode());
         }
     };
 };
 
 class ICCall_Scripted : public ICMonitoredStub
 {
     friend class ICStubSpace;
   public:
--- a/js/src/jit/BaselineJIT.cpp
+++ b/js/src/jit/BaselineJIT.cpp
@@ -1074,18 +1074,19 @@ BaselineScript::purgeOptimizedStubs(Zone
                 prev = stub;
                 stub = stub->next();
             }
 
             if (lastStub->isMonitoredFallback()) {
                 // Monitor stubs can't make calls, so are always in the
                 // optimized stub space.
                 ICTypeMonitor_Fallback* lastMonStub =
-                    lastStub->toMonitoredFallbackStub()->fallbackMonitorStub();
-                lastMonStub->resetMonitorStubChain(zone);
+                    lastStub->toMonitoredFallbackStub()->maybeFallbackMonitorStub();
+                if (lastMonStub)
+                    lastMonStub->resetMonitorStubChain(zone);
             }
         } else if (lastStub->isTypeMonitor_Fallback()) {
             lastStub->toTypeMonitor_Fallback()->resetMonitorStubChain(zone);
         } else {
             MOZ_ASSERT(lastStub->isTableSwitch());
         }
     }
 
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -1250,24 +1250,18 @@ IonScript::purgeOptimizedStubs(Zone* zon
                 }
 
                 prev = stub;
                 stub = stub->next();
             }
 
             lastStub->toFallbackStub()->setInvalid();
 
-            if (lastStub->isMonitoredFallback()) {
-                // Monitor stubs can't make calls, so are always in the
-                // optimized stub space.
-                ICTypeMonitor_Fallback* lastMonStub =
-                    lastStub->toMonitoredFallbackStub()->fallbackMonitorStub();
-                lastMonStub->resetMonitorStubChain(zone);
-                lastMonStub->setInvalid();
-            }
+            MOZ_ASSERT(!lastStub->isMonitoredFallback(),
+                       "None of the shared stubs used in Ion are monitored");
         } else if (lastStub->isTypeMonitor_Fallback()) {
             lastStub->toTypeMonitor_Fallback()->resetMonitorStubChain(zone);
             lastStub->toTypeMonitor_Fallback()->setInvalid();
         } else {
             MOZ_ASSERT(lastStub->isTableSwitch());
         }
     }
 
--- a/js/src/jit/SharedIC.cpp
+++ b/js/src/jit/SharedIC.cpp
@@ -221,20 +221,26 @@ ICStub::trace(JSTracer* trc)
 {
     traceCode(trc, "shared-stub-jitcode");
 
     // If the stub is a monitored fallback stub, then trace the monitor ICs hanging
     // off of that stub.  We don't need to worry about the regular monitored stubs,
     // because the regular monitored stubs will always have a monitored fallback stub
     // that references the same stub chain.
     if (isMonitoredFallback()) {
-        ICTypeMonitor_Fallback* lastMonStub = toMonitoredFallbackStub()->fallbackMonitorStub();
-        for (ICStubConstIterator iter(lastMonStub->firstMonitorStub()); !iter.atEnd(); iter++) {
-            MOZ_ASSERT_IF(iter->next() == nullptr, *iter == lastMonStub);
-            iter->trace(trc);
+        ICTypeMonitor_Fallback* lastMonStub =
+            toMonitoredFallbackStub()->maybeFallbackMonitorStub();
+        if (lastMonStub) {
+            for (ICStubConstIterator iter(lastMonStub->firstMonitorStub());
+                 !iter.atEnd();
+                 iter++)
+            {
+                MOZ_ASSERT_IF(iter->next() == nullptr, *iter == lastMonStub);
+                iter->trace(trc);
+            }
         }
     }
 
     if (isUpdated()) {
         for (ICStubConstIterator iter(toUpdatedStub()->firstUpdateStub()); !iter.atEnd(); iter++) {
             MOZ_ASSERT_IF(iter->next() == nullptr, iter->isTypeUpdate_Fallback());
             iter->trace(trc);
         }
@@ -361,17 +367,19 @@ ICFallbackStub::unlinkStub(Zone* zone, I
         stub->trace(zone->barrierTracer());
     }
 
     if (stub->makesGCCalls() && stub->isMonitored()) {
         // This stub can make calls so we can return to it if it's on the stack.
         // We just have to reset its firstMonitorStub_ field to avoid a stale
         // pointer when purgeOptimizedStubs destroys all optimized monitor
         // stubs (unlinked stubs won't be updated).
-        ICTypeMonitor_Fallback* monitorFallback = toMonitoredFallbackStub()->fallbackMonitorStub();
+        ICTypeMonitor_Fallback* monitorFallback =
+            toMonitoredFallbackStub()->maybeFallbackMonitorStub();
+        MOZ_ASSERT(monitorFallback);
         stub->toMonitoredStub()->resetFirstMonitorStub(monitorFallback);
     }
 
 #ifdef DEBUG
     // Poison stub code to ensure we don't call this stub again. However, if
     // this stub can make calls, a pointer to it may be stored in a stub frame
     // on the stack, so we can't touch the stubCode_ or GC will crash when
     // tracing this pointer.
@@ -452,33 +460,37 @@ ICMonitoredStub::ICMonitoredStub(Kind ki
     // If the first monitored stub is a ICTypeMonitor_Fallback stub, then
     // double check that _its_ firstMonitorStub is the same as this one.
     MOZ_ASSERT_IF(firstMonitorStub_->isTypeMonitor_Fallback(),
                   firstMonitorStub_->toTypeMonitor_Fallback()->firstMonitorStub() ==
                      firstMonitorStub_);
 }
 
 bool
-ICMonitoredFallbackStub::initMonitoringChain(JSContext* cx, ICStubSpace* space)
+ICMonitoredFallbackStub::initMonitoringChain(JSContext* cx, JSScript* script)
 {
     MOZ_ASSERT(fallbackMonitorStub_ == nullptr);
 
     ICTypeMonitor_Fallback::Compiler compiler(cx, this);
+    ICStubSpace* space = script->baselineScript()->fallbackStubSpace();
     ICTypeMonitor_Fallback* stub = compiler.getStub(space);
     if (!stub)
         return false;
     fallbackMonitorStub_ = stub;
     return true;
 }
 
 bool
 ICMonitoredFallbackStub::addMonitorStubForValue(JSContext* cx, BaselineFrame* frame,
                                                 StackTypeSet* types, HandleValue val)
 {
-    return fallbackMonitorStub_->addMonitorStubForValue(cx, frame, types, val);
+    ICTypeMonitor_Fallback* typeMonitorFallback = getFallbackMonitorStub(cx, frame->script());
+    if (!typeMonitorFallback)
+        return false;
+    return typeMonitorFallback->addMonitorStubForValue(cx, frame, types, val);
 }
 
 bool
 ICUpdatedStub::initUpdatingChain(JSContext* cx, ICStubSpace* space)
 {
     MOZ_ASSERT(firstUpdateStub_ == nullptr);
 
     ICTypeUpdate_Fallback::Compiler compiler(cx);
@@ -2157,17 +2169,18 @@ ICGetProp_Fallback::Compiler::generateSt
     // will point here.
     assumeStubFrame(masm);
     bailoutReturnOffset_.bind(masm.currentOffset());
 
     leaveStubFrame(masm, true);
 
     // When we get here, ICStubReg contains the ICGetProp_Fallback stub,
     // which we can't use to enter the TypeMonitor IC, because it's a MonitoredFallbackStub
-    // instead of a MonitoredStub. So, we cheat.
+    // instead of a MonitoredStub. So, we cheat. Note that we must have a
+    // non-null fallbackMonitorStub here because InitFromBailout delazifies.
     masm.loadPtr(Address(ICStubReg, ICMonitoredFallbackStub::offsetOfFallbackMonitorStub()),
                  ICStubReg);
     EmitEnterTypeMonitorIC(masm, ICTypeMonitor_Fallback::offsetOfFirstMonitorStub());
 
     return true;
 }
 
 void
--- a/js/src/jit/SharedIC.h
+++ b/js/src/jit/SharedIC.h
@@ -1196,29 +1196,36 @@ class SharedStubInfo
         return icEntry_;
     }
 };
 
 // Monitored fallback stubs - as the name implies.
 class ICMonitoredFallbackStub : public ICFallbackStub
 {
   protected:
-    // Pointer to the fallback monitor stub.
+    // Pointer to the fallback monitor stub. Created lazily by
+    // getFallbackMonitorStub if needed.
     ICTypeMonitor_Fallback* fallbackMonitorStub_;
 
     ICMonitoredFallbackStub(Kind kind, JitCode* stubCode)
       : ICFallbackStub(kind, ICStub::MonitoredFallback, stubCode),
         fallbackMonitorStub_(nullptr) {}
 
   public:
-    MOZ_MUST_USE bool initMonitoringChain(JSContext* cx, ICStubSpace* space);
+    MOZ_MUST_USE bool initMonitoringChain(JSContext* cx, JSScript* script);
     MOZ_MUST_USE bool addMonitorStubForValue(JSContext* cx, BaselineFrame* frame,
                                              StackTypeSet* types, HandleValue val);
 
-    inline ICTypeMonitor_Fallback* fallbackMonitorStub() const {
+    ICTypeMonitor_Fallback* maybeFallbackMonitorStub() const {
+        return fallbackMonitorStub_;
+    }
+    ICTypeMonitor_Fallback* getFallbackMonitorStub(JSContext* cx, JSScript* script) {
+        if (!fallbackMonitorStub_ && !initMonitoringChain(cx, script))
+            return nullptr;
+        MOZ_ASSERT(fallbackMonitorStub_);
         return fallbackMonitorStub_;
     }
 
     static inline size_t offsetOfFallbackMonitorStub() {
         return offsetof(ICMonitoredFallbackStub, fallbackMonitorStub_);
     }
 };
 
@@ -2356,20 +2363,17 @@ class ICGetProp_Fallback : public ICMoni
 
       public:
         explicit Compiler(JSContext* cx, Engine engine, bool hasReceiver = false)
           : ICStubCompiler(cx, ICStub::GetProp_Fallback, engine),
             hasReceiver_(hasReceiver)
         { }
 
         ICStub* getStub(ICStubSpace* space) {
-            ICGetProp_Fallback* stub = newStub<ICGetProp_Fallback>(space, getStubCode());
-            if (!stub || !stub->initMonitoringChain(cx, space))
-                return nullptr;
-            return stub;
+            return newStub<ICGetProp_Fallback>(space, getStubCode());
         }
     };
 };
 
 static inline uint32_t
 SimpleTypeDescrKey(SimpleTypeDescr* descr)
 {
     if (descr->is<ScalarTypeDescr>())
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -97,18 +97,18 @@ MSG_DEF(JSMSG_ALREADY_HAS_PRAGMA,      2
 MSG_DEF(JSMSG_GET_ITER_RETURNED_PRIMITIVE, 0, JSEXN_TYPEERR, "[Symbol.iterator]() returned a non-object value")
 MSG_DEF(JSMSG_ITER_METHOD_RETURNED_PRIMITIVE, 1, JSEXN_TYPEERR, "iterator.{0}() returned a non-object value")
 MSG_DEF(JSMSG_CANT_SET_PROTO,          0, JSEXN_TYPEERR, "can't set prototype of this object")
 MSG_DEF(JSMSG_CANT_SET_PROTO_OF,       1, JSEXN_TYPEERR, "can't set prototype of {0}")
 MSG_DEF(JSMSG_CANT_SET_PROTO_CYCLE,    0, JSEXN_TYPEERR, "can't set prototype: it would cause a prototype chain cycle")
 MSG_DEF(JSMSG_INVALID_ARG_TYPE,        3, JSEXN_TYPEERR, "Invalid type: {0} can't be a{1} {2}")
 MSG_DEF(JSMSG_TERMINATED,              1, JSEXN_ERR, "Script terminated by timeout at:\n{0}")
 MSG_DEF(JSMSG_CANT_CALL_CLASS_CONSTRUCTOR, 0, JSEXN_TYPEERR, "class constructors must be invoked with |new|")
-MSG_DEF(JSMSG_UNINITIALIZED_THIS,      1, JSEXN_REFERENCEERR, "|this| used uninitialized in {0} class constructor")
-MSG_DEF(JSMSG_UNINITIALIZED_THIS_ARROW, 0, JSEXN_REFERENCEERR, "|this| used uninitialized in arrow function in class constructor")
+MSG_DEF(JSMSG_UNINITIALIZED_THIS,      1, JSEXN_REFERENCEERR, "must call super constructor before using |this| in {0} class constructor")
+MSG_DEF(JSMSG_UNINITIALIZED_THIS_ARROW, 0, JSEXN_REFERENCEERR, "must call super constructor before using |this| in arrow function in derived class constructor")
 MSG_DEF(JSMSG_BAD_DERIVED_RETURN,      1, JSEXN_TYPEERR, "derived class constructor returned invalid value {0}")
 MSG_DEF(JSMSG_BAD_HERITAGE,            2, JSEXN_TYPEERR, "class heritage {0} is {1}")
 MSG_DEF(JSMSG_NOT_OBJORNULL,           1, JSEXN_TYPEERR, "{0} is not an object or null")
 
 // JSON
 MSG_DEF(JSMSG_JSON_BAD_PARSE,          3, JSEXN_SYNTAXERR, "JSON.parse: {0} at line {1} column {2} of the JSON data")
 MSG_DEF(JSMSG_JSON_CYCLIC_VALUE,       0, JSEXN_TYPEERR, "cyclic object value")
 
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -5664,17 +5664,17 @@ struct BufferStreamState
     {}
 
     ~BufferStreamState()
     {
         MOZ_ASSERT(jobs.empty());
     }
 };
 
-ExclusiveWaitableData<BufferStreamState> bufferStreamState(mutexid::BufferStreamState);
+static ExclusiveWaitableData<BufferStreamState> bufferStreamState(mutexid::BufferStreamState);
 
 static void
 BufferStreamMain(BufferStreamJob* job)
 {
     const uint8_t* const bytes = job->bytes.begin();
 
     size_t byteOffset = 0;
     while (true) {
@@ -5741,25 +5741,16 @@ ConsumeBufferSource(JSContext* cx, JS::H
         MOZ_ASSERT(!state->shutdown);
         if (!state->jobs.append(Move(job)))
             return false;
     }
 
     return jobPtr->thread.init(BufferStreamMain, jobPtr);
 }
 
-static void
-ShutdownBufferStreams()
-{
-    auto state = bufferStreamState.lock();
-    state->shutdown = true;
-    while (!state->jobs.empty())
-        state.wait(/* jobs empty */);
-}
-
 static bool
 SetBufferStreamParams(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     if (!args.requireAtLeast(cx, "setBufferStreamParams", 2))
         return false;
 
     double delayMillis;
@@ -8628,16 +8619,23 @@ PreInit()
 #endif
 }
 
 int
 main(int argc, char** argv, char** envp)
 {
     PreInit();
 
+    auto shutdownBufferStreams = MakeScopeExit([] {
+        auto state = bufferStreamState.lock();
+        state->shutdown = true;
+        while (!state->jobs.empty())
+            state.wait(/* jobs empty */);
+    });
+
     sArgc = argc;
     sArgv = argv;
 
     int result;
 
 #ifdef HAVE_SETLOCALE
     setlocale(LC_ALL, "");
 #endif
@@ -8970,15 +8968,14 @@ main(int argc, char** argv, char** envp)
 
     // Must clear out some of sc's pointer containers before JS_DestroyContext.
     sc->markObservers.reset();
 
     KillWatchdog(cx);
 
     KillWorkerThreads(cx);
 
-    ShutdownBufferStreams();
     DestructSharedArrayBufferMailbox();
 
     JS_DestroyContext(cx);
     JS_ShutDown();
     return result;
 }
--- a/js/src/tests/ecma_6/Class/uninitializedThisError.js
+++ b/js/src/tests/ecma_6/Class/uninitializedThisError.js
@@ -1,14 +1,14 @@
 function checkErr(f, className) {
     var expected;
     if (className !== "")
-        expected = "|this| used uninitialized in " + className + " class constructor";
+        expected = "must call super constructor before using |this| in " + className + " class constructor";
     else
-        expected = "|this| used uninitialized in arrow function in class constructor";
+        expected = "must call super constructor before using |this| in arrow function in derived class constructor";
     try {
         f();
         assertEq(0, 1);
     } catch (e) {
         assertEq(e.name, "ReferenceError");
         assertEq(e.message, expected);
     }
 }
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -2934,25 +2934,32 @@ nsDisplayItem::ComputeVisibility(nsDispl
                                  nsRegion* aVisibleRegion)
 {
   return !mVisibleRect.IsEmpty() &&
     !IsInvisibleInRect(aVisibleRegion->GetBounds());
 }
 
 bool
 nsDisplayItem::RecomputeVisibility(nsDisplayListBuilder* aBuilder,
-                                   nsRegion* aVisibleRegion) {
+                                   nsRegion* aVisibleRegion,
+                                   bool aUseClipBounds) {
   if (mForceNotVisible && !GetSameCoordinateSystemChildren()) {
     // mForceNotVisible wants to ensure that this display item doesn't render
     // anything itself. If this item has contents, then we obviously want to
     // render those, so we don't need this check in that case.
     NS_ASSERTION(mVisibleRect.IsEmpty(),
       "invisible items without children should have empty vis rect");
   } else {
-    nsRect bounds = GetClippedBounds(aBuilder);
+    nsRect bounds;
+    if (aUseClipBounds) {
+      bounds = GetClippedBounds(aBuilder);
+    } else {
+      bool snap;
+      bounds = GetBounds(aBuilder, &snap);
+    }
 
     nsRegion itemVisible;
     itemVisible.And(*aVisibleRegion, bounds);
     SetVisibleRect(itemVisible.GetBounds(), false);
   }
 
   // When we recompute visibility within layers we don't need to
   // expand the visible region for content behind plugins (the plugin
--- a/layout/painting/nsDisplayList.h
+++ b/layout/painting/nsDisplayList.h
@@ -2500,17 +2500,18 @@ public:
   /**
    * Like ComputeVisibility, but does the work that nsDisplayList
    * does per-item:
    * -- Intersects GetBounds with aVisibleRegion and puts the result
    * in mVisibleRect
    * -- Subtracts bounds from aVisibleRegion if the item is opaque
    */
   bool RecomputeVisibility(nsDisplayListBuilder* aBuilder,
-                           nsRegion* aVisibleRegion);
+                           nsRegion* aVisibleRegion,
+                           bool aUseClipBounds = true);
 
   /**
    * Returns the result of aBuilder->ToReferenceFrame(GetUnderlyingFrame())
    */
   const nsPoint& ToReferenceFrame() const {
     NS_ASSERTION(mFrame, "No frame?");
     return mToReferenceFrame;
   }
--- a/netwerk/test/gtest/TestBufferedInputStream.cpp
+++ b/netwerk/test/gtest/TestBufferedInputStream.cpp
@@ -1,126 +1,25 @@
 #include "gtest/gtest.h"
 
 #include "nsBufferedStreams.h"
-#include "nsCOMPtr.h"
 #include "nsStreamUtils.h"
-#include "nsString.h"
-#include "nsStringStream.h"
 #include "nsThreadUtils.h"
 #include "Helpers.h"
 
-class AsyncStringStream final : public nsIAsyncInputStream
-{
-  nsCOMPtr<nsIInputStream> mStream;
-
-public:
-  NS_DECL_THREADSAFE_ISUPPORTS
-
-  explicit AsyncStringStream(const nsACString& aBuffer)
-  {
-    NS_NewCStringInputStream(getter_AddRefs(mStream), aBuffer);
-  }
-
-  NS_IMETHOD
-  Available(uint64_t* aLength) override
-  {
-    return mStream->Available(aLength);
-  }
-
-  NS_IMETHOD
-  Read(char* aBuffer, uint32_t aCount, uint32_t* aReadCount) override
-  {
-    return mStream->Read(aBuffer, aCount, aReadCount);
-  }
-
-  NS_IMETHOD
-  ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
-               uint32_t aCount, uint32_t *aResult) override
-  {
-    return NS_ERROR_NOT_IMPLEMENTED;
-  }
-
-  NS_IMETHOD
-  Close() override
-  {
-    nsresult rv = mStream->Close();
-    if (NS_SUCCEEDED(rv)) {
-      MaybeExecCallback(mCallback, mCallbackEventTarget);
-    }
-    return rv;
-  }
-
-  NS_IMETHOD
-  IsNonBlocking(bool* aNonBlocking) override
-  {
-    return mStream->IsNonBlocking(aNonBlocking);
-  }
-
-  NS_IMETHOD
-  CloseWithStatus(nsresult aStatus) override
-  {
-    return Close();
-  }
-
-  NS_IMETHOD
-  AsyncWait(nsIInputStreamCallback* aCallback,
-            uint32_t aFlags, uint32_t aRequestedCount,
-            nsIEventTarget* aEventTarget) override
-  {
-    if (aFlags & nsIAsyncInputStream::WAIT_CLOSURE_ONLY) {
-      mCallback = aCallback;
-      mCallbackEventTarget = aEventTarget;
-      return NS_OK;
-    }
-
-    MaybeExecCallback(aCallback, aEventTarget);
-    return NS_OK;
-  }
-
-  void
-  MaybeExecCallback(nsIInputStreamCallback* aCallback,
-                    nsIEventTarget* aEventTarget)
-  {
-    if (!aCallback) {
-      return;
-    }
-
-    nsCOMPtr<nsIInputStreamCallback> callback = aCallback;
-    nsCOMPtr<nsIAsyncInputStream> self = this;
-
-    nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
-      "AsyncWait", [callback, self]() { callback->OnInputStreamReady(self); });
-
-    if (aEventTarget) {
-      aEventTarget->Dispatch(r.forget());
-    } else {
-      r->Run();
-    }
-  }
-
-private:
-  ~AsyncStringStream() = default;
-
-  nsCOMPtr<nsIInputStreamCallback> mCallback;
-  nsCOMPtr<nsIEventTarget> mCallbackEventTarget;
-};
-
-NS_IMPL_ISUPPORTS(AsyncStringStream, nsIAsyncInputStream, nsIInputStream)
-
-// Helper function for creating a AsyncStringStream
+// Helper function for creating a testing::AsyncStringStream
 already_AddRefed<nsBufferedInputStream>
 CreateStream(uint32_t aSize, nsCString& aBuffer)
 {
   aBuffer.SetLength(aSize);
   for (uint32_t i = 0; i < aSize; ++i) {
     aBuffer.BeginWriting()[i] = i % 10;
   }
 
-  nsCOMPtr<nsIInputStream> stream = new AsyncStringStream(aBuffer);
+  nsCOMPtr<nsIInputStream> stream = new testing::AsyncStringStream(aBuffer);
 
   RefPtr<nsBufferedInputStream> bis = new nsBufferedInputStream();
   bis->Init(stream, aSize);
   return bis.forget();
 }
 
 // Simple reading.
 TEST(TestBufferedInputStream, SimpleRead) {
--- a/testing/modules/FileTestUtils.jsm
+++ b/testing/modules/FileTestUtils.jsm
@@ -12,20 +12,22 @@ this.EXPORTED_SYMBOLS = [
   "FileTestUtils",
 ];
 
 const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 
 Cu.import("resource://gre/modules/AsyncShutdown.jsm", this);
 Cu.import("resource://gre/modules/DownloadPaths.jsm", this);
 Cu.import("resource://gre/modules/FileUtils.jsm", this);
+Cu.import("resource://gre/modules/osfile.jsm", this);
 Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
 Cu.import("resource://testing-common/Assert.jsm", this);
 
 let gFileCounter = 1;
+let gPathsToRemove = [];
 
 this.FileTestUtils = {
   /**
    * Returns a reference to a temporary file that is guaranteed not to exist and
    * to have never been created before. If a file or a directory with this name
    * is created by the test, it will be deleted when all tests terminate.
    *
    * @param suggestedName [optional]
@@ -45,30 +47,89 @@ this.FileTestUtils = {
     let [base, ext] = DownloadPaths.splitBaseNameAndExtension(suggestedName);
     let leafName = base + "-" + gFileCounter + ext;
     gFileCounter++;
 
     // Get a file reference under the temporary directory for this test file.
     let file = this._globalTemporaryDirectory.clone();
     file.append(leafName);
     Assert.ok(!file.exists(), "Sanity check the temporary file doesn't exist.");
+
+    // Since directory iteration on Windows may not see files that have just
+    // been created, keep track of the known file names to be removed.
+    gPathsToRemove.push(file.path);
     return file;
   },
+
+  /**
+   * Attemps to remove the given file or directory recursively, in a way that
+   * works even on Windows, where race conditions may occur in the file system
+   * when creating and removing files at the pace of the test suites.
+   *
+   * The function may fail silently if access is denied. This means that it
+   * should only be used to clean up temporary files, rather than for cases
+   * where the removal is part of a test and must be guaranteed.
+   *
+   * @param path
+   *        String representing the path to remove.
+   * @param isDir [optional]
+   *        Indicates whether this is a directory. If not specified, this will
+   *        be determined by querying the file system.
+   */
+  async tolerantRemove(path, isDir) {
+    try {
+      if (isDir === undefined) {
+        isDir = (await OS.File.stat(path)).isDir;
+      }
+      if (isDir) {
+        // The test created a directory, remove its contents recursively. The
+        // deletion may still fail with an access denied error on Windows if any
+        // of the files in the folder were recently deleted.
+        await OS.File.removeDir(path);
+      } else {
+        // This is the usual case of a test file that has to be removed.
+        await OS.File.remove(path);
+      }
+    } catch (ex) {
+      // On Windows, we may get an access denied error instead of a no such file
+      // error if the file existed before, and was recently deleted. There is no
+      // way to distinguish this from an access list issue because checking for
+      // the file existence would also result in the same error.
+      if (!(ex instanceof OS.File.Error) ||
+          !(ex.becauseNoSuchFile || ex.becauseAccessDenied)) {
+        throw ex;
+      }
+    }
+  },
 };
 
 /**
  * Returns a reference to a global temporary directory that will be deleted
  * when all tests terminate.
  */
-XPCOMUtils.defineLazyGetter(FileTestUtils, "_globalTemporaryDirectory", () => {
-  // While previous test runs should have deleted their temporary directories,
-  // on Windows they might still be pending deletion on the physical file
-  // system. This makes a simple nsIFile.createUnique call unreliable, and we
-  // have to use a random number to make a collision unlikely.
-  let randomNumber = Math.floor(Math.random() * 1000000);
-  let dir = FileUtils.getFile("TmpD", ["testdir-" + randomNumber]);
-  dir.createUnique(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
-  AsyncShutdown.profileBeforeChange.addBlocker("Removing test files", () => {
-    // Note that this may fail if any test leaves inaccessible files behind.
-    dir.remove(true);
+XPCOMUtils.defineLazyGetter(FileTestUtils, "_globalTemporaryDirectory",
+  function() {
+    // While previous test runs should have deleted their temporary directories,
+    // on Windows they might still be pending deletion on the physical file
+    // system. This makes a simple nsIFile.createUnique call unreliable, and we
+    // have to use a random number to make a collision unlikely.
+    let randomNumber = Math.floor(Math.random() * 1000000);
+    let dir = FileUtils.getFile("TmpD", ["testdir-" + randomNumber]);
+    dir.createUnique(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
+    AsyncShutdown.profileBeforeChange.addBlocker("Removing test files",
+      async () => {
+        // Remove the files we know about first.
+        for (let path of gPathsToRemove) {
+          await this.tolerantRemove(path);
+        }
+        // Detect any extra files, like the ".part" files of downloads.
+        let iterator = new OS.File.DirectoryIterator(dir.path);
+        try {
+          await iterator.forEach(entry => this.tolerantRemove(entry.path,
+                                                              entry.isDir));
+        } finally {
+          iterator.close();
+        }
+        // This will fail if any test leaves inaccessible files behind.
+        await OS.File.removeEmptyDir(dir.path);
+      });
+    return dir;
   });
-  return dir;
-});
--- a/toolkit/content/contentAreaUtils.js
+++ b/toolkit/content/contentAreaUtils.js
@@ -992,17 +992,17 @@ function getDefaultFileName(aDefaultFile
     } catch (e) {
       try {
         fileName = mhp.getParameter(aContentDisposition, "name", charset, true,
                                     dummy);
       } catch (e) {
       }
     }
     if (fileName)
-      return fileName;
+      return validateFileName(fileName);
   }
 
   let docTitle;
   if (aDocument && aDocument.title && aDocument.title.trim()) {
     // If the document looks like HTML or XML, try to use its original title.
     docTitle = validateFileName(aDocument.title);
     let contentType = aDocument.contentType;
     if (contentType == "application/xhtml+xml" ||
@@ -1038,17 +1038,17 @@ function getDefaultFileName(aDefaultFile
   // 6) If this is a directory, use the last directory name
   var path = aURI.pathQueryRef.match(/\/([^\/]+)\/$/);
   if (path && path.length > 1)
     return validateFileName(path[1]);
 
   try {
     if (aURI.host)
       // 7) Use the host.
-      return aURI.host;
+      return validateFileName(aURI.host);
   } catch (e) {
     // Some files have no information at all, like Javascript generated pages
   }
   try {
     // 8) Use the default file name
     return ContentAreaUtils.stringBundle.GetStringFromName("DefaultSaveFileName");
   } catch (e) {
     // in case localized string cannot be found
--- a/toolkit/mozapps/downloads/nsHelperAppDlg.js
+++ b/toolkit/mozapps/downloads/nsHelperAppDlg.js
@@ -202,17 +202,18 @@ nsUnknownContentTypeDialog.prototype = {
     let bundle =
       Services.strings.createBundle("chrome://mozapps/locale/downloads/unknownContentType.properties");
 
     Services.prompt.alert(this.dialog,
                    bundle.GetStringFromName("badPermissions.title"),
                    bundle.GetStringFromName("badPermissions"));
   },
 
-  promptForSaveToFileAsync: function(aLauncher, aContext, aDefaultFile, aSuggestedFileExtension, aForcePrompt) {
+  promptForSaveToFileAsync: function(aLauncher, aContext, aDefaultFileName,
+                                     aSuggestedFileExtension, aForcePrompt) {
     var result = null;
 
     this.mLauncher = aLauncher;
 
     let prefs = Components.classes["@mozilla.org/preferences-service;1"]
                           .getService(Components.interfaces.nsIPrefBranch);
     let bundle =
       Services.strings
@@ -255,17 +256,18 @@ nsUnknownContentTypeDialog.prototype = {
         let autodownload = prefs.getBoolPref(PREF_BD_USEDOWNLOADDIR, false);
 
         if (autodownload) {
           // Retrieve the user's default download directory
           let preferredDir = await Downloads.getPreferredDownloadsDirectory();
           let defaultFolder = new FileUtils.File(preferredDir);
 
           try {
-            result = this.validateLeafName(defaultFolder, aDefaultFile, aSuggestedFileExtension);
+            result = this.validateLeafName(defaultFolder, aDefaultFileName,
+                                           aSuggestedFileExtension);
           }
           catch (ex) {
             // When the default download directory is write-protected,
             // prompt the user for a different target file.
           }
 
           // Check to make sure we have a valid directory, otherwise, prompt
           if (result) {
@@ -279,17 +281,19 @@ nsUnknownContentTypeDialog.prototype = {
         }
       }
 
       // Use file picker to show dialog.
       var nsIFilePicker = Components.interfaces.nsIFilePicker;
       var picker = Components.classes["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
       var windowTitle = bundle.GetStringFromName("saveDialogTitle");
       picker.init(parent, windowTitle, nsIFilePicker.modeSave);
-      picker.defaultString = aDefaultFile;
+      if (aDefaultFileName) {
+        picker.defaultString = this.getFinalLeafName(aDefaultFileName);
+      }
 
       if (aSuggestedFileExtension) {
         // aSuggestedFileExtension includes the period, so strip it
         picker.defaultExtension = aSuggestedFileExtension.substring(1);
       }
       else {
         try {
           picker.defaultExtension = this.mLauncher.MIMEInfo.primaryExtension;
--- a/xpcom/io/SlicedInputStream.cpp
+++ b/xpcom/io/SlicedInputStream.cpp
@@ -111,16 +111,21 @@ SlicedInputStream::Available(uint64_t* a
 {
   NS_ENSURE_STATE(mInputStream);
 
   if (mClosed) {
     return NS_BASE_STREAM_CLOSED;
   }
 
   nsresult rv = mInputStream->Available(aLength);
+  if (rv == NS_BASE_STREAM_CLOSED) {
+    mClosed = true;
+    return rv;
+  }
+
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   // Let's remove extra length from the end.
   if (*aLength + mCurPos > mStart + mLength) {
     *aLength -= XPCOM_MIN(*aLength, (*aLength + mCurPos) - (mStart + mLength));
   }
@@ -154,17 +159,22 @@ SlicedInputStream::Read(char* aBuffer, u
 
       mCurPos = mStart;
     } else {
       char buf[4096];
       while (mCurPos < mStart) {
         uint32_t bytesRead;
         uint64_t bufCount = XPCOM_MIN(mStart - mCurPos, (uint64_t)sizeof(buf));
         nsresult rv = mInputStream->Read(buf, bufCount, &bytesRead);
-        if (NS_WARN_IF(NS_FAILED(rv)) || bytesRead == 0) {
+        if (NS_SUCCEEDED(rv) && bytesRead == 0) {
+          mClosed = true;
+          return rv;
+        }
+
+        if (NS_WARN_IF(NS_FAILED(rv))) {
           return rv;
         }
 
          mCurPos += bytesRead;
       }
     }
   }
 
@@ -174,17 +184,22 @@ SlicedInputStream::Read(char* aBuffer, u
   }
 
   // Nothing else to read.
   if (!aCount) {
     return NS_OK;
   }
 
   nsresult rv = mInputStream->Read(aBuffer, aCount, aReadCount);
-  if (NS_WARN_IF(NS_FAILED(rv)) || *aReadCount == 0) {
+  if (NS_SUCCEEDED(rv) && *aReadCount == 0) {
+    mClosed = true;
+    return rv;
+  }
+
+  if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   mCurPos += *aReadCount;
   return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -304,22 +319,27 @@ SlicedInputStream::OnInputStreamReady(ns
   }
 
   if (mCurPos < mStart) {
     char buf[4096];
     while (mCurPos < mStart) {
       uint32_t bytesRead;
       uint64_t bufCount = XPCOM_MIN(mStart - mCurPos, (uint64_t)sizeof(buf));
       nsresult rv = mInputStream->Read(buf, bufCount, &bytesRead);
+      if (NS_SUCCEEDED(rv) && bytesRead == 0) {
+        mClosed = true;
+        return RunAsyncWaitCallback();
+      }
+
       if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
         return mWeakAsyncInputStream->AsyncWait(this, 0, mStart - mCurPos,
                                                 mAsyncWaitEventTarget);
       }
 
-      if (NS_WARN_IF(NS_FAILED(rv)) || bytesRead == 0) {
+      if (NS_WARN_IF(NS_FAILED(rv))) {
         return RunAsyncWaitCallback();
       }
 
       mCurPos += bytesRead;
     }
 
     // Now we are ready to do the 'real' asyncWait.
     return mWeakAsyncInputStream->AsyncWait(this, mAsyncWaitFlags,
@@ -422,16 +442,21 @@ SlicedInputStream::Seek(int32_t aWhence,
       break;
     case NS_SEEK_CUR:
       // mCurPos could be lower than mStart if the reading has not started yet.
       offset = XPCOM_MAX(mStart, mCurPos) + aOffset;
       break;
     case NS_SEEK_END: {
       uint64_t available;
       rv = mInputStream->Available(&available);
+      if (rv == NS_BASE_STREAM_CLOSED) {
+        mClosed = true;
+        return rv;
+      }
+
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
 
       offset = XPCOM_MIN(mStart + mLength, available) + aOffset;
       break;
     }
     default:
--- a/xpcom/io/nsMultiplexInputStream.cpp
+++ b/xpcom/io/nsMultiplexInputStream.cpp
@@ -250,18 +250,16 @@ nsMultiplexInputStream::Close()
   for (uint32_t i = 0; i < len; ++i) {
     nsresult rv2 = mStreams[i]->Close();
     // We still want to close all streams, but we should return an error
     if (NS_FAILED(rv2)) {
       rv = rv2;
     }
   }
 
-  mAsyncWaitCallback = nullptr;
-
   return rv;
 }
 
 NS_IMETHODIMP
 nsMultiplexInputStream::Available(uint64_t* aResult)
 {
   MutexAutoLock lock(mLock);
   if (NS_FAILED(mStatus)) {
--- a/xpcom/tests/gtest/Helpers.cpp
+++ b/xpcom/tests/gtest/Helpers.cpp
@@ -8,16 +8,17 @@
 
 #include "Helpers.h"
 
 #include <algorithm>
 #include "gtest/gtest.h"
 #include "nsIOutputStream.h"
 #include "nsStreamUtils.h"
 #include "nsTArray.h"
+#include "nsThreadUtils.h"
 
 namespace testing {
 
 // Populate an array with the given number of bytes.  Data is lorem ipsum
 // random text, but deterministic across multiple calls.
 void
 CreateData(uint32_t aNumBytes, nsTArray<char>& aDataOut)
 {
@@ -125,9 +126,93 @@ InputStreamCallback::~InputStreamCallbac
 
 NS_IMETHODIMP
 InputStreamCallback::OnInputStreamReady(nsIAsyncInputStream* aStream)
 {
   mCalled = true;
   return NS_OK;
 }
 
+AsyncStringStream::AsyncStringStream(const nsACString& aBuffer)
+{
+  NS_NewCStringInputStream(getter_AddRefs(mStream), aBuffer);
+}
+
+NS_IMETHODIMP
+AsyncStringStream::Available(uint64_t* aLength)
+{
+  return mStream->Available(aLength);
+}
+
+NS_IMETHODIMP
+AsyncStringStream::Read(char* aBuffer, uint32_t aCount, uint32_t* aReadCount)
+{
+  return mStream->Read(aBuffer, aCount, aReadCount);
+}
+
+NS_IMETHODIMP
+AsyncStringStream::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
+                                uint32_t aCount, uint32_t *aResult)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+AsyncStringStream::Close()
+{
+  nsresult rv = mStream->Close();
+  if (NS_SUCCEEDED(rv)) {
+    MaybeExecCallback(mCallback, mCallbackEventTarget);
+  }
+  return rv;
+}
+
+NS_IMETHODIMP
+AsyncStringStream::IsNonBlocking(bool* aNonBlocking)
+{
+  return mStream->IsNonBlocking(aNonBlocking);
+}
+
+NS_IMETHODIMP
+AsyncStringStream::CloseWithStatus(nsresult aStatus)
+{
+  return Close();
+}
+
+NS_IMETHODIMP
+AsyncStringStream::AsyncWait(nsIInputStreamCallback* aCallback,
+                             uint32_t aFlags, uint32_t aRequestedCount,
+                             nsIEventTarget* aEventTarget)
+{
+  if (aFlags & nsIAsyncInputStream::WAIT_CLOSURE_ONLY) {
+    mCallback = aCallback;
+    mCallbackEventTarget = aEventTarget;
+    return NS_OK;
+  }
+
+  MaybeExecCallback(aCallback, aEventTarget);
+  return NS_OK;
+}
+
+void
+AsyncStringStream::MaybeExecCallback(nsIInputStreamCallback* aCallback,
+                                     nsIEventTarget* aEventTarget)
+{
+  if (!aCallback) {
+    return;
+  }
+
+  nsCOMPtr<nsIInputStreamCallback> callback = aCallback;
+  nsCOMPtr<nsIAsyncInputStream> self = this;
+
+  nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
+    "AsyncWait", [callback, self]() { callback->OnInputStreamReady(self); });
+
+  if (aEventTarget) {
+    aEventTarget->Dispatch(r.forget());
+  } else {
+    r->Run();
+  }
+}
+
+NS_IMPL_ISUPPORTS(AsyncStringStream, nsIAsyncInputStream, nsIInputStream)
+
 } // namespace testing
--- a/xpcom/tests/gtest/Helpers.h
+++ b/xpcom/tests/gtest/Helpers.h
@@ -2,19 +2,21 @@
 /* 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 __Helpers_h
 #define __Helpers_h
 
+#include "nsCOMPtr.h"
 #include "nsIAsyncInputStream.h"
 #include "nsIAsyncOutputStream.h"
 #include "nsString.h"
+#include "nsStringStream.h"
 #include "nsTArrayForwardDeclare.h"
 #include <stdint.h>
 
 class nsIInputStream;
 class nsIOutputStream;
 
 namespace testing {
 
@@ -63,11 +65,33 @@ private:
   ~InputStreamCallback();
 
   bool mCalled;
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIINPUTSTREAMCALLBACK
 };
 
+class AsyncStringStream final : public nsIAsyncInputStream
+{
+  nsCOMPtr<nsIInputStream> mStream;
+
+public:
+  NS_DECL_THREADSAFE_ISUPPORTS
+  NS_DECL_NSIINPUTSTREAM
+  NS_DECL_NSIASYNCINPUTSTREAM
+
+  explicit AsyncStringStream(const nsACString& aBuffer);
+
+private:
+  ~AsyncStringStream() = default;
+
+  void
+  MaybeExecCallback(nsIInputStreamCallback* aCallback,
+                    nsIEventTarget* aEventTarget);
+
+  nsCOMPtr<nsIInputStreamCallback> mCallback;
+  nsCOMPtr<nsIEventTarget> mCallbackEventTarget;
+};
+
 } // namespace testing
 
 #endif // __Helpers_h
--- a/xpcom/tests/gtest/TestMultiplexInputStream.cpp
+++ b/xpcom/tests/gtest/TestMultiplexInputStream.cpp
@@ -1,21 +1,22 @@
 /* -*- 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 "gtest/gtest.h"
+#include "nsIAsyncInputStream.h"
 #include "nsComponentManagerUtils.h"
-#include "nsCOMPtr.h"
 #include "nsIInputStream.h"
 #include "nsIMultiplexInputStream.h"
 #include "nsISeekableStream.h"
-#include "nsStringStream.h"
+#include "nsThreadUtils.h"
+#include "Helpers.h"
 
 TEST(MultiplexInputStream, Seek_SET)
 {
   nsCString buf1;
   nsCString buf2;
   nsCString buf3;
   buf1.AssignLiteral("Hello world");
   buf2.AssignLiteral("The quick brown fox jumped over the lazy dog");
@@ -96,8 +97,119 @@ TEST(MultiplexInputStream, Seek_SET)
   rv = stream->Read(readBuf, 6, &count);
   ASSERT_TRUE(NS_SUCCEEDED(rv));
   ASSERT_EQ((uint64_t)6, count);
   rv = stream->Available(&length);
   ASSERT_TRUE(NS_SUCCEEDED(rv));
   ASSERT_EQ((uint64_t)buf2.Length() - 6 + buf3.Length(), length);
   ASSERT_EQ(0, strncmp(readBuf, "The qu", count));
 }
+
+already_AddRefed<nsIInputStream>
+CreateStreamHelper()
+{
+  nsCOMPtr<nsIMultiplexInputStream> multiplexStream =
+    do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1");
+
+  nsCString buf1;
+  buf1.AssignLiteral("Hello");
+
+  nsCOMPtr<nsIInputStream> inputStream1 = new testing::AsyncStringStream(buf1);
+  multiplexStream->AppendStream(inputStream1);
+
+  nsCString buf2;
+  buf2.AssignLiteral(" ");
+
+  nsCOMPtr<nsIInputStream> inputStream2 = new testing::AsyncStringStream(buf2);
+  multiplexStream->AppendStream(inputStream2);
+
+  nsCString buf3;
+  buf3.AssignLiteral("World!");
+
+  nsCOMPtr<nsIInputStream> inputStream3 = new testing::AsyncStringStream(buf3);
+  multiplexStream->AppendStream(inputStream3);
+
+  nsCOMPtr<nsIInputStream> stream(do_QueryInterface(multiplexStream));
+  return stream.forget();
+}
+
+// AsyncWait - without EventTarget
+TEST(TestMultiplexInputStream, AsyncWait_withoutEventTarget) {
+  nsCOMPtr<nsIInputStream> is = CreateStreamHelper();
+
+  nsCOMPtr<nsIAsyncInputStream> ais = do_QueryInterface(is);
+  ASSERT_TRUE(!!ais);
+
+  RefPtr<testing::InputStreamCallback> cb =
+    new testing::InputStreamCallback();
+
+  ASSERT_EQ(NS_OK, ais->AsyncWait(cb, 0, 0, nullptr));
+  ASSERT_FALSE(cb->Called());
+
+  // Eventually it is called.
+  MOZ_ALWAYS_TRUE(mozilla::SpinEventLoopUntil([&]() { return cb->Called(); }));
+  ASSERT_TRUE(cb->Called());
+}
+
+// AsyncWait - with EventTarget
+TEST(TestMultiplexInputStream, AsyncWait_withEventTarget) {
+  nsCOMPtr<nsIInputStream> is = CreateStreamHelper();
+
+  nsCOMPtr<nsIAsyncInputStream> ais = do_QueryInterface(is);
+  ASSERT_TRUE(!!ais);
+
+  RefPtr<testing::InputStreamCallback> cb =
+    new testing::InputStreamCallback();
+  nsCOMPtr<nsIThread> thread = do_GetCurrentThread();
+
+  ASSERT_EQ(NS_OK, ais->AsyncWait(cb, 0, 0, thread));
+
+  ASSERT_FALSE(cb->Called());
+
+  // Eventually it is called.
+  MOZ_ALWAYS_TRUE(mozilla::SpinEventLoopUntil([&]() { return cb->Called(); }));
+  ASSERT_TRUE(cb->Called());
+}
+
+// AsyncWait - without EventTarget - closureOnly
+TEST(TestMultiplexInputStream, AsyncWait_withoutEventTarget_closureOnly) {
+  nsCOMPtr<nsIInputStream> is = CreateStreamHelper();
+
+  nsCOMPtr<nsIAsyncInputStream> ais = do_QueryInterface(is);
+  ASSERT_TRUE(!!ais);
+
+  RefPtr<testing::InputStreamCallback> cb =
+    new testing::InputStreamCallback();
+
+  ASSERT_EQ(NS_OK, ais->AsyncWait(cb, nsIAsyncInputStream::WAIT_CLOSURE_ONLY,
+                                  0, nullptr));
+  ASSERT_FALSE(cb->Called());
+
+  ais->CloseWithStatus(NS_ERROR_FAILURE);
+  ASSERT_FALSE(cb->Called());
+
+  // Eventually it is called.
+  MOZ_ALWAYS_TRUE(mozilla::SpinEventLoopUntil([&]() { return cb->Called(); }));
+  ASSERT_TRUE(cb->Called());
+}
+
+// AsyncWait - withEventTarget - closureOnly
+TEST(TestMultiplexInputStream, AsyncWait_withEventTarget_closureOnly) {
+  nsCOMPtr<nsIInputStream> is = CreateStreamHelper();
+
+  nsCOMPtr<nsIAsyncInputStream> ais = do_QueryInterface(is);
+  ASSERT_TRUE(!!ais);
+
+  RefPtr<testing::InputStreamCallback> cb =
+    new testing::InputStreamCallback();
+  nsCOMPtr<nsIThread> thread = do_GetCurrentThread();
+
+  ASSERT_EQ(NS_OK, ais->AsyncWait(cb, nsIAsyncInputStream::WAIT_CLOSURE_ONLY,
+                                  0, thread));
+
+  ASSERT_FALSE(cb->Called());
+  ais->CloseWithStatus(NS_ERROR_FAILURE);
+  ASSERT_FALSE(cb->Called());
+
+  // Eventually it is called.
+  MOZ_ALWAYS_TRUE(mozilla::SpinEventLoopUntil([&]() { return cb->Called(); }));
+  ASSERT_TRUE(cb->Called());
+}