Merge inbound to mozilla-central r=merge a=merge
authorCoroiu Cristina <ccoroiu@mozilla.com>
Tue, 07 Nov 2017 01:04:34 +0200
changeset 693830 c2fe4b3b1b930b3e7fdb84eae44cec165394f322
parent 693809 62aeebcc676e93dc56a97d44753f4e2f963d43c3 (current diff)
parent 693679 338f65684d59e760b9050470a0b5bf32cf4cd133 (diff)
child 693831 b09c5668a71461a9c2bb85d1e74024b36affbcac
child 693836 5eabb60aa49fafb304bb64bb0ae3b7657effcbd5
child 693851 ab8fb74b86d40aff7a8f8fcd35d3c49e382da583
child 693859 232e3a1a718fd710bf87d39e44cfb955cca384b9
child 693860 13bd7c5440476f94769a27b996d0770d7fd1fd16
child 693861 c6ccad60be8d06e6074ed6b9192f503979a4ca09
child 693889 de233d7d0afd81fb14c526e1355b2550ca83104f
child 693891 3c6ae79e6f08afe1a6e4dd758026e09dd815114d
child 693900 8024481dd125811eaa134670d15508e2bffc75ab
child 693905 4bac73b54120de96c35fa8bd50cfdbb6bb6bd9b6
child 693909 031eb7b0a628b18505b6357ce50ba225e4a9817a
child 693911 0875947fd31720801e233fe45ea69f1ce29bbf4e
child 693916 115e81107ed8981f28858318127deec1bf426f29
child 693918 d15a34e7944bfc31b451c21072a1ef933d2bc369
child 693919 12af8de3d244285b1442b6bd2c4337555dbd0104
child 693924 b370bb0592a060ddfebb606fdf4e02137b6eda7d
child 693931 fae582b35127395e9ddaa13933c592d1e3eb03d4
child 693933 2d3692c0e411efec60618eabd4183e7f7d4fc26e
child 693938 45721ee2bc04ee9c2aa301bac39e372b748c69e1
child 693943 bc7e9c5b19a93216bdc5a79a9a5bbc4ddff36387
child 693946 14fe87b077a88bd6aee3445ff6c5dc70a5e92a32
child 693958 3cfd93501b280a7db9c32c7b7a070387967c768d
child 693972 50d0a62a160e44f2df357f7b8eccbc61e786217e
child 693984 bd4a4b47ccce4105e2407c458347f4fdc7679ef9
child 693985 587f3415a173813ca8b8abc845f444bbc456b5e6
child 693992 ff9fcf9a72dab9c753e65f076fa3f56924b058ad
child 693997 9fb1e9ff0f440fab03b4207f5a30cbf8474152e8
child 693998 08bfcf9ef859de14b329a396d686bc00bc11fd4d
child 694008 eb570c6bfa9430c73f1bc88e44909a763ba1fc5f
child 694012 1d41b448013536d95ab97844788175a5fe26b7a9
child 694014 d10e202e7b7cb0822033749c21baf68081ed8de1
child 694016 feff782472693356eb8a5265487eaab96a304184
child 694028 6aadd5bb4fc344abd48380aa059eb7e4b8e3764c
child 694043 bee168acd12893a551d9a1e2b9204c667ce629a1
child 694113 ad97dcb9c6992685c969ec569c594c0950521e3c
child 694132 828b38f71a3be21669b1b372ab6a4f38d1c7afea
child 694249 987ec97287f2851ca86c21a05cf5933b4a863de1
child 694299 da7079dd73d672cea2233d30e34e2d6e7e36d49b
child 694314 f34285b106e0cc6639d2a0eedbbe8f5c2643b64b
child 694318 d51b6b505a1be8473cd545014046e2e8bcec6a58
child 694423 914e632bd6a6aa396cc246995c2ba50e15f49490
child 694667 4014b0690dadc47fe898a3f9e37e0e52ae211c7a
child 694678 822932b30642c8af6f66380c46ac8583ef950526
child 694708 8313b23e3cca685cec90a16c558e7b97c417cd04
child 694748 6bea3e1f91f8d19e6f6cc11751545725bae77c4e
child 694752 8eb05064127e07fb465912b0ae395055ac69c02c
child 695025 efab3c1d416c9e9451c99886d95407146841b4a2
child 695027 ef9d7c2eda6023020c4f55754407df836883597f
child 695028 c5f22bd7fcbef714a452456a33d850a20f24980f
child 695102 97471a1fea7002460eb93f3fad5a4b05820fb439
child 695349 6aa0475b88110fac7ebfe40127087af47fe65378
child 695478 873e952bdbc68f490e4ffe49b86d9744185a13d1
child 695486 ad08940e9ac71e59ad8e96ee967a24ea2cd5e439
child 695805 c76d25cc7a1fe91f91c304af9bfd5a6329452f3c
child 696271 b191959b0c0ebda8e51cf12ff5bc8db428ed9285
child 696858 9c2ae5748a67066d73cc383d95c3e85fdd95268f
child 697069 f7e2748817acd56e21c9e5f5f34f10737ee9325c
child 697075 59fdd180af4da2f1e6c0e00029fae4a02714dc50
child 697467 c8a6506926e8192822615b49e9ef2c30329db373
child 697481 1bf7ce9d20e8570f946ab7bddbbe24fc18e05f3b
child 697510 0976270acfc48d30918d5c993fbb83485c21e9cb
child 697750 58274a0b63dc60cd33e51874182bdbfe7e35e807
child 697955 240c410946f423ad0cfe5073f5ba06f2382baa22
child 699250 55976f7d863b14f7eb09f1263301773556e2a5c3
push id87934
push userdrno@ohlmeier.org
push dateMon, 06 Nov 2017 23:12:53 +0000
reviewersmerge, merge
milestone58.0a1
Merge inbound to mozilla-central r=merge a=merge
docshell/base/nsContextMenuInfo.cpp
docshell/base/nsContextMenuInfo.h
docshell/base/nsIContextMenuListener.idl
docshell/base/nsIContextMenuListener2.idl
--- 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
@@ -2924,25 +2924,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());
+}