Merge mozilla-central to fx-team
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Tue, 23 Feb 2016 16:56:52 +0100
changeset 321473 aea9ba90eaa8a729680f53c9c296134d2aead3a1
parent 321472 12c77a8f0e4ac5286ab5bc6f738fe8a1283640b6 (current diff)
parent 321466 a9e33d8c48b5ca93ca1937eba4220f681a0f05ec (diff)
child 321474 4720eaf2a4562e84fdc0d5576a7b5228ad044a5a
push id5913
push userjlund@mozilla.com
push dateMon, 25 Apr 2016 16:57:49 +0000
treeherdermozilla-beta@dcaf0a6fa115 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone47.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to fx-team
js/src/jit-test/tests/debug/Memory-tenurePromotionsLog-01.js
js/src/jit-test/tests/debug/Memory-tenurePromotionsLog-02.js
js/src/jit-test/tests/debug/Memory-tenurePromotionsLog-03.js
js/src/jit-test/tests/debug/Memory-tenurePromotionsLog-04.js
js/src/jit-test/tests/debug/Memory-tenurePromotionsLog-05.js
js/src/jit-test/tests/debug/Memory-tenurePromotionsLog-06.js
js/src/jit-test/tests/debug/Memory-tenurePromotionsLog-07.js
netwerk/base/PollableEvent.cpp
netwerk/base/PollableEvent.h
--- a/accessible/base/TreeWalker.cpp
+++ b/accessible/base/TreeWalker.cpp
@@ -22,18 +22,18 @@ using namespace mozilla::a11y;
 
 TreeWalker::
   TreeWalker(Accessible* aContext, nsIContent* aContent, uint32_t aFlags) :
   mDoc(aContext->Document()), mContext(aContext), mAnchorNode(aContent),
   mFlags(aFlags)
 {
   NS_ASSERTION(aContent, "No node for the accessible tree walker!");
 
-  mChildFilter = mContext->CanHaveAnonChildren() ?
-    nsIContent::eAllChildren : nsIContent::eAllButXBL;
+  mChildFilter = mContext->NoXBLKids() ?
+    nsIContent::eAllButXBL : nsIContent::eAllChildren;
   mChildFilter |= nsIContent::eSkipPlaceholderContent;
 
   if (aContent)
     PushState(aContent);
 
   MOZ_COUNT_CTOR(TreeWalker);
 }
 
--- a/accessible/generic/Accessible.cpp
+++ b/accessible/generic/Accessible.cpp
@@ -297,22 +297,16 @@ Accessible::AccessKey() const
 }
 
 KeyBinding
 Accessible::KeyboardShortcut() const
 {
   return KeyBinding();
 }
 
-bool
-Accessible::CanHaveAnonChildren()
-{
-  return true;
-}
-
 void
 Accessible::TranslateString(const nsString& aKey, nsAString& aStringOut)
 {
   nsCOMPtr<nsIStringBundleService> stringBundleService =
     services::GetStringBundleService();
   if (!stringBundleService)
     return;
 
--- a/accessible/generic/Accessible.h
+++ b/accessible/generic/Accessible.h
@@ -490,21 +490,16 @@ public:
 
   /**
    * Handle accessible event, i.e. process it, notifies observers and fires
    * platform specific event.
    */
   virtual nsresult HandleAccEvent(AccEvent* aAccEvent);
 
   /**
-   * Return true if this accessible allows accessible children from anonymous subtree.
-   */
-  virtual bool CanHaveAnonChildren();
-
-  /**
    * Return true if the accessible is an acceptable child.
    */
   virtual bool IsAcceptableChild(Accessible* aPossibleChild) const { return true; }
 
   /**
    * Returns text of accessible if accessible has text role otherwise empty
    * string.
    *
@@ -926,16 +921,22 @@ public:
   {
     if (aRelocated)
       mStateFlags |= eRelocated;
     else
       mStateFlags &= ~eRelocated;
   }
 
   /**
+   * Return true if the accessible doesn't allow accessible children from XBL
+   * anonymous subtree.
+   */
+  bool NoXBLKids() { return mStateFlags & eNoXBLKids; }
+
+  /**
    * Return true if this accessible has a parent whose name depends on this
    * accessible.
    */
   bool HasNameDependentParent() const
     { return mContextFlags & eHasNameDependentParent; }
 
   /**
    * Return true if aria-hidden="true" is applied to the accessible or inherited
@@ -1016,18 +1017,19 @@ protected:
     eSharedNode = 1 << 2, // accessible shares DOM node from another accessible
     eNotNodeMapEntry = 1 << 3, // accessible shouldn't be in document node map
     eHasNumericValue = 1 << 4, // accessible has a numeric value
     eGroupInfoDirty = 1 << 5, // accessible needs to update group info
     eSubtreeMutating = 1 << 6, // subtree is being mutated
     eIgnoreDOMUIEvent = 1 << 7, // don't process DOM UI events for a11y events
     eSurvivingInUpdate = 1 << 8, // parent drops children to recollect them
     eRelocated = 1 << 9, // accessible was moved in tree
+    eNoXBLKids = 1 << 10, // accessible don't allows XBL children
 
-    eLastStateFlag = eRelocated
+    eLastStateFlag = eNoXBLKids
   };
 
   /**
    * Flags used for contextual information about the accessible.
    */
   enum ContextFlags {
     eHasNameDependentParent = 1 << 0, // Parent's name depends on this accessible.
     eARIAHidden = 1 << 1,
@@ -1132,17 +1134,17 @@ protected:
   nsCOMPtr<nsIContent> mContent;
   DocAccessible* mDoc;
 
   RefPtr<Accessible> mParent;
   nsTArray<RefPtr<Accessible> > mChildren;
   int32_t mIndexInParent;
 
   static const uint8_t kChildrenFlagsBits = 2;
-  static const uint8_t kStateFlagsBits = 10;
+  static const uint8_t kStateFlagsBits = 11;
   static const uint8_t kContextFlagsBits = 2;
   static const uint8_t kTypeBits = 6;
   static const uint8_t kGenericTypesBits = 14;
 
   /**
    * Keep in sync with ChildrenFlags, StateFlags, ContextFlags, and AccTypes.
    */
   uint32_t mChildrenFlags : kChildrenFlagsBits;
--- a/accessible/xul/XULComboboxAccessible.cpp
+++ b/accessible/xul/XULComboboxAccessible.cpp
@@ -26,16 +26,25 @@ XULComboboxAccessible::
   XULComboboxAccessible(nsIContent* aContent, DocAccessible* aDoc) :
   AccessibleWrap(aContent, aDoc)
 {
   if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
                             nsGkAtoms::autocomplete, eIgnoreCase))
     mGenericTypes |= eAutoComplete;
   else
     mGenericTypes |= eCombobox;
+
+  // Both the XUL <textbox type="autocomplete"> and <menulist editable="true">
+  // widgets use XULComboboxAccessible. We need to walk the anonymous children
+  // for these so that the entry field is a child. Otherwise no XBL children.
+  if (!mContent->NodeInfo()->Equals(nsGkAtoms::textbox, kNameSpaceID_XUL) &&
+      !mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::editable,
+                             nsGkAtoms::_true, eIgnoreCase)) {
+    mStateFlags |= eNoXBLKids;
+  }
 }
 
 role
 XULComboboxAccessible::NativeRole()
 {
   return IsAutoComplete() ? roles::AUTOCOMPLETE : roles::COMBOBOX;
 }
 
@@ -91,33 +100,16 @@ XULComboboxAccessible::Value(nsString& a
   aValue.Truncate();
 
   // The value is the option or text shown entered in the combobox.
   nsCOMPtr<nsIDOMXULMenuListElement> menuList(do_QueryInterface(mContent));
   if (menuList)
     menuList->GetLabel(aValue);
 }
 
-bool
-XULComboboxAccessible::CanHaveAnonChildren()
-{
-  if (mContent->NodeInfo()->Equals(nsGkAtoms::textbox, kNameSpaceID_XUL) ||
-      mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::editable,
-                            nsGkAtoms::_true, eIgnoreCase)) {
-    // Both the XUL <textbox type="autocomplete"> and <menulist editable="true"> widgets
-    // use XULComboboxAccessible. We need to walk the anonymous children for these
-    // so that the entry field is a child
-    return true;
-  }
-
-  // Argument of false indicates we don't walk anonymous children for
-  // menuitems
-  return false;
-}
-
 uint8_t
 XULComboboxAccessible::ActionCount()
 {
   // Just one action (click).
   return 1;
 }
 
 bool
--- a/accessible/xul/XULComboboxAccessible.h
+++ b/accessible/xul/XULComboboxAccessible.h
@@ -21,17 +21,16 @@ public:
 
   XULComboboxAccessible(nsIContent* aContent, DocAccessible* aDoc);
 
   // Accessible
   virtual void Description(nsString& aDescription) override;
   virtual void Value(nsString& aValue) override;
   virtual a11y::role NativeRole() override;
   virtual uint64_t NativeState() override;
-  virtual bool CanHaveAnonChildren() override;
 
   // ActionAccessible
   virtual uint8_t ActionCount() override;
   virtual void ActionNameAt(uint8_t aIndex, nsAString& aName) override;
   virtual bool DoAction(uint8_t aIndex) override;
 
   // Widgets
   virtual bool IsActiveWidget() const override;
--- a/accessible/xul/XULListboxAccessible.cpp
+++ b/accessible/xul/XULListboxAccessible.cpp
@@ -539,16 +539,20 @@ XULListitemAccessible::
   XULListitemAccessible(nsIContent* aContent, DocAccessible* aDoc) :
   XULMenuitemAccessible(aContent, aDoc)
 {
   mIsCheckbox = mContent->AttrValueIs(kNameSpaceID_None,
                                       nsGkAtoms::type,
                                       nsGkAtoms::checkbox,
                                       eCaseMatters);
   mType = eXULListItemType;
+
+  // Walk XBL anonymous children for list items. Overrides the flag value from
+  // base XULMenuitemAccessible class.
+  mStateFlags &= ~eNoXBLKids;
 }
 
 XULListitemAccessible::~XULListitemAccessible()
 {
 }
 
 NS_IMPL_ISUPPORTS_INHERITED0(XULListitemAccessible, Accessible)
 
@@ -663,23 +667,16 @@ XULListitemAccessible::ActionNameAt(uint
     uint64_t states = NativeState();
     if (states & states::CHECKED)
       aName.AssignLiteral("uncheck");
     else
       aName.AssignLiteral("check");
   }
 }
 
-bool
-XULListitemAccessible::CanHaveAnonChildren()
-{
-  // That indicates we should walk anonymous children for listitems
-  return true;
-}
-
 ////////////////////////////////////////////////////////////////////////////////
 // XULListitemAccessible: Widgets
 
 Accessible*
 XULListitemAccessible::ContainerWidget() const
 {
   return Parent();
 }
--- a/accessible/xul/XULListboxAccessible.h
+++ b/accessible/xul/XULListboxAccessible.h
@@ -110,17 +110,16 @@ public:
 
   XULListitemAccessible(nsIContent* aContent, DocAccessible* aDoc);
 
   // Accessible
   virtual void Description(nsString& aDesc) override;
   virtual a11y::role NativeRole() override;
   virtual uint64_t NativeState() override;
   virtual uint64_t NativeInteractiveState() const override;
-  virtual bool CanHaveAnonChildren() override;
 
   // Actions
   virtual void ActionNameAt(uint8_t index, nsAString& aName) override;
 
   // Widgets
   virtual Accessible* ContainerWidget() const override;
 
 protected:
--- a/accessible/xul/XULMenuAccessible.cpp
+++ b/accessible/xul/XULMenuAccessible.cpp
@@ -36,16 +36,17 @@ using namespace mozilla::a11y;
 ////////////////////////////////////////////////////////////////////////////////
 // XULMenuitemAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 XULMenuitemAccessible::
   XULMenuitemAccessible(nsIContent* aContent, DocAccessible* aDoc) :
   AccessibleWrap(aContent, aDoc)
 {
+  mStateFlags |= eNoXBLKids;
 }
 
 uint64_t
 XULMenuitemAccessible::NativeState()
 {
   uint64_t state = Accessible::NativeState();
 
   // Has Popup?
@@ -263,23 +264,16 @@ XULMenuitemAccessible::NativeRole()
 
 int32_t
 XULMenuitemAccessible::GetLevelInternal()
 {
   return nsAccUtils::GetLevelForXULContainerItem(mContent);
 }
 
 bool
-XULMenuitemAccessible::CanHaveAnonChildren()
-{
-  // That indicates we don't walk anonymous children for menuitems
-  return false;
-}
-
-bool
 XULMenuitemAccessible::DoAction(uint8_t index)
 {
   if (index == eAction_Click) {   // default action
     DoCommand();
     return true;
   }
 
   return false;
--- a/accessible/xul/XULMenuAccessible.h
+++ b/accessible/xul/XULMenuAccessible.h
@@ -25,18 +25,16 @@ public:
 
   // Accessible
   virtual void Description(nsString& aDescription) override;
   virtual a11y::role NativeRole() override;
   virtual uint64_t NativeState() override;
   virtual uint64_t NativeInteractiveState() const override;
   virtual int32_t GetLevelInternal() override;
 
-  virtual bool CanHaveAnonChildren() override;
-
   // ActionAccessible
   virtual uint8_t ActionCount() override;
   virtual void ActionNameAt(uint8_t aIndex, nsAString& aName) override;
   virtual bool DoAction(uint8_t aIndex) override;
   virtual KeyBinding AccessKey() const override;
   virtual KeyBinding KeyboardShortcut() const override;
 
   // Widgets
--- a/accessible/xul/XULSliderAccessible.cpp
+++ b/accessible/xul/XULSliderAccessible.cpp
@@ -18,17 +18,17 @@ using namespace mozilla::a11y;
 ////////////////////////////////////////////////////////////////////////////////
 // XULSliderAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 XULSliderAccessible::
   XULSliderAccessible(nsIContent* aContent, DocAccessible* aDoc) :
   AccessibleWrap(aContent, aDoc)
 {
-  mStateFlags |= eHasNumericValue;
+  mStateFlags |= eHasNumericValue | eNoXBLKids;
 }
 
 // Accessible
 
 role
 XULSliderAccessible::NativeRole()
 {
   return roles::SLIDER;
@@ -122,23 +122,16 @@ bool
 XULSliderAccessible::SetCurValue(double aValue)
 {
   if (AccessibleWrap::SetCurValue(aValue))
     return true;
 
   return SetSliderAttr(nsGkAtoms::curpos, aValue);
 }
 
-bool
-XULSliderAccessible::CanHaveAnonChildren()
-{
-  // Do not allow anonymous xul:slider be accessible.
-  return false;
-}
-
 // Utils
 
 nsIContent*
 XULSliderAccessible::GetSliderElement() const
 {
   if (!mSliderNode) {
     // XXX: we depend on anonymous content.
     mSliderNode = mContent->OwnerDoc()->
--- a/accessible/xul/XULSliderAccessible.h
+++ b/accessible/xul/XULSliderAccessible.h
@@ -21,17 +21,16 @@ class XULSliderAccessible : public Acces
 public:
   XULSliderAccessible(nsIContent* aContent, DocAccessible* aDoc);
 
   // Accessible
   virtual void Value(nsString& aValue) override;
   virtual a11y::role NativeRole() override;
   virtual uint64_t NativeInteractiveState() const override;
   virtual bool NativelyUnavailable() const override;
-  virtual bool CanHaveAnonChildren() override;
 
   // Value
   virtual double MaxValue() const override;
   virtual double MinValue() const override;
   virtual double CurValue() const override;
   virtual double Step() const override;
   virtual bool SetCurValue(double aValue) override;
 
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -988,21 +988,26 @@ nsContextMenu.prototype = {
       try {
         let targetURI = this.linkURI;
         sm.checkSameOriginURI(referrerURI, targetURI, false);
         persistAllowMixedContentInChildTab = true;
       }
       catch (e) { }
     }
 
-    let params = this._openLinkInParameters({
+    let params = {
       allowMixedContent: persistAllowMixedContentInChildTab,
-      userContextId: parseInt(event.target.getAttribute('usercontextid')),
-    });
-    openLinkIn(this.linkURL, "tab", params);
+      userContextId: parseInt(event.target.getAttribute('usercontextid'))
+    };
+
+    if (params.userContextId != this.principal.originAttributes.userContextId) {
+      params.noReferrer = true;
+    }
+
+    openLinkIn(this.linkURL, "tab", this._openLinkInParameters(params));
   },
 
   // open URL in current tab
   openLinkInCurrent: function() {
     urlSecurityCheck(this.linkURL, this.principal);
     openLinkIn(this.linkURL, "current", this._openLinkInParameters());
   },
 
--- a/browser/base/content/test/referrer/browser.ini
+++ b/browser/base/content/test/referrer/browser.ini
@@ -8,8 +8,10 @@ support-files =
 [browser_referrer_middle_click.js]
 [browser_referrer_open_link_in_private.js]
 skip-if = os == 'linux' # Bug 1145199
 [browser_referrer_open_link_in_tab.js]
 skip-if = os == 'linux' # Bug 1144816
 [browser_referrer_open_link_in_window.js]
 skip-if = os == 'linux' # Bug 1145199
 [browser_referrer_simple_click.js]
+[browser_referrer_open_link_in_container_tab.js]
+skip-if = os == 'linux' # Bug 1144816
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/referrer/browser_referrer_open_link_in_container_tab.js
@@ -0,0 +1,50 @@
+// Tests referrer on context menu navigation - open link in new container tab.
+// Selects "open link in new container tab" from the context menu.
+
+function getReferrerTest(aTestNumber) {
+  let test = _referrerTests[aTestNumber];
+  if (test) {
+    // We want all the referrer tests to fail!
+    test.result = "";
+  }
+
+  return test;
+}
+
+function startNewTabTestCase(aTestNumber) {
+  info("browser_referrer_open_link_in_container_tab: " +
+       getReferrerTestDescription(aTestNumber));
+  contextMenuOpened(gTestWindow, "testlink").then(function(aContextMenu) {
+    someTabLoaded(gTestWindow).then(function(aNewTab) {
+      gTestWindow.gBrowser.selectedTab = aNewTab;
+
+      checkReferrerAndStartNextTest(aTestNumber, null, aNewTab,
+                                    startNewTabTestCase);
+    });
+
+    let menu = gTestWindow.document.getElementById("context-openlinkinusercontext-menu");
+    ok(menu && menu.firstChild, "The menu exists and it has a first child node.");
+
+    let menupopup = menu.firstChild;
+    is(menupopup.nodeType, Node.ELEMENT_NODE, "We have a menupopup.");
+    ok(menupopup.firstChild, "We have a first container entry.");
+
+    let firstContext = menupopup.firstChild;
+    is(firstContext.nodeType, Node.ELEMENT_NODE, "We have a first container entry.");
+    ok(firstContext.hasAttribute('usercontextid'), "We have a usercontextid value.");
+
+    firstContext.doCommand();
+    aContextMenu.hidePopup();
+  });
+}
+
+function test() {
+  waitForExplicitFinish();
+
+  SpecialPowers.pushPrefEnv(
+    {set: [["privacy.userContext.enabled", true]]},
+    function() {
+      requestLongerTimeout(10);  // slowwww shutdown on e10s
+      startReferrerTest(startNewTabTestCase);
+    });
+}
--- a/configure.in
+++ b/configure.in
@@ -48,17 +48,17 @@ dnl ====================================
 _SUBDIR_HOST_LDFLAGS="$HOST_LDFLAGS"
 _SUBDIR_CONFIG_ARGS="$ac_configure_args"
 
 dnl Set the version number of the libs included with mozilla
 dnl ========================================================
 MOZJPEG=62
 MOZPNG=10619
 NSPR_VERSION=4
-NSPR_MINVER=4.11
+NSPR_MINVER=4.12
 NSS_VERSION=3
 
 dnl Set the minimum version of toolkit libs used by mozilla
 dnl ========================================================
 GLIB_VERSION=2.22
 # 2_26 is the earliest version we can set GLIB_VERSION_MIN_REQUIRED.
 # The macro won't be used when compiling with earlier versions anyway.
 GLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_26
--- a/devtools/server/nsJSInspector.cpp
+++ b/devtools/server/nsJSInspector.cpp
@@ -42,19 +42,19 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsJSInspector)
   tmp->mRequestors.Clear();
   tmp->mLastRequestor = JS::NullValue();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsJSInspector)
   for (uint32_t i = 0; i < tmp->mRequestors.Length(); ++i) {
-    NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mRequestors[i])
+    NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mRequestors[i])
   }
-  NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mLastRequestor)
+  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mLastRequestor)
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 nsJSInspector::nsJSInspector() : mNestedLoopLevel(0), mRequestors(1), mLastRequestor(JS::NullValue())
 {
 }
 
 nsJSInspector::~nsJSInspector()
 {
--- a/dom/base/Console.cpp
+++ b/dom/base/Console.cpp
@@ -153,17 +153,17 @@ public:
 
   void
   Trace(const TraceCallbacks& aCallbacks, void* aClosure)
   {
     AssertIsOnOwningThread();
 
     ConsoleCallData* tmp = this;
     for (uint32_t i = 0; i < mCopiedArguments.Length(); ++i) {
-      NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mCopiedArguments[i]);
+      NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mCopiedArguments[i])
     }
   }
 
   void
   AssertIsOnOwningThread() const
   {
     MOZ_ASSERT(mOwningThread);
     MOZ_ASSERT(PR_GetCurrentThread() == mOwningThread);
--- a/dom/base/DOMException.cpp
+++ b/dom/base/DOMException.cpp
@@ -177,17 +177,17 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(Exception
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Exception)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocation)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Exception)
   NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
-  NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mThrownJSVal);
+  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mThrownJSVal)
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Exception)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocation)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
   tmp->mThrownJSVal.setNull();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
--- a/dom/base/DOMRequest.cpp
+++ b/dom/base/DOMRequest.cpp
@@ -57,17 +57,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_IN
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mPromise)
   tmp->mResult.setUndefined();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(DOMRequest,
                                                DOMEventTargetHelper)
   // Don't need NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER because
   // DOMEventTargetHelper does it for us.
-  NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mResult)
+  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mResult)
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(DOMRequest)
   NS_INTERFACE_MAP_ENTRY(nsIDOMDOMRequest)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
 NS_IMPL_ADDREF_INHERITED(DOMRequest, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(DOMRequest, DOMEventTargetHelper)
--- a/dom/base/StructuredCloneHolder.cpp
+++ b/dom/base/StructuredCloneHolder.cpp
@@ -1036,26 +1036,22 @@ StructuredCloneHolder::CustomReadTransfe
                                                  uint32_t aTag,
                                                  void* aContent,
                                                  uint64_t aExtraData,
                                                  JS::MutableHandleObject aReturnObject)
 {
   MOZ_ASSERT(mSupportsTransferring);
 
   if (aTag == SCTAG_DOM_MAP_MESSAGEPORT) {
-    // This can be null.
-    nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(mParent);
-
     MOZ_ASSERT(aExtraData < mPortIdentifiers.Length());
     const MessagePortIdentifier& portIdentifier = mPortIdentifiers[aExtraData];
 
-    // aExtraData is the index of this port identifier.
     ErrorResult rv;
     RefPtr<MessagePort> port =
-      MessagePort::Create(window, portIdentifier, rv);
+      MessagePort::Create(mParent, portIdentifier, rv);
     if (NS_WARN_IF(rv.Failed())) {
       return false;
     }
 
     mTransferredPorts.AppendElement(port);
 
     JS::Rooted<JS::Value> value(aCx);
     if (!GetOrCreateDOMReflector(aCx, port, &value)) {
--- a/dom/base/StructuredCloneHolder.h
+++ b/dom/base/StructuredCloneHolder.h
@@ -205,17 +205,17 @@ public:
   // MessagePorts are not thread-safe and they must be retrieved in the thread
   // where they are created.
   nsTArray<RefPtr<MessagePort>>&& TakeTransferredPorts()
   {
     MOZ_ASSERT(mSupportsTransferring);
     return Move(mTransferredPorts);
   }
 
-  nsTArray<MessagePortIdentifier>& PortIdentifiers()
+  nsTArray<MessagePortIdentifier>& PortIdentifiers() const
   {
     MOZ_ASSERT(mSupportsTransferring);
     return mPortIdentifiers;
   }
 
   nsTArray<RefPtr<gfx::DataSourceSurface>>& GetSurfaces()
   {
     return mClonedSurfaces;
@@ -305,17 +305,17 @@ protected:
 
   // This array contains the ports once we've finished the reading. It's
   // generated from the mPortIdentifiers array.
   nsTArray<RefPtr<MessagePort>> mTransferredPorts;
 
   // This array contains the identifiers of the MessagePorts. Based on these we
   // are able to reconnect the new transferred ports with the other
   // MessageChannel ports.
-  nsTArray<MessagePortIdentifier> mPortIdentifiers;
+  mutable nsTArray<MessagePortIdentifier> mPortIdentifiers;
 
 #ifdef DEBUG
   nsCOMPtr<nsIThread> mCreationThread;
 #endif
 };
 
 } // dom namespace
 } // mozilla namespace
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -1888,17 +1888,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
     }
   }
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsDocument)
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsDocument)
   if (tmp->PreservingWrapper()) {
-    NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mExpandoAndGeneration.expando);
+    NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mExpandoAndGeneration.expando)
   }
   NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDocument)
   tmp->mInUnlinkOrDeletion = true;
 
--- a/dom/base/nsFrameMessageManager.cpp
+++ b/dom/base/nsFrameMessageManager.cpp
@@ -28,16 +28,18 @@
 #include "nsIScriptSecurityManager.h"
 #include "nsIDOMClassInfo.h"
 #include "xpcpublic.h"
 #include "mozilla/CycleCollectedJSRuntime.h"
 #include "mozilla/IntentionalCrash.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/dom/File.h"
+#include "mozilla/dom/MessagePort.h"
+#include "mozilla/dom/MessagePortList.h"
 #include "mozilla/dom/nsIContentParent.h"
 #include "mozilla/dom/PermissionMessageUtils.h"
 #include "mozilla/dom/ProcessGlobal.h"
 #include "mozilla/dom/SameProcessMessageQueue.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/ipc/BlobChild.h"
 #include "mozilla/dom/ipc/BlobParent.h"
 #include "mozilla/dom/ipc/StructuredCloneData.h"
@@ -133,17 +135,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(
     }
   }
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildManagers)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParentManager)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsFrameMessageManager)
-  NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mInitialProcessData)
+  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mInitialProcessData)
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsFrameMessageManager)
   tmp->mListeners.Clear();
   for (int32_t i = tmp->mChildManagers.Count(); i > 0; --i) {
     static_cast<nsFrameMessageManager*>(tmp->mChildManagers[i - 1])->
       Disconnect(false);
   }
@@ -269,16 +271,18 @@ template<ActorFlavorEnum Flavor>
 static bool
 BuildClonedMessageData(typename BlobTraits<Flavor>::ConcreteContentManagerType* aManager,
                        StructuredCloneData& aData,
                        ClonedMessageData& aClonedData)
 {
   SerializedStructuredCloneBuffer& buffer = aClonedData.data();
   buffer.data = aData.Data();
   buffer.dataLength = aData.DataLength();
+  aClonedData.identfiers().AppendElements(aData.PortIdentifiers());
+
   const nsTArray<RefPtr<BlobImpl>>& blobImpls = aData.BlobImpls();
 
   if (!blobImpls.IsEmpty()) {
     typedef typename BlobTraits<Flavor>::ProtocolType ProtocolType;
     InfallibleTArray<ProtocolType*>& blobList = DataBlobs<Flavor>::Blobs(aClonedData);
     uint32_t length = blobImpls.Length();
     blobList.SetCapacity(length);
     for (uint32_t i = 0; i < length; ++i) {
@@ -312,19 +316,22 @@ MessageManagerCallback::BuildClonedMessa
 template<ActorFlavorEnum Flavor>
 static void
 UnpackClonedMessageData(const ClonedMessageData& aClonedData,
                         StructuredCloneData& aData)
 {
   const SerializedStructuredCloneBuffer& buffer = aClonedData.data();
   typedef typename BlobTraits<Flavor>::ProtocolType ProtocolType;
   const InfallibleTArray<ProtocolType*>& blobs = DataBlobs<Flavor>::Blobs(aClonedData);
+  const InfallibleTArray<MessagePortIdentifier>& identifiers = aClonedData.identfiers();
 
   aData.UseExternalData(buffer.data, buffer.dataLength);
 
+  aData.PortIdentifiers().AppendElements(identifiers);
+
   if (!blobs.IsEmpty()) {
     uint32_t length = blobs.Length();
     aData.BlobImpls().SetCapacity(length);
     for (uint32_t i = 0; i < length; ++i) {
       auto* blob =
         static_cast<typename BlobTraits<Flavor>::BlobType*>(blobs[i]);
       MOZ_ASSERT(blob);
 
@@ -611,22 +618,24 @@ JSONCreator(const char16_t* aBuf, uint32
   result->Append(static_cast<const char16_t*>(aBuf),
                  static_cast<uint32_t>(aLen));
   return true;
 }
 
 static bool
 GetParamsForMessage(JSContext* aCx,
                     const JS::Value& aValue,
+                    const JS::Value& aTransfer,
                     StructuredCloneData& aData)
 {
   // First try to use structured clone on the whole thing.
   JS::RootedValue v(aCx, aValue);
+  JS::RootedValue t(aCx, aTransfer);
   ErrorResult rv;
-  aData.Write(aCx, v, rv);
+  aData.Write(aCx, v, t, rv);
   if (!rv.Failed()) {
     return true;
   }
 
   rv.SuppressException();
   JS_ClearPendingException(aCx);
 
   nsCOMPtr<nsIConsoleService> console(do_GetService(NS_CONSOLESERVICE_CONTRACTID));
@@ -711,17 +720,17 @@ nsFrameMessageManager::SendMessage(const
   NS_ENSURE_TRUE(mCallback, NS_ERROR_NOT_INITIALIZED);
 
   if (sSendingSyncMessage && aIsSync) {
     // No kind of blocking send should be issued on top of a sync message.
     return NS_ERROR_UNEXPECTED;
   }
 
   StructuredCloneData data;
-  if (aArgc >= 2 && !GetParamsForMessage(aCx, aJSON, data)) {
+  if (aArgc >= 2 && !GetParamsForMessage(aCx, aJSON, JS::UndefinedHandleValue, data)) {
     return NS_ERROR_DOM_DATA_CLONE_ERR;
   }
 
   JS::Rooted<JSObject*> objects(aCx);
   if (aArgc >= 3 && aObjects.isObject()) {
     objects = &aObjects.toObject();
   }
 
@@ -786,60 +795,61 @@ nsFrameMessageManager::DispatchAsyncMess
   return NS_OK;
 }
 
 nsresult
 nsFrameMessageManager::DispatchAsyncMessage(const nsAString& aMessageName,
                                             const JS::Value& aJSON,
                                             const JS::Value& aObjects,
                                             nsIPrincipal* aPrincipal,
+                                            const JS::Value& aTransfers,
                                             JSContext* aCx,
                                             uint8_t aArgc)
 {
   StructuredCloneData data;
-  if (aArgc >= 2 && !GetParamsForMessage(aCx, aJSON, data)) {
+  if (aArgc >= 2 && !GetParamsForMessage(aCx, aJSON, aTransfers, data)) {
     return NS_ERROR_DOM_DATA_CLONE_ERR;
   }
 
   JS::Rooted<JSObject*> objects(aCx);
   if (aArgc >= 3 && aObjects.isObject()) {
     objects = &aObjects.toObject();
   }
 
   return DispatchAsyncMessageInternal(aCx, aMessageName, data, objects,
                                       aPrincipal);
 }
 
-
 // nsIMessageSender
 
 NS_IMETHODIMP
 nsFrameMessageManager::SendAsyncMessage(const nsAString& aMessageName,
                                         JS::Handle<JS::Value> aJSON,
                                         JS::Handle<JS::Value> aObjects,
                                         nsIPrincipal* aPrincipal,
+                                        JS::Handle<JS::Value> aTransfers,
                                         JSContext* aCx,
                                         uint8_t aArgc)
 {
-  return DispatchAsyncMessage(aMessageName, aJSON, aObjects, aPrincipal, aCx,
-                              aArgc);
+  return DispatchAsyncMessage(aMessageName, aJSON, aObjects, aPrincipal,
+                              aTransfers, aCx, aArgc);
 }
 
 
 // nsIMessageBroadcaster
 
 NS_IMETHODIMP
 nsFrameMessageManager::BroadcastAsyncMessage(const nsAString& aMessageName,
                                              JS::Handle<JS::Value> aJSON,
                                              JS::Handle<JS::Value> aObjects,
                                              JSContext* aCx,
                                              uint8_t aArgc)
 {
-  return DispatchAsyncMessage(aMessageName, aJSON, aObjects, nullptr, aCx,
-                              aArgc);
+  return DispatchAsyncMessage(aMessageName, aJSON, aObjects, nullptr,
+                              JS::UndefinedHandleValue, aCx, aArgc);
 }
 
 NS_IMETHODIMP
 nsFrameMessageManager::GetChildCount(uint32_t* aChildCount)
 {
   *aChildCount = static_cast<uint32_t>(mChildManagers.Count());
   return NS_OK;
 }
@@ -1136,28 +1146,44 @@ nsFrameMessageManager::ReceiveMessage(ns
         ErrorResult rv;
         aCloneData->Read(cx, &json, rv);
         if (NS_WARN_IF(rv.Failed())) {
           rv.SuppressException();
           JS_ClearPendingException(cx);
           return NS_OK;
         }
       }
+
+      // Get cloned MessagePort from StructuredCloneData.
+      nsTArray<RefPtr<mozilla::dom::MessagePort>> ports;
+      if (aCloneData) {
+        ports = aCloneData->TakeTransferredPorts();
+      }
+
+      JS::Rooted<JSObject*> transferredList(cx);
+      RefPtr<MessagePortList> portList = new MessagePortList(aTargetFrameLoader, ports);
+      transferredList = portList->WrapObject(cx, nullptr);
+      if (NS_WARN_IF(!transferredList)) {
+        return NS_ERROR_UNEXPECTED;
+      }
+
       JS::Rooted<JSString*> jsMessage(cx,
         JS_NewUCStringCopyN(cx,
                             static_cast<const char16_t*>(aMessage.BeginReading()),
                             aMessage.Length()));
       NS_ENSURE_TRUE(jsMessage, NS_ERROR_OUT_OF_MEMORY);
       JS::Rooted<JS::Value> syncv(cx, JS::BooleanValue(aIsSync));
       bool ok = JS_DefineProperty(cx, param, "target", targetv, JSPROP_ENUMERATE) &&
                 JS_DefineProperty(cx, param, "name", jsMessage, JSPROP_ENUMERATE) &&
                 JS_DefineProperty(cx, param, "sync", syncv, JSPROP_ENUMERATE) &&
                 JS_DefineProperty(cx, param, "json", json, JSPROP_ENUMERATE) && // deprecated
                 JS_DefineProperty(cx, param, "data", json, JSPROP_ENUMERATE) &&
-                JS_DefineProperty(cx, param, "objects", cpowsv, JSPROP_ENUMERATE);
+                JS_DefineProperty(cx, param, "objects", cpowsv, JSPROP_ENUMERATE) &&
+                JS_DefineProperty(cx, param, "ports", transferredList, JSPROP_ENUMERATE);
+
       NS_ENSURE_TRUE(ok, NS_ERROR_UNEXPECTED);
 
       if (aTargetFrameLoader) {
         JS::Rooted<JS::Value> targetFrameLoaderv(cx);
         nsresult rv = nsContentUtils::WrapNative(cx, aTargetFrameLoader, &targetFrameLoaderv);
         NS_ENSURE_SUCCESS(rv, rv);
 
         ok = JS_DefineProperty(cx, param, "targetFrameLoader", targetFrameLoaderv,
--- a/dom/base/nsFrameMessageManager.h
+++ b/dom/base/nsFrameMessageManager.h
@@ -210,18 +210,20 @@ public:
   {
     return mCallback;
   }
 
   nsresult DispatchAsyncMessage(const nsAString& aMessageName,
                                 const JS::Value& aJSON,
                                 const JS::Value& aObjects,
                                 nsIPrincipal* aPrincipal,
+                                const JS::Value& aTransfers,
                                 JSContext* aCx,
                                 uint8_t aArgc);
+
   nsresult DispatchAsyncMessageInternal(JSContext* aCx,
                                         const nsAString& aMessage,
                                         StructuredCloneData& aData,
                                         JS::Handle<JSObject*> aCpows,
                                         nsIPrincipal* aPrincipal);
   void RemoveFromParent();
   nsFrameMessageManager* GetParentManager() { return mParentManager; }
   void SetParentManager(nsFrameMessageManager* aParent)
--- a/dom/base/nsIMessageManager.idl
+++ b/dom/base/nsIMessageManager.idl
@@ -277,17 +277,18 @@ interface nsIMessageSender : nsIMessageL
    * @throws NS_ERROR_FAILURE when the message receiver cannot be found.  For
    *         example, we will throw NS_ERROR_FAILURE if we try to send a message
    *         to a cross-process frame whose process has crashed.
    */
   [implicit_jscontext, optional_argc]
   void sendAsyncMessage([optional] in AString messageName,
                         [optional] in jsval obj,
                         [optional] in jsval objects,
-                        [optional] in nsIPrincipal principal);
+                        [optional] in nsIPrincipal principal,
+                        [optional] in jsval transfers);
 };
 
 /**
  * Message "broadcasters" don't have a single "other side" that they
  * send messages to, but rather a set of subordinate message managers.
  * For example, broadcasting a message through a window message
  * manager will broadcast the message to all frame message managers
  * within its window.
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -211,16 +211,23 @@ static uint32_t sCompactOnUserInactiveDe
 static bool sIsCompactingOnUserInactive = false;
 
 // In testing, we call RunNextCollectorTimer() to ensure that the collectors are run more
 // aggressively than they would be in regular browsing. sExpensiveCollectorPokes keeps
 // us from triggering expensive full collections too frequently.
 static int32_t sExpensiveCollectorPokes = 0;
 static const int32_t kPokesBetweenExpensiveCollectorTriggers = 5;
 
+static const char*
+ProcessNameForCollectorLog()
+{
+  return XRE_GetProcessType() == GeckoProcessType_Default ?
+    "default" : "content";
+}
+
 static PRTime
 GetCollectionTimeDelta()
 {
   PRTime now = PR_Now();
   if (sFirstCollectionTime) {
     return now - sFirstCollectionTime;
   }
   sFirstCollectionTime = now;
@@ -1713,20 +1720,21 @@ nsJSContext::EndCycleCollectionCallback(
     }
 
     nsCString gcMsg;
     if (aResults.mForcedGC) {
       gcMsg.AssignLiteral(", forced a GC");
     }
 
     NS_NAMED_MULTILINE_LITERAL_STRING(kFmt,
-      MOZ_UTF16("CC(T+%.1f) max pause: %lums, total time: %lums, slices: %lu, suspected: %lu, visited: %lu RCed and %lu%s GCed, collected: %lu RCed and %lu GCed (%lu|%lu|%lu waiting for GC)%s\n")
+      MOZ_UTF16("CC(T+%.1f)[%s] max pause: %lums, total time: %lums, slices: %lu, suspected: %lu, visited: %lu RCed and %lu%s GCed, collected: %lu RCed and %lu GCed (%lu|%lu|%lu waiting for GC)%s\n")
       MOZ_UTF16("ForgetSkippable %lu times before CC, min: %lu ms, max: %lu ms, avg: %lu ms, total: %lu ms, max sync: %lu ms, removed: %lu"));
     nsString msg;
     msg.Adopt(nsTextFormatter::smprintf(kFmt.get(), double(delta) / PR_USEC_PER_SEC,
+                                        ProcessNameForCollectorLog(),
                                         gCCStats.mMaxSliceTime, gCCStats.mTotalSliceTime,
                                         aResults.mNumSlices, gCCStats.mSuspected,
                                         aResults.mVisitedRefCounted, aResults.mVisitedGCed, mergeMsg.get(),
                                         aResults.mFreedRefCounted, aResults.mFreedGCed,
                                         sCCollectedWaitingForGC, sCCollectedZonesWaitingForGC, sLikelyShortLivingObjectsNeedingGC,
                                         gcMsg.get(),
                                         sForgetSkippableBeforeCC,
                                         minForgetSkippableTime / PR_USEC_PER_MSEC,
@@ -2239,21 +2247,22 @@ DOMGCSliceCallback(JSRuntime *aRt, JS::G
 
       break;
     }
 
     case JS::GC_CYCLE_END: {
       PRTime delta = GetCollectionTimeDelta();
 
       if (sPostGCEventsToConsole) {
-        NS_NAMED_LITERAL_STRING(kFmt, "GC(T+%.1f) ");
+        NS_NAMED_LITERAL_STRING(kFmt, "GC(T+%.1f)[%s] ");
         nsString prefix, gcstats;
         gcstats.Adopt(aDesc.formatSummaryMessage(aRt));
         prefix.Adopt(nsTextFormatter::smprintf(kFmt.get(),
-                                             double(delta) / PR_USEC_PER_SEC));
+                                               double(delta) / PR_USEC_PER_SEC,
+                                               ProcessNameForCollectorLog()));
         nsString msg = prefix + gcstats;
         nsCOMPtr<nsIConsoleService> cs = do_GetService(NS_CONSOLESERVICE_CONTRACTID);
         if (cs) {
           cs->LogStringMessage(msg.get());
         }
       }
 
       if (sPostGCEventsToObserver) {
@@ -2793,18 +2802,18 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsJSArgArray)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsJSArgArray)
   if (tmp->mArgv) {
     for (uint32_t i = 0; i < tmp->mArgc; ++i) {
-      NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mArgv[i])
-      }
+      NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mArgv[i])
+    }
   }
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsJSArgArray)
   NS_INTERFACE_MAP_ENTRY(nsIArray)
   NS_INTERFACE_MAP_ENTRY(nsIJSArgArray)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIJSArgArray)
 NS_INTERFACE_MAP_END
--- a/dom/base/nsJSTimeoutHandler.cpp
+++ b/dom/base/nsJSTimeoutHandler.cpp
@@ -124,17 +124,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
   if (tmp->mFunction) {
     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFunction)
     NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
   }
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsJSScriptTimeoutHandler)
   for (uint32_t i = 0; i < tmp->mArgs.Length(); ++i) {
-    NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mArgs[i])
+    NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mArgs[i])
   }
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsJSScriptTimeoutHandler)
   NS_INTERFACE_MAP_ENTRY(nsIScriptTimeoutHandler)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
--- a/dom/base/nsXMLHttpRequest.cpp
+++ b/dom/base/nsXMLHttpRequest.cpp
@@ -437,17 +437,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_IN
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mProgressEventSink)
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mUpload)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(nsXMLHttpRequest,
                                                nsXHREventTarget)
   NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mResultArrayBuffer)
-  NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mResultJSON)
+  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mResultJSON)
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 // QueryInterface implementation for nsXMLHttpRequest
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsXMLHttpRequest)
   NS_INTERFACE_MAP_ENTRY(nsIXMLHttpRequest)
   NS_INTERFACE_MAP_ENTRY(nsIJSXMLHttpRequest)
   NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
   NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -16604,19 +16604,17 @@ class CGEventClass(CGBindingImplClass):
                     retVal += "  tmp->" + name + " = nullptr;\n"
         return retVal
 
     def implTrace(self):
         retVal = ""
         for m in self.descriptor.interface.members:
             if m.isAttr():
                 name = CGDictionary.makeMemberName(m.identifier.name)
-                if m.type.isAny():
-                    retVal += "  NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(" + name + ")\n"
-                elif m.type.isObject() or m.type.isSpiderMonkeyInterface():
+                if m.type.isAny() or m.type.isObject() or m.type.isSpiderMonkeyInterface():
                     retVal += "  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(" + name + ")\n"
                 elif typeNeedsRooting(m.type):
                     raise TypeError("Need to implement tracing for event "
                                     "member of type %s" % m.type)
         return retVal
 
     def define(self):
         dropJS = ""
--- a/dom/bluetooth/common/BluetoothCommon.cpp
+++ b/dom/bluetooth/common/BluetoothCommon.cpp
@@ -20,9 +20,32 @@ const BluetoothAddress BluetoothAddress:
                                              0x00, 0x00, 0x00);
 
 const BluetoothAddress BluetoothAddress::ALL(0xff, 0xff, 0xff,
                                              0xff, 0xff, 0xff);
 
 const BluetoothAddress BluetoothAddress::LOCAL(0x00, 0x00, 0x00,
                                                0xff, 0xff, 0xff);
 
+//
+// |BluetoothUuid|
+//
+
+const BluetoothUuid BluetoothUuid::ZERO(0x00, 0x00, 0x00, 0x00,
+                                        0x00, 0x00, 0x00, 0x00,
+                                        0x00, 0x00, 0x00, 0x00,
+                                        0x00, 0x00, 0x00, 0x00);
+
+/*
+ * [Bluetooth Specification Version 4.2, Volume 3, Part B, Section 2.5.1]
+ *
+ * To reduce the burden of storing and transferring 128-bit UUID values, a
+ * range of UUID values has been pre-allocated for assignment to often-used,
+ * registered purposes. The first UUID in this pre-allocated range is known as
+ * the Bluetooth Base UUID and has the value 00000000-0000-1000-8000-
+ * 00805F9B34FB, from the Bluetooth Assigned Numbers document.
+ */
+const BluetoothUuid BluetoothUuid::BASE(0x00, 0x00, 0x00, 0x00,
+                                        0x00, 0x00, 0x10, 0x00,
+                                        0x80, 0x00, 0x00, 0x80,
+                                        0x5f, 0x9b, 0x34, 0xfb);
+
 END_BLUETOOTH_NAMESPACE
--- a/dom/bluetooth/common/BluetoothCommon.h
+++ b/dom/bluetooth/common/BluetoothCommon.h
@@ -545,21 +545,23 @@ enum BluetoothServiceClass {
   HID              = 0x1124,
   PBAP_PCE         = 0x112e,
   PBAP_PSE         = 0x112f,
   MAP_MAS          = 0x1132,
   MAP_MNS          = 0x1133
 };
 
 struct BluetoothUuid {
+  static const BluetoothUuid ZERO;
+  static const BluetoothUuid BASE;
 
-  uint8_t mUuid[16];
+  uint8_t mUuid[16];  // store 128-bit UUID value in big-endian order
 
   BluetoothUuid()
-    : BluetoothUuid(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
+    : BluetoothUuid(ZERO)
   { }
 
   MOZ_IMPLICIT BluetoothUuid(const BluetoothUuid&) = default;
 
   BluetoothUuid(uint8_t aUuid0, uint8_t aUuid1,
                 uint8_t aUuid2, uint8_t aUuid3,
                 uint8_t aUuid4, uint8_t aUuid5,
                 uint8_t aUuid6, uint8_t aUuid7,
@@ -603,73 +605,69 @@ struct BluetoothUuid {
 
   BluetoothUuid& operator=(const BluetoothUuid& aRhs) = default;
 
   /**
    * |Clear| assigns an invalid value (i.e., zero) to the UUID.
    */
   void Clear()
   {
-    operator=(BluetoothUuid());
+    operator=(ZERO);
   }
 
   /**
    * |IsCleared| returns true if the UUID contains a value of
    * zero.
    */
   bool IsCleared() const
   {
-    return operator==(BluetoothUuid());
+    return operator==(ZERO);
   }
 
   bool operator==(const BluetoothUuid& aRhs) const
   {
     return std::equal(aRhs.mUuid,
                       aRhs.mUuid + MOZ_ARRAY_LENGTH(aRhs.mUuid), mUuid);
   }
 
   bool operator!=(const BluetoothUuid& aRhs) const
   {
     return !operator==(aRhs);
   }
 
+  /* This less-than operator is used for sorted insertion of nsTArray */
+  bool operator<(const BluetoothUuid& aUuid) const
+  {
+    return memcmp(mUuid, aUuid.mUuid, sizeof(aUuid.mUuid)) < 0;
+  };
+
   /*
    * Getter-setter methods for short UUIDS. The first 4 bytes in the
    * UUID are represented by the short notation UUID32, and bytes 3
    * and 4 (indices 2 and 3) are represented by UUID16. The rest of
-   * the UUID is filled with the SDP base UUID.
+   * the UUID is filled with the Bluetooth Base UUID.
    *
    * Below are helpers for accessing these values.
    */
 
   void SetUuid32(uint32_t aUuid32)
   {
+    operator=(BASE);
     BigEndian::writeUint32(&mUuid[0], aUuid32);
-    mUuid[4] = 0x00;
-    mUuid[5] = 0x00;
-    mUuid[6] = 0x10;
-    mUuid[7] = 0x00;
-    mUuid[8] = 0x80;
-    mUuid[9] = 0x00;
-    mUuid[10] = 0x00;
-    mUuid[11] = 0x80;
-    mUuid[12] = 0x5f;
-    mUuid[13] = 0x9b;
-    mUuid[14] = 0x34;
-    mUuid[15] = 0xfb;
   }
 
   uint32_t GetUuid32() const
   {
     return BigEndian::readUint32(&mUuid[0]);
   }
 
   void SetUuid16(uint16_t aUuid16)
   {
-    SetUuid32(aUuid16); // MSB is 0x0000
+    operator=(BASE);
+    BigEndian::writeUint16(&mUuid[2], aUuid16);
   }
 
   uint16_t GetUuid16() const
   {
     return BigEndian::readUint16(&mUuid[2]);
   }
 };
 
--- a/dom/bluetooth/common/BluetoothUtils.cpp
+++ b/dom/bluetooth/common/BluetoothUtils.cpp
@@ -273,16 +273,96 @@ StringToUuid(const nsAString& aString, B
   memcpy(&aUuid.mUuid[8], &uuid3, sizeof(uint16_t));
   memcpy(&aUuid.mUuid[10], &uuid4, sizeof(uint32_t));
   memcpy(&aUuid.mUuid[14], &uuid5, sizeof(uint16_t));
 
   return NS_OK;
 }
 
 nsresult
+BytesToUuid(const nsTArray<uint8_t>& aArray,
+            nsTArray<uint8_t>::index_type aOffset,
+            BluetoothUuidType aType,
+            BluetoothProfileEndian aEndian,
+            BluetoothUuid& aUuid)
+{
+  MOZ_ASSERT(aType == UUID_16_BIT ||
+             aType == UUID_32_BIT ||
+             aType == UUID_128_BIT);
+  MOZ_ASSERT(aEndian == ENDIAN_BIG || aEndian == ENDIAN_LITTLE);
+
+  size_t index = (aType == UUID_16_BIT) ? 2 : 0;
+  size_t length = 0;
+
+  if (aType == UUID_16_BIT) {
+    length =  sizeof(uint16_t);
+  } else if (aType == UUID_32_BIT) {
+    length = sizeof(uint32_t);
+  } else {
+    length = MOZ_ARRAY_LENGTH(aUuid.mUuid);
+  }
+
+  if (aArray.Length() < aOffset + length) {
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
+
+  aUuid = BluetoothUuid::BASE;
+
+  if (aEndian == ENDIAN_BIG) {
+    for (size_t i = 0; i < length; ++i) {
+      aUuid.mUuid[index + i] = aArray[aOffset + i];
+    }
+  } else {
+    for (size_t i = 0; i < length; ++i) {
+      aUuid.mUuid[index + i] = aArray[aOffset + length - i - 1];
+    }
+  }
+
+  return NS_OK;
+}
+
+nsresult
+UuidToBytes(const BluetoothUuid& aUuid,
+            BluetoothUuidType aType,
+            BluetoothProfileEndian aEndian,
+            nsTArray<uint8_t>& aArray,
+            nsTArray<uint8_t>::index_type aOffset)
+{
+  MOZ_ASSERT(aType == UUID_16_BIT ||
+             aType == UUID_32_BIT ||
+             aType == UUID_128_BIT);
+  MOZ_ASSERT(aEndian == ENDIAN_BIG || aEndian == ENDIAN_LITTLE);
+
+  size_t index = (aType == UUID_16_BIT) ? 2 : 0;
+  size_t length = 0;
+
+  if (aType == UUID_16_BIT) {
+    length =  sizeof(uint16_t);
+  } else if (aType == UUID_32_BIT) {
+    length = sizeof(uint32_t);
+  } else {
+    length = MOZ_ARRAY_LENGTH(aUuid.mUuid);
+  }
+
+  aArray.SetCapacity(aOffset + length);
+
+  if (aEndian == ENDIAN_BIG) {
+    for (size_t i = 0; i < length; ++i) {
+      aArray[aOffset + i] = aUuid.mUuid[index + i];
+    }
+  } else {
+    for (size_t i = 0; i < length; ++i) {
+      aArray[aOffset + length - i - 1] = aUuid.mUuid[index + i];
+    }
+  }
+
+  return NS_OK;
+}
+
+nsresult
 GenerateUuid(BluetoothUuid &aUuid)
 {
   nsresult rv;
   nsCOMPtr<nsIUUIDGenerator> uuidGenerator =
     do_GetService("@mozilla.org/uuid-generator;1", &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsID uuid;
--- a/dom/bluetooth/common/BluetoothUtils.h
+++ b/dom/bluetooth/common/BluetoothUtils.h
@@ -19,16 +19,40 @@ class BluetoothAdvertisingData;
 }
 
 BEGIN_BLUETOOTH_NAMESPACE
 
 class BluetoothNamedValue;
 class BluetoothReplyRunnable;
 class BluetoothValue;
 
+/*
+ * Each profile has its distinct endianness for multi-byte values
+ */
+enum BluetoothProfileEndian {
+  ENDIAN_BIG,
+  ENDIAN_LITTLE,
+  ENDIAN_SDP      = ENDIAN_BIG,     // SDP uses big endian
+  ENDIAN_GAP      = ENDIAN_LITTLE,  // GAP uses little endian
+  ENDIAN_GATT     = ENDIAN_LITTLE,  // GATT uses little endian
+};
+
+/*
+ * A UUID is a 128-bit value. To reduce the burden of storing and transferring
+ * 128-bit UUID values, a range of UUID values has been pre-allocated for
+ * assignment to often-used, registered purposes. UUID values in the
+ * pre-allocated range have aliases that are represented as 16-bit or 32-bit
+ * values.
+ */
+enum BluetoothUuidType {
+  UUID_16_BIT,
+  UUID_32_BIT,
+  UUID_128_BIT,
+};
+
 //
 // Address/String conversion
 //
 
 void
 AddressToString(const BluetoothAddress& aAddress, nsAString& aString);
 
 nsresult
@@ -100,16 +124,46 @@ UuidToString(const BluetoothUuid& aUuid,
  *
  * Note: This utility function is used by gecko internal only to convert uuid
  * string created by gecko back to BluetoothUuid representation.
  */
 nsresult
 StringToUuid(const nsAString& aString, BluetoothUuid& aUuid);
 
 /**
+ * Convert continuous bytes from nsTArray to BluetoothUuid object.
+ * @param aArray [in] The byte array.
+ * @param aOffset [in] The offset of continuous bytes of UUID value.
+ * @param aType [in] The type of UUID.
+ * @param aEndian [in] The endianness of UUID value.
+ * @param aUuid [out] The BluetoothUuid object.
+ */
+nsresult
+BytesToUuid(const nsTArray<uint8_t>& aArray,
+            nsTArray<uint8_t>::index_type aOffset,
+            BluetoothUuidType aType,
+            BluetoothProfileEndian aEndian,
+            BluetoothUuid& aUuid);
+
+/**
+ * Convert BluetoothUuid object to nsTArray with continuous bytes.
+ * @param aUuid [in] The BluetoothUuid object.
+ * @param aType [in] The type of UUID.
+ * @param aEndian [in] The endianness of UUID value.
+ * @param aArray [out] The byte array.
+ * @param aOffset [in] The offset of continuous bytes of UUID value.
+ */
+nsresult
+UuidToBytes(const BluetoothUuid& aUuid,
+            BluetoothUuidType aType,
+            BluetoothProfileEndian aEndian,
+            nsTArray<uint8_t>& aArray,
+            nsTArray<uint8_t>::index_type aOffset);
+
+/**
  * Generate a random uuid.
  *
  * @param aUuid [out] The generated uuid.
  */
 nsresult
 GenerateUuid(BluetoothUuid &aUuid);
 
 /**
--- a/dom/bluetooth/common/webapi/BluetoothAdapter.cpp
+++ b/dom/bluetooth/common/webapi/BluetoothAdapter.cpp
@@ -126,17 +126,17 @@ public:
 private:
   RefPtr<BluetoothAdapter> mAdapter;
 };
 
 class StartLeScanTask final : public BluetoothReplyRunnable
 {
 public:
   StartLeScanTask(BluetoothAdapter* aAdapter, Promise* aPromise,
-                  const nsTArray<nsString>& aServiceUuids)
+                  const nsTArray<BluetoothUuid>& aServiceUuids)
     : BluetoothReplyRunnable(nullptr, aPromise)
     , mAdapter(aAdapter)
     , mServiceUuids(aServiceUuids)
   {
     MOZ_ASSERT(aPromise);
     MOZ_ASSERT(aAdapter);
   }
 
@@ -145,25 +145,25 @@ public:
   {
     aValue.setUndefined();
 
     AutoJSAPI jsapi;
     NS_ENSURE_TRUE(jsapi.Init(mAdapter->GetParentObject()), false);
     JSContext* cx = jsapi.cx();
 
     const BluetoothValue& v = mReply->get_BluetoothReplySuccess().value();
-    NS_ENSURE_TRUE(v.type() == BluetoothValue::TnsString, false);
+    NS_ENSURE_TRUE(v.type() == BluetoothValue::TBluetoothUuid, false);
 
     /**
      * Create a new discovery handle and wrap it to return. Each
      * discovery handle is one-time-use only.
      */
     RefPtr<BluetoothDiscoveryHandle> discoveryHandle =
       BluetoothDiscoveryHandle::Create(mAdapter->GetParentObject(),
-                                       mServiceUuids, v.get_nsString());
+                                       mServiceUuids, v.get_BluetoothUuid());
 
     if (!ToJSValue(cx, discoveryHandle, aValue)) {
       JS_ClearPendingException(cx);
       return false;
     }
 
     // Append a BluetoothDiscoveryHandle to LeScan handle array.
     mAdapter->AppendLeScanHandle(discoveryHandle);
@@ -175,32 +175,32 @@ public:
   ReleaseMembers() override
   {
     BluetoothReplyRunnable::ReleaseMembers();
     mAdapter = nullptr;
   }
 
 private:
   RefPtr<BluetoothAdapter> mAdapter;
-  nsTArray<nsString> mServiceUuids;
+  nsTArray<BluetoothUuid> mServiceUuids;
 };
 
 class StopLeScanTask final : public BluetoothReplyRunnable
 {
 public:
   StopLeScanTask(BluetoothAdapter* aAdapter,
                  Promise* aPromise,
-                 const nsAString& aScanUuid)
+                 const BluetoothUuid& aScanUuid)
       : BluetoothReplyRunnable(nullptr, aPromise)
       , mAdapter(aAdapter)
       , mScanUuid(aScanUuid)
   {
     MOZ_ASSERT(aPromise);
     MOZ_ASSERT(aAdapter);
-    MOZ_ASSERT(!aScanUuid.IsEmpty());
+    MOZ_ASSERT(!aScanUuid.IsCleared());
   }
 
 protected:
   virtual bool
   ParseSuccessfulReply(JS::MutableHandle<JS::Value> aValue) override
   {
     mAdapter->RemoveLeScanHandle(mScanUuid);
     aValue.setUndefined();
@@ -211,17 +211,17 @@ protected:
   ReleaseMembers() override
   {
     BluetoothReplyRunnable::ReleaseMembers();
     mAdapter = nullptr;
   }
 
 private:
   RefPtr<BluetoothAdapter> mAdapter;
-  nsString mScanUuid;
+  BluetoothUuid mScanUuid;
 };
 
 class GetDevicesTask : public BluetoothReplyRunnable
 {
 public:
   GetDevicesTask(BluetoothAdapter* aAdapterPtr, nsIDOMDOMRequest* aReq)
     : BluetoothReplyRunnable(aReq)
     , mAdapterPtr(aAdapterPtr)
@@ -361,26 +361,22 @@ BluetoothAdapter::Cleanup()
 {
   UnregisterBluetoothSignalHandler(NS_LITERAL_STRING(KEY_ADAPTER), this);
 
   // Stop ongoing LE scans and clear the LeScan handle array
   if (!mLeScanHandleArray.IsEmpty()) {
     BluetoothService* bs = BluetoothService::Get();
     NS_ENSURE_TRUE_VOID(bs);
 
-    nsString uuidStr;
-    for (uint32_t i = 0; i < mLeScanHandleArray.Length(); ++i) {
-      mLeScanHandleArray[i]->GetLeScanUuid(uuidStr);
+    for (size_t i = 0; i < mLeScanHandleArray.Length(); ++i) {
+      BluetoothUuid uuid;
+      mLeScanHandleArray[i]->GetLeScanUuid(uuid);
       RefPtr<BluetoothVoidReplyRunnable> results =
         new BluetoothVoidReplyRunnable(nullptr);
-
-      BluetoothUuid uuid;
-      if (NS_SUCCEEDED(StringToUuid(uuidStr, uuid))) {
-        bs->StopLeScanInternal(uuid, results);
-      }
+      bs->StopLeScanInternal(uuid, results);
     }
     mLeScanHandleArray.Clear();
   }
 }
 
 BluetoothGattServer*
 BluetoothAdapter::GetGattServer()
 {
@@ -601,22 +597,22 @@ BluetoothAdapter::SetDiscoveryHandleInUs
 void
 BluetoothAdapter::AppendLeScanHandle(
   BluetoothDiscoveryHandle* aDiscoveryHandle)
 {
   mLeScanHandleArray.AppendElement(aDiscoveryHandle);
 }
 
 void
-BluetoothAdapter::RemoveLeScanHandle(const nsAString& aScanUuid)
+BluetoothAdapter::RemoveLeScanHandle(const BluetoothUuid& aScanUuid)
 {
-  nsString uuid;
-  for (uint32_t i = 0; i < mLeScanHandleArray.Length(); ++i) {
+  for (size_t i = 0; i < mLeScanHandleArray.Length(); ++i) {
+    BluetoothUuid uuid;
     mLeScanHandleArray[i]->GetLeScanUuid(uuid);
-    if (aScanUuid.Equals(uuid)) {
+    if (aScanUuid == uuid) {
       mLeScanHandleArray.RemoveElementAt(i);
       break;
     }
   }
 }
 
 already_AddRefed<Promise>
 BluetoothAdapter::StartDiscovery(ErrorResult& aRv)
@@ -713,17 +709,17 @@ BluetoothAdapter::StartLeScan(const nsTA
   BT_ENSURE_TRUE_REJECT(mState == BluetoothAdapterState::Enabled,
                         promise,
                         NS_ERROR_DOM_INVALID_STATE_ERR);
 
   BluetoothService* bs = BluetoothService::Get();
   BT_ENSURE_TRUE_REJECT(bs, promise, NS_ERROR_NOT_AVAILABLE);
 
   RefPtr<BluetoothReplyRunnable> result =
-    new StartLeScanTask(this, promise, aServiceUuids);
+    new StartLeScanTask(this, promise, serviceUuids);
   bs->StartLeScanInternal(serviceUuids, result);
 
   return promise.forget();
 }
 
 already_AddRefed<Promise>
 BluetoothAdapter::StopLeScan(BluetoothDiscoveryHandle& aDiscoveryHandle,
                              ErrorResult& aRv)
@@ -744,26 +740,21 @@ BluetoothAdapter::StopLeScan(BluetoothDi
   BluetoothService* bs = BluetoothService::Get();
   BT_ENSURE_TRUE_REJECT(bs, promise, NS_ERROR_NOT_AVAILABLE);
 
   // Reject the request if there's no ongoing LE Scan using this handle.
   BT_ENSURE_TRUE_REJECT(mLeScanHandleArray.Contains(&aDiscoveryHandle),
                         promise,
                         NS_ERROR_DOM_BLUETOOTH_DONE);
 
-  nsString scanUuidStr;
-  aDiscoveryHandle.GetLeScanUuid(scanUuidStr);
-
   BluetoothUuid scanUuid;
-  BT_ENSURE_TRUE_REJECT(NS_SUCCEEDED(StringToUuid(scanUuidStr, scanUuid)),
-                        promise,
-                        NS_ERROR_DOM_OPERATION_ERR);
+  aDiscoveryHandle.GetLeScanUuid(scanUuid);
 
   RefPtr<BluetoothReplyRunnable> result =
-    new StopLeScanTask(this, promise, scanUuidStr);
+    new StopLeScanTask(this, promise, scanUuid);
   bs->StopLeScanInternal(scanUuid, result);
 
   return promise.forget();
 }
 
 already_AddRefed<Promise>
 BluetoothAdapter::SetName(const nsAString& aName, ErrorResult& aRv)
 {
--- a/dom/bluetooth/common/webapi/BluetoothAdapter.h
+++ b/dom/bluetooth/common/webapi/BluetoothAdapter.h
@@ -215,17 +215,17 @@ public:
   void AppendLeScanHandle(BluetoothDiscoveryHandle* aDiscoveryHandle);
 
   /**
    * Remove the BluetoothDiscoverHandle with the given UUID from LeScan handle
    * array.
    *
    * @param aScanUuid [in] The UUID of the LE scan task.
    */
-  void RemoveLeScanHandle(const nsAString& aScanUuid);
+  void RemoveLeScanHandle(const BluetoothUuid& aScanUuid);
 
 private:
   BluetoothAdapter(nsPIDOMWindowInner* aOwner, const BluetoothValue& aValue);
   ~BluetoothAdapter();
 
   /**
    * Unregister signal handler and clean up LE scan handles.
    */
--- a/dom/bluetooth/common/webapi/BluetoothDevice.cpp
+++ b/dom/bluetooth/common/webapi/BluetoothDevice.cpp
@@ -113,16 +113,27 @@ BluetoothDevice::BluetoothDevice(nsPIDOM
 }
 
 BluetoothDevice::~BluetoothDevice()
 {
   UnregisterBluetoothSignalHandler(mAddress, this);
 }
 
 void
+BluetoothDevice::GetUuids(nsTArray<nsString>& aUuids) const
+{
+  aUuids.Clear();
+  for (size_t i = 0; i < mUuids.Length(); ++i) {
+    nsAutoString uuidStr;
+    UuidToString(mUuids[i], uuidStr);
+    aUuids.AppendElement(uuidStr);
+  }
+}
+
+void
 BluetoothDevice::DisconnectFromOwner()
 {
   DOMEventTargetHelper::DisconnectFromOwner();
   UnregisterBluetoothSignalHandler(mAddress, this);
 }
 
 BluetoothDeviceType
 BluetoothDevice::ConvertUint32ToDeviceType(const uint32_t aValue)
@@ -153,29 +164,25 @@ BluetoothDevice::SetPropertyByValue(cons
     } else {
       AddressToString(value.get_BluetoothAddress(), mAddress);
     }
   } else if (name.EqualsLiteral("Cod")) {
     mCod->Update(value.get_uint32_t());
   } else if (name.EqualsLiteral("Paired")) {
     mPaired = value.get_bool();
   } else if (name.EqualsLiteral("UUIDs")) {
-    // While converting to strings, we sort the received UUIDs and remove
-    // any duplicates.
+    // We sort the received UUIDs and remove any duplicates.
     const nsTArray<BluetoothUuid>& uuids = value.get_ArrayOfBluetoothUuid();
     nsTArray<nsString> uuidStrs;
+    mUuids.Clear();
     for (uint32_t index = 0; index < uuids.Length(); ++index) {
-      nsAutoString uuidStr;
-      UuidToString(uuids[index], uuidStr);
-
-      if (!uuidStrs.Contains(uuidStr)) { // filter out duplicate UUIDs
-        uuidStrs.InsertElementSorted(uuidStr);
+      if (!mUuids.Contains(uuids[index])) { // filter out duplicate UUIDs
+        mUuids.InsertElementSorted(uuids[index]);
       }
     }
-    mUuids = Move(uuidStrs);
     BluetoothDeviceBinding::ClearCachedUuidsValue(this);
   } else if (name.EqualsLiteral("Type")) {
     mType = ConvertUint32ToDeviceType(value.get_uint32_t());
   } else if (name.EqualsLiteral("GattAdv")) {
     MOZ_ASSERT(value.type() == BluetoothValue::TArrayOfuint8_t);
     nsTArray<uint8_t> advData;
     advData = value.get_ArrayOfuint8_t();
     UpdatePropertiesFromAdvData(advData);
@@ -270,33 +277,24 @@ BluetoothDevice::IsDeviceAttributeChange
         return !mName.Equals(remoteNameStr);
       }
     case BluetoothDeviceAttribute::Paired:
       MOZ_ASSERT(aValue.type() == BluetoothValue::Tbool);
       return mPaired != aValue.get_bool();
     case BluetoothDeviceAttribute::Uuids: {
       MOZ_ASSERT(aValue.type() == BluetoothValue::TArrayOfBluetoothUuid);
       const auto& uuids = aValue.get_ArrayOfBluetoothUuid();
-
-      nsTArray<nsString> uuidStrs;
-
-      // Construct a sorted uuid set
+      nsTArray<BluetoothUuid> sortedUuids;
+      // Construct a sorted UUID set
       for (size_t index = 0; index < uuids.Length(); ++index) {
-        nsAutoString uuidStr;
-        UuidToString(uuids[index], uuidStr);
-
-        if (!uuidStrs.Contains(uuidStr)) { // filter out duplicate uuids
-          uuidStrs.InsertElementSorted(uuidStr);
+        if (!sortedUuids.Contains(uuids[index])) { // filter out duplicate uuids
+          sortedUuids.InsertElementSorted(uuids[index]);
         }
       }
-
-      // We assume the received uuids array is sorted without duplicate items.
-      // If it's not, we require additional processing before comparing it
-      // directly.
-      return mUuids != uuidStrs;
+      return mUuids != sortedUuids;
     }
     default:
       BT_WARNING("Type %d is not handled", uint32_t(aType));
       return false;
   }
 }
 
 void
@@ -381,68 +379,54 @@ BluetoothDevice::UpdatePropertiesFromAdv
     // Length of the data field which is composed by AD type (1 byte) and
     // AD data (dataFieldLength -1 bytes)
     int dataLength = dataFieldLength - 1;
     if (offset + dataLength >= aAdvData.Length()) {
       break;
     }
 
     // Update UUIDs and name of BluetoothDevice.
-    int type = aAdvData[offset++];
+    BluetoothGapDataType type =
+      static_cast<BluetoothGapDataType>(aAdvData[offset++]);
     switch (type) {
       case GAP_INCOMPLETE_UUID16:
       case GAP_COMPLETE_UUID16:
       case GAP_INCOMPLETE_UUID32:
       case GAP_COMPLETE_UUID32:
       case GAP_INCOMPLETE_UUID128:
       case GAP_COMPLETE_UUID128: {
         mUuids.Clear();
 
-        // The length of uint16_t UUID array
-        uint8_t len = 0;
-        if (GAP_INCOMPLETE_UUID16 && GAP_COMPLETE_UUID16) {
-          len = 1;
-        } else if (GAP_INCOMPLETE_UUID32 && GAP_COMPLETE_UUID32) {
-          len = 2;
-        } else {
-          len = 8;
-        }
-        uint16_t uuid[len];
-
         while (dataLength > 0) {
-          // Read (len * 2) bytes from the data buffer and compose a 16-bits
-          // UUID array.
-          for (uint8_t i = 0; i < len; ++i) {
-            uuid[i] = aAdvData[offset++];
-            uuid[i] += (aAdvData[offset++] << 8);
-            dataLength -= 2;
-          }
-
-          char uuidStr[37]; // one more char to be null-terminated
+          BluetoothUuid uuid;
+          size_t length = 0;
           if (type == GAP_INCOMPLETE_UUID16 || type == GAP_COMPLETE_UUID16) {
-            // Convert 16-bits UUID into string.
-            snprintf(uuidStr, sizeof(uuidStr),
-                     "0000%04x-0000-1000-8000-00805f9b34fb", uuid[0]);
+            length = 2;
+            if (NS_FAILED(BytesToUuid(aAdvData, offset, UUID_16_BIT,
+                                      ENDIAN_GAP, uuid))) {
+              break;
+            }
           } else if (type == GAP_INCOMPLETE_UUID32 ||
                      type == GAP_COMPLETE_UUID32) {
-            // Convert 32-bits UUID into string.
-            snprintf(uuidStr, sizeof(uuidStr),
-                     "%04x%04x-0000-1000-8000-00805f9b34fb", uuid[1], uuid[0]);
+            length = 4;
+            if (NS_FAILED(BytesToUuid(aAdvData, offset, UUID_32_BIT,
+                                      ENDIAN_GAP, uuid))) {
+              break;
+            }
           } else if (type == GAP_INCOMPLETE_UUID128 ||
                      type == GAP_COMPLETE_UUID128) {
-            // Convert 128-bits UUID into string.
-            snprintf(uuidStr, sizeof(uuidStr),
-                     "%04x%04x-%04x-%04x-%04x-%04x%04x%04x",
-                     uuid[7], uuid[6], uuid[5], uuid[4],
-                     uuid[3], uuid[2], uuid[1], uuid[0]);
+            length = 16;
+            if (NS_FAILED(BytesToUuid(aAdvData, offset, UUID_128_BIT,
+                                      ENDIAN_GAP, uuid))) {
+              break;
+            }
           }
-          nsString uuidNsString;
-          uuidNsString.AssignLiteral(uuidStr);
-
-          mUuids.AppendElement(uuidNsString);
+          mUuids.AppendElement(uuid);
+          offset += length;
+          dataLength -= length;
         }
 
         BluetoothDeviceBinding::ClearCachedUuidsValue(this);
         break;
       }
       case GAP_SHORTENED_NAME:
         if (!mName.IsEmpty()) break;
       case GAP_COMPLETE_NAME: {
--- a/dom/bluetooth/common/webapi/BluetoothDevice.h
+++ b/dom/bluetooth/common/webapi/BluetoothDevice.h
@@ -54,20 +54,17 @@ public:
     aName = mName;
   }
 
   bool Paired() const
   {
     return mPaired;
   }
 
-  void GetUuids(nsTArray<nsString>& aUuids) const
-  {
-    aUuids = mUuids;
-  }
+  void GetUuids(nsTArray<nsString>& aUuids) const;
 
   BluetoothDeviceType Type() const
   {
     return mType;
   }
 
   BluetoothGatt* GetGatt();
 
@@ -92,16 +89,21 @@ public:
   {
      return GetOwner();
   }
 
   virtual JSObject* WrapObject(JSContext* aCx,
                                JS::Handle<JSObject*> aGivenProto) override;
   virtual void DisconnectFromOwner() override;
 
+  void GetUuids(nsTArray<BluetoothUuid>& aUuids) const
+  {
+    aUuids = mUuids;
+  }
+
 private:
   BluetoothDevice(nsPIDOMWindowInner* aOwner, const BluetoothValue& aValue);
   ~BluetoothDevice();
 
   /**
    * Set device properties according to properties array
    *
    * @param aValue [in] Properties array to set with
@@ -183,17 +185,17 @@ private:
   /**
    * Whether this device is paired or not.
    */
   bool mPaired;
 
   /**
    * Cached UUID list of services which this device provides.
    */
-  nsTArray<nsString> mUuids;
+  nsTArray<BluetoothUuid> mUuids;
 
   /**
    * Type of this device. Can be unknown/classic/le/dual.
    */
   BluetoothDeviceType mType;
 
   /**
    * GATT client object to interact with the remote device.
--- a/dom/bluetooth/common/webapi/BluetoothDiscoveryHandle.cpp
+++ b/dom/bluetooth/common/webapi/BluetoothDiscoveryHandle.cpp
@@ -24,18 +24,18 @@ NS_IMPL_RELEASE_INHERITED(BluetoothDisco
 BluetoothDiscoveryHandle::BluetoothDiscoveryHandle(nsPIDOMWindowInner* aWindow)
   : DOMEventTargetHelper(aWindow)
 {
   MOZ_ASSERT(aWindow);
 }
 
 BluetoothDiscoveryHandle::BluetoothDiscoveryHandle(
   nsPIDOMWindowInner* aWindow,
-  const nsTArray<nsString>& aServiceUuids,
-  const nsAString& aLeScanUuid)
+  const nsTArray<BluetoothUuid>& aServiceUuids,
+  const BluetoothUuid& aLeScanUuid)
   : DOMEventTargetHelper(aWindow)
   , mLeScanUuid(aLeScanUuid)
   , mServiceUuids(aServiceUuids)
 {
   MOZ_ASSERT(aWindow);
 }
 
 BluetoothDiscoveryHandle::~BluetoothDiscoveryHandle()
@@ -52,18 +52,18 @@ BluetoothDiscoveryHandle::Create(nsPIDOM
   RefPtr<BluetoothDiscoveryHandle> handle =
     new BluetoothDiscoveryHandle(aWindow);
   return handle.forget();
 }
 
 already_AddRefed<BluetoothDiscoveryHandle>
 BluetoothDiscoveryHandle::Create(
   nsPIDOMWindowInner* aWindow,
-  const nsTArray<nsString>& aServiceUuids,
-  const nsAString& aLeScanUuid)
+  const nsTArray<BluetoothUuid>& aServiceUuids,
+  const BluetoothUuid& aLeScanUuid)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aWindow);
 
   RefPtr<BluetoothDiscoveryHandle> handle =
     new BluetoothDiscoveryHandle(aWindow, aServiceUuids, aLeScanUuid);
   return handle.forget();
 }
@@ -84,17 +84,17 @@ BluetoothDiscoveryHandle::DispatchDevice
 }
 
 void
 BluetoothDiscoveryHandle::DispatchLeDeviceEvent(BluetoothDevice* aLeDevice,
   int32_t aRssi, nsTArray<uint8_t>& aScanRecord)
 {
   MOZ_ASSERT(aLeDevice);
 
-  nsTArray<nsString> remoteUuids;
+  nsTArray<BluetoothUuid> remoteUuids;
   aLeDevice->GetUuids(remoteUuids);
 
   bool hasUuidsFilter = !mServiceUuids.IsEmpty();
   bool noAdvertisingUuid  = remoteUuids.IsEmpty();
   // If an LE device doesn't advertise its service UUIDs, it can't possibly pass
   // the UUIDs filter.
   if (hasUuidsFilter && noAdvertisingUuid) {
     return;
--- a/dom/bluetooth/common/webapi/BluetoothDiscoveryHandle.h
+++ b/dom/bluetooth/common/webapi/BluetoothDiscoveryHandle.h
@@ -23,57 +23,57 @@ class BluetoothDiscoveryHandle final : p
 public:
   NS_DECL_ISUPPORTS_INHERITED
 
   static already_AddRefed<BluetoothDiscoveryHandle>
     Create(nsPIDOMWindowInner* aWindow);
 
   static already_AddRefed<BluetoothDiscoveryHandle>
     Create(nsPIDOMWindowInner* aWindow,
-           const nsTArray<nsString>& aServiceUuids,
-           const nsAString& aLeScanUuid);
+           const nsTArray<BluetoothUuid>& aServiceUuids,
+           const BluetoothUuid& aLeScanUuid);
 
   void DispatchDeviceEvent(BluetoothDevice* aDevice);
 
   void DispatchLeDeviceEvent(BluetoothDevice* aLeDevice,
                              int32_t aRssi,
                              nsTArray<uint8_t>& aScanRecord);
 
   IMPL_EVENT_HANDLER(devicefound);
 
-  void GetLeScanUuid(nsString& aLeScanUuid) const
+  void GetLeScanUuid(BluetoothUuid& aLeScanUuid) const
   {
     aLeScanUuid = mLeScanUuid;
   }
 
   virtual JSObject* WrapObject(JSContext* aCx,
                                JS::Handle<JSObject*> aGivenProto) override;
 
 private:
   BluetoothDiscoveryHandle(nsPIDOMWindowInner* aWindow);
 
   BluetoothDiscoveryHandle(nsPIDOMWindowInner* aWindow,
-                           const nsTArray<nsString>& aServiceUuids,
-                           const nsAString& aLeScanUuid);
+                           const nsTArray<BluetoothUuid>& aServiceUuids,
+                           const BluetoothUuid& aLeScanUuid);
 
   ~BluetoothDiscoveryHandle();
 
   /**
    * Random generated UUID of LE scan
    *
    * This UUID is used only when the handle is built for LE scan.
    * If BluetoothDiscoveryHandle is built for classic discovery, the value would
    * remain empty string during the entire life cycle.
    */
-  nsString mLeScanUuid;
+  BluetoothUuid mLeScanUuid;
 
   /**
-   * A DOMString array of service UUIDs to discover / scan for.
+   * A BluetoothUuid array of service UUIDs to discover / scan for.
    *
    * This array is only used by LE scan. If BluetoothDiscoveryHandle is built
    * for classic discovery, the array should be empty.
    */
-  nsTArray<nsString> mServiceUuids;
+  nsTArray<BluetoothUuid> mServiceUuids;
 };
 
 END_BLUETOOTH_NAMESPACE
 
 #endif // mozilla_dom_bluetooth_BluetoothDiscoveryHandle_h
--- a/dom/events/MessageEvent.cpp
+++ b/dom/events/MessageEvent.cpp
@@ -28,17 +28,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(MessageEvent, Event)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindowSource)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPortSource)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPorts)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(MessageEvent, Event)
-  NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mData)
+  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mData)
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MessageEvent)
 NS_INTERFACE_MAP_END_INHERITING(Event)
 
 NS_IMPL_ADDREF_INHERITED(MessageEvent, Event)
 NS_IMPL_RELEASE_INHERITED(MessageEvent, Event)
 
--- a/dom/html/HTMLFormElement.cpp
+++ b/dom/html/HTMLFormElement.cpp
@@ -146,17 +146,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_IN
                                                 nsGenericHTMLElement)
   tmp->Clear();
   tmp->mExpandoAndGeneration.Unlink();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(HTMLFormElement,
                                                nsGenericHTMLElement)
   if (tmp->PreservingWrapper()) {
-    NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mExpandoAndGeneration.expando);
+    NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mExpandoAndGeneration.expando)
   }
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_IMPL_ADDREF_INHERITED(HTMLFormElement, Element)
 NS_IMPL_RELEASE_INHERITED(HTMLFormElement, Element)
 
 
 // QueryInterface implementation for HTMLFormElement
--- a/dom/html/nsDOMStringMap.cpp
+++ b/dom/html/nsDOMStringMap.cpp
@@ -33,17 +33,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
     tmp->mElement = nullptr;
   }
   tmp->mExpandoAndGeneration.Unlink();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsDOMStringMap)
   NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
   if (tmp->PreservingWrapper()) {
-    NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mExpandoAndGeneration.expando);
+    NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mExpandoAndGeneration.expando)
   }
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMStringMap)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
--- a/dom/indexedDB/IDBCursor.cpp
+++ b/dom/indexedDB/IDBCursor.cpp
@@ -862,19 +862,19 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(IDBCursor)
   MOZ_ASSERT_IF(!tmp->mHaveCachedKey, tmp->mCachedKey.isUndefined());
   MOZ_ASSERT_IF(!tmp->mHaveCachedPrimaryKey,
                 tmp->mCachedPrimaryKey.isUndefined());
   MOZ_ASSERT_IF(!tmp->mHaveCachedValue, tmp->mCachedValue.isUndefined());
 
   NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
   NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mScriptOwner)
-  NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mCachedKey)
-  NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mCachedPrimaryKey)
-  NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mCachedValue)
+  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mCachedKey)
+  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mCachedPrimaryKey)
+  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mCachedValue)
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBCursor)
   // Don't unlink mRequest, mSourceObjectStore, or mSourceIndex!
   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
   tmp->DropJSObjects();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
--- a/dom/indexedDB/IDBIndex.cpp
+++ b/dom/indexedDB/IDBIndex.cpp
@@ -588,17 +588,17 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(IDBIndex)
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(IDBIndex)
   NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
-  NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mCachedKeyPath)
+  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mCachedKeyPath)
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IDBIndex)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mObjectStore)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBIndex)
--- a/dom/indexedDB/IDBKeyRange.cpp
+++ b/dom/indexedDB/IDBKeyRange.cpp
@@ -237,18 +237,18 @@ IDBKeyRange::BindToStatement(mozIStorage
 NS_IMPL_CYCLE_COLLECTION_CLASS(IDBKeyRange)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IDBKeyRange)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(IDBKeyRange)
-  NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mCachedLowerVal)
-  NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mCachedUpperVal)
+  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mCachedLowerVal)
+  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mCachedUpperVal)
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBKeyRange)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal)
   tmp->DropJSObjects();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBKeyRange)
--- a/dom/indexedDB/IDBObjectStore.cpp
+++ b/dom/indexedDB/IDBObjectStore.cpp
@@ -1606,17 +1606,17 @@ IDBObjectStore::Index(const nsAString& a
 
   return index.forget();
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(IDBObjectStore)
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(IDBObjectStore)
   NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
-  NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mCachedKeyPath)
+  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mCachedKeyPath)
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IDBObjectStore)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTransaction)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIndexes);
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
--- a/dom/indexedDB/IDBRequest.cpp
+++ b/dom/indexedDB/IDBRequest.cpp
@@ -431,17 +431,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_IN
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mSourceAsCursor)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mTransaction)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mError)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(IDBRequest, IDBWrapperCache)
   // Don't need NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER because
   // DOMEventTargetHelper does it for us.
-  NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mResultVal)
+  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mResultVal)
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(IDBRequest)
   if (aIID.Equals(kIDBRequestIID)) {
     foundInterface = this;
   } else
 NS_INTERFACE_MAP_END_INHERITING(IDBWrapperCache)
 
--- a/dom/ipc/DOMTypes.ipdlh
+++ b/dom/ipc/DOMTypes.ipdlh
@@ -19,20 +19,29 @@ namespace mozilla {
 namespace dom {
 
 union OptionalID
 {
   nsID;
   void_t;
 };
 
+struct MessagePortIdentifier
+{
+  nsID uuid;
+  nsID destinationUuid;
+  uint32_t sequenceId;
+  bool neutered;
+};
+
 struct ClonedMessageData
 {
   SerializedStructuredCloneBuffer data;
   PBlob[] blobs;
+  MessagePortIdentifier[] identfiers;
 };
 
 union BlobData
 {
   // For remote blobs.
   nsID;
 
   // For memory-backed blobs.
--- a/dom/ipc/StructuredCloneData.cpp
+++ b/dom/ipc/StructuredCloneData.cpp
@@ -36,16 +36,18 @@ StructuredCloneData::Copy(const Structur
     mSharedData = aData.SharedData();
   } else {
     mSharedData =
       SharedJSAllocatedData::CreateFromExternalData(aData.Data(),
                                                     aData.DataLength());
     NS_ENSURE_TRUE(mSharedData, false);
   }
 
+  PortIdentifiers().AppendElements(aData.PortIdentifiers());
+
   MOZ_ASSERT(BlobImpls().IsEmpty());
   BlobImpls().AppendElements(aData.BlobImpls());
 
   MOZ_ASSERT(GetSurfaces().IsEmpty());
 
   return true;
 }
 
@@ -62,19 +64,28 @@ StructuredCloneData::Read(JSContext* aCx
   ReadFromBuffer(global, aCx, Data(), DataLength(), aValue, aRv);
 }
 
 void
 StructuredCloneData::Write(JSContext* aCx,
                            JS::Handle<JS::Value> aValue,
                            ErrorResult &aRv)
 {
+  Write(aCx, aValue, JS::UndefinedHandleValue, aRv);
+}
+
+void
+StructuredCloneData::Write(JSContext* aCx,
+                           JS::Handle<JS::Value> aValue,
+                           JS::Handle<JS::Value> aTransfer,
+                           ErrorResult &aRv)
+{
   MOZ_ASSERT(!Data());
 
-  StructuredCloneHolder::Write(aCx, aValue, aRv);
+  StructuredCloneHolder::Write(aCx, aValue, aTransfer, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return;
   }
 
   uint64_t* data = nullptr;
   size_t dataLength = 0;
   mBuffer->steal(&data, &dataLength);
   mBuffer = nullptr;
--- a/dom/ipc/StructuredCloneData.h
+++ b/dom/ipc/StructuredCloneData.h
@@ -65,17 +65,17 @@ private:
   size_t mDataLength;
 };
 
 class StructuredCloneData : public StructuredCloneHolder
 {
 public:
   StructuredCloneData()
     : StructuredCloneHolder(StructuredCloneHolder::CloningSupported,
-                            StructuredCloneHolder::TransferringNotSupported,
+                            StructuredCloneHolder::TransferringSupported,
                             StructuredCloneHolder::DifferentProcess)
     , mExternalData(nullptr)
     , mExternalDataLength(0)
   {}
 
   StructuredCloneData(const StructuredCloneData&) = delete;
 
   ~StructuredCloneData()
@@ -101,16 +101,21 @@ public:
   void Read(JSContext* aCx,
             JS::MutableHandle<JS::Value> aValue,
             ErrorResult &aRv);
 
   void Write(JSContext* aCx,
              JS::Handle<JS::Value> aValue,
              ErrorResult &aRv);
 
+  void Write(JSContext* aCx,
+             JS::Handle<JS::Value> aValue,
+             JS::Handle<JS::Value> aTransfers,
+             ErrorResult &aRv);
+
   void UseExternalData(uint64_t* aData, size_t aDataLength)
   {
     MOZ_ASSERT(!Data());
     mExternalData = aData;
     mExternalDataLength = aDataLength;
   }
 
   bool CopyExternalData(const void* aData, size_t aDataLength);
--- a/dom/media/mediasink/OutputStreamManager.cpp
+++ b/dom/media/mediasink/OutputStreamManager.cpp
@@ -20,24 +20,28 @@ OutputStreamData::~OutputStreamData()
 
 void
 OutputStreamData::Init(OutputStreamManager* aOwner, ProcessedMediaStream* aStream)
 {
   mOwner = aOwner;
   mStream = aStream;
 }
 
-void
+bool
 OutputStreamData::Connect(MediaStream* aStream)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(!mPort, "Already connected?");
-  MOZ_ASSERT(!mStream->IsDestroyed(), "Can't connect a destroyed stream.");
+
+  if (mStream->IsDestroyed()) {
+    return false;
+  }
 
   mPort = mStream->AllocateInputPort(aStream);
+  return true;
 }
 
 bool
 OutputStreamData::Disconnect()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   // During cycle collection, DOMMediaStream can be destroyed and send
@@ -101,18 +105,21 @@ OutputStreamManager::Remove(MediaStream*
   }
 }
 
 void
 OutputStreamManager::Connect(MediaStream* aStream)
 {
   MOZ_ASSERT(NS_IsMainThread());
   mInputStream = aStream;
-  for (auto&& os : mStreams) {
-    os.Connect(aStream);
+  for (int32_t i = mStreams.Length() - 1; i >= 0; --i) {
+    if (!mStreams[i].Connect(aStream)) {
+      // Probably the DOMMediaStream was GCed. Clean up.
+      mStreams.RemoveElementAt(i);
+    }
   }
 }
 
 void
 OutputStreamManager::Disconnect()
 {
   MOZ_ASSERT(NS_IsMainThread());
   mInputStream = nullptr;
--- a/dom/media/mediasink/OutputStreamManager.h
+++ b/dom/media/mediasink/OutputStreamManager.h
@@ -19,17 +19,18 @@ class OutputStreamManager;
 class ProcessedMediaStream;
 
 class OutputStreamData {
 public:
   ~OutputStreamData();
   void Init(OutputStreamManager* aOwner, ProcessedMediaStream* aStream);
 
   // Connect mStream to the input stream.
-  void Connect(MediaStream* aStream);
+  // Return false is mStream is already destroyed, otherwise true.
+  bool Connect(MediaStream* aStream);
   // Disconnect mStream from its input stream.
   // Return false is mStream is already destroyed, otherwise true.
   bool Disconnect();
   // Return true if aStream points to the same object as mStream.
   // Used by OutputStreamManager to remove an output stream.
   bool Equals(MediaStream* aStream) const;
   // Return the graph mStream belongs to.
   MediaStreamGraph* Graph() const;
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..81fe51ffb3e7bf899377eb3c500c257c5d72a3a0
GIT binary patch
literal 3428
zc$}q|c|25Y8^>pmU5veC8I&wxkVGCv%pf!~mW+tOV5-sBDcQFWkx5c{ED7N;vaf@N
z#;$}U&19<-LKMn7)A+pa^Vj?Q@%+v?*Y{l4b*}4kpL5^mkNd2*w=IATu&2etEcQ*d
z1_M>10)4Po{4wloKmZrx0C+M_enwi7aRC7Ub^xoAOg_u(0{D9uMjBul{`KO1{oTLm
zRy3=nDkJNNBlxJF_4k&$jB{s2<sj-1IW0}ilbU*3T5?D2e1Zaev6nqD%FL!QtY_K=
zj&3#%T#~FUwXhTlxIYg`MP(I%xgn|ULUCYj<NuBUgs-&q&V0V(AiNWL^8Dp_m2W;z
z?2d{kD>@1N3Okv5)6148wAx<z_6^0D1Ki=Kq|-PHp;0XkDxy3b?qEjX5Pt$&hjEgg
znE2$JZkSp+1*6duORO7o|H~n{)+Aah(=1<b;dV~L7*;bo<uEEoFVh@+E3vC%f_70y
zl8@q^Z+r_TFT@CtpS!7va>qs+SFQY8k;z}dP$*(}m<|FXk;XI{!$l$ybRY=AAQA{-
zS|kz-VUcD3CjglQgl3S)jCwG#qoay8!f-n}$aEkCLmC<B06`cYojwAMCUuN3$gm7B
zZbp7{1W=U^jEU7ACX?xq!on^Bqdq1!%QlJ*!n`~l#7+mh+ZMsb0!L!B1NVt1igk%{
z%ms&JC)mYPHk3GF!H|C|Fc_JU&kz`jVIm)i`R(Sj>^}<x#;lr{m_WhM7<nc!F~TNH
zt}?xU78C@I+0fP1g@BPoN0^K*OkhA)Yn|!+vmnTSDKO&$_(f-s_Rz71kv)v;VPp^V
zJ<#_+2Z2CPzx+-h3v6H(<dH0(kSw5(ETA%2$Sl}HJ`f0mMt8DBvk<!njy-SyS>OP&
zz{$n}_a3+b;TN0(uV0w@Y@|_Hb<j&GJYK5fmB=;!ezT^u6Xb|FVbj(QQs&kxlumyo
zWg?E}dB+!NtL#Bc?Gu62GE~cUh#=J<376xNLgLWKfG0dbT=r~<@LzI22d@cJ;@Pw}
zhbGULGEmtaqn-64cJkC>BbeMf=5gh|QSwTQ@LK(jl60}m$O6i=o9)w>mo4H_+ks<X
z_(t?q(XXWbW(DIV#5MQ>|6F`X)wz8tsoZUfyD4Vn{(=pr2Dz?FV4M^`h4|+Q8_QY5
z<`w<SZ+1lPH9Z?*`Goq3WROdAQ8e{^Vsp^DUC-{-31zOQ<eEi;G&Khg!KmIe*iF+H
z)Y{$AM~$x!IflzBalD|`gdU@5fBGQqz~SUUB{EYy6*4WKIY-TQ*(ZKn=V^@lymj8K
zf^g)Hbsz;eZ?kM~9;G<GIMr%%B>SSk;8Tq7=m|bm0V9eGJc3Y`nYC}^f#<VA*LY$I
zmHKEB{-7a1DA8}}vg}8^*-`FF0%Z|xMod?9eG>xK9ld@xwJYCf%d_raGD0B^jc!`|
zVlf{v8E4v9^})$=(}wTv_l%(+F_c=l>(T4WQIj`z$BRQ@^0%;EqEnOV1}{V1_!iM6
zQJ-X_!khAKRL?*34^CPqn!bSlD29p5OAJ<$6W1R5;gc7Jc`xF82y>Fws*{Rq&Z#H*
z4>l%j+$K(R(-hupa`~}&*PKy$VE0&CY4{kXLC`>#_s9lX{*u6#1yzcd+uZ0?&xYOf
z<Wi?jz}AMQ$n4amN%i}kY83%Mx~lt))=Wb_bUWQ%KKt1fgPjN6K5Al9Z=)_+;o;R(
z5t|hB$++&Vc)Qd_{Xvl(-~FICia_m#ivh`{NGf|!I80oYTLlGM<~zNie5%6ZMvr>I
z=>S6iLBtzZuWZ#{q7C=m2MQED<6eBJ=sEFVo3{0e7?VWU^~*-ssT3Lb@I0Ed_L-;O
zKSBPZX6M@>7e|M%xG()A8cH4^P@Ij{iI0SHHHOx7wZX?!A>OjHJWE#)Xcx;6o+Sm2
zf!b^ft@w_}w*B!3=#%f#tlWPA?Um%M?t08}E(yBV1g%O0B=rL8p5oL)Dt}_#%gh~G
z^ZdC43uV-QV!h2)gJ&^GI{5O}y@f~P?dpNKu*td9`s+OGiSeIBt-mKza0dEL8-d|T
zDk$1jh}1=DaUdo#SSsJ4*6^86lgt^p+}%bl!Q*cY=am$#OA<JPQk3m80!}~1=u}^m
zU6^o^75dR7)n9L90RKxnMb+8oqtKCToM9o`y;NF_Qycq&Z2f%!QDNb_T-WeU8eAfL
z4vn>Rd>X_Z-p6Y%+mrwdkdYcg<3fdt@#Xj6c}i&`h6RMJPxgnZ1(>0S-M?0XZ@?m?
zF`aLCX}%u(@41G4*dl8AXP!=SdRXLMQp?eAtENmJlB6XYcF*os&h0ak^VEH%Z8evl
zCf$@={me*4!7y`E{N`|lM9Bf2{x{B!)<!;J6SjzynbMQ8ncIO^^u$p5?Q!#xtAEsh
z3)_eJM>r>otkpB-K4P)czRFW;@$Eoy#9LtR$}MYEeBj_I*=7$Y)^qu43F^DMx$Y}f
zi>JLw=Np1PL|_i8?woVWBR*+{BBmc{a$PE*)jyt2Iz#0jsI`1?`7V7oQF#<rO=`77
ztbo=PFLb1U>q_r=Yy2Xgv}uZY_au}g;n8car>J2`#+POdXm`(UBq0L;-&WJd9AL{8
zJw#>kp9*>ALgnSD61TN*x$Jnq=3fQUkI%rRk_Jsart}|h$K~RE>R*c)qx6?3IE&ry
z@z1PxMAI=kg(W-_gvji!YpLipw4159<s{x?xo>P#rKq8=olTSZVz+hdTvs9%$N3@9
zMtt(~u>1DrFmp?sHa?(WQ;h&#qXqYBe3ALq{r+0{IRG!DeSL>qoPTi{DA)hOYqIYS
zNMWh7M&qj9wqN7$Gb*GQUrU7XXux~#B?<mfUX@p0FTN5fs^E7j@8k`8MeehaHPxhd
z$Oc|^JX&W+Ttwr}GUv?tVl<;nGyl$s0Y#tvXR%wtWu|A3RbbvZa|`Qb#Z$+BF4<dP
zJxv1Wb!Czl8v4X@wzc~_xGsFPd^{Et#hi(v!Bb)2wpqcqRal|CKCva`Quahdy2xrJ
z*;V@8F@&4>VH~8caW2(*x_QSIvAp0wHdII<5+C}S?6fLyD&Vn4a1YOkjF8c22}Eg$
zLU==qR!*`1&DtKGLlp(=1JmgX%TYIb;V-w!GTLtmWrct!$(^%Lx^wDAJG~rtuRvQa
z#EkET^u!Zm?7P&QyzpgS5<_rheK|O9nI$r}`I9^wTwJX2Fkdkyat3cj!sYdf579p#
z7ehs*q8(=<sLnUCZ17KZ6C7*$o5m!*eH5XBg-+F4Lj?vSl{6Ja7|R#!-_y*y$*W)%
zIVgQ~f4hyjKrM5Kq|WU!Tfo<$&ZLSdQcv07Oq?xJ@KMWmV^}`;C}UAr9l_q*^p)MD
zBvXF{3BiDOj%hP~627JE^<Dq!+T+~eQE!FK#9IlAq>ogSmp@{E4}M-^X!?VSa_IZc
z<Gz_Y@uIPzRB%!hL7$)CopE__$?E$!eA~aU{fuguN*U~H=4lXs7AQ#@l7BrumGiYS
zePa$DamQiR-tOj%VuC!7`~lMO(Cq17joLFVvR``BeyUZ>mX(U32zezy;T5Zw+IVa)
zOz-sjUP?>by31>6*(E-Yghv|A?SG=3xpL#F{F14~xSYD`cSc;1@*e~j7JBW~%4Lxf
z6?9{Q`GqXTOIqZ3#J?>(Ka58Aj=%g4qbuQm%9#X4WNGHyQ6136SB#n#7u<dxy}lki
zlV{Sb5aoN!=z#l6EiZ}OOM$Hx0w$to9nM)6v4_b3SFxKmHJ1v1rV_lfTAB%kCC`Nl
z6?x3Ys#<bP7p@y^QQY2~JH@Cq{JDDAj`)9O@Ao!`!}xF89OnL~+TC{yD5cpq^~dJ)
ZcF*+o+U)fi>Snq`3_v1F<il+M;6JM5I<)`*
new file mode 100644
--- /dev/null
+++ b/dom/media/test/black100x100-aspect3to2.ogv^headers^
@@ -0,0 +1,1 @@
+Cache-Control: no-store
--- a/dom/media/test/mochitest.ini
+++ b/dom/media/test/mochitest.ini
@@ -306,16 +306,18 @@ support-files =
   bipbop_480wp_1001kbps-cenc-video-key2-1.m4s
   bipbop_480wp_1001kbps-cenc-video-key2-1.m4s^headers^
   bipbop_480wp_1001kbps-cenc-video-key2-2.m4s
   bipbop_480wp_1001kbps-cenc-video-key2-2.m4s^headers^
   bipbop_480wp_1001kbps-cenc-video-key2-init.mp4
   bipbop_480wp_1001kbps-cenc-video-key2-init.mp4^headers^
   bipbop-lateaudio.mp4
   bipbop-lateaudio.mp4^headers^
+  black100x100-aspect3to2.ogv
+  black100x100-aspect3to2.ogv^headers^
   bogus.duh
   bogus.ogv
   bogus.ogv^headers^
   bogus.wav
   bogus.wav^headers^
   bug461281.ogg
   bug461281.ogg^headers^
   bug482461-theora.ogv
@@ -601,16 +603,18 @@ skip-if = os == 'win' && !debug # bug 11
 [test_bug895091.html]
 [test_bug895305.html]
 [test_bug919265.html]
 [test_bug957847.html]
 [test_bug1018933.html]
 [test_bug1113600.html]
 tags=capturestream
 [test_bug1242338.html]
+[test_bug1248229.html]
+tags=capturestream
 [test_can_play_type.html]
 [test_can_play_type_mpeg.html]
 skip-if = buildapp == 'b2g' || (toolkit == 'android' && processor == 'x86') # bug 1021675 #x86 only bug 914439
 [test_can_play_type_no_ogg.html]
 [test_can_play_type_ogg.html]
 [test_chaining.html]
 [test_clone_media_element.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
new file mode 100644
--- /dev/null
+++ b/dom/media/test/test_bug1248229.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test garbage collection of captured stream (bug 1248229)</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+  <script type="text/javascript" src="manifest.js"></script>
+</head>
+<body onload="doTest()">
+<video id="v" src="black100x100-aspect3to2.ogv"></video>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+SimpleTest.waitForExplicitFinish();
+
+function doTest() {
+  window.oak = v.mozCaptureStreamUntilEnded();
+  v.mozCaptureStreamUntilEnded();
+  v.play();
+
+  v.onended = function() {
+    info("Got ended.");
+    v.onended = null;
+    SpecialPowers.exactGC(window, function() {
+      info("GC completed.");
+      v.play();
+      SimpleTest.finish();
+    });
+  }
+}
+
+</script>
+</pre>
+</body>
+</html>
--- a/dom/messagechannel/MessagePort.cpp
+++ b/dom/messagechannel/MessagePort.cpp
@@ -99,23 +99,20 @@ private:
     AutoJSAPI jsapi;
     if (!globalObject || !jsapi.Init(globalObject)) {
       NS_WARNING("Failed to initialize AutoJSAPI object.");
       return NS_ERROR_FAILURE;
     }
 
     JSContext* cx = jsapi.cx();
 
-    nsCOMPtr<nsPIDOMWindowInner> window =
-      do_QueryInterface(mPort->GetParentObject());
-
     ErrorResult rv;
     JS::Rooted<JS::Value> value(cx);
 
-    mData->Read(window, cx, &value, rv);
+    mData->Read(mPort->GetParentObject(), cx, &value, rv);
     if (NS_WARN_IF(rv.Failed())) {
       return rv.StealNSResult();
     }
 
     // Create the event
     nsCOMPtr<mozilla::dom::EventTarget> eventTarget =
       do_QueryInterface(mPort->GetOwner());
     RefPtr<MessageEvent> event =
@@ -257,49 +254,54 @@ private:
 
   const MessagePortIdentifier mIdentifier;
 };
 
 NS_IMPL_ISUPPORTS(ForceCloseHelper, nsIIPCBackgroundChildCreateCallback)
 
 } // namespace
 
-MessagePort::MessagePort(nsPIDOMWindowInner* aWindow)
-  : DOMEventTargetHelper(aWindow)
-  , mInnerID(0)
+MessagePort::MessagePort(nsISupports* aSupports)
+  : mInnerID(0)
   , mMessageQueueEnabled(false)
   , mIsKeptAlive(false)
 {
   mIdentifier = new MessagePortIdentifier();
   mIdentifier->neutered() = true;
   mIdentifier->sequenceId() = 0;
+
+  nsCOMPtr<nsIGlobalObject> globalObject = do_QueryInterface(aSupports);
+  if (NS_WARN_IF(!globalObject)) {
+    return;
+  }
+  BindToOwner(globalObject);
 }
 
 MessagePort::~MessagePort()
 {
   CloseForced();
   MOZ_ASSERT(!mWorkerFeature);
 }
 
 /* static */ already_AddRefed<MessagePort>
-MessagePort::Create(nsPIDOMWindowInner* aWindow, const nsID& aUUID,
+MessagePort::Create(nsISupports* aSupport, const nsID& aUUID,
                     const nsID& aDestinationUUID, ErrorResult& aRv)
 {
-  RefPtr<MessagePort> mp = new MessagePort(aWindow);
+  RefPtr<MessagePort> mp = new MessagePort(aSupport);
   mp->Initialize(aUUID, aDestinationUUID, 1 /* 0 is an invalid sequence ID */,
                  false /* Neutered */, eStateUnshippedEntangled, aRv);
   return mp.forget();
 }
 
 /* static */ already_AddRefed<MessagePort>
-MessagePort::Create(nsPIDOMWindowInner* aWindow,
+MessagePort::Create(nsISupports* aSupport,
                     const MessagePortIdentifier& aIdentifier,
                     ErrorResult& aRv)
 {
-  RefPtr<MessagePort> mp = new MessagePort(aWindow);
+  RefPtr<MessagePort> mp = new MessagePort(aSupport);
   mp->Initialize(aIdentifier.uuid(), aIdentifier.destinationUuid(),
                  aIdentifier.sequenceId(), aIdentifier.neutered(),
                  eStateEntangling, aRv);
   return mp.forget();
 }
 
 void
 MessagePort::UnshippedEntangle(MessagePort* aEntangledPort)
--- a/dom/messagechannel/MessagePort.h
+++ b/dom/messagechannel/MessagePort.h
@@ -40,21 +40,21 @@ class MessagePort final : public DOMEven
 public:
   NS_DECL_NSIIPCBACKGROUNDCHILDCREATECALLBACK
   NS_DECL_NSIOBSERVER
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(MessagePort,
                                            DOMEventTargetHelper)
 
   static already_AddRefed<MessagePort>
-  Create(nsPIDOMWindowInner* aWindow, const nsID& aUUID,
+  Create(nsISupports* aSupport, const nsID& aUUID,
          const nsID& aDestinationUUID, ErrorResult& aRv);
 
   static already_AddRefed<MessagePort>
-  Create(nsPIDOMWindowInner* aWindow, const MessagePortIdentifier& aIdentifier,
+  Create(nsISupports* aSupport, const MessagePortIdentifier& aIdentifier,
          ErrorResult& aRv);
 
   // For IPC.
   static void
   ForceClose(const MessagePortIdentifier& aIdentifier);
 
   virtual JSObject*
   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
@@ -83,17 +83,17 @@ public:
   // These methods are useful for MessagePortChild
 
   void Entangled(nsTArray<MessagePortMessage>& aMessages);
   void MessagesReceived(nsTArray<MessagePortMessage>& aMessages);
   void StopSendingDataConfirmed();
   void Closed();
 
 private:
-  explicit MessagePort(nsPIDOMWindowInner* aWindow);
+  explicit MessagePort(nsISupports* nsISupports);
   ~MessagePort();
 
   enum State {
     // When a port is created by a MessageChannel it is entangled with the
     // other. They both run on the same thread, same event loop and the
     // messages are added to the queues without using PBackground actors.
     // When one of the port is shipped, the state is changed to
     // StateEntangling.
--- a/dom/messagechannel/PMessagePort.ipdl
+++ b/dom/messagechannel/PMessagePort.ipdl
@@ -1,28 +1,20 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 include protocol PBackground;
 include protocol PBlob;
 
-using struct nsID from "nsID.h";
+include DOMTypes;
 
 namespace mozilla {
 namespace dom {
 
-struct MessagePortIdentifier
-{
-  nsID uuid;
-  nsID destinationUuid;
-  uint32_t sequenceId;
-  bool neutered;
-};
-
 struct MessagePortMessage
 {
   MessagePortIdentifier[] transferredPorts;
   uint8_t[] data;
   PBlob[] blobs;
 };
 
 // This protocol is used for the MessageChannel/MessagePort API
--- a/dom/messagechannel/tests/chrome.ini
+++ b/dom/messagechannel/tests/chrome.ini
@@ -1,5 +1,12 @@
 [DEFAULT]
 support-files =
   iframe_messageChannel_chrome.html
+  mm_messageChannelParent.xul
+  mm_messageChannelParentNotRemote.xul
+  mm_messageChannelParent.js
+  mm_messageChannel.js
 
 [test_messageChannel.xul]
+[test_messageChannelWithMessageManager.xul]
+skip-if = os == 'android'
+[test_messageChannelWithMessageManagerNotRemote.xul]
new file mode 100644
--- /dev/null
+++ b/dom/messagechannel/tests/mm_messageChannel.js
@@ -0,0 +1,76 @@
+function debug(msg) {
+  dump("[mmMessageChannelChild]" + msg + "\n");
+}
+
+/**
+ * Preparation Test
+ */
+let port;
+let toString = Object.prototype.toString;
+
+(function prepare() {
+  debug("Script loaded.");
+  addTestReceiver();
+  sendAsyncMessage("mmMessagePort:finishScriptLoad", {}, {});
+})();
+
+function ok(condition, message) {
+  debug('condition: ' + condition  + ', ' + message + '\n');
+  if (!condition) {
+    sendAsyncMessage("mmMessagePort:fail", { message: message });
+    throw 'failed check: ' + message;
+  }
+}
+
+function is(a, b, message) {
+  ok(a===b, message);
+}
+
+
+/**
+ * Testing codes.
+ */
+function addTestReceiver() {
+  addMessageListener("BasicTest:PortCreated", basicTest);
+  addMessageListener("CloseTest:PortCreated", closeTest);
+  addMessageListener("EmptyTest:PortCreated", emptyTest);
+  addMessageListener("NotTransferableTest:PortCreated", notTransferableTest);
+}
+
+function basicTest(msg) {
+  port = msg.ports[0];
+  is(toString.call(port), "[object MessagePort]", "created MessagePort.");
+
+  port.onmessage = (msg) => {
+    is(msg.data, "BasicTest:StartTest", "Replied message is correct.");
+    port.postMessage("BasicTest:TestOK");
+  };
+
+  sendAsyncMessage("BasicTest:FinishPrepare", { message: "OK" });
+}
+
+function closeTest(msg) {
+  port = msg.ports[0];
+  is(toString.call(port), "[object MessagePort]", "created MessagePort.");
+
+  port.onmessage = (msg) => {
+    ok(msg.data,"CloseTest:StartTest", "Replied message is correct.");
+    port.postMessage("CloseTest:TestOK");
+  };
+
+  port.close();
+
+  sendAsyncMessage("CloseTest:FinishPrepare", { message: "OK"});
+}
+
+function emptyTest(msg) {
+  let portSize = msg.ports.length;
+  is(portSize, 0, "transfered port size is zero.");
+
+  sendAsyncMessage("EmptyTest:FinishPrepare", { message: "OK"});
+}
+
+function notTransferableTest(msg) {
+  sendAsyncMessage("NotTransferableTest:FinishPrepare", {message: "OK"});
+}
+
new file mode 100644
--- /dev/null
+++ b/dom/messagechannel/tests/mm_messageChannelParent.js
@@ -0,0 +1,143 @@
+Components.utils.import("resource://gre/modules/Services.jsm");
+let port;
+let mm;
+
+function info(message) {
+  return opener.wrappedJSObject.info(message);
+}
+
+function ok(condition, message) {
+  return opener.wrappedJSObject.ok(condition, message);
+}
+
+function is(v1, v2, message) {
+  return opener.wrappedJSObject.is(v1, v2, message);
+}
+
+function todo_is(v1, v2, message) {
+  return opener.wrappedJSObject.todo_is(v1, v2, message);
+}
+
+function finish() {
+  opener.setTimeout("done()", 0);
+  window.close();
+}
+
+function debug(msg) {
+  dump("[mmMessageChannelParent]" + msg + "\n");
+}
+
+let tests = [ basic_test,
+              close_test,
+              empty_transferable,
+              not_transferable];
+
+// Test Routine
+function run_tests() {
+  let test = tests.shift();
+  if (test === undefined) {
+    finish();
+    return;
+  }
+
+  test(function() {
+    setTimeout(run_tests,0);
+  });
+}
+
+// Basic communication test.
+function basic_test(finish) {
+  ok(mm, "basic_test");
+
+  let finishPrepare = (msg) => {
+    is(msg.data.message, "OK", "");
+    ok(port, "");
+    port.onmessage = (msg) => {
+      is(msg.data, "BasicTest:TestOK", "");
+      finish();
+    }
+    port.postMessage("BasicTest:StartTest");
+    mm.removeMessageListener("BasicTest:FinishPrepare", finishPrepare);
+  };
+
+  let channel = new MessageChannel();
+  port = channel.port2;
+  mm.addMessageListener("BasicTest:FinishPrepare", finishPrepare);
+  mm.sendAsyncMessage("BasicTest:PortCreated", {}, {}, undefined, [channel.port1]);
+}
+
+// Communicate with closed port.
+function close_test(finish) {
+  ok(mm, "close_test");
+
+  let finishPrepare = (msg) => {
+    is(msg.data.message, "OK", "");
+    ok(port, "");
+
+    port.onmessage = (msg) => {
+      ok(false, "Port is alive.");
+      finish();
+    }
+
+    port.postMessage("CloseTest:StartTest");
+    mm.removeMessageListener("CloseTest:FinishPrepare", finishPrepare);
+    finish();
+  }
+
+  let channel = new MessageChannel();
+  port = channel.port2;
+  mm.addMessageListener("CloseTest:FinishPrepare", finishPrepare);
+  mm.sendAsyncMessage("CloseTest:PortCreated", {}, {}, undefined, [channel.port1]);
+}
+
+// Empty transferable object
+function empty_transferable(finish) {
+  ok(mm, "empty_transferable");
+
+  let finishPrepare = (msg) => {
+    ok(true, "Same basic test.");
+    mm.removeMessageListener("EmptyTest:FinishPrepare", finishPrepare);
+    finish();
+  };
+
+  mm.addMessageListener("EmptyTest:FinishPrepare", finishPrepare);
+  mm.sendAsyncMessage("EmptyTest:PortCreated", {}, {}, undefined, []);
+}
+
+// Not transferable object.
+function not_transferable(finish) {
+  ok(mm, "not_transferable");
+
+  let finishPrepare = (msg) => {
+    ok(true, "Same basic test.");
+    finish();
+  }
+
+  mm.addMessageListener("NotTransferableTest:FinishPrepare", finishPrepare);
+  mm.sendAsyncMessage("NotTransferableTest:PortCreated", {}, {}, undefined, [""]);
+}
+
+/*
+ * Test preparation
+ */
+function finishLoad(msg) {
+  run_tests();
+}
+
+function prepare_test() {
+  debug("start run_tests()");
+  var node = document.getElementById('messagechannel_remote');
+  mm = node.messageManager;  //Services.ppmm.getChildAt(1);
+  ok(mm, "created MessageManager.")
+
+  mm.addMessageListener("mmMessagePort:finishScriptLoad", finishLoad);
+  mm.addMessageListener("mmMessagePort:fail", failed_test);
+  //mm.loadProcessScript("chrome://mochitests/content/chrome/dom/messagechannel/tests/mm_messageChannel.js", true);
+  mm.loadFrameScript("chrome://mochitests/content/chrome/dom/messagechannel/tests/mm_messageChannel.js", true);
+  ok(true, "Loaded");
+}
+
+function failed_test() {
+  debug("failed test in child process");
+  ok(false, "");
+}
new file mode 100644
--- /dev/null
+++ b/dom/messagechannel/tests/mm_messageChannelParent.xul
@@ -0,0 +1,12 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<window title="Test MessageChannel API with nsFrameMessageManager(bug 1174624)"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+        onload="prepare_test()">
+
+  <!-- test code goes here -->
+  <script type="application/javascript"
+          src="chrome://mochitests/content/chrome/dom/messagechannel/tests/mm_messageChannelParent.js"></script>
+  <browser type="content" src="about:blank" id="messagechannel_remote" remote="true"/>
+</window>
new file mode 100644
--- /dev/null
+++ b/dom/messagechannel/tests/mm_messageChannelParentNotRemote.xul
@@ -0,0 +1,12 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<window title="Test MessageChannel API with nsFrameMessageManager(bug 1174624)"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+        onload="prepare_test()">
+
+  <!-- test code goes here -->
+  <script type="application/javascript"
+          src="chrome://mochitests/content/chrome/dom/messagechannel/tests/mm_messageChannelParent.js"></script>
+  <browser type="content" src="about:blank" id="messagechannel_remote"/>
+</window>
new file mode 100644
--- /dev/null
+++ b/dom/messagechannel/tests/test_messageChannelWithMessageManager.xul
@@ -0,0 +1,28 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+                 type="text/css"?>
+<window title="Test MessageChannel API with nsFrameMessageManager(bug 1174624)"
+  xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+  <!-- test results are displayed in the html:body -->
+  <body xmlns="http://www.w3.org/1999/xhtml">
+  </body>
+
+  <!-- test code goes here -->
+  <script type="application/javascript"><![CDATA[
+
+  SimpleTest.waitForExplicitFinish();
+
+  function done() {
+    info("done called");
+    SimpleTest.finish();
+  }
+
+  addLoadEvent(function() {
+    window.open("mm_messageChannelParent.xul", "", "chrome");
+  });
+  ]]></script>
+</window>
new file mode 100644
--- /dev/null
+++ b/dom/messagechannel/tests/test_messageChannelWithMessageManagerNotRemote.xul
@@ -0,0 +1,28 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+                 type="text/css"?>
+<window title="Test MessageChannel API with nsFrameMessageManager(bug 1174624)"
+  xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+  <!-- test results are displayed in the html:body -->
+  <body xmlns="http://www.w3.org/1999/xhtml">
+  </body>
+
+  <!-- test code goes here -->
+  <script type="application/javascript"><![CDATA[
+
+  SimpleTest.waitForExplicitFinish();
+
+  function done() {
+    info("done called");
+    SimpleTest.finish();
+  }
+
+  addLoadEvent(function() {
+    window.open("mm_messageChannelParentNotRemote.xul", "", "chrome");
+  });
+  ]]></script>
+</window>
--- a/dom/notification/Notification.cpp
+++ b/dom/notification/Notification.cpp
@@ -1223,17 +1223,17 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(Notificat
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(Notification, DOMEventTargetHelper)
   tmp->mData.setUndefined();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(Notification, DOMEventTargetHelper)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(Notification, DOMEventTargetHelper)
-  NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mData);
+  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mData)
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_IMPL_ADDREF_INHERITED(Notification, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(Notification, DOMEventTargetHelper)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(Notification)
   NS_INTERFACE_MAP_ENTRY(nsIObserver)
   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
--- a/dom/promise/Promise.cpp
+++ b/dom/promise/Promise.cpp
@@ -411,17 +411,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResolveCallbacks)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRejectCallbacks)
 #endif // SPIDERMONKEY_PROMISE
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Promise)
 #ifndef SPIDERMONKEY_PROMISE
-  NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mResult)
+  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mResult)
   NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mAllocationStack)
   NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mRejectionStack)
   NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mFullfillmentStack)
   NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
 #else // SPIDERMONKEY_PROMISE
   NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mPromiseObj);
 #endif // SPIDERMONKEY_PROMISE
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
--- a/dom/vr/VRDevice.cpp
+++ b/dom/vr/VRDevice.cpp
@@ -346,17 +346,17 @@ HMDPositionVRDevice::GetState()
   RefPtr<VRPositionState> obj = new VRPositionState(mParent, state);
 
   return obj.forget();
 }
 
 already_AddRefed<VRPositionState>
 HMDPositionVRDevice::GetImmediateState()
 {
-  gfx::VRHMDSensorState state = mHMD->GetSensorState();
+  gfx::VRHMDSensorState state = mHMD->GetImmediateSensorState();
   RefPtr<VRPositionState> obj = new VRPositionState(mParent, state);
 
   return obj.forget();
 }
 
 void
 HMDPositionVRDevice::ResetSensor()
 {
--- a/dom/workers/ScriptLoader.cpp
+++ b/dom/workers/ScriptLoader.cpp
@@ -235,16 +235,20 @@ struct ScriptLoadInfo
   nsString mFullURL;
 
   // This promise is set only when the script is for a ServiceWorker but
   // it's not in the cache yet. The promise is resolved when the full body is
   // stored into the cache.  mCachePromise will be set to nullptr after
   // resolution.
   RefPtr<Promise> mCachePromise;
 
+  // The reader stream the cache entry should be filled from, for those cases
+  // when we're going to have an mCachePromise.
+  nsCOMPtr<nsIInputStream> mCacheReadStream;
+
   nsCOMPtr<nsIChannel> mChannel;
   char16_t* mScriptTextBuf;
   size_t mScriptTextLength;
 
   nsresult mLoadResult;
   bool mLoadingFinished;
   bool mExecutionScheduled;
   bool mExecutionResult;
@@ -529,17 +533,16 @@ class ScriptLoaderRunnable final : publi
   friend class CachePromiseHandler;
   friend class CacheScriptLoader;
   friend class LoaderListener;
 
   WorkerPrivate* mWorkerPrivate;
   nsCOMPtr<nsIEventTarget> mSyncLoopTarget;
   nsTArray<ScriptLoadInfo> mLoadInfos;
   RefPtr<CacheCreator> mCacheCreator;
-  nsCOMPtr<nsIInputStream> mReader;
   bool mIsMainScript;
   WorkerScriptType mWorkerScriptType;
   bool mCanceled;
   bool mCanceledMainThread;
   ErrorResult& mRv;
 
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
@@ -636,17 +639,20 @@ private:
     ScriptLoadInfo& loadInfo = mLoadInfos[aIndex];
 
     nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
     MOZ_ASSERT(channel == loadInfo.mChannel);
 
     // We synthesize the result code, but its never exposed to content.
     RefPtr<mozilla::dom::InternalResponse> ir =
       new mozilla::dom::InternalResponse(200, NS_LITERAL_CSTRING("OK"));
-    ir->SetBody(mReader);
+    ir->SetBody(loadInfo.mCacheReadStream);
+    // Drop our reference to the stream now that we've passed it along, so it
+    // doesn't hang around once the cache is done with it and keep data alive.
+    loadInfo.mCacheReadStream = nullptr;
 
     // Set the channel info of the channel on the response so that it's
     // saved in the cache.
     ir->InitChannelInfo(channel);
 
     // Save the principal of the channel since its URI encodes the script URI
     // rather than the ServiceWorkerRegistrationInfo URI.
     nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
@@ -912,17 +918,18 @@ private:
         return rv;
       }
     } else {
       nsCOMPtr<nsIOutputStream> writer;
 
       // In case we return early.
       loadInfo.mCacheStatus = ScriptLoadInfo::Cancel;
 
-      rv = NS_NewPipe(getter_AddRefs(mReader), getter_AddRefs(writer), 0,
+      rv = NS_NewPipe(getter_AddRefs(loadInfo.mCacheReadStream),
+                      getter_AddRefs(writer), 0,
                       UINT32_MAX, // unlimited size to avoid writer WOULD_BLOCK case
                       true, false); // non-blocking reader, blocking writer
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
 
       nsCOMPtr<nsIStreamListenerTee> tee =
         do_CreateInstance(NS_STREAMLISTENERTEE_CONTRACTID);
--- a/dom/workers/ServiceWorkerEvents.cpp
+++ b/dom/workers/ServiceWorkerEvents.cpp
@@ -1242,17 +1242,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ExtendableMessageEvent, Event)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mClient)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mServiceWorker)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMessagePort)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPorts)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(ExtendableMessageEvent, Event)
-  NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mData)
+  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mData)
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(ExtendableMessageEvent)
 NS_INTERFACE_MAP_END_INHERITING(Event)
 
 NS_IMPL_ADDREF_INHERITED(ExtendableMessageEvent, Event)
 NS_IMPL_RELEASE_INHERITED(ExtendableMessageEvent, Event)
 
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -2422,29 +2422,32 @@ ServiceWorkerManager::GetActiveWorkerInf
 }
 
 class ServiceWorkerUnregisterJob final : public ServiceWorkerJob
 {
   RefPtr<ServiceWorkerRegistrationInfo> mRegistration;
   const nsCString mScope;
   nsCOMPtr<nsIServiceWorkerUnregisterCallback> mCallback;
   nsCOMPtr<nsIPrincipal> mPrincipal;
+  const bool mSendToParent;
 
   ~ServiceWorkerUnregisterJob()
   {}
 
 public:
   ServiceWorkerUnregisterJob(ServiceWorkerJobQueue* aQueue,
                              const nsACString& aScope,
                              nsIServiceWorkerUnregisterCallback* aCallback,
-                             nsIPrincipal* aPrincipal)
+                             nsIPrincipal* aPrincipal,
+                             bool aSendToParent = true)
     : ServiceWorkerJob(aQueue, Type::UnregisterJob)
     , mScope(aScope)
     , mCallback(aCallback)
     , mPrincipal(aPrincipal)
+    , mSendToParent(aSendToParent)
   {
     AssertIsOnMainThread();
   }
 
   void
   Start() override
   {
     AssertIsOnMainThread();
@@ -2467,26 +2470,16 @@ private:
     PrincipalInfo principalInfo;
     if (NS_WARN_IF(NS_FAILED(PrincipalToPrincipalInfo(mPrincipal,
                                                       &principalInfo)))) {
       return mCallback ? mCallback->UnregisterSucceeded(false) : NS_OK;
     }
 
     RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
 
-    // Note, we send the message to remove the registration from disk now even
-    // though we may only set the mPendingUninstall flag below.  This is
-    // necessary to ensure the registration is removed if the controlled
-    // clients are closed by shutting down the browser.  If the registration
-    // is resurrected by clearing mPendingUninstall then it should be saved
-    // to disk again.
-    if (swm->mActor) {
-      swm->mActor->SendUnregister(principalInfo, NS_ConvertUTF8toUTF16(mScope));
-    }
-
     nsAutoCString scopeKey;
     nsresult rv = swm->PrincipalToScopeKey(mPrincipal, scopeKey);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return mCallback ? mCallback->UnregisterSucceeded(false) : NS_OK;
     }
 
     // "Let registration be the result of running [[Get Registration]]
     // algorithm passing scope as the argument."
@@ -2499,33 +2492,42 @@ private:
     RefPtr<ServiceWorkerRegistrationInfo> registration;
     if (!data->mInfos.Get(mScope, getter_AddRefs(registration))) {
       // "If registration is null, then, resolve promise with false."
       return mCallback ? mCallback->UnregisterSucceeded(false) : NS_OK;
     }
 
     MOZ_ASSERT(registration);
 
+    // Note, we send the message to remove the registration from disk now even
+    // though we may only set the mPendingUninstall flag below.  This is
+    // necessary to ensure the registration is removed if the controlled
+    // clients are closed by shutting down the browser.  If the registration
+    // is resurrected by clearing mPendingUninstall then it should be saved
+    // to disk again.
+    if (mSendToParent && !registration->mPendingUninstall && swm->mActor) {
+      swm->mActor->SendUnregister(principalInfo, NS_ConvertUTF8toUTF16(mScope));
+    }
+
     // "Set registration's uninstalling flag."
     registration->mPendingUninstall = true;
     // "Resolve promise with true"
     rv = mCallback ? mCallback->UnregisterSucceeded(true) : NS_OK;
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     // "If no service worker client is using registration..."
     if (!registration->IsControllingDocuments()) {
       // "If registration's uninstalling flag is set.."
       if (!registration->mPendingUninstall) {
         return NS_OK;
       }
 
       // "Invoke [[Clear Registration]]..."
-      registration->Clear();
       swm->RemoveRegistration(registration);
     }
 
     return NS_OK;
   }
 
   // The unregister job is done irrespective of success or failure of any sort.
   void
@@ -2577,16 +2579,57 @@ ServiceWorkerManager::Unregister(nsIPrin
     queue->Append(job);
     return NS_OK;
   }
 
   AppendPendingOperation(queue, job);
   return NS_OK;
 }
 
+nsresult
+ServiceWorkerManager::NotifyUnregister(nsIPrincipal* aPrincipal,
+                                       const nsAString& aScope)
+{
+  AssertIsOnMainThread();
+  MOZ_ASSERT(aPrincipal);
+
+  nsresult rv;
+
+// This is not accessible by content, and callers should always ensure scope is
+// a correct URI, so this is wrapped in DEBUG
+#ifdef DEBUG
+  nsCOMPtr<nsIURI> scopeURI;
+  rv = NS_NewURI(getter_AddRefs(scopeURI), aScope, nullptr, nullptr);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+#endif
+
+  nsAutoCString originSuffix;
+  rv = PrincipalToScopeKey(aPrincipal, originSuffix);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  NS_ConvertUTF16toUTF8 scope(aScope);
+  ServiceWorkerJobQueue* queue = GetOrCreateJobQueue(originSuffix, scope);
+  MOZ_ASSERT(queue);
+
+  RefPtr<ServiceWorkerUnregisterJob> job =
+    new ServiceWorkerUnregisterJob(queue, scope, nullptr, aPrincipal, false);
+
+  if (mActor) {
+    queue->Append(job);
+    return NS_OK;
+  }
+
+  AppendPendingOperation(queue, job);
+  return NS_OK;
+}
+
 ServiceWorkerJobQueue*
 ServiceWorkerManager::GetOrCreateJobQueue(const nsACString& aKey,
                                           const nsACString& aScope)
 {
   ServiceWorkerManager::RegistrationDataPerPrincipal* data;
   if (!mRegistrationInfos.Get(aKey, &data)) {
     data = new RegistrationDataPerPrincipal();
     mRegistrationInfos.Put(aKey, data);
@@ -3352,17 +3395,16 @@ ServiceWorkerManager::StartControllingAD
 }
 
 void
 ServiceWorkerManager::StopControllingADocument(ServiceWorkerRegistrationInfo* aRegistration)
 {
   aRegistration->StopControllingADocument();
   if (!aRegistration->IsControllingDocuments()) {
     if (aRegistration->mPendingUninstall) {
-      aRegistration->Clear();
       RemoveRegistration(aRegistration);
     } else {
       // If the registration has an active worker that is running
       // this might be a good time to stop it.
       if (aRegistration->mActiveWorker) {
         ServiceWorkerPrivate* serviceWorkerPrivate =
           aRegistration->mActiveWorker->WorkerPrivate();
         serviceWorkerPrivate->NoteStoppedControllingDocuments();
@@ -4253,56 +4295,45 @@ ServiceWorkerManager::CreateNewRegistrat
 }
 
 void
 ServiceWorkerManager::MaybeRemoveRegistration(ServiceWorkerRegistrationInfo* aRegistration)
 {
   MOZ_ASSERT(aRegistration);
   RefPtr<ServiceWorkerInfo> newest = aRegistration->Newest();
   if (!newest && HasScope(aRegistration->mPrincipal, aRegistration->mScope)) {
-    aRegistration->Clear();
     RemoveRegistration(aRegistration);
   }
 }
 
 void
-ServiceWorkerManager::RemoveRegistrationInternal(ServiceWorkerRegistrationInfo* aRegistration)
-{
-  MOZ_ASSERT(aRegistration);
-  MOZ_ASSERT(!aRegistration->IsControllingDocuments());
-
-  if (mShuttingDown) {
-    return;
-  }
-
-  // All callers should be either from a job in which case the actor is
-  // available, or from MaybeStopControlling(), in which case, this will only be
-  // called if a valid registration is found. If a valid registration exists,
-  // it means the actor is available since the original map of registrations is
-  // populated by it, and any new registrations wait until the actor is
-  // available before proceeding (See ServiceWorkerRegisterJob::Start).
-  MOZ_ASSERT(mActor);
-
-  PrincipalInfo principalInfo;
-  if (NS_WARN_IF(NS_FAILED(PrincipalToPrincipalInfo(aRegistration->mPrincipal,
-                                                    &principalInfo)))) {
-    //XXXnsm I can't think of any other reason a stored principal would fail to
-    //convert.
-    NS_WARNING("Unable to unregister serviceworker due to possible OOM");
-    return;
-  }
-
-  mActor->SendUnregister(principalInfo, NS_ConvertUTF8toUTF16(aRegistration->mScope));
-}
-
-void
 ServiceWorkerManager::RemoveRegistration(ServiceWorkerRegistrationInfo* aRegistration)
 {
-  RemoveRegistrationInternal(aRegistration);
+  // Note, we do not need to call mActor->SendUnregister() here.  There are a few
+  // ways we can get here:
+  // 1) Through a normal unregister which calls SendUnregister() in the unregister
+  //    job Start() method.
+  // 2) Through origin storage being purged.  These result in ForceUnregister()
+  //    starting unregister jobs which in turn call SendUnregister().
+  // 3) Through the failure to install a new service worker.  Since we don't store
+  //    the registration until install succeeds, we do not need to call
+  //    SendUnregister here.
+  // Assert these conditions by testing for pending uninstall (cases 1 and 2) or
+  // null workers (case 3).
+#ifdef DEBUG
+  RefPtr<ServiceWorkerInfo> newest = aRegistration->Newest();
+  MOZ_ASSERT(aRegistration->mPendingUninstall || !newest);
+#endif
+
   MOZ_ASSERT(HasScope(aRegistration->mPrincipal, aRegistration->mScope));
+
+  // When a registration is removed, we must clear its contents since the DOM
+  // object may be held by content script.
+  aRegistration->Clear();
+
   RemoveScopeAndRegistration(aRegistration);
 }
 
 namespace {
 /**
  * See browser/components/sessionstore/Utils.jsm function hasRootDomain().
  *
  * Returns true if the |url| passed in is part of the given root |domain|.
--- a/dom/workers/ServiceWorkerManager.h
+++ b/dom/workers/ServiceWorkerManager.h
@@ -494,16 +494,19 @@ public:
   void
   MaybeCheckNavigationUpdate(nsIDocument* aDoc);
 
   nsresult
   SendPushEvent(const nsACString& aOriginAttributes,
                 const nsACString& aScope,
                 Maybe<nsTArray<uint8_t>> aData);
 
+  nsresult
+  NotifyUnregister(nsIPrincipal* aPrincipal, const nsAString& aScope);
+
 private:
   ServiceWorkerManager();
   ~ServiceWorkerManager();
 
   void
   Init();
 
   ServiceWorkerJobQueue*
@@ -633,24 +636,16 @@ private:
     return !!mActor;
   }
 
   nsClassHashtable<nsISupportsHashKey, PendingReadyPromise> mPendingReadyPromises;
 
   void
   MaybeRemoveRegistration(ServiceWorkerRegistrationInfo* aRegistration);
 
-  // Does all cleanup except removing the registration from
-  // mServiceWorkerRegistrationInfos. This is useful when we clear
-  // registrations via remove()/removeAll() since we are iterating over the
-  // hashtable and can cleanly remove within the hashtable enumeration
-  // function.
-  void
-  RemoveRegistrationInternal(ServiceWorkerRegistrationInfo* aRegistration);
-
   // Removes all service worker registrations that matches the given pattern.
   void
   RemoveAllRegistrations(OriginAttributesPattern* aPattern);
 
   RefPtr<ServiceWorkerManagerChild> mActor;
 
   struct PendingOperation;
   nsTArray<PendingOperation> mPendingOperations;
--- a/dom/workers/ServiceWorkerManagerChild.cpp
+++ b/dom/workers/ServiceWorkerManagerChild.cpp
@@ -57,17 +57,17 @@ ServiceWorkerManagerChild::RecvNotifyUnr
   RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
   MOZ_ASSERT(swm);
 
   nsCOMPtr<nsIPrincipal> principal = PrincipalInfoToPrincipal(aPrincipalInfo);
   if (NS_WARN_IF(!principal)) {
     return true;
   }
 
-  nsresult rv = swm->Unregister(principal, nullptr, aScope);
+  nsresult rv = swm->NotifyUnregister(principal, aScope);
   Unused << NS_WARN_IF(NS_FAILED(rv));
   return true;
 }
 
 bool
 ServiceWorkerManagerChild::RecvNotifyRemove(const nsCString& aHost)
 {
   if (mShuttingDown) {
--- a/dom/workers/ServiceWorkerManagerParent.cpp
+++ b/dom/workers/ServiceWorkerManagerParent.cpp
@@ -68,19 +68,21 @@ private:
   ServiceWorkerRegistrationData mData;
   const uint64_t mParentID;
 };
 
 class UnregisterServiceWorkerCallback final : public nsRunnable
 {
 public:
   UnregisterServiceWorkerCallback(const PrincipalInfo& aPrincipalInfo,
-                                  const nsString& aScope)
+                                  const nsString& aScope,
+                                  uint64_t aParentID)
     : mPrincipalInfo(aPrincipalInfo)
     , mScope(aScope)
+    , mParentID(aParentID)
   {
     AssertIsInMainProcess();
     AssertIsOnBackgroundThread();
   }
 
   NS_IMETHODIMP
   Run()
   {
@@ -88,22 +90,31 @@ public:
     AssertIsOnBackgroundThread();
 
     RefPtr<dom::ServiceWorkerRegistrar> service =
       dom::ServiceWorkerRegistrar::Get();
     MOZ_ASSERT(service);
 
     service->UnregisterServiceWorker(mPrincipalInfo,
                                      NS_ConvertUTF16toUTF8(mScope));
+
+    RefPtr<ServiceWorkerManagerService> managerService =
+      ServiceWorkerManagerService::Get();
+    if (managerService) {
+      managerService->PropagateUnregister(mParentID, mPrincipalInfo,
+                                          mScope);
+    }
+
     return NS_OK;
   }
 
 private:
   const PrincipalInfo mPrincipalInfo;
   nsString mScope;
+  uint64_t mParentID;
 };
 
 class CheckPrincipalWithCallbackRunnable final : public nsRunnable
 {
 public:
   CheckPrincipalWithCallbackRunnable(already_AddRefed<ContentParent> aParent,
                                      const PrincipalInfo& aPrincipalInfo,
                                      nsRunnable* aCallback)
@@ -217,17 +228,17 @@ ServiceWorkerManagerParent::RecvUnregist
   // Basic validation.
   if (aScope.IsEmpty() ||
       aPrincipalInfo.type() == PrincipalInfo::TNullPrincipalInfo ||
       aPrincipalInfo.type() == PrincipalInfo::TSystemPrincipalInfo) {
     return false;
   }
 
   RefPtr<UnregisterServiceWorkerCallback> callback =
-    new UnregisterServiceWorkerCallback(aPrincipalInfo, aScope);
+    new UnregisterServiceWorkerCallback(aPrincipalInfo, aScope, mID);
 
   RefPtr<ContentParent> parent =
     BackgroundParent::GetContentParent(Manager());
 
   // If the ContentParent is null we are dealing with a same-process actor.
   if (!parent) {
     callback->Run();
     return true;
--- a/dom/workers/ServiceWorkerMessageEvent.cpp
+++ b/dom/workers/ServiceWorkerMessageEvent.cpp
@@ -28,17 +28,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ServiceWorkerMessageEvent, Event)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mServiceWorker)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMessagePort)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPorts)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(ServiceWorkerMessageEvent, Event)
-  NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mData)
+  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mData)
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(ServiceWorkerMessageEvent)
 NS_INTERFACE_MAP_END_INHERITING(Event)
 
 NS_IMPL_ADDREF_INHERITED(ServiceWorkerMessageEvent, Event)
 NS_IMPL_RELEASE_INHERITED(ServiceWorkerMessageEvent, Event)
 
--- a/dom/workers/XMLHttpRequest.cpp
+++ b/dom/workers/XMLHttpRequest.cpp
@@ -1603,17 +1603,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_IN
                                                 nsXHREventTarget)
   tmp->ReleaseProxy(XHRIsGoingAway);
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mUpload)
   tmp->mStateData.mResponse.setUndefined();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(XMLHttpRequest,
                                                nsXHREventTarget)
-  NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mStateData.mResponse)
+  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mStateData.mResponse)
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 JSObject*
 XMLHttpRequest::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return XMLHttpRequestBinding_workers::Wrap(aCx, this, aGivenProto);
 }
 
--- a/dom/workers/test/serviceworkers/importscript_worker.js
+++ b/dom/workers/test/serviceworkers/importscript_worker.js
@@ -1,28 +1,36 @@
 var counter = 0;
 function callByScript() {
   ++counter;
 }
 
-importScripts(['importscript.sjs']);
-importScripts(['importscript.sjs']);
+// Use multiple scripts in this load to verify we support that case correctly.
+// See bug 1249351 for a case where we broke this.
+importScripts('lorem_script.js', 'importscript.sjs');
 
+importScripts('importscript.sjs');
+
+var missingScriptFailed = false;
 try {
   importScripts(['there-is-nothing-here.js']);
-} catch (ex) {
-  // Importing a non-existent script should not abort the SW load, bug 1198982.
+} catch(e) {
+  missingScriptFailed = true;
 }
 
 onmessage = function(e) {
   self.clients.matchAll().then(function(res) {
     if (!res.length) {
       dump("ERROR: no clients are currently controlled.\n");
     }
 
+    if (!missingScriptFailed) {
+      res[0].postMessage("KO");
+    }
+
     try {
       importScripts(['importscript.sjs']);
       res[0].postMessage("KO");
       return;
     } catch(e) {}
 
     res[0].postMessage(counter == 2 ? "OK" : "KO");
   });
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/lorem_script.js
@@ -0,0 +1,8 @@
+var lorem_str = `
+Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor
+incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis
+nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
+consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum
+dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident,
+sunt in culpa qui officia deserunt mollit anim id est laborum.
+`
--- a/dom/workers/test/serviceworkers/mochitest.ini
+++ b/dom/workers/test/serviceworkers/mochitest.ini
@@ -192,16 +192,17 @@ support-files =
   redirect_post.sjs
   xslt_worker.js
   xslt/*
   unresolved_fetch_worker.js
   header_checker.sjs
   openWindow_worker.js
   redirect.sjs
   open_window/client.html
+  lorem_script.js
 
 [test_bug1151916.html]
 skip-if = e10s && debug && os == 'win'
 [test_claim.html]
 skip-if = e10s && debug && os == 'win'
 [test_claim_fetch.html]
 skip-if = e10s && debug && os == 'win'
 [test_claim_oninstall.html]
--- a/gfx/layers/ipc/GonkNativeHandleUtils.cpp
+++ b/gfx/layers/ipc/GonkNativeHandleUtils.cpp
@@ -57,12 +57,15 @@ ParamTraits<GonkNativeHandle>::Read(cons
   for (size_t i = 0; i < static_cast<size_t>(nativeHandle->numFds); ++i) {
     base::FileDescriptor fd;
     if (!aMsg->ReadFileDescriptor(aIter, &fd)) {
       return false;
     }
     nativeHandle->data[i] = fd.fd;
   }
 
+  GonkNativeHandle handle(new GonkNativeHandle::NhObj(nativeHandle));
+  handle.TransferToAnother(*aResult);
+
   return true;
 }
 
 } // namespace IPC
--- a/gfx/skia/skia/include/private/SkTLogic.h
+++ b/gfx/skia/skia/include/private/SkTLogic.h
@@ -27,20 +27,33 @@
 #ifdef MOZ_SKIA
 #include "mozilla/Move.h"
 #include "mozilla/TypeTraits.h"
 
 #if SKIA_IMPLEMENTATION
 #include "mozilla/Function.h"
 #endif
 
-namespace std {
+// In libc++, symbols such as std::forward may be defined in std::__1.
+// The _LIBCPP_BEGIN_NAMESPACE_STD and _LIBCPP_END_NAMESPACE_STD macros
+// will expand to the correct namespace.
+#ifdef _LIBCPP_BEGIN_NAMESPACE_STD
+#define MOZ_BEGIN_STD_NAMESPACE _LIBCPP_BEGIN_NAMESPACE_STD
+#define MOZ_END_STD_NAMESPACE _LIBCPP_END_NAMESPACE_STD
+#else
+#define MOZ_BEGIN_STD_NAMESPACE namespace std {
+#define MOZ_END_STD_NAMESPACE }
+#endif
+
+MOZ_BEGIN_STD_NAMESPACE
     using mozilla::Forward;
     #define forward Forward
+MOZ_END_STD_NAMESPACE
 
+namespace std {
 #if SKIA_IMPLEMENTATION
     using mozilla::IntegralConstant;
     using mozilla::IsEmpty;
     using mozilla::FalseType;
     using mozilla::TrueType;
     #define integral_constant IntegralConstant
     #define is_empty IsEmpty
     #define false_type FalseType
--- a/gfx/skia/skia/include/private/SkUniquePtr.h
+++ b/gfx/skia/skia/include/private/SkUniquePtr.h
@@ -10,16 +10,21 @@
 
 #include "SkTLogic.h"
 #include <cstddef>
 #include <utility>
 
 #ifdef MOZ_SKIA
 #include "mozilla/UniquePtr.h"
 
+namespace std {
+    using mozilla::DefaultDelete;
+    using mozilla::UniquePtr;
+}
+
 namespace skstd {
     using mozilla::DefaultDelete;
     using mozilla::UniquePtr;
     #define default_delete DefaultDelete
     #define unique_ptr UniquePtr
 }
 #else
 namespace skstd {
--- a/gfx/thebes/gfxPlatformMac.cpp
+++ b/gfx/thebes/gfxPlatformMac.cpp
@@ -425,17 +425,18 @@ gfxPlatformMac::ReadAntiAliasingThreshol
 
     return threshold;
 }
 
 bool
 gfxPlatformMac::UseAcceleratedSkiaCanvas()
 {
   // Lion or later is required
-  return nsCocoaFeatures::OnLionOrLater() && gfxPlatform::UseAcceleratedSkiaCanvas();
+  // Bug 1249659 - Lion has some gfx issues so disabled on lion and earlier
+  return nsCocoaFeatures::OnMountainLionOrLater() && gfxPlatform::UseAcceleratedSkiaCanvas();
 }
 
 bool
 gfxPlatformMac::UseProgressivePaint()
 {
   // Progressive painting requires cross-process mutexes, which don't work so
   // well on OS X 10.6 so we disable there.
   return nsCocoaFeatures::OnLionOrLater() && gfxPlatform::UseProgressivePaint();
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -194,16 +194,17 @@ private:
 
   DECL_GFX_PREF(Live, "dom.ipc.plugins.asyncdrawing.enabled",  PluginAsyncDrawingEnabled, bool, false);
   DECL_GFX_PREF(Live, "dom.meta-viewport.enabled",             MetaViewportEnabled, bool, false);
   DECL_GFX_PREF(Once, "dom.vr.enabled",                        VREnabled, bool, false);
   DECL_GFX_PREF(Once, "dom.vr.oculus.enabled",                 VROculusEnabled, bool, true);
   DECL_GFX_PREF(Once, "dom.vr.oculus050.enabled",              VROculus050Enabled, bool, true);
   DECL_GFX_PREF(Once, "dom.vr.cardboard.enabled",              VRCardboardEnabled, bool, false);
   DECL_GFX_PREF(Once, "dom.vr.add-test-devices",               VRAddTestDevices, int32_t, 1);
+  DECL_GFX_PREF(Live, "dom.vr.poseprediction.enabled",         VRPosePredictionEnabled, bool, false);
   DECL_GFX_PREF(Live, "dom.w3c_pointer_events.enabled",        PointerEventsEnabled, bool, false);
   DECL_GFX_PREF(Live, "dom.w3c_touch_events.enabled",          TouchEventsEnabled, int32_t, 0);
 
   DECL_GFX_PREF(Live, "general.smoothScroll",                  SmoothScrollEnabled, bool, true);
   DECL_GFX_PREF(Live, "general.smoothScroll.currentVelocityWeighting",
                 SmoothScrollCurrentVelocityWeighting, float, 0.25);
   DECL_GFX_PREF(Live, "general.smoothScroll.durationToIntervalRatio",
                 SmoothScrollDurationToIntervalRatio, int32_t, 200);
--- a/gfx/vr/VRDeviceProxy.cpp
+++ b/gfx/vr/VRDeviceProxy.cpp
@@ -74,23 +74,31 @@ VRDeviceProxy::SetFOV(const VRFieldOfVie
 void
 VRDeviceProxy::ZeroSensor()
 {
   VRManagerChild *vm = VRManagerChild::Get();
   vm->SendResetSensor(mDeviceInfo.mDeviceID);
 }
 
 VRHMDSensorState
-VRDeviceProxy::GetSensorState(double timeOffset)
+VRDeviceProxy::GetSensorState()
 {
   VRManagerChild *vm = VRManagerChild::Get();
   Unused << vm->SendKeepSensorTracking(mDeviceInfo.mDeviceID);
   return mSensorState;
 }
 
+VRHMDSensorState
+VRDeviceProxy::GetImmediateSensorState()
+{
+  // XXX TODO - Need to perform IPC call to get the current sensor
+  // state rather than the predictive state used for the frame rendering.
+  return GetSensorState();
+}
+
 void
 VRDeviceProxy::UpdateSensorState(const VRHMDSensorState& aSensorState)
 {
   mSensorState = aSensorState;
 }
 
 // Dummy nsIScreen implementation, for when we just need to specify a size
 class FakeScreen : public nsIScreen
--- a/gfx/vr/VRDeviceProxy.h
+++ b/gfx/vr/VRDeviceProxy.h
@@ -23,17 +23,18 @@ public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VRDeviceProxy)
 
   explicit VRDeviceProxy(const VRDeviceUpdate& aDeviceUpdate);
 
   void UpdateDeviceInfo(const VRDeviceUpdate& aDeviceUpdate);
   void UpdateSensorState(const VRHMDSensorState& aSensorState);
 
   const VRDeviceInfo& GetDeviceInfo() const { return mDeviceInfo; }
-  virtual VRHMDSensorState GetSensorState(double timeOffset = 0.0);
+  virtual VRHMDSensorState GetSensorState();
+  virtual VRHMDSensorState GetImmediateSensorState();
 
   bool SetFOV(const VRFieldOfView& aFOVLeft, const VRFieldOfView& aFOVRight,
               double zNear, double zFar);
 
   virtual void ZeroSensor();
 
 
   // The nsIScreen that represents this device
--- a/gfx/vr/VRDeviceProxyOrientationFallBack.cpp
+++ b/gfx/vr/VRDeviceProxyOrientationFallBack.cpp
@@ -182,18 +182,25 @@ VRDeviceProxyOrientationFallBack::Comput
   mSensorState.orientation[3] = q.w;
 
   mSensorState.timestamp = mSavedLastSensorTime / 1000000.0;
 
   mNeedsSensorCompute = false;
 }
 
 VRHMDSensorState
-VRDeviceProxyOrientationFallBack::GetSensorState(double timeOffset)
+VRDeviceProxyOrientationFallBack::GetSensorState()
 {
   StartSensorTracking();
   ComputeStateFromLastSensor();
   return mSensorState;
 }
 
+VRHMDSensorState
+VRDeviceProxyOrientationFallBack::GetImmediateSensorState()
+{
+  // XXX TODO - Should return actual immediate sensor state
+  return GetSensorState();
+}
+
 } // namespace gfx
 } // namespace mozilla
 
--- a/gfx/vr/VRDeviceProxyOrientationFallBack.h
+++ b/gfx/vr/VRDeviceProxyOrientationFallBack.h
@@ -18,17 +18,18 @@ class VRDeviceProxyOrientationFallBack :
                                        , public hal::ISensorObserver
                                        , public hal::ScreenConfigurationObserver
 {
 public:
 
   explicit VRDeviceProxyOrientationFallBack(const VRDeviceUpdate& aDeviceUpdate);
 
   virtual void ZeroSensor() override;
-  virtual VRHMDSensorState GetSensorState(double timeOffset = 0.0) override;
+  virtual VRHMDSensorState GetSensorState() override;
+  virtual VRHMDSensorState GetImmediateSensorState() override;
 
   // ISensorObserver interface
   void Notify(const hal::SensorData& SensorData) override;
   // ScreenConfigurationObserver interface
   void Notify(const hal::ScreenConfiguration& ScreenConfiguration) override;
 
 
 protected:
--- a/gfx/vr/gfxVR.h
+++ b/gfx/vr/gfxVR.h
@@ -259,17 +259,18 @@ public:
   const VRDeviceInfo& GetDeviceInfo() const { return mDeviceInfo; }
 
   /* set the FOV for this HMD unit; this triggers a computation of all the remaining bits.  Returns false if it fails */
   virtual bool SetFOV(const VRFieldOfView& aFOVLeft, const VRFieldOfView& aFOVRight,
                       double zNear, double zFar) = 0;
 
   virtual bool KeepSensorTracking() = 0;
   virtual void NotifyVsync(const TimeStamp& aVsyncTimestamp) = 0;
-  virtual VRHMDSensorState GetSensorState(double timeOffset = 0.0) = 0;
+  virtual VRHMDSensorState GetSensorState() = 0;
+  virtual VRHMDSensorState GetImmediateSensorState() = 0;
 
   virtual void ZeroSensor() = 0;
 
   // if rendering is offloaded
   virtual VRHMDRenderingSupport *GetRenderingSupport() { return nullptr; }
 
   // distortion mesh stuff; we should implement renderingsupport for this
   virtual void FillDistortionConstants(uint32_t whichEye,
--- a/gfx/vr/gfxVRCardboard.cpp
+++ b/gfx/vr/gfxVRCardboard.cpp
@@ -46,25 +46,32 @@ HMDInfoCardboard::HMDInfoCardboard()
   mDeviceInfo.mScreenRect.y = 0;
   mDeviceInfo.mScreenRect.width = 1920;
   mDeviceInfo.mScreenRect.height = 1080;
   mDeviceInfo.mIsFakeScreen = true;
 }
 
 
 VRHMDSensorState
-HMDInfoCardboard::GetSensorState(double timeOffset)
+HMDInfoCardboard::GetSensorState()
 {
   // Actual sensor state is calculated on the main thread,
   // within VRDeviceProxyOrientationFallBack
   VRHMDSensorState result;
   result.Clear();
   return result;
 }
 
+VRHMDSensorState
+HMDInfoCardboard::GetImmediateSensorState()
+{
+  return GetSensorState();
+}
+
+
 void
 HMDInfoCardboard::ZeroSensor()
 {
   MOZ_ASSERT(0, "HMDInfoCardboard::ZeroSensor not implemented.  "
                 "Should use VRDeviceProxyOrientationFallBack on main thread");
 }
 
 
--- a/gfx/vr/gfxVRCardboard.h
+++ b/gfx/vr/gfxVRCardboard.h
@@ -20,17 +20,18 @@ class HMDInfoCardboard :
     public VRHMDInfo
 {
 public:
   explicit HMDInfoCardboard();
 
   bool SetFOV(const VRFieldOfView& aFOVLeft, const VRFieldOfView& aFOVRight,
               double zNear, double zFar) override;
 
-  VRHMDSensorState GetSensorState(double timeOffset) override;
+  virtual VRHMDSensorState GetSensorState() override;
+  virtual VRHMDSensorState GetImmediateSensorState() override;
   void ZeroSensor() override;
   bool KeepSensorTracking() override;
   void NotifyVsync(const TimeStamp& aVsyncTimestamp) override;
 
   void FillDistortionConstants(uint32_t whichEye,
                                const IntSize& textureSize, const IntRect& eyeViewport,
                                const Size& destViewport, const Rect& destRect,
                                VRDistortionConstants& values) override;
--- a/gfx/vr/gfxVROculus.cpp
+++ b/gfx/vr/gfxVROculus.cpp
@@ -36,16 +36,17 @@ static pfn_ovr_Shutdown ovr_Shutdown = n
 static pfn_ovr_GetTimeInSeconds ovr_GetTimeInSeconds = nullptr;
 static pfn_ovr_GetHmdDesc ovr_GetHmdDesc = nullptr;
 
 static pfn_ovr_Create ovr_Create = nullptr;
 static pfn_ovr_Destroy ovr_Destroy = nullptr;
 
 static pfn_ovr_RecenterPose ovr_RecenterPose = nullptr;
 static pfn_ovr_GetTrackingState ovr_GetTrackingState = nullptr;
+static pfn_ovr_GetPredictedDisplayTime ovr_GetPredictedDisplayTime = nullptr;
 static pfn_ovr_GetFovTextureSize ovr_GetFovTextureSize = nullptr;
 static pfn_ovr_GetRenderDesc ovr_GetRenderDesc = nullptr;
 
 static pfn_ovr_DestroySwapTextureSet ovr_DestroySwapTextureSet = nullptr;
 static pfn_ovr_SubmitFrame ovr_SubmitFrame = nullptr;
 
 #ifdef XP_WIN
 static pfn_ovr_CreateSwapTextureSetD3D11 ovr_CreateSwapTextureSetD3D11 = nullptr;
@@ -165,16 +166,17 @@ InitializeOculusCAPI()
   REQUIRE_FUNCTION(ovr_GetTimeInSeconds);
   REQUIRE_FUNCTION(ovr_GetHmdDesc);
   
   REQUIRE_FUNCTION(ovr_Create);
   REQUIRE_FUNCTION(ovr_Destroy);
   
   REQUIRE_FUNCTION(ovr_RecenterPose);
   REQUIRE_FUNCTION(ovr_GetTrackingState);
+  REQUIRE_FUNCTION(ovr_GetPredictedDisplayTime);
   REQUIRE_FUNCTION(ovr_GetFovTextureSize);
   REQUIRE_FUNCTION(ovr_GetRenderDesc);
 
   REQUIRE_FUNCTION(ovr_DestroySwapTextureSet);
   REQUIRE_FUNCTION(ovr_SubmitFrame);
 #ifdef XP_WIN
   REQUIRE_FUNCTION(ovr_CreateSwapTextureSetD3D11);
 #endif
@@ -194,36 +196,16 @@ InitializeOculusCAPI()
 // we're statically linked; it's available
 static bool InitializeOculusCAPI()
 {
   return true;
 }
 
 #endif
 
-static void
-do_CalcEyePoses(ovrPosef headPose,
-                const ovrVector3f hmdToEyeViewOffset[2],
-                ovrPosef outEyePoses[2])
-{
-  if (!hmdToEyeViewOffset || !outEyePoses)
-    return;
-
-  for (uint32_t i = 0; i < 2; ++i) {
-    gfx::Quaternion o(headPose.Orientation.x, headPose.Orientation.y, headPose.Orientation.z, headPose.Orientation.w);
-    Point3D vo(hmdToEyeViewOffset[i].x, hmdToEyeViewOffset[i].y, hmdToEyeViewOffset[i].z);
-    Point3D p = o.RotatePoint(vo);
-
-    outEyePoses[i].Orientation = headPose.Orientation;
-    outEyePoses[i].Position.x = p.x + headPose.Position.x;
-    outEyePoses[i].Position.y = p.y + headPose.Position.y;
-    outEyePoses[i].Position.z = p.z + headPose.Position.z;
-  }
-}
-
 ovrFovPort
 ToFovPort(const gfx::VRFieldOfView& aFOV)
 {
   ovrFovPort fovPort;
   fovPort.LeftTan = tan(aFOV.leftDegrees * M_PI / 180.0);
   fovPort.RightTan = tan(aFOV.rightDegrees * M_PI / 180.0);
   fovPort.UpTan = tan(aFOV.upDegrees * M_PI / 180.0);
   fovPort.DownTan = tan(aFOV.downDegrees * M_PI / 180.0);
@@ -275,16 +257,20 @@ HMDInfoOculus::HMDInfoOculus(ovrSession 
   uint32_t h = mDesc.Resolution.h;
   mDeviceInfo.mScreenRect.x = 0;
   mDeviceInfo.mScreenRect.y = 0;
   mDeviceInfo.mScreenRect.width = std::max(w, h);
   mDeviceInfo.mScreenRect.height = std::min(w, h);
   mDeviceInfo.mIsFakeScreen = true;
 
   SetFOV(mDeviceInfo.mRecommendedEyeFOV[VRDeviceInfo::Eye_Left], mDeviceInfo.mRecommendedEyeFOV[VRDeviceInfo::Eye_Right], 0.01, 10000.0);
+
+  for (int i = 0; i < kMaxLatencyFrames; i++) {
+    mLastSensorState[i].Clear();
+  }
 }
 
 void
 HMDInfoOculus::Destroy()
 {
   if (mSession) {
     ovr_Destroy(mSession);
     mSession = nullptr;
@@ -351,24 +337,42 @@ HMDInfoOculus::NotifyVsync(const mozilla
 
 void
 HMDInfoOculus::ZeroSensor()
 {
   ovr_RecenterPose(mSession);
 }
 
 VRHMDSensorState
+HMDInfoOculus::GetSensorState()
+{
+  VRHMDSensorState result;
+  double frameTiming = 0.0f;
+  if (gfxPrefs::VRPosePredictionEnabled()) {
+    frameTiming = ovr_GetPredictedDisplayTime(mSession, mInputFrameID);
+  }
+  result = GetSensorState(frameTiming);
+  result.inputFrameID = mInputFrameID;
+  mLastSensorState[mInputFrameID % kMaxLatencyFrames] = result;
+  return result;
+}
+
+VRHMDSensorState
+HMDInfoOculus::GetImmediateSensorState()
+{
+  return GetSensorState(0.0);
+}
+
+VRHMDSensorState
 HMDInfoOculus::GetSensorState(double timeOffset)
 {
   VRHMDSensorState result;
   result.Clear();
 
-  // XXX this is the wrong time base for timeOffset; we need to figure out how to synchronize
-  // the Oculus time base and the browser one.
-  ovrTrackingState state = ovr_GetTrackingState(mSession, ovr_GetTimeInSeconds() + timeOffset, true);
+  ovrTrackingState state = ovr_GetTrackingState(mSession, timeOffset, true);
   ovrPoseStatef& pose(state.HeadPose);
 
   result.timestamp = pose.TimeInSeconds;
 
   if (state.StatusFlags & ovrStatus_OrientationTracked) {
     result.flags |= VRStateValidFlags::State_Orientation;
 
     result.orientation[0] = pose.ThePose.Orientation.x;
@@ -395,18 +399,16 @@ HMDInfoOculus::GetSensorState(double tim
     result.linearVelocity[0] = pose.LinearVelocity.x;
     result.linearVelocity[1] = pose.LinearVelocity.y;
     result.linearVelocity[2] = pose.LinearVelocity.z;
 
     result.linearAcceleration[0] = pose.LinearAcceleration.x;
     result.linearAcceleration[1] = pose.LinearAcceleration.y;
     result.linearAcceleration[2] = pose.LinearAcceleration.z;
   }
-
-  mLastTrackingState = state;
   
   return result;
 }
 
 struct RenderTargetSetOculus : public VRHMDRenderingSupport::RenderTargetSet
 {
   RenderTargetSetOculus(const IntSize& aSize,
                         HMDInfoOculus *aHMD,
@@ -520,16 +522,23 @@ HMDInfoOculus::DestroyRenderTargetSet(Re
 
 void
 HMDInfoOculus::SubmitFrame(RenderTargetSet *aRTSet, int32_t aInputFrameID)
 {
   RenderTargetSetOculus *rts = static_cast<RenderTargetSetOculus*>(aRTSet);
   MOZ_ASSERT(rts->hmd != nullptr);
   MOZ_ASSERT(rts->textureSet != nullptr);
 
+  VRHMDSensorState sensorState = mLastSensorState[aInputFrameID % kMaxLatencyFrames];
+  // It is possible to get a cache miss on mLastSensorState if latency is
+  // longer than kMaxLatencyFrames.  An optimization would be to find a frame
+  // that is closer than the one selected with the modulus.
+  // If we hit this; however, latency is already so high that the site is
+  // un-viewable and a more accurate pose prediction is not likely to
+  // compensate.
   ovrLayerEyeFov layer;
   layer.Header.Type = ovrLayerType_EyeFov;
   layer.Header.Flags = 0;
   layer.ColorTexture[0] = rts->textureSet;
   layer.ColorTexture[1] = nullptr;
   layer.Fov[0] = mFOVPort[0];
   layer.Fov[1] = mFOVPort[1];
   layer.Viewport[0].Pos.x = 0;
@@ -540,17 +549,33 @@ HMDInfoOculus::SubmitFrame(RenderTargetS
   layer.Viewport[1].Pos.y = 0;
   layer.Viewport[1].Size.w = rts->size.width / 2;
   layer.Viewport[1].Size.h = rts->size.height;
 
   const Point3D& l = rts->hmd->mDeviceInfo.mEyeTranslation[0];
   const Point3D& r = rts->hmd->mDeviceInfo.mEyeTranslation[1];
   const ovrVector3f hmdToEyeViewOffset[2] = { { l.x, l.y, l.z },
                                               { r.x, r.y, r.z } };
-  do_CalcEyePoses(rts->hmd->mLastTrackingState.HeadPose.ThePose, hmdToEyeViewOffset, layer.RenderPose);
+
+  for (uint32_t i = 0; i < 2; ++i) {
+    gfx::Quaternion o(sensorState.orientation[0],
+                      sensorState.orientation[1],
+                      sensorState.orientation[2],
+                      sensorState.orientation[3]);
+    Point3D vo(hmdToEyeViewOffset[i].x, hmdToEyeViewOffset[i].y, hmdToEyeViewOffset[i].z);
+    Point3D p = o.RotatePoint(vo);
+
+    layer.RenderPose[i].Orientation.x = o.x;
+    layer.RenderPose[i].Orientation.y = o.y;
+    layer.RenderPose[i].Orientation.z = o.z;
+    layer.RenderPose[i].Orientation.w = o.w;
+    layer.RenderPose[i].Position.x = p.x + sensorState.position[0];
+    layer.RenderPose[i].Position.y = p.y + sensorState.position[1];
+    layer.RenderPose[i].Position.z = p.z + sensorState.position[2];
+  }
 
   ovrLayerHeader *layers = &layer.Header;
   ovrResult orv = ovr_SubmitFrame(mSession, aInputFrameID, nullptr, &layers, 1);
   //printf_stderr("Submitted frame %d, result: %d\n", rts->textureSet->CurrentIndex, orv);
   if (orv != ovrSuccess) {
     // not visible? failed?
   }
 }
--- a/gfx/vr/gfxVROculus.h
+++ b/gfx/vr/gfxVROculus.h
@@ -23,17 +23,18 @@ namespace impl {
 
 class HMDInfoOculus : public VRHMDInfo, public VRHMDRenderingSupport {
 public:
   explicit HMDInfoOculus(ovrSession aSession);
 
   bool SetFOV(const VRFieldOfView& aFOVLeft, const VRFieldOfView& aFOVRight,
               double zNear, double zFar) override;
 
-  VRHMDSensorState GetSensorState(double timeOffset) override;
+  virtual VRHMDSensorState GetSensorState() override;
+  virtual VRHMDSensorState GetImmediateSensorState() override;
   void ZeroSensor() override;
   bool KeepSensorTracking() override;
   void NotifyVsync(const TimeStamp& aVsyncTimestamp) override;
 
   void FillDistortionConstants(uint32_t whichEye,
                                const IntSize& textureSize, const IntRect& eyeViewport,
                                const Size& destViewport, const Rect& destRect,
                                VRDistortionConstants& values) override;
@@ -62,18 +63,29 @@ protected:
     float texG[2];
     float texB[2];
     float genericAttribs[4];
   };
 
   ovrSession mSession;
   ovrHmdDesc mDesc;
   ovrFovPort mFOVPort[2];
-  ovrTrackingState mLastTrackingState;
-  int mInputFrameID;
+
+  VRHMDSensorState GetSensorState(double timeOffset);
+
+  // The maximum number of frames of latency that we would expect before we
+  // should give up applying pose prediction.
+  // If latency is greater than one second, then the experience is not likely
+  // to be corrected by pose prediction.  Setting this value too
+  // high may result in unnecessary memory allocation.
+  // As the current fastest refresh rate is 90hz, 100 is selected as a
+  // conservative value.
+  static const int kMaxLatencyFrames = 100;
+  VRHMDSensorState mLastSensorState[kMaxLatencyFrames];
+  int32_t mInputFrameID;
 };
 
 } // namespace impl
 
 class VRHMDManagerOculus : public VRHMDManager
 {
 public:
   static already_AddRefed<VRHMDManagerOculus> Create();
--- a/gfx/vr/gfxVROculus050.cpp
+++ b/gfx/vr/gfxVROculus050.cpp
@@ -457,24 +457,30 @@ HMDInfoOculus050::StopSensorTracking()
 
 void
 HMDInfoOculus050::ZeroSensor()
 {
   ovrHmd_RecenterPose(mHMD);
 }
 
 VRHMDSensorState
-HMDInfoOculus050::GetSensorState(double timeOffset)
+HMDInfoOculus050::GetImmediateSensorState()
+{
+  return GetSensorState();
+}
+
+VRHMDSensorState
+HMDInfoOculus050::GetSensorState()
 {
   VRHMDSensorState result;
   result.Clear();
 
   // XXX this is the wrong time base for timeOffset; we need to figure out how to synchronize
   // the Oculus time base and the browser one.
-  ovrTrackingState state = ovrHmd_GetTrackingState(mHMD, ovr_GetTimeInSeconds() + timeOffset);
+  ovrTrackingState state = ovrHmd_GetTrackingState(mHMD, ovr_GetTimeInSeconds());
   ovrPoseStatef& pose(state.HeadPose);
 
   result.timestamp = pose.TimeInSeconds;
 
   if (state.StatusFlags & ovrStatus_OrientationTracked) {
     result.flags |= VRStateValidFlags::State_Orientation;
 
     result.orientation[0] = pose.ThePose.Orientation.x;
--- a/gfx/vr/gfxVROculus050.h
+++ b/gfx/vr/gfxVROculus050.h
@@ -21,17 +21,18 @@ namespace impl {
 
 class HMDInfoOculus050 : public VRHMDInfo {
 public:
   explicit HMDInfoOculus050(ovr050::ovrHmd aHMD, bool aDebug, int aDeviceID);
 
   bool SetFOV(const VRFieldOfView& aFOVLeft, const VRFieldOfView& aFOVRight,
               double zNear, double zFar) override;
 
-  VRHMDSensorState GetSensorState(double timeOffset) override;
+  virtual VRHMDSensorState GetSensorState() override;
+  virtual VRHMDSensorState GetImmediateSensorState() override;
   void ZeroSensor() override;
   bool KeepSensorTracking() override;
   void NotifyVsync(const TimeStamp& aVsyncTimestamp) override;
 
   void FillDistortionConstants(uint32_t whichEye,
                                const IntSize& textureSize, const IntRect& eyeViewport,
                                const Size& destViewport, const Rect& destRect,
                                VRDistortionConstants& values) override;
--- a/ipc/app/moz.build
+++ b/ipc/app/moz.build
@@ -61,23 +61,20 @@ if CONFIG['MOZ_SANDBOX'] and CONFIG['OS_
         USE_LIBS += [
             'sandbox_s',
         ]
     else:
         USE_LIBS += [
             'sandbox_staticruntime_s',
         ]
 
-    # clang-cl can't deal with this delay-load due to bug 1188045
-    # (also filed as https://llvm.org/bugs/show_bug.cgi?id=24291)
-    if not CONFIG['CLANG_CL']:
-        DELAYLOAD_DLLS += [
-            'nss3.dll',
-            'xul.dll'
-        ]
+    DELAYLOAD_DLLS += [
+        'nss3.dll',
+        'xul.dll'
+    ]
 
     DEFINES['HASH_NODE_ID_WITH_DEVICE_ID'] = 1;
     SOURCES += [
         'sha256.c',
     ]
 
 if CONFIG['MOZ_SANDBOX'] and CONFIG['OS_TARGET'] in ('Linux', 'Android'):
     USE_LIBS += [
--- a/ipc/glue/SharedMemoryBasic_android.cpp
+++ b/ipc/glue/SharedMemoryBasic_android.cpp
@@ -125,15 +125,16 @@ SharedMemoryBasic::Unmap()
     LogError("ShmemAndroid::Unmap()");
   }
   mMemory = nullptr;
 }
 
 void
 SharedMemoryBasic::CloseHandle()
 {
-  if (mShmFd > 0) {
+  if (mShmFd != -1) {
     close(mShmFd);
+    mShmFd = -1;
   }
 }
 
 } // namespace ipc
 } // namespace mozilla
--- a/js/src/builtin/ModuleObject.cpp
+++ b/js/src/builtin/ModuleObject.cpp
@@ -104,17 +104,17 @@ GlobalObject::initImportEntryProto(JSCon
     if (!DefinePropertiesAndFunctions(cx, proto, protoAccessors, nullptr))
         return false;
 
     global->setReservedSlot(IMPORT_ENTRY_PROTO, ObjectValue(*proto));
     return true;
 }
 
 /* static */ ImportEntryObject*
-ImportEntryObject::create(JSContext* cx,
+ImportEntryObject::create(ExclusiveContext* cx,
                           HandleAtom moduleRequest,
                           HandleAtom importName,
                           HandleAtom localName)
 {
     RootedObject proto(cx, cx->global()->getImportEntryPrototype());
     RootedObject obj(cx, NewObjectWithGivenProto(cx, &class_, proto));
     if (!obj)
         return nullptr;
@@ -176,17 +176,17 @@ GlobalObject::initExportEntryProto(JSCon
 
 static Value
 StringOrNullValue(JSString* maybeString)
 {
     return maybeString ? StringValue(maybeString) : NullValue();
 }
 
 /* static */ ExportEntryObject*
-ExportEntryObject::create(JSContext* cx,
+ExportEntryObject::create(ExclusiveContext* cx,
                           HandleAtom maybeExportName,
                           HandleAtom maybeModuleRequest,
                           HandleAtom maybeImportName,
                           HandleAtom maybeLocalName)
 {
     RootedObject proto(cx, cx->global()->getExportEntryPrototype());
     RootedObject obj(cx, NewObjectWithGivenProto(cx, &class_, proto));
     if (!obj)
@@ -694,16 +694,72 @@ ModuleObject::initImportExportData(Handl
 {
     initReservedSlot(RequestedModulesSlot, ObjectValue(*requestedModules));
     initReservedSlot(ImportEntriesSlot, ObjectValue(*importEntries));
     initReservedSlot(LocalExportEntriesSlot, ObjectValue(*localExportEntries));
     initReservedSlot(IndirectExportEntriesSlot, ObjectValue(*indirectExportEntries));
     initReservedSlot(StarExportEntriesSlot, ObjectValue(*starExportEntries));
 }
 
+static bool
+FreezeObjectProperty(JSContext* cx, HandleNativeObject obj, uint32_t slot)
+{
+    RootedObject property(cx, &obj->getSlot(slot).toObject());
+    return FreezeObject(cx, property);
+}
+
+/* static */ bool
+ModuleObject::FreezeArrayProperties(JSContext* cx, HandleModuleObject self)
+{
+    return FreezeObjectProperty(cx, self, RequestedModulesSlot) &&
+           FreezeObjectProperty(cx, self, ImportEntriesSlot) &&
+           FreezeObjectProperty(cx, self, LocalExportEntriesSlot) &&
+           FreezeObjectProperty(cx, self, IndirectExportEntriesSlot) &&
+           FreezeObjectProperty(cx, self, StarExportEntriesSlot);
+}
+
+static inline void
+AssertObjectPropertyFrozen(JSContext* cx, HandleNativeObject obj, uint32_t slot)
+{
+#ifdef DEBUG
+    bool frozen = false;
+    RootedObject property(cx, &obj->getSlot(slot).toObject());
+    MOZ_ALWAYS_TRUE(TestIntegrityLevel(cx, property, IntegrityLevel::Frozen, &frozen));
+    MOZ_ASSERT(frozen);
+#endif
+}
+
+/* static */ inline void
+ModuleObject::AssertArrayPropertiesFrozen(JSContext* cx, HandleModuleObject self)
+{
+    AssertObjectPropertyFrozen(cx, self, RequestedModulesSlot);
+    AssertObjectPropertyFrozen(cx, self, ImportEntriesSlot);
+    AssertObjectPropertyFrozen(cx, self, LocalExportEntriesSlot);
+    AssertObjectPropertyFrozen(cx, self, IndirectExportEntriesSlot);
+    AssertObjectPropertyFrozen(cx, self, StarExportEntriesSlot);
+}
+
+inline static void
+AssertModuleScopesMatch(ModuleObject* module)
+{
+    MOZ_ASSERT(IsStaticGlobalLexicalScope(module->enclosingStaticScope()));
+    MOZ_ASSERT(module->initialEnvironment().enclosingScope().as<ClonedBlockObject>().staticScope() ==
+               module->enclosingStaticScope());
+}
+
+void
+ModuleObject::fixScopesAfterCompartmentMerge(JSContext* cx)
+{
+    AssertModuleScopesMatch(this);
+    Rooted<ClonedBlockObject*> lexicalScope(cx, &script()->global().lexicalScope());
+    setReservedSlot(StaticScopeSlot, ObjectValue(lexicalScope->staticBlock()));
+    initialEnvironment().setEnclosingScope(lexicalScope);
+    AssertModuleScopesMatch(this);
+}
+
 bool
 ModuleObject::hasScript() const
 {
     // When modules are parsed via the Reflect.parse() API, the module object
     // doesn't have a script.
     return !getReservedSlot(ScriptSlot).isUndefined();
 }
 
@@ -770,16 +826,18 @@ ModuleObject::noteFunctionDeclaration(Ex
     }
 
     return true;
 }
 
 /* static */ bool
 ModuleObject::instantiateFunctionDeclarations(JSContext* cx, HandleModuleObject self)
 {
+    AssertArrayPropertiesFrozen(cx, self);
+
     FunctionDeclarationVector* funDecls = self->functionDeclarations();
     if (!funDecls) {
         JS_ReportError(cx, "Module function declarations have already been instantiated");
         return false;
     }
 
     RootedModuleEnvironmentObject env(cx, &self->initialEnvironment());
     RootedFunction fun(cx);
@@ -806,16 +864,18 @@ ModuleObject::setEvaluated()
 {
     MOZ_ASSERT(!evaluated());
     setReservedSlot(EvaluatedSlot, TrueHandleValue);
 }
 
 /* static */ bool
 ModuleObject::evaluate(JSContext* cx, HandleModuleObject self, MutableHandleValue rval)
 {
+    AssertArrayPropertiesFrozen(cx, self);
+
     RootedScript script(cx, self->script());
     RootedModuleEnvironmentObject scope(cx, self->environment());
     if (!scope) {
         JS_ReportError(cx, "Module declarations have not yet been instantiated");
         return false;
     }
 
     return Execute(cx, script, *scope, rval.address());
@@ -897,17 +957,17 @@ js::InitModuleClasses(JSContext* cx, Han
 
 #undef DEFINE_GETTER_FUNCTIONS
 #undef DEFINE_STRING_ACCESSOR_METHOD
 #undef DEFINE_ARRAY_SLOT_ACCESSOR
 
 ///////////////////////////////////////////////////////////////////////////
 // ModuleBuilder
 
-ModuleBuilder::ModuleBuilder(JSContext* cx, HandleModuleObject module)
+ModuleBuilder::ModuleBuilder(ExclusiveContext* cx, HandleModuleObject module)
   : cx_(cx),
     module_(cx, module),
     requestedModules_(cx, AtomVector(cx)),
     importedBoundNames_(cx, AtomVector(cx)),
     importEntries_(cx, ImportEntryVector(cx)),
     exportEntries_(cx, ExportEntryVector(cx)),
     localExportEntries_(cx, ExportEntryVector(cx)),
     indirectExportEntries_(cx, ExportEntryVector(cx)),
@@ -1180,13 +1240,11 @@ ArrayObject* ModuleBuilder::createArray(
     uint32_t length = vector.length();
     RootedArrayObject array(cx_, NewDenseFullyAllocatedArray(cx_, length));
     if (!array)
         return nullptr;
 
     array->setDenseInitializedLength(length);
     for (uint32_t i = 0; i < length; i++)
         array->initDenseElement(i, MakeElementValue(vector[i]));
-    if (!JS_FreezeObject(cx_, array))
-        return nullptr;
 
     return array;
 }
--- a/js/src/builtin/ModuleObject.h
+++ b/js/src/builtin/ModuleObject.h
@@ -38,19 +38,19 @@ class ImportEntryObject : public NativeO
     {
         ModuleRequestSlot = 0,
         ImportNameSlot,
         LocalNameSlot,
         SlotCount
     };
 
     static const Class class_;
-    static JSObject* initClass(JSContext* cx, HandleObject obj);
+    static JSObject* initClass(ExclusiveContext* cx, HandleObject obj);
     static bool isInstance(HandleValue value);
-    static ImportEntryObject* create(JSContext* cx,
+    static ImportEntryObject* create(ExclusiveContext* cx,
                                      HandleAtom moduleRequest,
                                      HandleAtom importName,
                                      HandleAtom localName);
     JSAtom* moduleRequest() const;
     JSAtom* importName() const;
     JSAtom* localName() const;
 };
 
@@ -65,19 +65,19 @@ class ExportEntryObject : public NativeO
         ExportNameSlot = 0,
         ModuleRequestSlot,
         ImportNameSlot,
         LocalNameSlot,
         SlotCount
     };
 
     static const Class class_;
-    static JSObject* initClass(JSContext* cx, HandleObject obj);
+    static JSObject* initClass(ExclusiveContext* cx, HandleObject obj);
     static bool isInstance(HandleValue value);
-    static ExportEntryObject* create(JSContext* cx,
+    static ExportEntryObject* create(ExclusiveContext* cx,
                                      HandleAtom maybeExportName,
                                      HandleAtom maybeModuleRequest,
                                      HandleAtom maybeImportName,
                                      HandleAtom maybeLocalName);
     JSAtom* exportName() const;
     JSAtom* moduleRequest() const;
     JSAtom* importName() const;
     JSAtom* localName() const;
@@ -227,16 +227,19 @@ class ModuleObject : public NativeObject
     static ModuleObject* create(ExclusiveContext* cx, HandleObject enclosingStaticScope);
     void init(HandleScript script);
     void setInitialEnvironment(Handle<ModuleEnvironmentObject*> initialEnvironment);
     void initImportExportData(HandleArrayObject requestedModules,
                               HandleArrayObject importEntries,
                               HandleArrayObject localExportEntries,
                               HandleArrayObject indiretExportEntries,
                               HandleArrayObject starExportEntries);
+    static bool FreezeArrayProperties(JSContext* cx, HandleModuleObject self);
+    static void AssertArrayPropertiesFrozen(JSContext* cx, HandleModuleObject self);
+    void fixScopesAfterCompartmentMerge(JSContext* cx);
 
     JSScript* script() const;
     JSObject* enclosingStaticScope() const;
     ModuleEnvironmentObject& initialEnvironment() const;
     ModuleEnvironmentObject* environment() const;
     ModuleNamespaceObject* namespace_();
     bool evaluated() const;
     ArrayObject& requestedModules() const;
@@ -268,17 +271,17 @@ class ModuleObject : public NativeObject
     FunctionDeclarationVector* functionDeclarations();
 };
 
 // Process a module's parse tree to collate the import and export data used when
 // creating a ModuleObject.
 class MOZ_STACK_CLASS ModuleBuilder
 {
   public:
-    explicit ModuleBuilder(JSContext* cx, HandleModuleObject module);
+    explicit ModuleBuilder(ExclusiveContext* cx, HandleModuleObject module);
 
     bool processImport(frontend::ParseNode* pn);
     bool processExport(frontend::ParseNode* pn);
     bool processExportFrom(frontend::ParseNode* pn);
 
     bool hasExportedName(JSAtom* name) const;
 
     using ExportEntryVector = GCVector<ExportEntryObject*>;
@@ -291,17 +294,17 @@ class MOZ_STACK_CLASS ModuleBuilder
 
   private:
     using AtomVector = GCVector<JSAtom*>;
     using RootedAtomVector = JS::Rooted<AtomVector>;
     using ImportEntryVector = GCVector<ImportEntryObject*>;
     using RootedImportEntryVector = JS::Rooted<ImportEntryVector>;
     using RootedExportEntryVector = JS::Rooted<ExportEntryVector>;
 
-    JSContext* cx_;
+    ExclusiveContext* cx_;
     RootedModuleObject module_;
     RootedAtomVector requestedModules_;
     RootedAtomVector importedBoundNames_;
     RootedImportEntryVector importEntries_;
     RootedExportEntryVector exportEntries_;
     RootedExportEntryVector localExportEntries_;
     RootedExportEntryVector indirectExportEntries_;
     RootedExportEntryVector starExportEntries_;
--- a/js/src/builtin/SIMD.cpp
+++ b/js/src/builtin/SIMD.cpp
@@ -707,37 +707,47 @@ struct Xor {
 template<typename T>
 struct And {
     static T apply(T l, T r) { return l & r; }
 };
 template<typename T>
 struct Or {
     static T apply(T l, T r) { return l | r; }
 };
+
 // For the following three operators, if the value v we're trying to shift is
 // such that v << bits can't fit in the int32 range, then we have undefined
-// behavior, according to C++11 [expr.shift]p2.
+// behavior, according to C++11 [expr.shift]p2. However, left-shifting an
+// unsigned type is well-defined.
+//
+// In C++, shifting by an amount outside the range [0;N-1] is undefined
+// behavior. SIMD.js reduces the shift amount modulo the number of bits in a
+// lane and has defined behavior for all shift amounts.
 template<typename T>
 struct ShiftLeft {
     static T apply(T v, int32_t bits) {
-        return uint32_t(bits) >= sizeof(T) * 8 ? 0 : v << bits;
+        typedef typename mozilla::MakeUnsigned<T>::Type UnsignedT;
+        uint32_t maskedBits = uint32_t(bits) % (sizeof(T) * 8);
+        return UnsignedT(v) << maskedBits;
     }
 };
 template<typename T>
 struct ShiftRightArithmetic {
     static T apply(T v, int32_t bits) {
         typedef typename mozilla::MakeSigned<T>::Type SignedT;
-        uint32_t maxBits = sizeof(T) * 8;
-        return SignedT(v) >> (uint32_t(bits) >= maxBits ? maxBits - 1 : bits);
+        uint32_t maskedBits = uint32_t(bits) % (sizeof(T) * 8);
+        return SignedT(v) >> maskedBits;
     }
 };
 template<typename T>
 struct ShiftRightLogical {
     static T apply(T v, int32_t bits) {
-        return uint32_t(bits) >= sizeof(T) * 8 ? 0 : uint32_t(v) >> bits;
+        typedef typename mozilla::MakeUnsigned<T>::Type UnsignedT;
+        uint32_t maskedBits = uint32_t(bits) % (sizeof(T) * 8);
+        return UnsignedT(v) >> maskedBits;
     }
 };
 
 // Saturating arithmetic is only defined on types smaller than int.
 // Clamp `x` into the range supported by the integral type T.
 template<typename T>
 static T
 Saturate(int x)
--- a/js/src/builtin/Sorting.js
+++ b/js/src/builtin/Sorting.js
@@ -38,16 +38,125 @@ function CountingSort(array, len, signed
             } else {
                 val++;
             }
         }
     }
     return array;
 }
 
+// Helper for RadixSort
+function ByteAtCol(x, pos) {
+    return  (x >> (pos * 8)) & 0xFF;
+}
+
+function SortByColumn(array, len, aux, col) {
+    const R = 256;
+    let counts = new List();
+
+    // |counts| is used to compute the starting index position for each key.
+    // Letting counts[0] always be 0, simplifies the transform step below.
+    // Example:
+    //
+    // Computing frequency counts for the input [1 2 1] gives:
+    //      0 1 2 3 ... (keys)
+    //      0 0 2 1     (frequencies)
+    //
+    // Transforming frequencies to indexes gives:
+    //      0 1 2 3 ... (keys)
+    //      0 0 2 3     (indexes)
+    for (let r = 0; r < R + 1; r++) {
+        counts[r] = 0;
+    }
+    // Compute frequency counts
+    for (let i = 0; i < len; i++) {
+        let val = array[i];
+        let b = ByteAtCol(val, col);
+        counts[b + 1]++;
+    }
+
+    // Transform counts to indices.
+    for (let r = 0; r < R; r++) {
+        counts[r+1] += counts[r];
+    }
+
+    // Distribute
+    for (let i = 0; i < len; i++) {
+        let val = array[i];
+        let b  = ByteAtCol(val, col);
+        aux[counts[b]++] = val;
+    }
+
+    // Copy back
+    for (let i = 0; i < len; i++) {
+        array[i] = aux[i];
+    }
+}
+
+// Sorts integers and float32. |signed| is true for int16 and int32, |floating|
+// is true for float32.
+function RadixSort(array, len, nbytes, signed, floating, comparefn) {
+
+    // Determined by performance testing.
+    if (len < 128) {
+        QuickSort(array, len, comparefn);
+        return array;
+    }
+
+    let aux = new List();
+    for (let i = 0; i < len; i++) {
+        aux[i] = 0;
+    }
+
+    let view = array;
+    let signMask = 1 << nbytes * 8 - 1;
+
+    // Preprocess
+    if (floating) {
+        view = new Int32Array(array.buffer);
+
+        // Flip sign bit for positive numbers; flip all bits for negative
+        // numbers
+        for (let i = 0; i < len; i++) {
+            if (view[i] & signMask) {
+                view[i] ^= 0xFFFFFFFF;
+            } else {
+                view[i] ^= signMask
+            }
+        }
+    } else if (signed) {
+        // Flip sign bit
+        for (let i = 0; i < len; i++) {
+            view[i] ^= signMask
+        }
+    }
+
+    // Sort
+    for (let col = 0; col < nbytes; col++) {
+        SortByColumn(view, len, aux, col);
+    }
+
+    // Restore original bit representation
+    if (floating) {
+        for (let i = 0; i < len; i++) {
+            if (view[i] & signMask) {
+                view[i] ^= signMask;
+            } else {
+                view[i] ^= 0xFFFFFFFF;
+            }
+        }
+    } else if (signed) {
+        for (let i = 0; i < len; i++) {
+            view[i] ^= signMask
+        }
+    }
+    return array;
+}
+
+
 // For sorting small arrays.
 function InsertionSort(array, from, to, comparefn) {
     let item, swap, i, j;
     for (i = from + 1; i <= to; i++) {
         item = array[i];
         for (j = i - 1; j >= from; j--) {
             swap = array[j];
             if (comparefn(swap, item) <= 0)
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -1645,17 +1645,17 @@ DisplayName(JSContext* cx, unsigned argc
 
     JSFunction* fun = &args[0].toObject().as<JSFunction>();
     JSString* str = fun->displayAtom();
     args.rval().setString(str ? str : cx->runtime()->emptyString);
     return true;
 }
 
 static JSObject*
-ShellObjectMetadataCallback(JSContext* cx, JSObject*)
+ShellObjectMetadataCallback(JSContext* cx, HandleObject)
 {
     AutoEnterOOMUnsafeRegion oomUnsafe;
 
     RootedObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx));
     if (!obj)
         oomUnsafe.crash("ShellObjectMetadataCallback");
 
     RootedObject stack(cx, NewDenseEmptyArray(cx));
--- a/js/src/builtin/TypedArray.js
+++ b/js/src/builtin/TypedArray.js
@@ -982,25 +982,33 @@ function TypedArraySort(comparefn) {
 
     // Step 2.
     var buffer = GetAttachedArrayBuffer(obj);
 
     // Step 3.
     var len = TypedArrayLength(obj);
 
     if (comparefn === undefined) {
+        comparefn = TypedArrayCompare;
         // CountingSort doesn't invoke the comparefn
         if (IsUint8TypedArray(obj)) {
             return CountingSort(obj, len, false /* signed */);
         } else if (IsInt8TypedArray(obj)) {
             return CountingSort(obj, len, true /* signed */);
+        } else if (IsUint16TypedArray(obj)) {
+            return RadixSort(obj, len, 2 /* nbytes */, false /* signed */, false /* floating */, comparefn);
+        } else if (IsInt16TypedArray(obj)) {
+            return RadixSort(obj, len, 2 /* nbytes */, true /* signed */, false /* floating */, comparefn);
+        } else if (IsUint32TypedArray(obj)) {
+            return RadixSort(obj, len, 4 /* nbytes */, false /* signed */, false /* floating */, comparefn);
+        } else if (IsInt32TypedArray(obj)) {
+            return RadixSort(obj, len, 4 /* nbytes */, true /* signed */, false /* floating */, comparefn);
+        } else if (IsFloat32TypedArray(obj)) {
+            return RadixSort(obj, len, 4 /* nbytes */, true /* signed */, true /* floating */, comparefn);
         }
-
-        comparefn = TypedArrayCompare;
-    } else {
         // To satisfy step 2 from TypedArray SortCompare described in 22.2.3.26
         // the user supplied comparefn is wrapped.
         var wrappedCompareFn = comparefn;
         comparefn = function(x, y) {
             // Step a.
             var v = wrappedCompareFn(x, y);
             // Step b.
             if (IsDetachedBuffer(buffer))
--- a/js/src/devtools/automation/cgc-jittest-timeouts.txt
+++ b/js/src/devtools/automation/cgc-jittest-timeouts.txt
@@ -1,8 +1,9 @@
+SIMD/nursery-overflow.js
 asm.js/testParallelCompile.js
 auto-regress/bug653395.js
 auto-regress/bug654392.js
 auto-regress/bug675251.js
 baseline/bug847446.js
 baseline/bug852175.js
 basic/bug632964-regexp.js
 basic/bug656261.js
@@ -14,14 +15,14 @@ basic/testManyVars.js
 basic/testTypedArrayInit.js
 gc/bug-1014972.js
 gc/bug-1246593.js
 gc/bug-906236.js
 gc/bug-906241.js
 ion/bug787921.js
 parallel/alloc-many-objs.js
 parallel/alloc-too-many-objs.js
+saved-stacks/bug-1006876-too-much-recursion.js
 self-test/assertDeepEq.js
 v8-v5/check-earley-boyer.js
 v8-v5/check-raytrace.js
 v8-v5/check-regexp.js
 v8-v5/check-splay.js
-SIMD/nursery-overflow.js
--- a/js/src/devtools/rootAnalysis/analyze.py
+++ b/js/src/devtools/rootAnalysis/analyze.py
@@ -122,17 +122,18 @@ JOBS = { 'dbs':
          'allFunctions':
              (('%(sixgill_bin)s/xdbkeys', 'src_body.xdb',),
               'allFunctions.txt'),
 
          'hazards':
              (generate_hazards, 'rootingHazards.txt'),
 
          'explain':
-             (('python', '%(analysis_scriptdir)s/explain.py',
+             ((os.environ.get('PYTHON', 'python2.7'),
+               '%(analysis_scriptdir)s/explain.py',
                '%(hazards)s', '%(gcFunctions)s',
                '[explained_hazards]', '[unnecessary]', '[refs]'),
               ('hazards.txt', 'unnecessary.txt', 'refs.txt'))
          }
 
 def out_indexes(command):
     for i in range(len(command)):
         m = re.match(r'^\[(.*)\]$', command[i])
--- a/js/src/devtools/rootAnalysis/analyzeRoots.js
+++ b/js/src/devtools/rootAnalysis/analyzeRoots.js
@@ -682,23 +682,16 @@ xdb.open("src_body.xdb");
 var minStream = xdb.min_data_stream()|0;
 var maxStream = xdb.max_data_stream()|0;
 
 var N = (maxStream - minStream) + 1;
 var each = Math.floor(N/numBatches);
 var start = minStream + each * (batch - 1);
 var end = Math.min(minStream + each * batch - 1, maxStream);
 
-// For debugging: Set this variable to the function name you're interested in
-// debugging and run once. That will print out the nameIndex of that function.
-// Insert that into the following statement to go directly to just that
-// function. Add your debugging printouts or debugger; statements or whatever.
-var theFunctionNameToFind;
-// var start = end = 12345;
-
 function process(name, json) {
     functionName = name;
     functionBodies = JSON.parse(json);
 
     for (var body of functionBodies)
         body.suppressed = [];
     for (var body of functionBodies) {
         for (var [pbody, id] of allRAIIGuardedCallPoints(body, isSuppressConstructor))
--- a/js/src/devtools/rootAnalysis/computeCallgraph.js
+++ b/js/src/devtools/rootAnalysis/computeCallgraph.js
@@ -1,16 +1,22 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 4 -*- */
 
 "use strict";
 
 loadRelativeToScript('utility.js');
 loadRelativeToScript('annotations.js');
 loadRelativeToScript('CFG.js');
 
+var theFunctionNameToFind;
+if (scriptArgs[0] == '--function') {
+    theFunctionNameToFind = scriptArgs[1];
+    scriptArgs = scriptArgs.slice(2);
+}
+
 var subclasses = {};
 var superclasses = {};
 var classFunctions = {};
 
 var fieldCallSeen = {};
 
 function addClassEntry(index, name, other)
 {
@@ -253,16 +259,25 @@ for (var csuIndex = minStream; csuIndex 
 
 xdb.open("src_body.xdb");
 
 printErr("Finished loading data structures");
 
 var minStream = xdb.min_data_stream();
 var maxStream = xdb.max_data_stream();
 
+if (theFunctionNameToFind) {
+    var index = xdb.lookup_key(theFunctionNameToFind);
+    if (!index) {
+        printErr("Function not found");
+        quit(1);
+    }
+    minStream = maxStream = index;
+}
+
 for (var nameIndex = minStream; nameIndex <= maxStream; nameIndex++) {
     var name = xdb.read_key(nameIndex);
     var data = xdb.read_entry(name);
     functionBodies = JSON.parse(data.readString());
     for (var body of functionBodies)
         body.suppressed = [];
     for (var body of functionBodies) {
         for (var [pbody, id] of allRAIIGuardedCallPoints(body, isSuppressConstructor))
--- a/js/src/devtools/rootAnalysis/utility.js
+++ b/js/src/devtools/rootAnalysis/utility.js
@@ -130,24 +130,30 @@ function readable(fullname)
 {
     var [ mangled, unmangled ] = splitFunction(fullname);
     return unmangled;
 }
 
 function xdbLibrary()
 {
     var lib = ctypes.open(os.getenv('XDB'));
-    return {
+    var api = {
         open: lib.declare("xdb_open", ctypes.default_abi, ctypes.void_t, ctypes.char.ptr),
         min_data_stream: lib.declare("xdb_min_data_stream", ctypes.default_abi, ctypes.int),
         max_data_stream: lib.declare("xdb_max_data_stream", ctypes.default_abi, ctypes.int),
         read_key: lib.declare("xdb_read_key", ctypes.default_abi, ctypes.char.ptr, ctypes.int),
         read_entry: lib.declare("xdb_read_entry", ctypes.default_abi, ctypes.char.ptr, ctypes.char.ptr),
         free_string: lib.declare("xdb_free", ctypes.default_abi, ctypes.void_t, ctypes.char.ptr)
     };
+    try {
+        api.lookup_key = lib.declare("xdb_lookup_key", ctypes.default_abi, ctypes.int, ctypes.char.ptr);
+    } catch (e) {
+        // lookup_key is for development use only and is not strictly necessary.
+    }
+    return api;
 }
 
 function cLibrary()
 {
     var lib;
     try {
         lib = ctypes.open("libc.so.6");
     } catch(e) {
--- a/js/src/doc/Debugger/Conventions.md
+++ b/js/src/doc/Debugger/Conventions.md
@@ -106,17 +106,19 @@ resumption value has one of the followin
 
 <code>{ return: <i>value</i> }</code>
 :   Return <i>value</i> immediately as the current value of the function.
     <i>Value</i> must be a debuggee value. (Most handler functions support
     this, except those whose descriptions say otherwise.) If the function
     was called as a constructor (that is, via a `new` expression), then
     <i>value</i> serves as the value returned by the function's body, not
     that produced by the `new` expression: if the value is not an object,
-    the `new` expression returns the frame's `this` value.
+    the `new` expression returns the frame's `this` value. Similarly, if
+    the function is the constructor for a subclass, then a non-object
+    value may result in a TypeError.
 
 <code>{ yield: <i>value</i> }</code>
 :   <i>(Not yet implemented.)</i> Yield <i>value</i> immediately as the
     next value of the current frame, which must be a generator frame.
     <i>Value</i> is a debuggee value. The current frame must be a generator
     frame that has not yet completed in some other way. You may use `yield`
     resumption values to substitute a new value or one already yielded by a
     generator, or to make a generator yield additional values.
--- a/js/src/doc/Debugger/Debugger.Memory.md
+++ b/js/src/doc/Debugger/Debugger.Memory.md
@@ -104,35 +104,16 @@ following accessor properties from its p
     value is `5000`.
 
 <code id='allocationsLogOverflowed'>allocationsLogOverflowed</code>
 :   Returns `true` if there have been more than
     [`maxAllocationsLogLength`][#max-alloc-log] allocations since the last time
     [`drainAllocationsLog`][#drain-alloc-log] was called and some data has been
     lost. Returns `false` otherwise.
 
-`trackingTenurePromotions`
-:   A boolean value indicating whether this `Debugger.Memory` instance is
-    observing promotions from the nursery to the tenured heap. It is an accessor
-    property that has both a getter and setter: assigning to it enables or
-    disables the tenure promotion tracking. Reading the accessor produces `true`
-    if the Debugger is logging promotions, and `false` otherwise. Tenure
-    promotion tracking is initially disabled in a new Debugger.
-
-<code id="max-tenure-log">maxTenurePromotionsLogLength</code>
-:   The maximum number of entries to accumulate in the tenure promotions log at
-    a time. This accessor can be both fetched and stored to. Its default value
-    is `5000`.
-
-`tenurePromotionsLogOverflowed`
-:   Returns `true` if there have been more than
-    [`maxTenurePromotionsLogLength`][#max-tenure-log] allocations since the last time
-    [`drainTenurePromotionsLog`][#drain-tenure-log] was called and some data has been
-    lost. Returns `false` otherwise.
-
 Debugger.Memory Handler Functions
 ---------------------------------
 
 Similar to [`Debugger`'s handler functions][debugger], `Debugger.Memory`
 inherits accessor properties that store handler functions for SpiderMonkey to
 call when given events occur in debuggee code.
 
 Unlike `Debugger`'s hooks, `Debugger.Memory`'s handlers' return values are not
@@ -271,56 +252,16 @@ Function Properties of the `Debugger.Mem
     * *byteSize* is the size of the object in bytes.
 
     * *inNursery* is true if the allocation happened inside the nursery. False
       if the allocation skipped the nursery and started in the tenured heap.
 
     When `trackingAllocationSites` is `false`, `drainAllocationsLog()` throws an
     `Error`.
 
-<code id='drain-tenure-log'>drainTenurePromotionsLog</code>
-:   When `trackingTenurePromotions` is `true`, this method returns an array of
-    recent promotions from the nursery to the tenured heap within this
-    Debugger's set of debuggees. *Recent* is defined as the
-    `maxTenurePromotionsLogLength` most recent promotions since the last call to
-    `drainTenurePromotionsLog`. Therefore, calling this method effectively
-    clears the log.
-
-    Objects in the array are of the form:
-
-    <pre class='language-js'><code>
-    {
-      "timestamp": <i>timestamp</i>,
-      "frame": <i>allocationSite</i>,
-      "class": <i>className</i>,
-      "size": <i>byteSize</i>,
-    }
-    </pre>
-
-    Where
-
-    * *timestamp* is the [timestamp][timestamps] of the allocation event.
-
-    * *allocationSite* is an allocation site (as a
-      [captured stack][saved-frame]) if the promoted object's allocation site
-      was captured. Note that this property can be `null` if the object was
-      allocated with no JavaScript frames on the stack, the object's allocation
-      site was not [sampled](#alloc-sampling-probability), or if allocation
-      sites are not being [tracked](#trackingallocationsites').
-
-    * *className* is the string name of the allocated object's internal
-      `[[Class]]` property, for example "Array", "Date", "RegExp", or (most
-      commonly) "Object".
-
-    * *byteSize* is the size of the newly tenured object (within the tenured
-      heap, not the nursery) in bytes.
-
-    When `trackingTenurePromotions` is `false`, `drainTenurePromotionsLog()`
-    throws an `Error`.
-
 <code id='take-census'>takeCensus(<i>options</i>)</code>
 :   Carry out a census of the debuggee compartments' contents. A *census* is a
     complete traversal of the graph of all reachable memory items belonging to a
     particular `Debugger`'s debuggees. The census produces a count of those
     items, broken down by various criteria.
 
     The <i>options</i> argument is an object whose properties specify how the
     census should be carried out.
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -571,17 +571,17 @@ ModuleObject* BytecodeCompiler::compileM
     if (!module)
         return nullptr;
 
     if (!createScript(module))
         return nullptr;
 
     module->init(script);
 
-    ModuleBuilder builder(cx->asJSContext(), module);
+    ModuleBuilder builder(cx, module);
     ParseNode* pn = parser->standaloneModule(module, builder);
     if (!pn)
         return nullptr;
 
     if (!NameFunctions(cx, pn) ||
         !maybeSetDisplayURL(parser->tokenStream) ||
         !maybeSetSourceMap(parser->tokenStream))
     {
@@ -754,30 +754,42 @@ frontend::CompileScript(ExclusiveContext
     // which reference the partially-initialized SSO.
     if (sourceObjectOut)
         *sourceObjectOut = compiler.sourceObjectPtr();
 
     return script;
 }
 
 ModuleObject*
-frontend::CompileModule(JSContext* cx, HandleObject obj,
-                        const ReadOnlyCompileOptions& optionsInput,
-                        SourceBufferHolder& srcBuf)
+frontend::CompileModule(ExclusiveContext* cx, const ReadOnlyCompileOptions& optionsInput,
+                        SourceBufferHolder& srcBuf, LifoAlloc* alloc)
 {
     MOZ_ASSERT(srcBuf.get());
+    MOZ_ASSERT(cx->isJSContext() == (alloc == nullptr));
+
+    if (!alloc)
+        alloc = &cx->asJSContext()->tempLifoAlloc();
 
     CompileOptions options(cx, optionsInput);
     options.maybeMakeStrictMode(true); // ES6 10.2.1 Module code is always strict mode code.
     options.setIsRunOnce(true);
 
     Rooted<StaticScope*> staticScope(cx, &cx->global()->lexicalScope().staticBlock());
-    BytecodeCompiler compiler(cx, &cx->tempLifoAlloc(), options, srcBuf, staticScope,
+    BytecodeCompiler compiler(cx, alloc, options, srcBuf, staticScope,
                               TraceLogger_ParserCompileModule);
-    return compiler.compileModule();
+    RootedModuleObject module(cx, compiler.compileModule());
+    if (!module)
+        return nullptr;
+
+    // This happens in GlobalHelperThreadState::finishModuleParseTask() when a
+    // module is compiled off main thread.
+    if (cx->isJSContext() && !ModuleObject::FreezeArrayProperties(cx->asJSContext(), module))
+        return nullptr;
+
+    return module;
 }
 
 bool
 frontend::CompileLazyFunction(JSContext* cx, Handle<LazyScript*> lazy, const char16_t* chars, size_t length)
 {
     MOZ_ASSERT(cx->compartment() == lazy->functionNonDelazifying()->compartment());
 
     CompileOptions options(cx, lazy->version());
--- a/js/src/frontend/BytecodeCompiler.h
+++ b/js/src/frontend/BytecodeCompiler.h
@@ -27,19 +27,19 @@ namespace frontend {
 JSScript*
 CompileScript(ExclusiveContext* cx, LifoAlloc* alloc,
               HandleObject scopeChain, Handle<StaticScope*> enclosingStaticScope,
               HandleScript evalCaller, const ReadOnlyCompileOptions& options,
               SourceBufferHolder& srcBuf, JSString* source_ = nullptr,
               SourceCompressionTask* extraSct = nullptr,
               ScriptSourceObject** sourceObjectOut = nullptr);
 
-ModuleObject *
-CompileModule(JSContext *cx, HandleObject obj, const ReadOnlyCompileOptions &options,
-              SourceBufferHolder &srcBuf);
+ModuleObject*
+CompileModule(ExclusiveContext *cx, const ReadOnlyCompileOptions &options,
+              SourceBufferHolder &srcBuf, LifoAlloc* alloc = nullptr);
 
 bool
 CompileLazyFunction(JSContext* cx, Handle<LazyScript*> lazy, const char16_t* chars, size_t length);
 
 /*
  * enclosingStaticScope is a static enclosing scope (e.g. a StaticWithScope).
  * Must be null if the enclosing scope is a global.
  */
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -2126,20 +2126,16 @@ js::TenuringTracer::moveToTenured(JSObje
     }
     JSObject* dst = reinterpret_cast<JSObject*>(t);
     tenuredSize += moveObjectToTenured(dst, src, dstKind);
 
     RelocationOverlay* overlay = RelocationOverlay::fromCell(src);
     overlay->forwardTo(dst);
     insertIntoFixupList(overlay);
 
-    if (MOZ_UNLIKELY(zone->hasDebuggers())) {
-        zone->enqueueForPromotionToTenuredLogging(*dst);
-    }
-
     TracePromoteToTenured(src, dst);
     MemProfiler::MoveNurseryToTenured(src, dst);
     return dst;
 }
 
 void
 js::Nursery::collectToFixedPoint(TenuringTracer& mover, TenureCountCache& tenureCounts)
 {
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -520,22 +520,16 @@ js::Nursery::collect(JSRuntime* rt, JS::
         for (size_t i = 0; i < ArrayLength(tenureCounts.entries); i++) {
             const TenureCount& entry = tenureCounts.entries[i];
             if (entry.count >= 3000)
                 mozilla::Unused << pretenureGroups->append(entry.group); // ignore alloc failure
         }
     }
     TIME_END(pretenure);
 
-    TIME_START(logPromotionsToTenured);
-    for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) {
-        zone->logPromotionsToTenured();
-    }
-    TIME_END(logPromotionsToTenured);
-
     // We ignore gcMaxBytes when allocating for minor collection. However, if we
     // overflowed, we disable the nursery. The next time we allocate, we'll fail
     // because gcBytes >= gcMaxBytes.
     if (rt->gc.usage.gcBytes() >= rt->gc.tunables.gcMaxBytes())
         disable();
 
     int64_t totalTime = PRMJ_Now() - timestampStart_total;
     rt->addTelemetry(JS_TELEMETRY_GC_MINOR_US, totalTime);
@@ -564,18 +558,17 @@ js::Nursery::collect(JSRuntime* rt, JS::
             {"collct", TIME_TOTAL(collectToFP)},
             {" tenCB", TIME_TOTAL(objectsTenuredCallback)},
             {"swpABO", TIME_TOTAL(sweepArrayBufferViewList)},
             {"updtIn", TIME_TOTAL(updateJitActivations)},
             {"frSlts", TIME_TOTAL(freeMallocedBuffers)},
             {" clrSB", TIME_TOTAL(clearStoreBuffer)},
             {" sweep", TIME_TOTAL(sweep)},
             {"resize", TIME_TOTAL(resize)},
-            {"pretnr", TIME_TOTAL(pretenure)},
-            {"logPtT", TIME_TOTAL(logPromotionsToTenured)}
+            {"pretnr", TIME_TOTAL(pretenure)}
         };
         static int printedHeader = 0;
         if ((printedHeader++ % 200) == 0) {
             fprintf(stderr, "MinorGC:               Reason  PRate Size    Time");
             for (auto &entry : PrintList)
                 fprintf(stderr, " %s", entry.name);
             fprintf(stderr, "\n");
         }
--- a/js/src/gc/Zone.cpp
+++ b/js/src/gc/Zone.cpp
@@ -20,16 +20,17 @@
 using namespace js;
 using namespace js::gc;
 
 Zone * const Zone::NotOnList = reinterpret_cast<Zone*>(1);
 
 JS::Zone::Zone(JSRuntime* rt)
   : JS::shadow::Zone(rt, &rt->gc.marker),
     debuggers(nullptr),
+    suppressObjectMetadataCallback(false),
     arenas(rt),
     types(this),
     compartments(),
     gcGrayRoots(),
     gcMallocBytes(0),
     gcMallocGCTriggered(false),
     usage(&rt->gc.usage),
     gcDelayBytes(0),
@@ -129,39 +130,16 @@ Zone::getOrCreateDebuggers(JSContext* cx
 
     debuggers = js_new<DebuggerVector>();
     if (!debuggers)
         ReportOutOfMemory(cx);
     return debuggers;
 }
 
 void
-Zone::logPromotionsToTenured()
-{
-    auto* dbgs = getDebuggers();
-    if (MOZ_LIKELY(!dbgs))
-        return;
-
-    auto now = JS_GetCurrentEmbedderTime();
-    JSRuntime* rt = runtimeFromAnyThread();
-
-    for (auto** dbgp = dbgs->begin(); dbgp != dbgs->end(); dbgp++) {
-        if (!(*dbgp)->isEnabled() || !(*dbgp)->isTrackingTenurePromotions())
-            continue;
-
-        for (auto range = awaitingTenureLogging.all(); !range.empty(); range.popFront()) {
-            if ((*dbgp)->isDebuggeeUnbarriered(range.front()->compartment()))
-                (*dbgp)->logTenurePromotion(rt, *range.front(), now);
-        }
-    }
-
-    awaitingTenureLogging.clear();
-}
-
-void
 Zone::sweepBreakpoints(FreeOp* fop)
 {
     if (fop->runtime()->debuggerList.isEmpty())
         return;
 
     /*
      * Sweep all compartments in a zone at the same time, since there is no way
      * to iterate over the scripts belonging to a single compartment in a zone.
--- a/js/src/gc/Zone.h
+++ b/js/src/gc/Zone.h
@@ -255,19 +255,16 @@ struct Zone : public JS::shadow::Zone,
     unsigned lastZoneGroupIndex() { return gcLastZoneGroupIndex; }
 #endif
 
     using DebuggerVector = js::Vector<js::Debugger*, 0, js::SystemAllocPolicy>;
 
   private:
     DebuggerVector* debuggers;
 
-    using LogTenurePromotionQueue = js::Vector<JSObject*, 0, js::SystemAllocPolicy>;
-    LogTenurePromotionQueue awaitingTenureLogging;
-
     void sweepBreakpoints(js::FreeOp* fop);
     void sweepUniqueIds(js::FreeOp* fop);
     void sweepWeakMaps();
     void sweepCompartments(js::FreeOp* fop, bool keepAtleastOne, bool lastGC);
 
     js::jit::JitZone* createJitZone(JSContext* cx);
 
     bool isQueuedForBackgroundSweep() {
@@ -277,24 +274,26 @@ struct Zone : public JS::shadow::Zone,
     // Side map for storing a unique ids for cells, independent of address.
     js::gc::UniqueIdMap uniqueIds_;
 
   public:
     bool hasDebuggers() const { return debuggers && debuggers->length(); }
     DebuggerVector* getDebuggers() const { return debuggers; }
     DebuggerVector* getOrCreateDebuggers(JSContext* cx);
 
-    void enqueueForPromotionToTenuredLogging(JSObject& obj) {
-        MOZ_ASSERT(hasDebuggers());
-        MOZ_ASSERT(!IsInsideNursery(&obj));
-        js::AutoEnterOOMUnsafeRegion oomUnsafe;
-        if (!awaitingTenureLogging.append(&obj))
-            oomUnsafe.crash("Zone::enqueueForPromotionToTenuredLogging");
-    }
-    void logPromotionsToTenured();
+    /*
+     * When true, skip calling the metadata callback. We use this:
+     * - to avoid invoking the callback recursively;
+     * - to avoid observing lazy prototype setup (which confuses callbacks that
+     *   want to use the types being set up!);
+     * - to avoid attaching allocation stacks to allocation stack nodes, which
+     *   is silly
+     * And so on.
+     */
+    bool suppressObjectMetadataCallback;
 
     js::gc::ArenaLists arenas;
 
     js::TypeZone types;
 
     /* Live weakmaps in this zone. */
     mozilla::LinkedList<js::WeakMapBase> gcWeakMapList;
 
--- a/js/src/jit-test/tests/SIMD/shift.js
+++ b/js/src/jit-test/tests/SIMD/shift.js
@@ -1,65 +1,68 @@
 load(libdir + 'simd.js');
 
 setJitCompilerOption("ion.warmup.trigger", 50);
 
 function curry(f, arg) { return f.bind(null, arg); }
 
-function binaryLsh(count, v) { if (count>>>0 >= 32) return 0; return (v << count) | 0; }
+function binaryLsh(count, v) { count &= 31; return (v << count) | 0; }
 function lsh(count) { return curry(binaryLsh, count); }
 
-function binaryRsh(count, v) { if (count>>>0 >= 32) count = 31; return (v >> count) | 0; }
+function binaryRsh(count, v) { count &= 31; return (v >> count) | 0; }
 function rsh(count) { return curry(binaryRsh, count); }
 
-function binaryUlsh(count, v) { if (count>>>0 >= 32) return 0; return (v << count) >>> 0; }
+function binaryUlsh(count, v) { count &= 31; return (v << count) >>> 0; }
 function ulsh(count) { return curry(binaryUlsh, count); }
 
-function binaryUrsh(count, v) { if (count>>>0 >= 32)  return 0; return v >>> count; }
+function binaryUrsh(count, v) { count &= 31; return v >>> count; }
 function ursh(count) { return curry(binaryUrsh, count); }
 
 function f() {
     var v = SIMD.Int32x4(1, 2, -3, 4);
     var u = SIMD.Uint32x4(1, 0x55005500, -3, 0xaa00aa00);
     var a = [1, 2, -3, 4];
     var b = [1, 0x55005500, -3, 0xaa00aa00];
-    var zeros = [0,0,0,0];
 
-    var shifts = [-1, 0, 1, 31, 32];
+    var shifts = [-2, -1, 0, 1, 31, 32, 33];
 
     var r;
     for (var i = 0; i < 150; i++) {
         // Constant shift counts
         assertEqX4(SIMD.Int32x4.shiftLeftByScalar(v, -1), a.map(lsh(-1)));
         assertEqX4(SIMD.Int32x4.shiftLeftByScalar(v, 0),  a.map(lsh(0)));
         assertEqX4(SIMD.Int32x4.shiftLeftByScalar(v, 1),  a.map(lsh(1)));
         assertEqX4(SIMD.Int32x4.shiftLeftByScalar(v, 2),  a.map(lsh(2)));
         assertEqX4(SIMD.Int32x4.shiftLeftByScalar(v, 31), a.map(lsh(31)));
         assertEqX4(SIMD.Int32x4.shiftLeftByScalar(v, 32), a.map(lsh(32)));
+        assertEqX4(SIMD.Int32x4.shiftLeftByScalar(v, 33), a.map(lsh(33)));
 
         assertEqX4(SIMD.Int32x4.shiftRightByScalar(v, -1), a.map(rsh(31)));
         assertEqX4(SIMD.Int32x4.shiftRightByScalar(v, 0),  a.map(rsh(0)));
         assertEqX4(SIMD.Int32x4.shiftRightByScalar(v, 1),  a.map(rsh(1)));
         assertEqX4(SIMD.Int32x4.shiftRightByScalar(v, 2),  a.map(rsh(2)));
         assertEqX4(SIMD.Int32x4.shiftRightByScalar(v, 31), a.map(rsh(31)));
-        assertEqX4(SIMD.Int32x4.shiftRightByScalar(v, 32), a.map(rsh(31)));
+        assertEqX4(SIMD.Int32x4.shiftRightByScalar(v, 32), a.map(rsh(32)));
+        assertEqX4(SIMD.Int32x4.shiftRightByScalar(v, 33), a.map(rsh(33)));
 
         assertEqX4(SIMD.Uint32x4.shiftLeftByScalar(u, -1), b.map(ulsh(-1)));
         assertEqX4(SIMD.Uint32x4.shiftLeftByScalar(u, 0),  b.map(ulsh(0)));
         assertEqX4(SIMD.Uint32x4.shiftLeftByScalar(u, 1),  b.map(ulsh(1)));
         assertEqX4(SIMD.Uint32x4.shiftLeftByScalar(u, 2),  b.map(ulsh(2)));
         assertEqX4(SIMD.Uint32x4.shiftLeftByScalar(u, 31), b.map(ulsh(31)));
         assertEqX4(SIMD.Uint32x4.shiftLeftByScalar(u, 32), b.map(ulsh(32)));
+        assertEqX4(SIMD.Uint32x4.shiftLeftByScalar(u, 33), b.map(ulsh(33)));
 
         assertEqX4(SIMD.Uint32x4.shiftRightByScalar(u, -1), b.map(ursh(-1)));
         assertEqX4(SIMD.Uint32x4.shiftRightByScalar(u, 0),  b.map(ursh(0)));
         assertEqX4(SIMD.Uint32x4.shiftRightByScalar(u, 1),  b.map(ursh(1)));
         assertEqX4(SIMD.Uint32x4.shiftRightByScalar(u, 2),  b.map(ursh(2)));
         assertEqX4(SIMD.Uint32x4.shiftRightByScalar(u, 31), b.map(ursh(31)));
         assertEqX4(SIMD.Uint32x4.shiftRightByScalar(u, 32), b.map(ursh(32)));
+        assertEqX4(SIMD.Uint32x4.shiftRightByScalar(u, 33), b.map(ursh(33)));
 
         // Non constant shift counts
         var c = shifts[i % shifts.length];
 
         assertEqX4(SIMD.Int32x4.shiftLeftByScalar(v, c), a.map(lsh(c)));
         assertEqX4(SIMD.Int32x4.shiftRightByScalar(v, c), a.map(rsh(c)));
 
         assertEqX4(SIMD.Uint32x4.shiftLeftByScalar(u, c), b.map(ulsh(c)));
--- a/js/src/jit-test/tests/asm.js/testSIMD.js
+++ b/js/src/jit-test/tests/asm.js/testSIMD.js
@@ -1077,21 +1077,18 @@ const RSHI = 'var rsh=i4.shiftRightBySca
 assertAsmTypeFail('glob', USE_ASM + I32 + CI32 + F32 + FROUND + LSHI + "function f() {var x=f4(1,2,3,4); return ci4(lsh(x,f32(42)));} return f");
 assertAsmTypeFail('glob', USE_ASM + I32 + CI32 + F32 + FROUND + LSHI + "function f() {var x=f4(1,2,3,4); return ci4(lsh(x,42));} return f");
 assertAsmTypeFail('glob', USE_ASM + I32 + CI32 + FROUND + LSHI + "function f() {var x=i4(1,2,3,4); return ci4(lsh(x,42.0));} return f");
 assertAsmTypeFail('glob', USE_ASM + I32 + CI32 + FROUND + LSHI + "function f() {var x=i4(1,2,3,4); return ci4(lsh(x,f32(42)));} return f");
 
 var input = 'i4(0, 1, ' + INT32_MIN + ', ' + INT32_MAX + ')';
 var vinput = [0, 1, INT32_MIN, INT32_MAX];
 
-// TODO: What to do for masks > 31? Should we keep only the five low bits of
-// the mask (JS) or not (x86)?
-// See bug 1246800.
-function Lsh(i) { if (i > 31) return () => 0; return function(x) { return (x << i) | 0 } }
-function Rsh(i) { if (i > 31) return (x) => (x<0)?-1:0; return function(x) { return (x >> i) | 0 } }
+function Lsh(i) { return function(x) { return (x << (i & 31)) | 0 } }
+function Rsh(i) { return function(x) { return (x >> (i & 31)) | 0 } }
 
 var asmLsh = asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + LSHI + 'function f(x, y){x=x|0;y=y|0; var v=' + input + ';return ci4(lsh(v, x+y))} return f;'), this)
 var asmRsh = asmLink(asmCompile('glob', USE_ASM + I32 + CI32 + RSHI + 'function f(x, y){x=x|0;y=y|0; var v=' + input + ';return ci4(rsh(v, x+y))} return f;'), this)
 
 for (var i = 1; i < 64; i++) {
     CheckI4(LSHI,  'var x=' + input + '; x=lsh(x, ' + i + ')',   vinput.map(Lsh(i)));
     CheckI4(RSHI,  'var x=' + input + '; x=rsh(x, ' + i + ')',   vinput.map(Rsh(i)));
 
@@ -1101,18 +1098,18 @@ for (var i = 1; i < 64; i++) {
 
 // Same thing for Uint32x4.
 const LSHU = 'var lsh=u4.shiftLeftByScalar;'
 const RSHU = 'var rsh=u4.shiftRightByScalar;'
 
 input = 'u4(0, 1, 0x80008000, ' + INT32_MAX + ')';
 vinput = [0, 1, 0x80008000, INT32_MAX];
 
-function uLsh(i) { if (i > 31) return () => 0; return function(x) { return (x << i) >>> 0 } }
-function uRsh(i) { if (i > 31) return () => 0; return function(x) { return (x >>> i) } }
+function uLsh(i) { return function(x) { return (x << (i & 31)) >>> 0 } }
+function uRsh(i) { return function(x) { return (x >>> (i & 31)) } }
 
 // Need to bitcast to Int32x4 before returning result.
 asmLsh = asmLink(asmCompile('glob', USE_ASM + U32 + CU32 + LSHU + I32 + CI32 + I32U32 +
                             'function f(x, y){x=x|0;y=y|0; var v=' + input + ';return ci4(i4u4(lsh(v, x+y)));} return f;'), this)
 asmRsh = asmLink(asmCompile('glob', USE_ASM + U32 + CU32 + RSHU + I32 + CI32 + I32U32 +
                             'function f(x, y){x=x|0;y=y|0; var v=' + input + ';return ci4(i4u4(rsh(v, x+y)));} return f;'), this)
 
 for (var i = 1; i < 64; i++) {
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/Debugger-findScripts-22.js
@@ -0,0 +1,8 @@
+// Bug 1239813: Don't let compartments get GC'd while findScripts is holding
+// them in its ScriptQuery's hash set.
+
+var g = newGlobal();
+var dbg = new Debugger();
+dbg.addDebuggee(g);
+g = newGlobal({sameZoneAs: g.Math});
+dbg.findScripts({get source() { gc(); return undefined; }});
deleted file mode 100644
--- a/js/src/jit-test/tests/debug/Memory-tenurePromotionsLog-01.js
+++ /dev/null
@@ -1,43 +0,0 @@
-// Test basic usage of drainTenurePromotionsLog() with various objects.
-
-const root = newGlobal();
-const dbg = new Debugger();
-const wrappedRoot = dbg.addDebuggee(root)
-
-dbg.memory.trackingAllocationSites = true;
-dbg.memory.trackingTenurePromotions = true;
-
-root.eval(`
-  (function immediate() {
-    this.tests = [
-      { name: "({})",       cls: "Object", obj: ({})                  },
-      { name: "[]",         cls: "Array",  obj: []                    },
-      { name: "new Object", cls: "Object", obj: new Object()          },
-      { name: "new Array",  cls: "Array",  obj: new Array()           },
-      { name: "new Date",   cls: "Date",   obj: new Date()            },
-    ];
-  }());
-  minorgc();
-`);
-
-const promotions = dbg.memory.drainTenurePromotionsLog();
-
-// Assert that the promotions happen in chronological order.
-let lastWhen = -Infinity;
-for (let p of promotions) {
-  assertEq(p.timestamp >= lastWhen, true);
-  lastWhen = p.timestamp;
-}
-
-for (let { name, cls, obj } of root.tests) {
-  print(name + ": " + cls);
-
-  // The promoted object's allocation site should be in the log.
-  let wrappedObj = wrappedRoot.makeDebuggeeValue(obj);
-  let allocSite = wrappedObj.allocationSite;
-  let idx = promotions.map(x => x.frame).indexOf(allocSite);
-  assertEq(idx >= 0, true);
-
-  // The promoted object's class name should be present.
-  assertEq(promotions.map(x => x.class).indexOf(cls) >= 0, true);
-}
deleted file mode 100644
--- a/js/src/jit-test/tests/debug/Memory-tenurePromotionsLog-02.js
+++ /dev/null
@@ -1,72 +0,0 @@
-// Test usage of drainTenurePromotionsLog() with multiple debuggees.
-
-const root1 = newGlobal();
-const root2 = newGlobal();
-const dbg = new Debugger();
-
-function tenureObjectsAndDrainLog(root) {
-  gc();
-
-  dbg.memory.trackingAllocationSites = true;
-  dbg.memory.trackingTenurePromotions = true;
-
-  root.eval(
-    `
-    (function immediate() {
-      this.tests = [
-        { name: "({})",       cls: "Object", obj: ({})                  },
-        { name: "[]",         cls: "Array",  obj: []                    },
-        { name: "new Object", cls: "Object", obj: new Object()          },
-        { name: "new Array",  cls: "Array",  obj: new Array()           },
-        { name: "new Date",   cls: "Date",   obj: new Date()            },
-      ];
-    }());
-    minorgc();
-    `
-  );
-
-  const promotions = dbg.memory.drainTenurePromotionsLog();
-
-  dbg.memory.trackingAllocationSites = false;
-  dbg.memory.trackingTenurePromotions = false;
-
-  return promotions;
-}
-
-{
-  dbg.addDebuggee(root1);
-  dbg.addDebuggee(root2);
-
-  // Should get logs from root1,
-  assertEq(!!tenureObjectsAndDrainLog(root1).length, true);
-  // and logs from root2.
-  assertEq(!!tenureObjectsAndDrainLog(root2).length, true);
-}
-
-{
-  dbg.removeAllDebuggees();
-
-  // Should *not* get logs from root1,
-  assertEq(!!tenureObjectsAndDrainLog(root1).length, false);
-  // nor logs from root2.
-  assertEq(!!tenureObjectsAndDrainLog(root2).length, false);
-}
-
-{
-  dbg.addDebuggee(root1);
-
-  // Should get logs from root1,
-  assertEq(!!tenureObjectsAndDrainLog(root1).length, true);
-  // but *not* logs from root2.
-  assertEq(!!tenureObjectsAndDrainLog(root2).length, false);
-}
-
-{
-  dbg.removeAllDebuggees();
-  dbg.addDebuggee(root2);
-
-  // Should *not* get logs from root1,
-  assertEq(!!tenureObjectsAndDrainLog(root1).length, false);
-  // but should get logs from root2.
-  assertEq(!!tenureObjectsAndDrainLog(root2).length, true);
-}
deleted file mode 100644
--- a/js/src/jit-test/tests/debug/Memory-tenurePromotionsLog-03.js
+++ /dev/null
@@ -1,81 +0,0 @@
-// Test usage of drainTenurePromotionsLog() with multiple debuggers.
-
-const root = newGlobal();
-const dbg1 = new Debugger(root);
-const dbg2 = new Debugger(root);
-
-function setTracking(dbg, bool) {
-  dbg.memory.trackingAllocationSites = bool;
-  dbg.memory.trackingTenurePromotions = bool;
-}
-
-function tenureObjectsAndDrainLog() {
-  gc();
-
-  root.eval(
-    `
-    (function immediate() {
-      this.tests = [
-        { name: "({})",       cls: "Object", obj: ({})                  },
-        { name: "[]",         cls: "Array",  obj: []                    },
-        { name: "new Object", cls: "Object", obj: new Object()          },
-        { name: "new Array",  cls: "Array",  obj: new Array()           },
-        { name: "new Date",   cls: "Date",   obj: new Date()            },
-      ];
-    }());
-    minorgc();
-    `
-  );
-
-  const promotions2 = dbg2.memory.trackingTenurePromotions ? dbg2.memory.drainTenurePromotionsLog() : [];
-  const promotions1 = dbg1.memory.trackingTenurePromotions ? dbg1.memory.drainTenurePromotionsLog() : [];
-
-  setTracking(dbg1, false);
-  setTracking(dbg2, false);
-
-  return { promotions1, promotions2 };
-}
-
-{
-  setTracking(dbg1, true);
-  setTracking(dbg2, true);
-  const { promotions1, promotions2 } = tenureObjectsAndDrainLog();
-
-  // Should get logs in dbg1,
-  assertEq(!!promotions1.length, true);
-  // and logs in dbg2.
-  assertEq(!!promotions2.length, true);
-}
-
-{
-  setTracking(dbg1, false);
-  setTracking(dbg2, false);
-  const { promotions1, promotions2 } = tenureObjectsAndDrainLog();
-
-  // Should *not* get logs in dbg1,
-  assertEq(!!promotions1.length, false);
-  // nor logs in dbg2.
-  assertEq(!!promotions2.length, false);
-}
-
-{
-  setTracking(dbg1, true);
-  setTracking(dbg2, false);
-  const { promotions1, promotions2 } = tenureObjectsAndDrainLog();
-
-  // Should get logs in dbg1,
-  assertEq(!!promotions1.length, true);
-  // but *not* logs in dbg2.
-  assertEq(!!promotions2.length, false);
-}
-
-{
-  setTracking(dbg1, false);
-  setTracking(dbg2, true);
-  const { promotions1, promotions2 } = tenureObjectsAndDrainLog();
-
-  // Should *not* get logs in dbg1,
-  assertEq(!!promotions1.length, false);
-  // but *should* get logs in dbg2.
-  assertEq(!!promotions2.length, true);
-}
deleted file mode 100644
--- a/js/src/jit-test/tests/debug/Memory-tenurePromotionsLog-04.js
+++ /dev/null
@@ -1,12 +0,0 @@
-// Test draining tenure log without tracking tenures.
-
-const root = newGlobal();
-const dbg = new Debugger(root);
-
-let threw = false;
-try {
-  dbg.drainTenurePromotionsLog();
-} catch (e) {
-  threw = true;
-}
-assertEq(threw, true);
deleted file mode 100644
--- a/js/src/jit-test/tests/debug/Memory-tenurePromotionsLog-05.js
+++ /dev/null
@@ -1,30 +0,0 @@
-// Test that objects that do not get tenured do not go in the log.
-
-// Can't let gc zeal mess with our assumptions about gc behavior here.
-gczeal(0);
-
-const root = newGlobal();
-const dbg = root.dbg = new Debugger(root);
-
-root.eval(
-  `
-  this.forceLazyInstantiation = new Date;
-  gc();
-
-  this.allocTemp = function allocTemp() {
-    gc();
-    this.dbg.memory.trackingTenurePromotions = true;
-    // Create an object, remove references to it, and then do a minor gc. It
-    // should not be in the tenure promotions log because it should have died in
-    // the nursery. We use a Date object so it is easier to find when asserting.
-    var d = new Date;
-    d = null;
-    minorgc();
-  };
-  `
-);
-
-root.allocTemp();
-
-const promotions = dbg.memory.drainTenurePromotionsLog();
-assertEq(promotions.some(e => e.class === "Date"), false);
deleted file mode 100644
--- a/js/src/jit-test/tests/debug/Memory-tenurePromotionsLog-06.js
+++ /dev/null
@@ -1,73 +0,0 @@
-// Test usage of drainTenurePromotionsLog() with multiple debuggees in the same
-// zone.
-
-const root1 = newGlobal();
-const root2 = newGlobal({ sameZoneAs: root1 });
-const dbg = new Debugger();
-
-function tenureObjectsAndDrainLog(root) {
-  gc();
-
-  dbg.memory.trackingAllocationSites = true;
-  dbg.memory.trackingTenurePromotions = true;
-
-  root.eval(
-    `
-    (function immediate() {
-      this.tests = [
-        { name: "({})",       cls: "Object", obj: ({})                  },
-        { name: "[]",         cls: "Array",  obj: []                    },
-        { name: "new Object", cls: "Object", obj: new Object()          },
-        { name: "new Array",  cls: "Array",  obj: new Array()           },
-        { name: "new Date",   cls: "Date",   obj: new Date()            },
-      ];
-    }());
-    minorgc();
-    `
-  );
-
-  const promotions = dbg.memory.drainTenurePromotionsLog();
-
-  dbg.memory.trackingAllocationSites = false;
-  dbg.memory.trackingTenurePromotions = false;
-
-  return promotions;
-}
-
-{
-  dbg.addDebuggee(root1);
-  dbg.addDebuggee(root2);
-
-  // Should get logs from root1,
-  assertEq(!!tenureObjectsAndDrainLog(root1).length, true);
-  // and logs from root2.
-  assertEq(!!tenureObjectsAndDrainLog(root2).length, true);
-}
-
-{
-  dbg.removeAllDebuggees();
-
-  // Should *not* get logs from root1,
-  assertEq(!!tenureObjectsAndDrainLog(root1).length, false);
-  // nor logs from root2.
-  assertEq(!!tenureObjectsAndDrainLog(root2).length, false);
-}
-
-{
-  dbg.addDebuggee(root1);
-
-  // Should get logs from root1,
-  assertEq(!!tenureObjectsAndDrainLog(root1).length, true);
-  // but *not* logs from root2.
-  assertEq(!!tenureObjectsAndDrainLog(root2).length, false);
-}
-
-{
-  dbg.removeAllDebuggees();
-  dbg.addDebuggee(root2);
-
-  // Should *not* get logs from root1,
-  assertEq(!!tenureObjectsAndDrainLog(root1).length, false);
-  // but should get logs from root2.
-  assertEq(!!tenureObjectsAndDrainLog(root2).length, true);
-}
deleted file mode 100644
--- a/js/src/jit-test/tests/debug/Memory-tenurePromotionsLog-07.js
+++ /dev/null
@@ -1,48 +0,0 @@
-// Test basic sizes of entries in the tenure promotions log. Does not attempt to
-// test the actual object sizes in depth, as heap-analysis/byteSize-of-object.js
-// does that pretty thoroughly, just that they are present where expected.
-
-const root = newGlobal();
-const dbg = root.dbg = new Debugger();
-const wrappedRoot = dbg.addDebuggee(root)
-
-root.eval(`
-  this.tests = [];
-
-  function Ctor() { }
-
-  function AsmModule(stdlib, foreign, heap) {
-    "use asm";
-
-    function test() {
-      return 5|0;
-    }
-
-    return { test: test };
-  }
-  const buf = new ArrayBuffer(1024*8);
-
-  (function immediate() {
-    this.dbg.memory.trackingTenurePromotions = true;
-
-    this.tests.push( new Object() );
-    this.tests.push( new Array() );
-    this.tests.push( new Uint8Array(256) );
-    this.tests.push( (function () { return arguments; }()) );
-    this.tests.push( AsmModule(this, {}, buf) );
-    this.tests.push( /2manyproblemz/g );
-    this.tests.push( [1,2,3][Symbol.iterator]() );
-    this.tests.push( Error() );
-    this.tests.push( new Ctor );
-    this.tests.push( {} );
-    this.tests.push( new Date );
-    this.tests.push( [1,2,3] );
-
-  }).call(this);
-
-  minorgc();
-`);
-
-const promotions = dbg.memory.drainTenurePromotionsLog();
-print(uneval(promotions));
-assertEq(promotions.every(p => p.size >= 1), true);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/bug1221378.js
@@ -0,0 +1,11 @@
+// Bug 1221378: allocating an array from within the object metadata callback
+// shouldn't cause re-entrant resolution of lazy prototypes.
+
+// To test for this regression, we need some way to make the code that collects
+// metadata about object allocations allocate an Array. Presently,
+// enableShellObjectMetadataCallback installs a callback that does this, but if
+// that hook gets removed (in production there's only ever one callback we
+// actually use), then the ability to make whatever metadata collection code
+// remains allocate an Array will cover this regression. For example, it could
+// be a flag that one can only set in debug builds from TestingFunctions.cpp.
+newGlobal().eval('enableShellObjectMetadataCallback(); Array');
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/class-01.js
@@ -0,0 +1,20 @@
+// |jit-test| error: TypeError
+
+let g = newGlobal();
+let dbg = Debugger(g);
+
+let forceException = g.eval(`
+    (class extends class {} {
+        // Calling this will throw for using |this| uninitialized.
+        constructor() { }
+    })
+`);
+
+dbg.onExceptionUnwind = function() {
+    return {
+        // Force the return of an illegal value.
+        return: 1
+    }
+}
+
+new forceException;
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/class-02.js
@@ -0,0 +1,20 @@
+// |jit-test| error: TypeError
+
+let g = newGlobal();
+let dbg = Debugger(g);
+
+let forceException = g.eval(`
+    (class extends class {} {
+        // Calling this will return a primitive immediately.
+        constructor() { return {}; }
+    })
+`);
+
+dbg.onEnterFrame = function() {
+    return {
+        // Force the return of an illegal value.
+        return: 1
+    }
+}
+
+new forceException;
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/class-03.js
@@ -0,0 +1,23 @@
+// |jit-test| error: TypeError
+
+let g = newGlobal();
+let dbg = Debugger(g);
+
+let forceException = g.eval(`
+    (class extends class {} {
+        // Calling this will return a primitive immediately.
+        constructor() {
+            debugger;
+            return {};
+        }
+    })
+`);
+
+dbg.onDebuggerStatement = function() {
+    return {
+        // Force the return of an illegal value.
+        return: 1
+    }
+}
+
+new forceException;
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/class-04.js
@@ -0,0 +1,22 @@
+// |jit-test| error: TypeError
+
+let g = newGlobal();
+let dbg = Debugger(g);
+
+let forceException = g.eval(`
+    (class extends class {} {
+        // Calling this will return a primitive on return.
+        constructor() { return {}; }
+    })
+`);
+
+dbg.onEnterFrame = function(f) {
+    f.onPop = function() {
+        return {
+            // Force the return of an illegal value.
+            return: 1
+        }
+    }
+}
+
+new forceException;
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/class-05.js
@@ -0,0 +1,31 @@
+// |jit-test| error: TypeError
+
+let g = newGlobal();
+let dbg = Debugger(g);
+
+let forceException = g.eval(`
+    (class extends class {} {
+        // Calling this will return a primitive immediately.
+        constructor() {
+            debugger;
+            return {};
+        }
+    })
+`);
+
+let handler = {
+    hit() {
+        return {
+            // Force the return of an illegal value.
+            return: 1
+        }
+    }
+};
+
+dbg.onDebuggerStatement = function(frame) {
+    var line0 = frame.script.getOffsetLocation(frame.offset).lineNumber;
+    var offs = frame.script.getLineOffsets(line0 + 1);
+    frame.script.setBreakpoint(offs[0], handler);
+}
+
+new forceException;
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/class-06.js
@@ -0,0 +1,22 @@
+// |jit-test| error: TypeError
+
+let g = newGlobal();
+let dbg = Debugger(g);
+
+let forceException = g.eval(`
+    (class extends class {} {
+        // Calling this will return a primitive immediately.
+        constructor() { return {}; }
+    })
+`);
+
+dbg.onEnterFrame = function(f) {
+    f.onStep = function() {
+        return {
+            // Force the return of an illegal value.
+            return: 1
+        }
+    }
+}
+
+new forceException;
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/class-07.js
@@ -0,0 +1,21 @@
+// |jit-test| error: ReferenceError
+
+let g = newGlobal();
+let dbg = Debugger(g);
+
+let forceException = g.eval(`
+    (class extends class {} {
+        // Calling this will return a primitive immediately.
+        constructor() { return {}; }
+    })
+`);
+
+dbg.onEnterFrame = function() {
+    return {
+        // Force the return undefined, which will throw for returning
+        // while |this| is still undefined.
+        return: undefined
+    }
+}
+print("break here");
+new forceException;
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug1244502.js
@@ -0,0 +1,12 @@
+function f(arg) {
+    bailout();
+    assertEq(arguments.length, 2);
+    assertEq(arg, "");
+    assertEq(arguments[0], "");
+    assertEq(arguments[1], 0);
+}
+for (var i = 0; i < 100; ++i) {
+    (function() {
+        f.call(1, "", 0);
+    })();
+}
--- a/js/src/jit-test/tests/ion/fold-in.js
+++ b/js/src/jit-test/tests/ion/fold-in.js
@@ -25,8 +25,20 @@ function g(arr) {
 	res += "x" in o;
 	res += "abcd" in o;
     }
     return res;
 }
 assertEq(g(arr), 250);
 arr[0].abcd = 3;
 assertEq(g(arr), 375);
+
+function testPrimitive() {
+    var x = 7;
+    var c = 0;
+    for (var i=0; i<5; i++) {
+	try {
+	    "z" in x;
+	} catch(e) { c++; }
+    }
+    assertEq(c, 5);
+}
+testPrimitive();
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/modules/global-scope.js
@@ -0,0 +1,32 @@
+// Test interaction with global object and global lexical scope.
+
+function parseAndEvaluate(source) {
+    let m = parseModule(source);
+    m.declarationInstantiation();
+    return m.evaluation();
+}
+
+var x = 1;
+assertEq(parseAndEvaluate("let r = x; x = 2; r"), 1);
+assertEq(x, 2);
+
+let y = 3;
+assertEq(parseAndEvaluate("let r = y; y = 4; r"), 3);
+assertEq(y, 4);
+
+if (helperThreadCount() == 0)
+    quit();
+
+function offThreadParseAndEvaluate(source) {
+    offThreadCompileModule(source);
+    let m = finishOffThreadModule();
+    print("compiled");
+    m.declarationInstantiation();
+    return m.evaluation();
+}
+
+assertEq(offThreadParseAndEvaluate("let r = x; x = 5; r"), 2);
+assertEq(x, 5);
+
+assertEq(offThreadParseAndEvaluate("let r = y; y = 6; r"), 4);
+assertEq(y, 6);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/modules/off-thread-compile.js
@@ -0,0 +1,16 @@
+// Test off thread module compilation.
+
+if (helperThreadCount() == 0)
+    quit();
+
+load(libdir + "asserts.js");
+load(libdir + "dummyModuleResolveHook.js");
+
+function offThreadParseAndEvaluate(source) {
+    offThreadCompileModule(source);
+    let m = finishOffThreadModule();
+    m.declarationInstantiation();
+    return m.evaluation();
+}
+
+offThreadParseAndEvaluate("export let x = 2 * 3;");
--- a/js/src/jit/BacktrackingAllocator.cpp
+++ b/js/src/jit/BacktrackingAllocator.cpp
@@ -1933,16 +1933,18 @@ BacktrackingAllocator::reifyAllocations(
                 // add copies if the use and def have different allocations.
                 LNode* ins = insData[iter->pos];
                 if (LDefinition* def = FindReusingDefinition(ins, alloc)) {
                     LiveRange* outputRange = vreg(def).rangeFor(outputOf(ins));
                     LAllocation res = outputRange->bundle()->allocation();
                     LAllocation sourceAlloc = range->bundle()->allocation();
 
                     if (res != *alloc) {
+                        if (!this->alloc().ensureBallast())
+                            return false;
                         LMoveGroup* group = getInputMoveGroup(ins->toInstruction());
                         if (!group->addAfter(sourceAlloc, res, reg.type()))
                             return false;
                         *alloc = res;
                     }
                 }
             }
 
--- a/js/src/jit/Bailouts.cpp
+++ b/js/src/jit/Bailouts.cpp
@@ -144,26 +144,24 @@ jit::InvalidationBailout(InvalidationBai
         //
         // However, if the bailout was during argument check, then a
         // pseudostack frame would not have been pushed in the first
         // place, so don't pop anything in that case.
         JSScript* script = iter.script();
         probes::ExitScript(cx, script, script->functionNonDelazifying(),
                            /* popSPSFrame = */ false);
 
+#ifdef JS_JITSPEW
         JitFrameLayout* frame = iter.jsFrame();
-        JitSpew(JitSpew_IonInvalidate, "Bailout failed (%s): converting to exit frame",
+        JitSpew(JitSpew_IonInvalidate, "Bailout failed (%s)",
                 (retval == BAILOUT_RETURN_FATAL_ERROR) ? "Fatal Error" : "Over Recursion");
-        JitSpew(JitSpew_IonInvalidate, "   orig calleeToken %p", (void*) frame->calleeToken());
-        JitSpew(JitSpew_IonInvalidate, "   orig frameSize %u", unsigned(frame->prevFrameLocalSize()));
-        JitSpew(JitSpew_IonInvalidate, "   orig ra %p", (void*) frame->returnAddress());
-
-        frame->replaceCalleeToken(nullptr);
-
-        JitSpew(JitSpew_IonInvalidate, "   new  calleeToken %p", (void*) frame->calleeToken());
+        JitSpew(JitSpew_IonInvalidate, "   calleeToken %p", (void*) frame->calleeToken());
+        JitSpew(JitSpew_IonInvalidate, "   frameSize %u", unsigned(frame->prevFrameLocalSize()));
+        JitSpew(JitSpew_IonInvalidate, "   ra %p", (void*) frame->returnAddress());
+#endif
     }
 
     iter.ionScript()->decrementInvalidationCount(cx->runtime()->defaultFreeOp());
 
     // Make the frame being bailed out the top profiled frame.
     if (cx->runtime()->jitRuntime()->isProfilerInstrumentationEnabled(cx->runtime()))
         cx->runtime()->jitActivation->setLastProfilingFrame(currentFramePtr);
 
--- a/js/src/jit/BaselineBailouts.cpp
+++ b/js/src/jit/BaselineBailouts.cpp
@@ -640,22 +640,16 @@ InitFromBailout(JSContext* cx, HandleScr
         return false;
     prevFramePtr = builder.virtualPointerAtStackOffset(0);
 
     // Write struct BaselineFrame.
     if (!builder.subtract(BaselineFrame::Size(), "BaselineFrame"))
         return false;
     BufferPointer<BaselineFrame> blFrame = builder.pointerAtStackOffset<BaselineFrame>(0);
 
-    // Initialize BaselineFrame::frameSize
-    uint32_t frameSize = BaselineFrame::Size() + BaselineFrame::FramePointerOffset +
-                         (sizeof(Value) * (script->nfixed() + exprStackSlots));
-    JitSpew(JitSpew_BaselineBailouts, "      FrameSize=%d", (int) frameSize);
-    blFrame->setFrameSize(frameSize);
-
     uint32_t flags = 0;
 
     // If we are bailing to a script whose execution is observed, mark the
     // baseline frame as a debuggee frame. This is to cover the case where we
     // don't rematerialize the Ion frame via the Debugger.
     if (script->isDebuggee())
         flags |= BaselineFrame::DEBUGGEE;
 
@@ -925,17 +919,25 @@ InitFromBailout(JSContext* cx, HandleScr
             }
         } else {
             v = iter.read();
         }
         if (!builder.writeValue(v, "StackValue"))
             return false;
     }
 
-    size_t endOfBaselineJSFrameStack = builder.framePushed();
+    // BaselineFrame::frameSize is the size of everything pushed since
+    // the builder.resetFramePushed() call.
+    uint32_t frameSize = builder.framePushed();
+    blFrame->setFrameSize(frameSize);
+    JitSpew(JitSpew_BaselineBailouts, "      FrameSize=%u", frameSize);
+
+    // numValueSlots() is based on the frame size, do some sanity checks.
+    MOZ_ASSERT(blFrame->numValueSlots() >= script->nfixed());
+    MOZ_ASSERT(blFrame->numValueSlots() <= script->nslots());
 
     // If we are resuming at a LOOPENTRY op, resume at the next op to avoid
     // a bailout -> enter Ion -> bailout loop with --ion-eager. See also
     // ThunkToInterpreter.
     //
     // The algorithm below is the "tortoise and the hare" algorithm. See bug
     // 994444 for more explanation.
     if (!resumeAfter) {
@@ -1221,23 +1223,25 @@ InitFromBailout(JSContext* cx, HandleScr
     if (!builder.writePtr(prevFramePtr, "PrevFramePtr"))
         return false;
     prevFramePtr = builder.virtualPointerAtStackOffset(0);
 
     // Write out actual arguments (and thisv), copied from unpacked stack of BaselineJS frame.
     // Arguments are reversed on the BaselineJS frame's stack values.
     MOZ_ASSERT(IsIonInlinablePC(pc));
     unsigned actualArgc;
+    Value callee;
     if (needToSaveArgs) {
         // For FUNAPPLY or an accessor, the arguments are not on the stack anymore,
         // but they are copied in a vector and are written here.
         if (op == JSOP_FUNAPPLY)
             actualArgc = blFrame->numActualArgs();
         else
             actualArgc = IsSetPropPC(pc);
+        callee = savedCallerArgs[0];
 
         // Align the stack based on the number of arguments.
         size_t afterFrameSize = (actualArgc + 1) * sizeof(Value) + JitFrameLayout::Size();
         if (!builder.maybeWritePadding(JitStackAlignment, afterFrameSize, "Padding"))
             return false;
 
         // Push arguments.
         MOZ_ASSERT(actualArgc + 2 <= exprStackSlots);
@@ -1255,22 +1259,26 @@ InitFromBailout(JSContext* cx, HandleScr
         }
 
         // Align the stack based on the number of arguments.
         size_t afterFrameSize = (actualArgc + 1 + pushedNewTarget) * sizeof(Value) +
                                 JitFrameLayout::Size();
         if (!builder.maybeWritePadding(JitStackAlignment, afterFrameSize, "Padding"))
             return false;
 
-        MOZ_ASSERT(actualArgc + 2 + pushedNewTarget <= exprStackSlots);
-        for (unsigned i = 0; i < actualArgc + 1 + pushedNewTarget; i++) {
-            size_t argSlot = (script->nfixed() + exprStackSlots) - (i + 1);
-            if (!builder.writeValue(*blFrame->valueSlot(argSlot), "ArgVal"))
+        // Copy the arguments and |this| from the BaselineFrame, in reverse order.
+        size_t valueSlot = blFrame->numValueSlots() - 1;
+        size_t calleeSlot = valueSlot - actualArgc - 1 - pushedNewTarget;
+
+        for (size_t i = valueSlot; i > calleeSlot; i--) {
+            if (!builder.writeValue(*blFrame->valueSlot(i), "ArgVal"))
                 return false;
         }
+
+        callee = *blFrame->valueSlot(calleeSlot);
     }
 
     // In case these arguments need to be copied on the stack again for a rectifier frame,
     // save the framePushed values here for later use.
     size_t endOfBaselineStubArgs = builder.framePushed();
 
     // Calculate frame size for descriptor.
     size_t baselineStubFrameSize = builder.framePushed() - startOfBaselineStubFrame;
@@ -1278,30 +1286,18 @@ InitFromBailout(JSContext* cx, HandleScr
                                                         JitFrame_BaselineStub,
                                                         JitFrameLayout::Size());
 
     // Push actual argc
     if (!builder.writeWord(actualArgc, "ActualArgc"))
         return false;
 
     // Push callee token (must be a JS Function)
-    Value callee;
-    if (needToSaveArgs) {
-        // The arguments of FUNAPPLY or inlined accessors are not writen to the stack.
-        // So get the callee from the specially saved vector.
-        callee = savedCallerArgs[0];
-    } else {
-        uint32_t calleeStackSlot = exprStackSlots - uint32_t(actualArgc + 2 + pushedNewTarget);
-        size_t calleeOffset = (builder.framePushed() - endOfBaselineJSFrameStack)
-            + ((exprStackSlots - (calleeStackSlot + 1)) * sizeof(Value));
-        callee = *builder.valuePointerAtStackOffset(calleeOffset);
-        JitSpew(JitSpew_BaselineBailouts, "      CalleeStackSlot=%d", (int) calleeStackSlot);
-    }
-    JitSpew(JitSpew_BaselineBailouts, "      Callee = %016llx", *((uint64_t*) &callee));
-    MOZ_ASSERT(callee.isObject() && callee.toObject().is<JSFunction>());
+    JitSpew(JitSpew_BaselineBailouts, "      Callee = %016llx", callee.asRawBits());
+
     JSFunction* calleeFun = &callee.toObject().as<JSFunction>();
     if (!builder.writePtr(CalleeToToken(calleeFun, JSOp(*pc) == JSOP_NEW), "CalleeToken"))
         return false;
     nextCallee.set(calleeFun);
 
     // Push BaselineStub frame descriptor
     if (!builder.writeWord(baselineStubFrameDescr, "Descriptor"))
         return false;
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -920,16 +920,19 @@ jit::EliminateDeadResumePointOperands(MI
                 if (mrp->block() != *block ||
                     !mrp->instruction() ||
                     mrp->instruction() == *ins ||
                     mrp->instruction()->id() <= maxDefinition)
                 {
                     continue;
                 }
 
+                if (!graph.alloc().ensureBallast())
+                    return false;
+
                 // Store an optimized out magic value in place of all dead
                 // resume point operands. Making any such substitution can in
                 // general alter the interpreter's behavior, even though the
                 // code is dead, as the interpreter will still execute opcodes
                 // whose effects cannot be observed. If the magic value value
                 // were to flow to, say, a dead property access the
                 // interpreter could throw an exception; we avoid this problem
                 // by removing dead operands before removing dead code.
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -13425,17 +13425,17 @@ IonBuilder::inTryFold(bool* emitted, MDe
     jsid propId;
     if (!idConst || !ValueToIdPure(idConst->toJSValue(), &propId))
         return true;
 
     if (propId != IdToTypeId(propId))
         return true;
 
     TemporaryTypeSet* types = obj->resultTypeSet();
-    if (!types || types->unknownObject())
+    if (!types || types->unknownObject() || types->getKnownMIRType() != MIRType_Object)
         return true;
 
     for (unsigned i = 0, count = types->getObjectCount(); i < count; i++) {
         TypeSet::ObjectKey* key = types->getObject(i);
         if (!key)
             continue;
 
         while (true) {
--- a/js/src/jit/JitFrames.cpp
+++ b/js/src/jit/JitFrames.cpp
@@ -683,17 +683,17 @@ HandleExceptionBaseline(JSContext* cx, c
                 // No need to increment the PCCounts number of execution here,
                 // as the interpreter increments any PCCounts if present.
                 MOZ_ASSERT_IF(script->hasScriptCounts(), script->maybeGetPCCounts(pc));
                 return;
             }
         }
 
         frameOk = HandleClosingGeneratorReturn(cx, frame.baselineFrame(), frameOk);
-        frameOk = Debugger::onLeaveFrame(cx, frame.baselineFrame(), frameOk);
+        frameOk = Debugger::onLeaveFrame(cx, frame.baselineFrame(), pc, frameOk);
     } else if (script->hasTrynotes()) {
         CloseLiveIteratorsBaselineForUncatchableException(cx, frame, pc);
     }
 
     OnLeaveBaselineFrame(cx, frame, pc, rfe, frameOk);
 }
 
 struct AutoDeleteDebugModeOSRInfo
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -4370,17 +4370,20 @@ void
 LIRGenerator::visitSimdShift(MSimdShift* ins)
 {
     MOZ_ASSERT(ins->type() == MIRType_Int32x4);
     MOZ_ASSERT(ins->lhs()->type() == MIRType_Int32x4);
     MOZ_ASSERT(ins->rhs()->type() == MIRType_Int32);
 
     LUse vector = useRegisterAtStart(ins->lhs());
     LAllocation value = useRegisterOrConstant(ins->rhs());
-    LSimdShift* lir = new(alloc()) LSimdShift(vector, value);
+    // We need a temp register to mask the shift amount, but not if the shift
+    // amount is a constant.
+    LDefinition tempReg = value.isConstant() ? LDefinition::BogusTemp() : temp();
+    LSimdShift* lir = new(alloc()) LSimdShift(vector, value, tempReg);
     defineReuseInput(lir, ins, 0);
 }
 
 void
 LIRGenerator::visitLexicalCheck(MLexicalCheck* ins)
 {
     MDefinition* input = ins->input();
     MOZ_ASSERT(input->type() == MIRType_Value);
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -705,17 +705,17 @@ DebugEpilogueOnBaselineReturn(JSContext*
 }
 
 bool
 DebugEpilogue(JSContext* cx, BaselineFrame* frame, jsbytecode* pc, bool ok)
 {
     // If Debugger::onLeaveFrame returns |true| we have to return the frame's
     // return value. If it returns |false|, the debugger threw an exception.
     // In both cases we have to pop debug scopes.
-    ok = Debugger::onLeaveFrame(cx, frame, ok);
+    ok = Debugger::onLeaveFrame(cx, frame, pc, ok);
 
     // Unwind to the outermost scope and set pc to the end of the script,
     // regardless of error.
     ScopeIter si(cx, frame, pc);
     UnwindAllScopesInFrame(cx, si);
     JSScript* script = frame->script();
     frame->setOverridePc(script->lastPC());
 
--- a/js/src/jit/ValueNumbering.cpp
+++ b/js/src/jit/ValueNumbering.cpp
@@ -434,16 +434,17 @@ ValueNumberer::fixupOSROnlyLoop(MBasicBl
     MBasicBlock* fake = MBasicBlock::NewAsmJS(graph_, block->info(),
                                               nullptr, MBasicBlock::NORMAL);
     if (fake == nullptr)
         return false;
 
     graph_.insertBlockBefore(block, fake);
     fake->setImmediateDominator(fake);
     fake->addNumDominated(1);
+    fake->setDomIndex(fake->id());
 
     // Create zero-input phis to use as inputs for any phis in |block|.
     // Again, this is a little odd, but it's the least-odd thing we can do
     // without significant complexity.
     for (MPhiIterator iter(block->phisBegin()), end(block->phisEnd()); iter != end; ++iter) {
         MPhi* phi = *iter;
         MPhi* fakePhi = MPhi::New(graph_.alloc(), phi->type());
         fake->addPhi(fakePhi);
--- a/js/src/jit/arm/Assembler-arm.cpp
+++ b/js/src/jit/arm/Assembler-arm.cpp
@@ -2772,18 +2772,22 @@ Assembler::bind(Label* label, BufferOffs
     if (label->used()) {
         bool more;
         // If our caller didn't give us an explicit target to bind to then we
         // want to bind to the location of the next instruction.
         BufferOffset dest = boff.assigned() ? boff : nextOffset();
         BufferOffset b(label);
         do {
             // Even a 0 offset may be invalid if we're out of memory.
-            if (oom())
+            if (oom()) {
+                // Ensure we always bind the label. This matches what we do on
+                // x86/x64 and silences the assert in ~Label.
+                label->bind(0);
                 return;
+            }
             BufferOffset next;
             more = nextLink(b, &next);
             Instruction branch = *editSrc(b);
             Condition c = branch.extractCond();
             if (branch.is<InstBImm>())
                 as_b(dest.diffB<BOffImm>(b), c, b);
             else if (branch.is<InstBLImm>())
                 as_bl(dest.diffB<BOffImm>(b), c, b);
--- a/js/src/jit/shared/LIR-shared.h
+++ b/js/src/jit/shared/LIR-shared.h
@@ -573,30 +573,37 @@ class LSimdBinaryBitwiseX4 : public LIns
     const char* extraName() const {
         return MSimdBinaryBitwise::OperationName(operation());
     }
     MIRType type() const {
         return mir_->type();
     }
 };
 
-class LSimdShift : public LInstructionHelper<1, 2, 0>
+// Shift a SIMD vector by a scalar amount.
+// The temp register is only required if the shift amount is a dynamical
+// value. If it is a constant, use a BogusTemp instead.
+class LSimdShift : public LInstructionHelper<1, 2, 1>
 {
   public:
     LIR_HEADER(SimdShift)
-    LSimdShift(const LAllocation& vec, const LAllocation& val) {
+    LSimdShift(const LAllocation& vec, const LAllocation& val, const LDefinition& temp) {
         setOperand(0, vec);
         setOperand(1, val);
+        setTemp(0, temp);
     }
     const LAllocation* vector() {
         return getOperand(0);
     }
     const LAllocation* value() {
         return getOperand(1);
     }
+    const LDefinition* temp() {
+        return getTemp(0);
+    }
     MSimdShift::Operation operation() const {
         return mir_->toSimdShift()->operation();
     }
     const char* extraName() const {
         return MSimdShift::OperationName(operation());
     }
     MSimdShift* mir() const {
         return mir_->toSimdShift();
--- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp
+++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp
@@ -3428,51 +3428,42 @@ CodeGeneratorX86Shared::visitSimdBinaryB
 }
 
 void
 CodeGeneratorX86Shared::visitSimdShift(LSimdShift* ins)
 {
     FloatRegister out = ToFloatRegister(ins->output());
     MOZ_ASSERT(ToFloatRegister(ins->vector()) == out); // defineReuseInput(0);
 
-    // If the shift count is greater than 31, this will just zero all lanes by
-    // default for lsh and ursh, and for rsh extend the sign bit to all bits,
-    // per the SIMD.js spec (as of March 19th 2015).
+    // If the shift count is out of range, only use the low 5 bits.
     const LAllocation* val = ins->value();
     if (val->isConstant()) {
-        uint32_t c = uint32_t(ToInt32(val));
-        if (c > 31) {
-            switch (ins->operation()) {
-              case MSimdShift::lsh:
-              case MSimdShift::ursh:
-                masm.zeroInt32x4(out);
-                return;
-              default:
-                c = 31;
-                break;
-            }
-        }
-        Imm32 count(c);
+        MOZ_ASSERT(ins->temp()->isBogusTemp());
+        Imm32 count(uint32_t(ToInt32(val)) % 32);
         switch (ins->operation()) {
           case MSimdShift::lsh:
             masm.packedLeftShiftByScalar(count, out);
             return;
           case MSimdShift::rsh:
             masm.packedRightShiftByScalar(count, out);
             return;
           case MSimdShift::ursh:
             masm.packedUnsignedRightShiftByScalar(count, out);
             return;
         }
         MOZ_CRASH("unexpected SIMD bitwise op");
     }
 
+    // Truncate val to 5 bits. We should have a temp register for that.
     MOZ_ASSERT(val->isRegister());
+    Register count = ToRegister(ins->temp());
+    masm.mov(ToRegister(val), count);
+    masm.andl(Imm32(31), count);
     ScratchFloat32Scope scratch(masm);
-    masm.vmovd(ToRegister(val), scratch);
+    masm.vmovd(count, scratch);
 
     switch (ins->operation()) {
       case MSimdShift::lsh:
         masm.packedLeftShiftByScalar(scratch, out);
         return;
       case MSimdShift::rsh:
         masm.packedRightShiftByScalar(scratch, out);
         return;
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -4128,21 +4128,21 @@ JS_PUBLIC_API(JSScript*)
 JS::FinishOffThreadScript(JSContext* maybecx, JSRuntime* rt, void* token)
 {
     MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt));
 
     if (maybecx) {
         RootedScript script(maybecx);
         {
             AutoLastFrameCheck lfc(maybecx);
-            script = HelperThreadState().finishParseTask(maybecx, rt, token);
+            script = HelperThreadState().finishScriptParseTask(maybecx, rt, token);
         }
         return script;
     } else {
-        return HelperThreadState().finishParseTask(maybecx, rt, token);
+        return HelperThreadState().finishScriptParseTask(maybecx, rt, token);
     }
 }
 
 JS_PUBLIC_API(bool)
 JS_CompileScript(JSContext* cx, const char* ascii, size_t length,
                  const JS::CompileOptions& options, MutableHandleScript script)
 {
     return Compile(cx, options, ascii, length, script);
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -881,17 +881,17 @@ JSCompartment::setObjectMetadataCallback
 void
 JSCompartment::clearObjectMetadata()
 {
     js_delete(objectMetadataTable);
     objectMetadataTable = nullptr;
 }
 
 void
-JSCompartment::setNewObjectMetadata(JSContext* cx, JSObject* obj)
+JSCompartment::setNewObjectMetadata(JSContext* cx, HandleObject obj)
 {
     assertSameCompartment(cx, this, obj);
 
     if (JSObject* metadata = objectMetadataCallback(cx, obj)) {
         AutoEnterOOMUnsafeRegion oomUnsafe;
         assertSameCompartment(cx, metadata);
         if (!objectMetadataTable) {
             objectMetadataTable = cx->new_<ObjectWeakMap>(cx);
@@ -1188,20 +1188,33 @@ AutoSetNewObjectMetadata::AutoSetNewObje
 AutoSetNewObjectMetadata::~AutoSetNewObjectMetadata()
 {
     // If we don't have a cx, we didn't change the metadata state, so no need to
     // reset it here.
     if (!cx_)
         return;
 
     if (!cx_->isExceptionPending() && cx_->compartment()->hasObjectPendingMetadata()) {
+        // This destructor often runs upon exit from a function that is
+        // returning an unrooted pointer to a Cell. The allocation metadata
+        // callback often allocates; if it causes a GC, then the Cell pointer
+        // being returned won't be traced or relocated.
+        //
+        // The only extant callbacks are those internal to SpiderMonkey that
+        // capture the JS stack. In fact, we're considering removing general
+        // callbacks altogther in bug 1236748. Since it's not running arbitrary
+        // code, it's adequate to simply suppress GC while we run the callback.
+        AutoSuppressGC autoSuppressGC(cx_);
+
         JSObject* obj = cx_->compartment()->objectMetadataState.as<PendingMetadata>();
+
         // Make sure to restore the previous state before setting the object's
         // metadata. SetNewObjectMetadata asserts that the state is not
         // PendingMetadata in order to ensure that metadata callbacks are called
         // in order.
         cx_->compartment()->objectMetadataState = prevState_;
-        SetNewObjectMetadata(cx_, obj);
+
+        obj = SetNewObjectMetadata(cx_, obj);
     } else {
         cx_->compartment()->objectMetadataState = prevState_;
     }
 }
 
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -583,17 +583,17 @@ struct JSCompartment
     void fixupGlobal();
 
     bool hasObjectMetadataCallback() const { return objectMetadataCallback; }
     js::ObjectMetadataCallback getObjectMetadataCallback() const { return objectMetadataCallback; }
     void setObjectMetadataCallback(js::ObjectMetadataCallback callback);
     void forgetObjectMetadataCallback() {
         objectMetadataCallback = nullptr;
     }
-    void setNewObjectMetadata(JSContext* cx, JSObject* obj);
+    void setNewObjectMetadata(JSContext* cx, JS::HandleObject obj);
     void clearObjectMetadata();
     const void* addressOfMetadataCallback() const {
         return &objectMetadataCallback;
     }
 
     js::SavedStacks& savedStacks() { return savedStacks_; }
 
     void findOutgoingEdges(js::gc::ComponentFinder<JS::Zone>& finder);
@@ -935,11 +935,32 @@ class MOZ_RAII AutoWrapperRooter : priva
 
     friend void JS::AutoGCRooter::trace(JSTracer* trc);
 
   private:
     WrapperValue value;
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
+class MOZ_RAII AutoSuppressObjectMetadataCallback {
+    JS::Zone* zone;
+    bool saved;
+
+  public:
+    explicit AutoSuppressObjectMetadataCallback(ExclusiveContext* cx)
+      : AutoSuppressObjectMetadataCallback(cx->compartment()->zone())
+    { }
+
+    explicit AutoSuppressObjectMetadataCallback(JS::Zone* zone)
+      : zone(zone),
+        saved(zone->suppressObjectMetadataCallback)
+    {
+        zone->suppressObjectMetadataCallback = true;
+    }
+
+    ~AutoSuppressObjectMetadataCallback() {
+        zone->suppressObjectMetadataCallback = saved;
+    }
+};
+
 } /* namespace js */
 
 #endif /* jscompartment_h */
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -133,18 +133,17 @@ JS_NewObjectWithUniqueType(JSContext* cx
     if (!JS_SplicePrototype(cx, obj, proto))
         return nullptr;
     return obj;
 }
 
 JS_FRIEND_API(JSObject*)
 JS_NewObjectWithoutMetadata(JSContext* cx, const JSClass* clasp, JS::Handle<JSObject*> proto)
 {
-    // Use an AutoEnterAnalysis to suppress invocation of the metadata callback.
-    AutoEnterAnalysis enter(cx);
+    AutoSuppressObjectMetadataCallback suppressMetadata(cx);
     return JS_NewObjectWithGivenProto(cx, clasp, proto);
 }
 
 JS_FRIEND_API(JSPrincipals*)
 JS_GetCompartmentPrincipals(JSCompartment* compartment)
 {
     return compartment->principals();
 }
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -2671,17 +2671,17 @@ class MOZ_RAII JS_FRIEND_API(AutoCTypesA
         if (callback) {
             callback(cx, endType);
             callback = nullptr;
         }
     }
 };
 
 typedef JSObject*
-(* ObjectMetadataCallback)(JSContext* cx, JSObject* obj);
+(* ObjectMetadataCallback)(JSContext* cx, JS::HandleObject obj);
 
 /**
  * Specify a callback to invoke when creating each JS object in the current
  * compartment, which may return a metadata object to associate with the
  * object.
  */
 JS_FRIEND_API(void)
 SetObjectMetadataCallback(JSContext* cx, ObjectMetadataCallback callback);
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -270,35 +270,47 @@ ClassCanHaveFixedData(const Class* clasp
     // arrays we only use enough to cover the class reserved slots, so that
     // the remaining space in the object's allocation is available for the
     // buffer's data.
     return !clasp->isNative()
         || clasp == &js::ArrayBufferObject::class_
         || js::IsTypedArrayClass(clasp);
 }
 
-static MOZ_ALWAYS_INLINE void
+// This function is meant to be called from allocation fast paths.
+//
+// If we do have an allocation metadata hook, it can cause a GC, so the object
+// must be rooted. The usual way to do this would be to make our callers pass a
+// HandleObject, but that would require them to pay the cost of rooting the
+// object unconditionally, even though collecting metadata is rare. Instead,
+// SetNewObjectMetadata's contract is that the caller must use the pointer
+// returned in place of the pointer passed. If a GC occurs, the returned pointer
+// may be the passed pointer, relocated by GC. If no GC could occur, it's just
+// passed through. We root nothing unless necessary.
+static MOZ_ALWAYS_INLINE MOZ_WARN_UNUSED_RESULT JSObject*
 SetNewObjectMetadata(ExclusiveContext* cxArg, JSObject* obj)
 {
     MOZ_ASSERT(!cxArg->compartment()->hasObjectPendingMetadata());
 
     // The metadata callback is invoked for each object created on the main
     // thread, except when analysis/compilation is active, to avoid recursion.
     if (JSContext* cx = cxArg->maybeJSContext()) {
         if (MOZ_UNLIKELY((size_t)cx->compartment()->hasObjectMetadataCallback()) &&
-            !cx->zone()->types.activeAnalysis)
+            !cx->zone()->suppressObjectMetadataCallback)
         {
-            // Use AutoEnterAnalysis to prohibit both any GC activity under the
-            // callback, and any reentering of JS via Invoke() etc.
-            AutoEnterAnalysis enter(cx);
+            // Don't collect metadata on objects that represent metadata.
+            AutoSuppressObjectMetadataCallback suppressMetadata(cx);
 
-            RootedObject hobj(cx, obj);
-            cx->compartment()->setNewObjectMetadata(cx, hobj);
+            RootedObject rooted(cx, obj);
+            cx->compartment()->setNewObjectMetadata(cx, rooted);
+            return rooted;
         }
     }
+
+    return obj;
 }
 
 } // namespace js
 
 /* static */ inline JSObject*
 JSObject::create(js::ExclusiveContext* cx, js::gc::AllocKind kind, js::gc::InitialHeap heap,
                  js::HandleShape shape, js::HandleObjectGroup group)
 {
@@ -347,22 +359,27 @@ JSObject::create(js::ExclusiveContext* c
 
     // JSFunction's fixed slots expect POD-style initialization.
     if (group->clasp()->isJSFunction()) {
         MOZ_ASSERT(kind == js::gc::AllocKind::FUNCTION ||
                    kind == js::gc::AllocKind::FUNCTION_EXTENDED);
         size_t size =
             kind == js::gc::AllocKind::FUNCTION ? sizeof(JSFunction) : sizeof(js::FunctionExtended);
         memset(obj->as<JSFunction>().fixedSlots(), 0, size - sizeof(js::NativeObject));
+        if (kind == js::gc::AllocKind::FUNCTION_EXTENDED) {
+            // SetNewObjectMetadata may gc, which will be unhappy if flags &
+            // EXTENDED doesn't match the arena's AllocKind.
+            obj->as<JSFunction>().setFlags(JSFunction::EXTENDED);
+        }
     }
 
     if (group->clasp()->shouldDelayMetadataCallback())
         cx->compartment()->setObjectPendingMetadata(cx, obj);
     else
-        SetNewObjectMetadata(cx, obj);
+        obj = SetNewObjectMetadata(cx, obj);
 
     js::gc::TraceCreateObject(obj);
 
     return obj;
 }
 
 inline void
 JSObject::setInitialShapeMaybeNonNative(js::Shape* shape)
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -3419,17 +3419,16 @@ ParseModule(JSContext* cx, unsigned argc
     }
 
     if (!args[0].isString()) {
         const char* typeName = InformalValueTypeName(args[0]);
         JS_ReportError(cx, "expected string to compile, got %s", typeName);
         return false;
     }
 
-    RootedObject global(cx, JS::CurrentGlobalOrNull(cx));
     JSFlatString* scriptContents = args[0].toString()->ensureFlat(cx);
     if (!scriptContents)
         return false;
 
     UniqueChars filename;
     CompileOptions options(cx);
     if (args.length() > 1) {
         if (!args[1].isString()) {
@@ -3451,17 +3450,17 @@ ParseModule(JSContext* cx, unsigned argc
     AutoStableStringChars stableChars(cx);
     if (!stableChars.initTwoByte(cx, scriptContents))
         return false;
 
     const char16_t* chars = stableChars.twoByteRange().start().get();
     SourceBufferHolder srcBuf(chars, scriptContents->length(),
                               SourceBufferHolder::NoOwnership);
 
-    RootedObject module(cx, frontend::CompileModule(cx, global, options, srcBuf));
+    RootedObject module(cx, frontend::CompileModule(cx, options, srcBuf));
     if (!module)
         return false;
 
     args.rval().setObject(*module);
     return true;
 }
 
 static bool
@@ -3785,16 +3784,119 @@ runOffThreadScript(JSContext* cx, unsign
 
     RootedScript script(cx, JS::FinishOffThreadScript(cx, rt, token));
     if (!script)
         return false;
 
     return JS_ExecuteScript(cx, script, args.rval());
 }
 
+static bool
+CompileOffThreadModule(JSContext* cx, const ReadOnlyCompileOptions& options,
+                       const char16_t* chars, size_t length,
+                       JS::OffThreadCompileCallback callback, void* callbackData)
+{
+    MOZ_ASSERT(JS::CanCompileOffThread(cx, options, length));
+    return StartOffThreadParseModule(cx, options, chars, length, callback, callbackData);
+}
+
+static JSObject*
+FinishOffThreadModule(JSContext* maybecx, JSRuntime* rt, void* token)
+{
+    MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt));
+    return HelperThreadState().finishModuleParseTask(maybecx, rt, token);
+}
+
+static bool
+OffThreadCompileModule(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    if (args.length() != 1 || !args[0].isString()) {
+        JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS,
+                             "offThreadCompileModule");
+        return false;
+    }
+
+    JSAutoByteString fileNameBytes;
+    CompileOptions options(cx);
+    options.setIntroductionType("js shell offThreadCompileModule")
+           .setFileAndLine("<string>", 1);
+    options.setIsRunOnce(true)
+           .setSourceIsLazy(false);
+    options.forceAsync = true;
+
+    JSString* scriptContents = args[0].toString();
+    AutoStableStringChars stableChars(cx);
+    if (!stableChars.initTwoByte(cx, scriptContents))
+        return false;
+
+    size_t length = scriptContents->length();
+    const char16_t* chars = stableChars.twoByteRange().start().get();
+
+    // Make sure we own the string's chars, so that they are not freed before
+    // the compilation is finished.
+    ScopedJSFreePtr<char16_t> ownedChars;
+    if (stableChars.maybeGiveOwnershipToCaller()) {
+        ownedChars = const_cast<char16_t*>(chars);
+    } else {
+        char16_t* copy = cx->pod_malloc<char16_t>(length);
+        if (!copy)
+            return false;
+
+        mozilla::PodCopy(copy, chars, length);
+        ownedChars = copy;
+        chars = copy;
+    }
+
+    if (!JS::CanCompileOffThread(cx, options, length)) {
+        JS_ReportError(cx, "cannot compile code on worker thread");
+        return false;
+    }
+
+    if (!offThreadState.startIfIdle(cx, ownedChars)) {
+        JS_ReportError(cx, "called offThreadCompileModule without receiving prior off-thread "
+                       "compilation");
+        return false;
+    }
+
+    if (!CompileOffThreadModule(cx, options, chars, length,
+                                OffThreadCompileScriptCallback, nullptr))
+    {
+        offThreadState.abandon(cx);
+        return false;
+    }
+
+    args.rval().setUndefined();
+    return true;
+}
+
+static bool
+FinishOffThreadModule(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    JSRuntime* rt = cx->runtime();
+    if (OffThreadParsingMustWaitForGC(rt))
+        gc::AutoFinishGC finishgc(rt);
+
+    void* token = offThreadState.waitUntilDone(cx);
+    if (!token) {
+        JS_ReportError(cx, "called finishOffThreadModule when no compilation is pending");
+        return false;
+    }
+
+    RootedObject module(cx, FinishOffThreadModule(cx, rt, token));
+    if (!module)
+        return false;
+
+    args.rval().setObject(*module);
+    return true;
+}
+
 struct MOZ_RAII FreeOnReturn
 {
     JSContext* cx;
     const char* ptr;
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 
     explicit FreeOnReturn(JSContext* cx, const char* ptr = nullptr
                  MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
@@ -5200,16 +5302,26 @@ static const JSFunctionSpecWithHelp shel
 "         Debugger.Source.prototype.elementAttributeName returns.\n"),
 
     JS_FN_HELP("runOffThreadScript", runOffThreadScript, 0, 0,
 "runOffThreadScript()",
 "  Wait for off-thread compilation to complete. If an error occurred,\n"
 "  throw the appropriate exception; otherwise, run the script and return\n"
 "  its value."),
 
+    JS_FN_HELP("offThreadCompileModule", OffThreadCompileModule, 1, 0,
+"offThreadCompileModule(code)",
+"  Compile |code| on a helper thread. To wait for the compilation to finish\n"
+"  and get the module object, call |finishOffThreadModule|."),
+
+    JS_FN_HELP("finishOffThreadModule", FinishOffThreadModule, 0, 0,
+"finishOffThreadModule()",
+"  Wait for off-thread compilation to complete. If an error occurred,\n"
+"  throw the appropriate exception; otherwise, return the module object"),
+
     JS_FN_HELP("timeout", Timeout, 1, 0,
 "timeout([seconds], [func])",
 "  Get/Set the limit in seconds for the execution time for the current context.\n"
 "  A negative value (default) means that the execution time is unlimited.\n"
 "  If a second argument is provided, it will be invoked when the timer elapses.\n"
 "  Calling this function will replace any callback set by |setInterruptCallback|.\n"),
 
     JS_FN_HELP("interruptIf", InterruptIf, 1, 0,
--- a/js/src/tests/ecma_7/SIMD/shifts.js
+++ b/js/src/tests/ecma_7/SIMD/shifts.js
@@ -9,78 +9,78 @@ var Int8x16 = SIMD.Int8x16;
 var Int16x8 = SIMD.Int16x8;
 var Int32x4 = SIMD.Int32x4;
 var Uint8x16 = SIMD.Uint8x16;
 var Uint16x8 = SIMD.Uint16x8;
 var Uint32x4 = SIMD.Uint32x4;
 
 // Int8 shifts.
 function lsh8(a, b) {
-    return (b >>> 0) >= 8 ? 0 : (a << b) << 24 >> 24;
+    return (a << (b & 7)) << 24 >> 24;
 }
 function rsha8(a, b) {
-    return (a >> Math.min(b >>> 0, 7)) << 24 >> 24;
+    return (a >> (b & 7)) << 24 >> 24;
 }
 function rshl8(a, b) {
-    return (b >>> 0) >= 8 ? 0 : (a >>> b) << 24 >> 24;
+    return (a >>> (b & 7)) << 24 >> 24;
 }
 
 // Int16 shifts.
 function lsh16(a, b) {
-    return (b >>> 0) >= 16 ? 0 : (a << b) << 16 >> 16;
+    return (a << (b & 15)) << 16 >> 16;
 }
 function rsha16(a, b) {
-    return (a >> Math.min(b >>> 0, 15)) << 16 >> 16;
+    return (a >> (b & 15)) << 16 >> 16;
 }
 function rshl16(a, b) {
-    return (b >>> 0) >= 16 ? 0 : (a >>> b) << 16 >> 16;
+    return (a >>> (b & 15)) << 16 >> 16;
 }
 
 // Int32 shifts.
 function lsh32(a, b) {
-    return (b >>> 0) >= 32 ? 0 : (a << b) | 0;
+    return (a << (b & 31)) | 0;
 }
 function rsha32(a, b) {
-    return (a >> Math.min(b >>> 0, 31)) | 0;
+    return (a >> (b & 31)) | 0;
 }
 function rshl32(a, b) {
-    return (b >>> 0) >= 32 ? 0 : (a >>> b) | 0;
+    return (a >>> (b & 31)) | 0;
 }
 
 // Uint8 shifts.
 function ulsh8(a, b) {
-    return (b >>> 0) >= 8 ? 0 : (a << b) << 24 >>> 24;
+    return (a << (b & 7)) << 24 >>> 24;
 }
 function ursha8(a, b) {
-    return ((a << 24 >> 24) >> Math.min(b >>> 0, 7)) << 24 >>> 24;
+    return ((a << 24 >> 24) >> (b & 7)) << 24 >>> 24;
 }
 function urshl8(a, b) {
-    return (b >>> 0) >= 8 ? 0 : (a >>> b) << 24 >>> 24;
+    return (a >>> (b & 7)) << 24 >>> 24;
 }
 
 // Uint16 shifts.
 function ulsh16(a, b) {
-    return (b >>> 0) >= 16 ? 0 : (a << b) << 16 >>> 16;
+    return (a << (b & 15)) << 16 >>> 16;
 }
 function ursha16(a, b) {
-    return ((a << 16 >> 16) >> Math.min(b >>> 0, 15)) << 16 >>> 16;
+    return ((a << 16 >> 16) >> (b & 15)) << 16 >>> 16;
 }
 function urshl16(a, b) {
-    return (b >>> 0) >= 16 ? 0 : (a >>> b) << 16 >>> 16;
+    return (a >>> (b & 15)) << 16 >>> 16;
 }
 
 // Uint32 shifts.
 function ulsh32(a, b) {
-    return (b >>> 0) >= 32 ? 0 : (a << b) >>> 0;
+    return (a << (b & 31)) >>> 0;
 }
 function ursha32(a, b) {
-    return ((a | 0) >> Math.min(b >>> 0, 31)) >>> 0;
+    return ((a | 0) >> (b & 31)) >>> 0;
 }
 function urshl32(a, b) {
-    return (b >>> 0) >= 32 ? 0 : (a >>> b) >>> 0;
+    return (a >>> (b & 31)) >>> 0;
 }
 
 function test() {
   function TestError() {};
 
   var good = {valueOf: () => 21};
   var bad = {valueOf: () => {throw new TestError(); }};
 
--- a/js/src/vm/Debugger-inl.h
+++ b/js/src/vm/Debugger-inl.h
@@ -7,26 +7,26 @@
 #ifndef vm_Debugger_inl_h
 #define vm_Debugger_inl_h
 
 #include "vm/Debugger.h"
 
 #include "vm/Stack-inl.h"
 
 /* static */ inline bool
-js::Debugger::onLeaveFrame(JSContext* cx, AbstractFramePtr frame, bool ok)
+js::Debugger::onLeaveFrame(JSContext* cx, AbstractFramePtr frame, jsbytecode* pc, bool ok)
 {
     MOZ_ASSERT_IF(frame.isInterpreterFrame(), frame.asInterpreterFrame() == cx->interpreterFrame());
     MOZ_ASSERT_IF(frame.script()->isDebuggee(), frame.isDebuggee());
     /* Traps must be cleared from eval frames, see slowPathOnLeaveFrame. */
     mozilla::DebugOnly<bool> evalTraps = frame.isEvalFrame() &&
                                          frame.script()->hasAnyBreakpointsOrStepMode();
     MOZ_ASSERT_IF(evalTraps, frame.isDebuggee());
     if (frame.isDebuggee())
-        ok = slowPathOnLeaveFrame(cx, frame, ok);
+        ok = slowPathOnLeaveFrame(cx, frame, pc, ok);
     MOZ_ASSERT(!inFrameMaps(frame));
     return ok;
 }
 
 /* static */ inline js::Debugger*
 js::Debugger::fromJSObject(const JSObject* obj)
 {
     MOZ_ASSERT(js::GetObjectClass(obj) == &jsclass);
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -508,20 +508,16 @@ Breakpoint::nextInSite()
 Debugger::Debugger(JSContext* cx, NativeObject* dbg)
   : object(dbg),
     debuggees(cx->runtime()),
     uncaughtExceptionHook(nullptr),
     enabled(true),
     allowUnobservedAsmJS(false),
     collectCoverageInfo(false),
     observedGCs(cx->runtime()),
-    tenurePromotionsLog(cx),
-    trackingTenurePromotions(false),
-    maxTenurePromotionsLogLength(DEFAULT_MAX_LOG_LENGTH),
-    tenurePromotionsLogOverflowed(false),
     allocationsLog(cx),
     trackingAllocationSites(false),
     allocationSamplingProbability(1.0),
     maxAllocationsLogLength(DEFAULT_MAX_LOG_LENGTH),
     allocationsLogOverflowed(false),
     frames(cx->runtime()),
     scripts(cx),
     sources(cx),
@@ -540,17 +536,16 @@ Debugger::Debugger(JSContext* cx, Native
     JS_INIT_CLIST(&breakpoints);
     JS_INIT_CLIST(&onNewGlobalObjectWatchersLink);
 }
 
 Debugger::~Debugger()
 {
     MOZ_ASSERT_IF(debuggees.initialized(), debuggees.empty());
     allocationsLog.clear();
-    tenurePromotionsLog.clear();
 
     /*
      * Since the inactive state for this link is a singleton cycle, it's always
      * safe to apply JS_REMOVE_LINK to it, regardless of whether we're in the list or not.
      *
      * We don't have to worry about locking here since Debugger is not
      * background finalized.
      */
@@ -739,17 +734,17 @@ static void
 DebuggerFrame_freeScriptFrameIterData(FreeOp* fop, JSObject* obj);
 
 /*
  * Handle leaving a frame with debuggers watching. |frameOk| indicates whether
  * the frame is exiting normally or abruptly. Set |cx|'s exception and/or
  * |cx->fp()|'s return value, and return a new success value.
  */
 /* static */ bool
-Debugger::slowPathOnLeaveFrame(JSContext* cx, AbstractFramePtr frame, bool frameOk)
+Debugger::slowPathOnLeaveFrame(JSContext* cx, AbstractFramePtr frame, jsbytecode* pc, bool frameOk)
 {
     Handle<GlobalObject*> global = cx->global();
 
     // The onPop handler and associated clean up logic should not run multiple
     // times on the same frame. If slowPathOnLeaveFrame has already been
     // called, the frame will not be present in the Debugger frame maps.
     FrameRange frameRange(frame, global);
     if (frameRange.empty())
@@ -783,34 +778,36 @@ Debugger::slowPathOnLeaveFrame(JSContext
 
         /* For each Debugger.Frame, fire its onPop handler, if any. */
         for (JSObject** p = frames.begin(); p != frames.end(); p++) {
             RootedNativeObject frameobj(cx, &(*p)->as<NativeObject>());
             Debugger* dbg = Debugger::fromChildJSObject(frameobj);
             EnterDebuggeeNoExecute nx(cx, *dbg);
 
             if (dbg->enabled &&
-                !frameobj->getReservedSlot(JSSLOT_DEBUGFRAME_ONPOP_HANDLER).isUndefined()) {
+                !frameobj->getReservedSlot(JSSLOT_DEBUGFRAME_ONPOP_HANDLER).isUndefined())
+            {
                 RootedValue handler(cx, frameobj->getReservedSlot(JSSLOT_DEBUGFRAME_ONPOP_HANDLER));
 
                 Maybe<AutoCompartment> ac;
                 ac.emplace(cx, dbg->object);
 
                 RootedValue completion(cx);
                 if (!dbg->newCompletionValue(cx, status, value, &completion)) {
                     status = dbg->handleUncaughtException(ac, false);
                     break;
                 }
 
                 /* Call the onPop handler. */
                 RootedValue rval(cx);
                 bool hookOk = Invoke(cx, ObjectValue(*frameobj), handler, 1, completion.address(),
                                      &rval);
                 RootedValue nextValue(cx);
-                JSTrapStatus nextStatus = dbg->parseResumptionValue(ac, hookOk, rval, &nextValue);
+                JSTrapStatus nextStatus = dbg->parseResumptionValue(ac, hookOk, rval,
+                                                                    frame, pc, &nextValue);
 
                 /*
                  * At this point, we are back in the debuggee compartment, and any error has
                  * been wrapped up as a completion value.
                  */
                 MOZ_ASSERT(cx->compartment() == global->compartment());
                 MOZ_ASSERT(!cx->isExceptionPending());
 
@@ -1145,34 +1142,38 @@ public:
 
 private:
     RootedValue& exn_;
 };
 } // anonymous namespace
 
 JSTrapStatus
 Debugger::handleUncaughtExceptionHelper(Maybe<AutoCompartment>& ac,
-                                        MutableHandleValue* vp, bool callHook)
+                                        MutableHandleValue* vp, bool callHook,
+                                        const Maybe<HandleValue>& thisVForCheck,
+                                        AbstractFramePtr frame)
 {
     JSContext* cx = ac->context()->asJSContext();
 
     // Uncaught exceptions arise from Debugger code, and so we must already be
     // in an NX section.
     MOZ_ASSERT(EnterDebuggeeNoExecute::isUniqueLockedInStack(cx, *this));
 
     if (cx->isExceptionPending()) {
         if (callHook && uncaughtExceptionHook) {
             RootedValue exc(cx);
             if (!cx->getPendingException(&exc))
                 return JSTRAP_ERROR;
             cx->clearPendingException();
             RootedValue fval(cx, ObjectValue(*uncaughtExceptionHook));
             RootedValue rv(cx);
-            if (Invoke(cx, ObjectValue(*object), fval, 1, exc.address(), &rv))
-                return vp ? parseResumptionValue(ac, true, rv, *vp, false) : JSTRAP_CONTINUE;
+            if (Invoke(cx, ObjectValue(*object), fval, 1, exc.address(), &rv)) {
+                return vp ? parseResumptionValueHelper(ac, true, rv, thisVForCheck, frame, *vp, false)
+                          : JSTRAP_CONTINUE;
+            }
         }
 
         if (cx->isExceptionPending()) {
             /*
              * We want to report the pending exception, but we want to let the
              * embedding handle it however it wants to.  So pretend like we're
              * starting a new script execution on our current compartment (which
              * is the debugger compartment, so reported errors won't get
@@ -1199,25 +1200,26 @@ Debugger::handleUncaughtExceptionHelper(
             cx->clearPendingException();
         }
     }
     ac.reset();
     return JSTRAP_ERROR;
 }
 
 JSTrapStatus
-Debugger::handleUncaughtException(Maybe<AutoCompartment>& ac, MutableHandleValue vp, bool callHook)
-{
-    return handleUncaughtExceptionHelper(ac, &vp, callHook);
+Debugger::handleUncaughtException(Maybe<AutoCompartment>& ac, MutableHandleValue vp, bool callHook,
+                                  const Maybe<HandleValue>& thisVForCheck, AbstractFramePtr frame)
+{
+    return handleUncaughtExceptionHelper(ac, &vp, callHook, thisVForCheck, frame);
 }
 
 JSTrapStatus
 Debugger::handleUncaughtException(Maybe<AutoCompartment>& ac, bool callHook)
 {
-    return handleUncaughtExceptionHelper(ac, nullptr, callHook);
+    return handleUncaughtExceptionHelper(ac, nullptr, callHook, mozilla::Nothing(), NullFramePtr());
 }
 
 /* static */ void
 Debugger::resultToCompletion(JSContext* cx, bool ok, const Value& rv,
                              JSTrapStatus* status, MutableHandleValue value)
 {
     MOZ_ASSERT_IF(ok, !cx->isExceptionPending());
 
@@ -1324,51 +1326,105 @@ ParseResumptionValueAsObject(JSContext* 
     if (hits != 1) {
         JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_BAD_RESUMPTION);
         return false;
     }
     return true;
 }
 
 JSTrapStatus
-Debugger::parseResumptionValue(Maybe<AutoCompartment>& ac, bool ok, const Value& rv, MutableHandleValue vp,
-                               bool callHook)
+Debugger::parseResumptionValueHelper(Maybe<AutoCompartment>& ac, bool ok, const Value& rv,
+                                     const Maybe<HandleValue>& thisVForCheck, AbstractFramePtr frame,
+                                     MutableHandleValue vp, bool callHook)
 {
     vp.setUndefined();
     if (!ok)
-        return handleUncaughtException(ac, vp, callHook);
+        return handleUncaughtException(ac, vp, callHook, thisVForCheck, frame);
     if (rv.isUndefined()) {
         ac.reset();
         return JSTRAP_CONTINUE;
     }
     if (rv.isNull()) {
         ac.reset();
         return JSTRAP_ERROR;
     }
 
     JSContext* cx = ac->context()->asJSContext();
     JSTrapStatus status = JSTRAP_CONTINUE;
     RootedValue v(cx);
     RootedValue rvRoot(cx, rv);
+
     if (!ParseResumptionValueAsObject(cx, rvRoot, &status, &v) ||
         !unwrapDebuggeeValue(cx, &v))
     {
-        return handleUncaughtException(ac, vp, callHook);
+        return handleUncaughtException(ac, vp, callHook, thisVForCheck, frame);
+    }
+
+    if (status == JSTRAP_RETURN && thisVForCheck.isSome() && v.isPrimitive()) {
+        if (v.isUndefined()) {
+            if (thisVForCheck.ref().isMagic(JS_UNINITIALIZED_LEXICAL)) {
+                MOZ_ALWAYS_FALSE(ThrowUninitializedThis(cx, frame));
+                return handleUncaughtException(ac, vp, callHook, thisVForCheck, frame);
+            }
+
+            v = thisVForCheck.ref();
+        } else {
+            ReportValueError(cx, JSMSG_BAD_DERIVED_RETURN, JSDVG_IGNORE_STACK, v, nullptr);
+            return handleUncaughtException(ac, vp, callHook, thisVForCheck, frame);
+        }
     }
 
     ac.reset();
     if (!cx->compartment()->wrap(cx, &v)) {
         vp.setUndefined();
         return JSTRAP_ERROR;
     }
     vp.set(v);
 
     return status;
 }
 
+JSTrapStatus
+Debugger::parseResumptionValue(Maybe<AutoCompartment>& ac, bool ok, const Value& rv,
+                               AbstractFramePtr frame, jsbytecode* pc, MutableHandleValue vp,
+                               bool callHook)
+{
+    JSContext* cx = ac->context()->asJSContext();
+    RootedValue rootThis(cx);
+    Maybe<HandleValue> thisArg;
+    if (frame.debuggerNeedsCheckPrimitiveReturn()) {
+        bool success;
+        {
+            AutoCompartment ac2(cx, frame.scopeChain());
+            success = GetThisValueForDebuggerMaybeOptimizedOut(cx, frame, pc, &rootThis);
+        }
+        if (!success || !cx->compartment()->wrap(cx, &rootThis)) {
+            ac.reset();
+            return JSTRAP_ERROR;
+        }
+        MOZ_ASSERT_IF(rootThis.isMagic(), rootThis.isMagic(JS_UNINITIALIZED_LEXICAL));
+        thisArg.emplace(HandleValue(rootThis));
+    }
+    return parseResumptionValueHelper(ac, ok, rv, thisArg, frame, vp, callHook);
+}
+
+JSTrapStatus
+Debugger::parseResumptionValue(Maybe<AutoCompartment>& ac, bool ok, const Value& rv,
+                               const Value& thisV, AbstractFramePtr frame, MutableHandleValue vp,
+                               bool callHook)
+{
+    JSContext* cx = ac->context()->asJSContext();
+    RootedValue rootThis(cx, thisV);
+    Maybe<HandleValue> thisArg;
+    if (frame.debuggerNeedsCheckPrimitiveReturn())
+        thisArg.emplace(rootThis);
+
+    return parseResumptionValueHelper(ac, ok, rv, thisArg, frame, vp, callHook);
+}
+
 static bool
 CallMethodIfPresent(JSContext* cx, HandleObject obj, const char* name, int argc, Value* argv,
                     MutableHandleValue rval)
 {
     rval.setUndefined();
     JSAtom* atom = Atomize(cx, name, strlen(name));
     if (!atom)
         return false;
@@ -1391,17 +1447,17 @@ Debugger::fireDebuggerStatement(JSContex
 
     ScriptFrameIter iter(cx);
     RootedValue scriptFrame(cx);
     if (!getScriptFrame(cx, iter, &scriptFrame))
         return handleUncaughtException(ac, false);
 
     RootedValue rv(cx);
     bool ok = Invoke(cx, ObjectValue(*object), ObjectValue(*hook), 1, scriptFrame.address(), &rv);
-    return parseResumptionValue(ac, ok, rv, vp);
+    return parseResumptionValue(ac, ok, rv, iter.abstractFramePtr(), iter.pc(), vp);
 }
 
 JSTrapStatus
 Debugger::fireExceptionUnwind(JSContext* cx, MutableHandleValue vp)
 {
     RootedObject hook(cx, getHook(OnExceptionUnwind));
     MOZ_ASSERT(hook);
     MOZ_ASSERT(hook->isCallable());
@@ -1419,17 +1475,17 @@ Debugger::fireExceptionUnwind(JSContext*
     argv[1].set(exc);
 
     ScriptFrameIter iter(cx);
     if (!getScriptFrame(cx, iter, argv[0]) || !wrapDebuggeeValue(cx, argv[1]))
         return handleUncaughtException(ac, false);
 
     RootedValue rv(cx);
     bool ok = Invoke(cx, ObjectValue(*object), ObjectValue(*hook), 2, argv.begin(), &rv);
-    JSTrapStatus st = parseResumptionValue(ac, ok, rv, vp);
+    JSTrapStatus st = parseResumptionValue(ac, ok, rv, iter.abstractFramePtr(), iter.pc(), vp);
     if (st == JSTRAP_CONTINUE)
         cx->setPendingException(exc);
     return st;
 }
 
 JSTrapStatus
 Debugger::fireEnterFrame(JSContext* cx, AbstractFramePtr frame, MutableHandleValue vp)
 {
@@ -1441,17 +1497,18 @@ Debugger::fireEnterFrame(JSContext* cx, 
     ac.emplace(cx, object);
 
     RootedValue scriptFrame(cx);
     if (!getScriptFrame(cx, frame, &scriptFrame))
         return handleUncaughtException(ac, false);
 
     RootedValue rv(cx);
     bool ok = Invoke(cx, ObjectValue(*object), ObjectValue(*hook), 1, scriptFrame.address(), &rv);
-    return parseResumptionValue(ac, ok, rv, vp);
+
+    return parseResumptionValue(ac, ok, rv, MagicValue(JS_UNINITIALIZED_LEXICAL), frame, vp);
 }
 
 void
 Debugger::fireNewScript(JSContext* cx, HandleScript script)
 {
     RootedObject hook(cx, getHook(OnNewScript));
     MOZ_ASSERT(hook);
     MOZ_ASSERT(hook->isCallable());
@@ -1672,17 +1729,18 @@ Debugger::onTrap(JSContext* cx, MutableH
             EnterDebuggeeNoExecute nx(cx, *dbg);
 
             RootedValue scriptFrame(cx);
             if (!dbg->getScriptFrame(cx, iter, &scriptFrame))
                 return dbg->handleUncaughtException(ac, false);
             RootedValue rv(cx);
             Rooted<JSObject*> handler(cx, bp->handler);
             bool ok = CallMethodIfPresent(cx, handler, "hit", 1, scriptFrame.address(), &rv);
-            JSTrapStatus st = dbg->parseResumptionValue(ac, ok, rv, vp, true);
+            JSTrapStatus st = dbg->parseResumptionValue(ac, ok, rv,  iter.abstractFramePtr(),
+                                                        iter.pc(), vp, true);
             if (st != JSTRAP_CONTINUE)
                 return st;
 
             /* Calling JS code invalidates site. Reload it. */
             site = script->getBreakpointSite(pc);
         }
     }
 
@@ -1762,18 +1820,20 @@ Debugger::onSingleStep(JSContext* cx, Mu
         Debugger* dbg = Debugger::fromChildJSObject(frame);
         EnterDebuggeeNoExecute nx(cx, *dbg);
 
         Maybe<AutoCompartment> ac;
         ac.emplace(cx, dbg->object);
 
         const Value& handler = frame->getReservedSlot(JSSLOT_DEBUGFRAME_ONSTEP_HANDLER);
         RootedValue rval(cx);
+
         bool ok = Invoke(cx, ObjectValue(*frame), handler, 0, nullptr, &rval);
-        JSTrapStatus st = dbg->parseResumptionValue(ac, ok, rval, vp);
+        JSTrapStatus st = dbg->parseResumptionValue(ac, ok, rval, iter.abstractFramePtr(),
+                                                    iter.pc(), vp);
         if (st != JSTRAP_CONTINUE)
             return st;
     }
 
     vp.setUndefined();
     if (exceptionPending)
         cx->setPendingException(exception);
     return JSTRAP_CONTINUE;
@@ -1868,16 +1928,29 @@ Debugger::slowPathOnNewGlobalObject(JSCo
 
 /* static */ bool
 Debugger::slowPathOnLogAllocationSite(JSContext* cx, HandleObject obj, HandleSavedFrame frame,
                                       double when, GlobalObject::DebuggerVector& dbgs)
 {
     MOZ_ASSERT(!dbgs.empty());
     mozilla::DebugOnly<Debugger**> begin = dbgs.begin();
 
+    // Root all the Debuggers while we're iterating over them;
+    // appendAllocationSite calls JSCompartment::wrap, and thus can GC.
+    //
+    // SpiderMonkey protocol is generally for the caller to prove that it has
+    // rooted the stuff it's asking you to operate on (i.e. by passing a
+    // Handle), but in this case, we're iterating over a global's list of
+    // Debuggers, and globals only hold their Debuggers weakly.
+    Rooted<GCVector<JSObject*>> activeDebuggers(cx, GCVector<JSObject*>(cx));
+    for (Debugger** dbgp = dbgs.begin(); dbgp < dbgs.end(); dbgp++) {
+        if (!activeDebuggers.append((*dbgp)->object))
+            return false;
+    }
+
     for (Debugger** dbgp = dbgs.begin(); dbgp < dbgs.end(); dbgp++) {
         // The set of debuggers had better not change while we're iterating,
         // such that the vector gets reallocated.
         MOZ_ASSERT(dbgs.begin() == begin);
 
         if ((*dbgp)->trackingAllocationSites &&
             (*dbgp)->enabled &&
             !(*dbgp)->appendAllocationSite(cx, obj, frame, when))
@@ -1910,40 +1983,16 @@ Debugger::slowPathOnIonCompilation(JSCon
 
 bool
 Debugger::isDebuggeeUnbarriered(const JSCompartment* compartment) const
 {
     MOZ_ASSERT(compartment);
     return compartment->isDebuggee() && debuggees.has(compartment->unsafeUnbarrieredMaybeGlobal());
 }
 
-Debugger::TenurePromotionsLogEntry::TenurePromotionsLogEntry(JSRuntime* rt, JSObject& obj, double when)
-  : className(obj.getClass()->name),
-    when(when),
-    frame(getObjectAllocationSite(obj)),
-    size(JS::ubi::Node(&obj).size(rt->debuggerMallocSizeOf))
-{ }
-
-
-void
-Debugger::logTenurePromotion(JSRuntime* rt, JSObject& obj, double when)
-{
-    AutoEnterOOMUnsafeRegion oomUnsafe;
-
-    if (!tenurePromotionsLog.emplaceBack(rt, obj, when))
-        oomUnsafe.crash("Debugger::logTenurePromotion");
-
-    if (tenurePromotionsLog.length() > maxTenurePromotionsLogLength) {
-        if (!tenurePromotionsLog.popFront())
-            oomUnsafe.crash("Debugger::logTenurePromotion");
-        MOZ_ASSERT(tenurePromotionsLog.length() == maxTenurePromotionsLogLength);
-        tenurePromotionsLogOverflowed = true;
-    }
-}
-
 bool
 Debugger::appendAllocationSite(JSContext* cx, HandleObject obj, HandleSavedFrame frame,
                                double when)
 {
     MOZ_ASSERT(trackingAllocationSites && enabled);
 
     AutoCompartment ac(cx, object);
     RootedObject wrappedFrame(cx, frame);
@@ -2610,22 +2659,16 @@ Debugger::removeAllocationsTrackingForAl
 
 void
 Debugger::markCrossCompartmentEdges(JSTracer* trc)
 {
     objects.markCrossCompartmentEdges<DebuggerObject_trace>(trc);
     environments.markCrossCompartmentEdges<DebuggerEnv_trace>(trc);
     scripts.markCrossCompartmentEdges<DebuggerScript_trace>(trc);
     sources.markCrossCompartmentEdges<DebuggerSource_trace>(trc);
-
-    // Because we don't have access to a `cx` inside
-    // `Debugger::logTenurePromotion`, we can't hold onto CCWs inside the log,
-    // and instead have unwrapped cross-compartment edges. We need to be sure to
-    // mark those here.
-    tenurePromotionsLog.trace(trc);
 }
 
 /*
  * Ordinarily, WeakMap keys and values are marked because at some point it was
  * discovered that the WeakMap was live; that is, some object containing the
  * WeakMap was marked during mark phase.
  *
  * However, during zone GC, we have to do something about cross-compartment
@@ -2792,17 +2835,16 @@ Debugger::trace(JSTracer* trc)
      */
     for (FrameMap::Range r = frames.all(); !r.empty(); r.popFront()) {
         RelocatablePtrNativeObject& frameobj = r.front().value();
         MOZ_ASSERT(MaybeForwarded(frameobj.get())->getPrivate());
         TraceEdge(trc, &frameobj, "live Debugger.Frame");
     }
 
     allocationsLog.trace(trc);
-    tenurePromotionsLog.trace(trc);
 
     /* Trace the weak map from JSScript instances to Debugger.Script objects. */
     scripts.trace(trc);
 
     /* Trace the referent ->Debugger.Source weak map */
     sources.trace(trc);
 
     /* Trace the referent -> Debugger.Object weak map. */
@@ -3777,18 +3819,25 @@ static inline ScriptSourceObject* GetSou
  * A class for parsing 'findScripts' query arguments and searching for
  * scripts that match the criteria they represent.
  */
 class MOZ_STACK_CLASS Debugger::ScriptQuery
 {
   public:
     /* Construct a ScriptQuery to use matching scripts for |dbg|. */
     ScriptQuery(JSContext* cx, Debugger* dbg):
-        cx(cx), debugger(dbg), compartments(cx->runtime()), url(cx), displayURLString(cx),
-        source(cx), innermostForCompartment(cx->runtime()), vector(cx, ScriptVector(cx))
+        cx(cx),
+        debugger(dbg),
+        iterMarker(&cx->runtime()->gc),
+        compartments(cx->runtime()),
+        url(cx),
+        displayURLString(cx),
+        source(cx),
+        innermostForCompartment(cx->runtime()),
+        vector(cx, ScriptVector(cx))
     {}
 
     /*
      * Initialize this ScriptQuery. Raise an error and return false if we
      * haven't enough memory.
      */
     bool init() {
         if (!compartments.init() ||
@@ -3978,16 +4027,19 @@ class MOZ_STACK_CLASS Debugger::ScriptQu
 
   private:
     /* The context in which we should do our work. */
     JSContext* cx;
 
     /* The debugger for which we conduct queries. */
     Debugger* debugger;
 
+    /* Require the set of compartments to stay fixed while the ScriptQuery is alive. */
+    gc::AutoEnterIteration iterMarker;
+
     typedef HashSet<JSCompartment*, DefaultHasher<JSCompartment*>, RuntimeAllocPolicy>
         CompartmentSet;
 
     /* A script must be in one of these compartments to match the query. */
     CompartmentSet compartments;
 
     /* If this is a string, matching scripts have urls equal to it. */
     RootedValue url;
--- a/js/src/vm/Debugger.h
+++ b/js/src/vm/Debugger.h
@@ -268,42 +268,22 @@ class Debugger : private mozilla::Linked
     }
 
     // Notify this Debugger that one or more of its debuggees is participating
     // in the GC identified by the given GC number.
     bool debuggeeIsBeingCollected(uint64_t majorGCNumber) {
         return observedGCs.put(majorGCNumber);
     }
 
-    bool isTrackingTenurePromotions() const {
-        return trackingTenurePromotions;
-    }
-
     bool isEnabled() const {
         return enabled;
     }
 
-    void logTenurePromotion(JSRuntime* rt, JSObject& obj, double when);
     static SavedFrame* getObjectAllocationSite(JSObject& obj);
 
-    struct TenurePromotionsLogEntry
-    {
-        TenurePromotionsLogEntry(JSRuntime* rt, JSObject& obj, double when);
-
-        const char* className;
-        double when;
-        RelocatablePtrObject frame;
-        size_t size;
-
-        void trace(JSTracer* trc) {
-            if (frame)
-                TraceEdge(trc, &frame, "Debugger::TenurePromotionsLogEntry::frame");
-        }
-    };
-
     struct AllocationsLogEntry
     {
         AllocationsLogEntry(HandleObject frame, double when, const char* className,
                             HandleAtom ctorName, size_t size, bool inNursery)
             : frame(frame),
               when(when),
               className(className),
               ctorName(ctorName),
@@ -341,22 +321,16 @@ class Debugger : private mozilla::Linked
 
     JSCList breakpoints;                /* Circular list of all js::Breakpoints in this debugger */
 
     // The set of GC numbers for which one or more of this Debugger's observed
     // debuggees participated in.
     using GCNumberSet = HashSet<uint64_t, DefaultHasher<uint64_t>, RuntimeAllocPolicy>;
     GCNumberSet observedGCs;
 
-    using TenurePromotionsLog = js::TraceableFifo<TenurePromotionsLogEntry>;
-    TenurePromotionsLog tenurePromotionsLog;
-    bool trackingTenurePromotions;
-    size_t maxTenurePromotionsLogLength;
-    bool tenurePromotionsLogOverflowed;
-
     using AllocationsLog = js::TraceableFifo<AllocationsLogEntry>;
 
     AllocationsLog allocationsLog;
     bool trackingAllocationSites;
     double allocationSamplingProbability;
     size_t maxAllocationsLogLength;
     bool allocationsLogOverflowed;
 
@@ -474,20 +448,23 @@ class Debugger : private mozilla::Linked
      * If there is no uncaughtExceptionHook, or if it fails, report and clear
      * the pending exception on ac.context and return JSTRAP_ERROR.
      *
      * This always calls ac.leave(); ac is a parameter because this method must
      * do some things in the debugger compartment and some things in the
      * debuggee compartment.
      */
     JSTrapStatus handleUncaughtException(mozilla::Maybe<AutoCompartment>& ac, bool callHook);
-    JSTrapStatus handleUncaughtException(mozilla::Maybe<AutoCompartment>& ac, MutableHandleValue vp, bool callHook);
+    JSTrapStatus handleUncaughtException(mozilla::Maybe<AutoCompartment>& ac, MutableHandleValue vp, bool callHook,
+                                         const mozilla::Maybe<HandleValue>& thisVForCheck = mozilla::Nothing(),
+                                         AbstractFramePtr frame = NullFramePtr());
 
     JSTrapStatus handleUncaughtExceptionHelper(mozilla::Maybe<AutoCompartment>& ac,
-                                               MutableHandleValue* vp, bool callHook);
+                                               MutableHandleValue* vp, bool callHook,
+                                               const mozilla::Maybe<HandleValue>& thisVForCheck, AbstractFramePtr frame);
 
     /*
      * Handle the result of a hook that is expected to return a resumption
      * value <https://wiki.mozilla.org/Debugger#Resumption_Values>. This is called
      * when we return from a debugging hook to debuggee code. The interpreter wants
      * a (JSTrapStatus, Value) pair telling it how to proceed.
      *
      * Precondition: ac is entered. We are in the debugger compartment.
@@ -504,19 +481,35 @@ class Debugger : private mozilla::Linked
      *         unwrap value. Store the result in *vp and return JSTRAP_RETURN
      *         or JSTRAP_THROW. The interpreter will force the current frame to
      *         return or throw an exception.
      *     null - Return JSTRAP_ERROR to terminate the debuggee with an
      *         uncatchable error.
      *     anything else - Make a new TypeError the pending exception and
      *         return handleUncaughtException(ac, vp, callHook).
      */
-    JSTrapStatus parseResumptionValue(mozilla::Maybe<AutoCompartment>& ac, bool ok, const Value& rv,
+    JSTrapStatus parseResumptionValue(mozilla::Maybe<AutoCompartment>& ac, bool OK, const Value& rv,
+                                      AbstractFramePtr frame, jsbytecode* pc, MutableHandleValue vp,
+                                      bool callHook = true);
+
+    /*
+     * When we run the onEnterFrame hook, the |this| slot hasn't been fully
+     * initialized, because the initialzation happens in the function's
+     * prologue. To combat this, we pass the this for the primitive return
+     * check directly. When bug 1249193 is fixed, this overload should be
+     * removed.
+     */
+    JSTrapStatus parseResumptionValue(mozilla::Maybe<AutoCompartment>& ac, bool OK, const Value& rv,
+                                      const Value& thisVForCheck, AbstractFramePtr frame,
                                       MutableHandleValue vp, bool callHook = true);
 
+    JSTrapStatus parseResumptionValueHelper(mozilla::Maybe<AutoCompartment>& ac, bool ok, const Value& rv,
+                                            const mozilla::Maybe<HandleValue>& thisVForCheck, AbstractFramePtr frame,
+                                            MutableHandleValue vp, bool callHook);
+
     GlobalObject* unwrapDebuggeeArgument(JSContext* cx, const Value& v);
 
     static void traceObject(JSTracer* trc, JSObject* obj);
     void trace(JSTracer* trc);
     static void finalize(FreeOp* fop, JSObject* obj);
     void markCrossCompartmentEdges(JSTracer* tracer);
 
     static const Class jsclass;
@@ -612,17 +605,17 @@ class Debugger : private mozilla::Linked
     bool updateObservesCoverageOnDebuggees(JSContext* cx, IsObserving observing);
     void updateObservesAsmJSOnDebuggees(IsObserving observing);
 
     JSObject* getHook(Hook hook) const;
     bool hasAnyLiveHooks() const;
 
     static bool slowPathCheckNoExecute(JSContext* cx, HandleScript script);
     static JSTrapStatus slowPathOnEnterFrame(JSContext* cx, AbstractFramePtr frame);
-    static bool slowPathOnLeaveFrame(JSContext* cx, AbstractFramePtr frame, bool ok);
+    static bool slowPathOnLeaveFrame(JSContext* cx, AbstractFramePtr frame, jsbytecode* pc, bool ok);
     static JSTrapStatus slowPathOnDebuggerStatement(JSContext* cx, AbstractFramePtr frame);
     static JSTrapStatus slowPathOnExceptionUnwind(JSContext* cx, AbstractFramePtr frame);
     static void slowPathOnNewScript(JSContext* cx, HandleScript script);
     static void slowPathOnNewGlobalObject(JSContext* cx, Handle<GlobalObject*> global);
     static bool slowPathOnLogAllocationSite(JSContext* cx, HandleObject obj, HandleSavedFrame frame,
                                             double when, GlobalObject::DebuggerVector& dbgs);
     static void slowPathPromiseHook(JSContext* cx, Hook hook, HandleObject promise);
     static void slowPathOnIonCompilation(JSContext* cx, Handle<ScriptVector> scripts,
@@ -780,17 +773,17 @@ class Debugger : private mozilla::Linked
      * in behavior the hooks request, if any. Return the new error/success value.
      *
      * This function may be called twice for the same outgoing frame; only the
      * first call has any effect. (Permitting double calls simplifies some
      * cases where an onPop handler's resumption value changes a return to a
      * throw, or vice versa: we can redirect to a complete copy of the
      * alternative path, containing its own call to onLeaveFrame.)
      */
-    static inline bool onLeaveFrame(JSContext* cx, AbstractFramePtr frame, bool ok);
+    static inline bool onLeaveFrame(JSContext* cx, AbstractFramePtr frame, jsbytecode* pc, bool ok);
 
     static inline void onNewScript(JSContext* cx, HandleScript script);
     static inline void onNewGlobalObject(JSContext* cx, Handle<GlobalObject*> global);
     static inline bool onLogAllocationSite(JSContext* cx, JSObject* obj, HandleSavedFrame frame,
                                            double when);
     static inline bool observesIonCompilation(JSContext* cx);
     static inline void onIonCompilation(JSContext* cx, Handle<ScriptVector> scripts,
                                         LSprinter& graph);
--- a/js/src/vm/DebuggerMemory.cpp
+++ b/js/src/vm/DebuggerMemory.cpp
@@ -333,152 +333,16 @@ DebuggerMemory::setAllocationSamplingPro
 DebuggerMemory::getAllocationsLogOverflowed(JSContext* cx, unsigned argc, Value* vp)
 {
     THIS_DEBUGGER_MEMORY(cx, argc, vp, "(get allocationsLogOverflowed)", args, memory);
     args.rval().setBoolean(memory->getDebugger()->allocationsLogOverflowed);
     return true;
 }
 
 /* static */ bool
-DebuggerMemory::setTrackingTenurePromotions(JSContext* cx, unsigned argc, Value* vp)
-{
-    THIS_DEBUGGER_MEMORY(cx, argc, vp, "(set trackingTenurePromotions)", args, memory);
-    if (!args.requireAtLeast(cx, "(set trackingTenurePromotions)", 1))
-        return false;
-
-    Debugger* dbg = memory->getDebugger();
-    dbg->trackingTenurePromotions = ToBoolean(args[0]);
-    return undefined(args);
-}
-
-/* static */ bool
-DebuggerMemory::getTrackingTenurePromotions(JSContext* cx, unsigned argc, Value* vp)
-{
-    THIS_DEBUGGER_MEMORY(cx, argc, vp, "(get trackingTenurePromotions)", args, memory);
-    args.rval().setBoolean(memory->getDebugger()->trackingTenurePromotions);
-    return true;
-}
-
-/* static */ bool
-DebuggerMemory::drainTenurePromotionsLog(JSContext* cx, unsigned argc, Value* vp)
-{
-    THIS_DEBUGGER_MEMORY(cx, argc, vp, "drainTenurePromotionsLog", args, memory);
-    Debugger* dbg = memory->getDebugger();
-
-    if (!dbg->trackingTenurePromotions) {
-        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_NOT_TRACKING_TENURINGS,
-                             "drainTenurePromotionsLog");
-        return false;
-    }
-
-    size_t length = dbg->tenurePromotionsLog.length();
-
-    RootedArrayObject result(cx, NewDenseFullyAllocatedArray(cx, length));
-    if (!result)
-        return false;
-    result->ensureDenseInitializedLength(cx, 0, length);
-
-    for (size_t i = 0; i < length; i++) {
-        RootedPlainObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx));
-        if (!obj)
-            return false;
-
-        // Don't pop the TenurePromotionsEntry yet. The queue's links are
-        // followed by the GC to find the TenurePromotionsEntry, but are not
-        // barriered, so we must edit them with great care. Use the queue entry
-        // in place, and then pop and delete together.
-        auto& entry = dbg->tenurePromotionsLog.front();
-
-        RootedValue frame(cx, ObjectOrNullValue(entry.frame));
-        if (!cx->compartment()->wrap(cx, &frame) ||
-            !DefineProperty(cx, obj, cx->names().frame, frame))
-        {
-            return false;
-        }
-
-        RootedValue timestampValue(cx, NumberValue(entry.when));
-        if (!DefineProperty(cx, obj, cx->names().timestamp, timestampValue))
-            return false;
-
-        RootedString className(cx, Atomize(cx, entry.className, strlen(entry.className)));
-        if (!className)
-            return false;
-        RootedValue classNameValue(cx, StringValue(className));
-        if (!DefineProperty(cx, obj, cx->names().class_, classNameValue))
-            return false;
-
-        RootedValue sizeValue(cx, NumberValue(entry.size));
-        if (!DefineProperty(cx, obj, cx->names().size, sizeValue))
-            return false;
-
-        result->setDenseElement(i, ObjectValue(*obj));
-
-        // Pop the front queue entry, and delete it immediately, so that the GC
-        // sees the TenurePromotionsEntry's RelocatablePtr barriers run
-        // atomically with the change to the graph (the queue link).
-        if (!dbg->tenurePromotionsLog.popFront()) {
-            ReportOutOfMemory(cx);
-            return false;
-        }
-    }
-
-    dbg->tenurePromotionsLogOverflowed = false;
-    args.rval().setObject(*result);
-    return true;
-}
-
-/* static */ bool
-DebuggerMemory::getMaxTenurePromotionsLogLength(JSContext* cx, unsigned argc, Value* vp)
-{
-    THIS_DEBUGGER_MEMORY(cx, argc, vp, "(get maxTenurePromotionsLogLength)", args, memory);
-    args.rval().setInt32(memory->getDebugger()->maxTenurePromotionsLogLength);
-    return true;
-}
-
-/* static */ bool
-DebuggerMemory::setMaxTenurePromotionsLogLength(JSContext* cx, unsigned argc, Value* vp)
-{
-    THIS_DEBUGGER_MEMORY(cx, argc, vp, "(set maxTenurePromotionsLogLength)", args, memory);
-    if (!args.requireAtLeast(cx, "(set maxTenurePromotionsLogLength)", 1))
-        return false;
-
-    int32_t max;
-    if (!ToInt32(cx, args[0], &max))
-        return false;
-
-    if (max < 1) {
-        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
-                             "(set maxTenurePromotionsLogLength)'s parameter",
-                             "not a positive integer");
-        return false;
-    }
-
-    Debugger* dbg = memory->getDebugger();
-    dbg->maxTenurePromotionsLogLength = max;
-
-    while (dbg->tenurePromotionsLog.length() > dbg->maxAllocationsLogLength) {
-        if (!dbg->tenurePromotionsLog.popFront()) {
-            ReportOutOfMemory(cx);
-            return false;
-        }
-    }
-
-    args.rval().setUndefined();
-    return true;
-}
-
-/* static */ bool
-DebuggerMemory::getTenurePromotionsLogOverflowed(JSContext* cx, unsigned argc, Value* vp)
-{
-    THIS_DEBUGGER_MEMORY(cx, argc, vp, "(get tenurePromotionsLogOverflowed)", args, memory);
-    args.rval().setBoolean(memory->getDebugger()->tenurePromotionsLogOverflowed);
-    return true;
-}
-
-/* static */ bool
 DebuggerMemory::getOnGarbageCollection(JSContext* cx, unsigned argc, Value* vp)
 {
     THIS_DEBUGGER_MEMORY(cx, argc, vp, "(get onGarbageCollection)", args, memory);
     return Debugger::getHookImpl(cx, args, *memory->getDebugger(), Debugger::OnGarbageCollection);
 }
 
 /* static */ bool
 DebuggerMemory::setOnGarbageCollection(JSContext* cx, unsigned argc, Value* vp)
@@ -579,22 +443,17 @@ DebuggerMemory::takeCensus(JSContext* cx
 
 
 /* static */ const JSPropertySpec DebuggerMemory::properties[] = {
     JS_PSGS("trackingAllocationSites", getTrackingAllocationSites, setTrackingAllocationSites, 0),
     JS_PSGS("maxAllocationsLogLength", getMaxAllocationsLogLength, setMaxAllocationsLogLength, 0),
     JS_PSGS("allocationSamplingProbability", getAllocationSamplingProbability, setAllocationSamplingProbability, 0),
     JS_PSG("allocationsLogOverflowed", getAllocationsLogOverflowed, 0),
 
-    JS_PSGS("trackingTenurePromotions", getTrackingTenurePromotions, setTrackingTenurePromotions, 0),
-    JS_PSGS("maxTenurePromotionsLogLength", getMaxTenurePromotionsLogLength, setMaxTenurePromotionsLogLength, 0),
-    JS_PSG("tenurePromotionsLogOverflowed", getTenurePromotionsLogOverflowed, 0),
-
     JS_PSGS("onGarbageCollection", getOnGarbageCollection, setOnGarbageCollection, 0),
     JS_PS_END
 };
 
 /* static */ const JSFunctionSpec DebuggerMemory::methods[] = {
     JS_FN("drainAllocationsLog", DebuggerMemory::drainAllocationsLog, 0, 0),
-    JS_FN("drainTenurePromotionsLog", DebuggerMemory::drainTenurePromotionsLog, 0, 0),
     JS_FN("takeCensus", takeCensus, 0, 0),
     JS_FS_END
 };
--- a/js/src/vm/DebuggerMemory.h
+++ b/js/src/vm/DebuggerMemory.h
@@ -40,27 +40,20 @@ class DebuggerMemory : public NativeObje
     static bool setTrackingAllocationSites(JSContext* cx, unsigned argc, Value* vp);
     static bool getTrackingAllocationSites(JSContext* cx, unsigned argc, Value* vp);
     static bool setMaxAllocationsLogLength(JSContext* cx, unsigned argc, Value* vp);
     static bool getMaxAllocationsLogLength(JSContext* cx, unsigned argc, Value* vp);
     static bool setAllocationSamplingProbability(JSContext* cx, unsigned argc, Value* vp);
     static bool getAllocationSamplingProbability(JSContext* cx, unsigned argc, Value* vp);
     static bool getAllocationsLogOverflowed(JSContext* cx, unsigned argc, Value* vp);
 
-    static bool setTrackingTenurePromotions(JSContext* cx, unsigned argc, Value* vp);
-    static bool getTrackingTenurePromotions(JSContext* cx, unsigned argc, Value* vp);
-    static bool setMaxTenurePromotionsLogLength(JSContext* cx, unsigned argc, Value* vp);
-    static bool getMaxTenurePromotionsLogLength(JSContext* cx, unsigned argc, Value* vp);
-    static bool getTenurePromotionsLogOverflowed(JSContext* cx, unsigned argc, Value* vp);
-
     static bool getOnGarbageCollection(JSContext* cx, unsigned argc, Value* vp);
     static bool setOnGarbageCollection(JSContext* cx, unsigned argc, Value* vp);
 
     // Function properties of Debugger.Memory.prototype.
 
     static bool takeCensus(JSContext* cx, unsigned argc, Value* vp);
     static bool drainAllocationsLog(JSContext* cx, unsigned argc, Value* vp);
-    static bool drainTenurePromotionsLog(JSContext* cx, unsigned argc, Value* vp);
 };
 
 } /* namespace js */
 
 #endif /* vm_DebuggerMemory_h */
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -115,16 +115,22 @@ GlobalObject::ensureConstructor(JSContex
     return resolveConstructor(cx, global, key);
 }
 
 /* static*/ bool
 GlobalObject::resolveConstructor(JSContext* cx, Handle<GlobalObject*> global, JSProtoKey key)
 {
     MOZ_ASSERT(!global->isStandardClassResolved(key));
 
+    // Prohibit collection of allocation metadata. Metadata builders shouldn't
+    // need to observe lazily-constructed prototype objects coming into
+    // existence. And assertions start to fail when the builder itself attempts
+    // an allocation that re-entrantly tries to create the same prototype.
+    AutoSuppressObjectMetadataCallback suppressMetadata(cx);
+
     // There are two different kinds of initialization hooks. One of them is
     // the class js::InitFoo hook, defined in a JSProtoKey-keyed table at the
     // top of this file. The other lives in the ClassSpec for classes that
     // define it. Classes may use one or the other, but not both.
     ClassInitializerOp init = protoTable[key].init;
     if (init == InitViaClassSpec)
         init = nullptr;
 
@@ -174,16 +180,23 @@ GlobalObject::resolveConstructor(JSConte
     // |createPrototype|, |prototypeFunctions|, and |prototypeProperties|
     // should all be null.
     RootedObject proto(cx);
     if (clasp->spec.createPrototypeHook()) {
         proto = clasp->spec.createPrototypeHook()(cx, key);
         if (!proto)
             return false;
 
+        // Make sure that creating the prototype didn't recursively resolve our
+        // own constructor. We can't just assert that there's no prototype; OOMs
+        // can result in incomplete resolutions in which the prototype is saved
+        // but not the constructor. So use the same criteria that protects entry
+        // into this function.
+        MOZ_ASSERT(!global->isStandardClassResolved(key));
+
         global->setPrototype(key, ObjectValue(*proto));
     }
 
     // Create the constructor.
     RootedObject ctor(cx, clasp->spec.createConstructorHook()(cx, key));
     if (!ctor)
         return false;
 
@@ -429,16 +442,17 @@ GlobalObject::initSelfHostingBuiltins(JS
                            JSPROP_PERMANENT | JSPROP_READONLY))
     {
         return false;
     }
 
     return InitBareBuiltinCtor(cx, global, JSProto_Array) &&
            InitBareBuiltinCtor(cx, global, JSProto_TypedArray) &&
            InitBareBuiltinCtor(cx, global, JSProto_Uint8Array) &&
+           InitBareBuiltinCtor(cx, global, JSProto_Int32Array) &&
            InitBareWeakMapCtor(cx, global) &&
            InitStopIterationClass(cx, global) &&
            InitSelfHostingCollectionIteratorFunctions(cx, global) &&
            DefineFunctions(cx, global, builtins, AsIntrinsic);
 }
 
 /* static */ bool
 GlobalObject::isRuntimeCodeGenEnabled(JSContext* cx, Handle<GlobalObject*> global)
@@ -740,8 +754,23 @@ GlobalObject::addIntrinsicValue(JSContex
         return false;
 
     if (!holder->setLastProperty(cx, shape))
         return false;
 
     holder->setSlot(shape->slot(), value);
     return true;
 }
+
+/* static */ bool
+GlobalObject::ensureModulePrototypesCreated(JSContext *cx, Handle<GlobalObject*> global)
+{
+    if (global->getSlot(MODULE_PROTO).isUndefined()) {
+        MOZ_ASSERT(global->getSlot(IMPORT_ENTRY_PROTO).isUndefined() &&
+                   global->getSlot(EXPORT_ENTRY_PROTO).isUndefined());
+        if (!js::InitModuleClasses(cx, global))
+            return false;
+    }
+    MOZ_ASSERT(global->getSlot(MODULE_PROTO).isObject() &&
+               global->getSlot(IMPORT_ENTRY_PROTO).isObject() &&
+               global->getSlot(EXPORT_ENTRY_PROTO).isObject());
+    return true;
+}
--- a/js/src/vm/GlobalObject.h
+++ b/js/src/vm/GlobalObject.h
@@ -464,26 +464,49 @@ class GlobalObject : public NativeObject
     JSObject* getOrCreateNumberFormatPrototype(JSContext* cx) {
         return getOrCreateObject(cx, NUMBER_FORMAT_PROTO, initNumberFormatProto);
     }
 
     JSObject* getOrCreateDateTimeFormatPrototype(JSContext* cx) {
         return getOrCreateObject(cx, DATE_TIME_FORMAT_PROTO, initDateTimeFormatProto);
     }
 
+    static bool ensureModulePrototypesCreated(JSContext *cx, Handle<GlobalObject*> global);
+
+    JSObject* maybeGetModulePrototype() {
+        Value value = getSlot(MODULE_PROTO);
+        return value.isUndefined() ? nullptr : &value.toObject();
+    }
+
+    JSObject* maybeGetImportEntryPrototype() {
+        Value value = getSlot(IMPORT_ENTRY_PROTO);
+        return value.isUndefined() ? nullptr : &value.toObject();
+    }
+
+    JSObject* maybeGetExportEntryPrototype() {
+        Value value = getSlot(EXPORT_ENTRY_PROTO);
+        return value.isUndefined() ? nullptr : &value.toObject();
+    }
+
     JSObject* getModulePrototype() {
-        return &getSlot(MODULE_PROTO).toObject();
+        JSObject* proto = maybeGetModulePrototype();
+        MOZ_ASSERT(proto);
+        return proto;
     }
 
     JSObject* getImportEntryPrototype() {
-        return &getSlot(IMPORT_ENTRY_PROTO).toObject();
+        JSObject* proto = maybeGetImportEntryPrototype();
+        MOZ_ASSERT(proto);
+        return proto;
     }
 
     JSObject* getExportEntryPrototype() {
-        return &getSlot(EXPORT_ENTRY_PROTO).toObject();
+        JSObject* proto = maybeGetExportEntryPrototype();
+        MOZ_ASSERT(proto);
+        return proto;
     }
 
     static JSFunction*
     getOrCreateTypedArrayConstructor(JSContext* cx, Handle<GlobalObject*> global) {
         if (!ensureConstructor(cx, global, JSProto_TypedArray))
             return nullptr;
         return &global->getConstructor(JSProto_TypedArray).toObject().as<JSFunction>();
     }
--- a/js/src/vm/HelperThreads.cpp
+++ b/js/src/vm/HelperThreads.cpp
@@ -190,20 +190,20 @@ js::CancelOffThreadIonCompile(JSCompartm
 static const JSClass parseTaskGlobalClass = {
     "internal-parse-task-global", JSCLASS_GLOBAL_FLAGS,
     nullptr, nullptr, nullptr, nullptr,
     nullptr, nullptr, nullptr, nullptr,
     nullptr, nullptr, nullptr,
     JS_GlobalObjectTraceHook
 };
 
-ParseTask::ParseTask(ExclusiveContext* cx, JSObject* exclusiveContextGlobal, JSContext* initCx,
-                     const char16_t* chars, size_t length,
+ParseTask::ParseTask(ParseTaskKind kind, ExclusiveContext* cx, JSObject* exclusiveContextGlobal,
+                     JSContext* initCx, const char16_t* chars, size_t length,
                      JS::OffThreadCompileCallback callback, void* callbackData)
-  : cx(cx), options(initCx), chars(chars), length(length),
+  : kind(kind), cx(cx), options(initCx), chars(chars), length(length),
     alloc(JSRuntime::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
     exclusiveContextGlobal(initCx->runtime(), exclusiveContextGlobal),
     callback(callback), callbackData(callbackData),
     script(initCx->runtime()), sourceObject(initCx->runtime()),
     errors(cx), overRecursed(false), outOfMemory(false)
 {
 }
 
@@ -239,16 +239,62 @@ ParseTask::~ParseTask()
 {
     // ParseTask takes over ownership of its input exclusive context.
     js_delete(cx);
 
     for (size_t i = 0; i < errors.length(); i++)
         js_delete(errors[i]);
 }
 
+ScriptParseTask::ScriptParseTask(ExclusiveContext* cx, JSObject* exclusiveContextGlobal,
+                                 JSContext* initCx, const char16_t* chars, size_t length,
+                                 JS::OffThreadCompileCallback callback, void* callbackData)
+  : ParseTask(ParseTaskKind::Script, cx, exclusiveContextGlobal, initCx, chars, length, callback,
+              callbackData)
+{
+}
+
+void
+ScriptParseTask::parse()
+{
+    SourceBufferHolder srcBuf(chars, length, SourceBufferHolder::NoOwnership);
+
+    // ! WARNING WARNING WARNING !
+    //
+    // See comment in Parser::bindLexical about optimizing global lexical
+    // bindings. If we start optimizing them, passing in task->cx's
+    // global lexical scope would be incorrect!
+    //
+    // ! WARNING WARNING WARNING !
+    Rooted<ClonedBlockObject*> globalLexical(cx, &cx->global()->lexicalScope());
+    Rooted<StaticScope*> staticScope(cx, &globalLexical->staticBlock());
+    script = frontend::CompileScript(cx, &alloc, globalLexical, staticScope, nullptr,
+                                     options, srcBuf,
+                                     /* source_ = */ nullptr,
+                                     /* extraSct = */ nullptr,
+                                     /* sourceObjectOut = */ sourceObject.address());
+}
+
+ModuleParseTask::ModuleParseTask(ExclusiveContext* cx, JSObject* exclusiveContextGlobal,
+                                 JSContext* initCx, const char16_t* chars, size_t length,
+                                 JS::OffThreadCompileCallback callback, void* callbackData)
+  : ParseTask(ParseTaskKind::Module, cx, exclusiveContextGlobal, initCx, chars, length, callback,
+              callbackData)
+{
+}
+
+void
+ModuleParseTask::parse()
+{
+    SourceBufferHolder srcBuf(chars, length, SourceBufferHolder::NoOwnership);
+    ModuleObject* module = frontend::CompileModule(cx, options, srcBuf, &alloc);
+    if (module)
+        script = module->script();
+}
+
 void
 js::CancelOffThreadParses(JSRuntime* rt)
 {
     AutoLockHelperThreadState lock;
 
     if (!HelperThreadState().threads)
         return;
 
@@ -280,17 +326,18 @@ js::CancelOffThreadParses(JSRuntime* rt)
     GlobalHelperThreadState::ParseTaskVector& finished = HelperThreadState().parseFinishedList();
     while (true) {
         bool found = false;
         for (size_t i = 0; i < finished.length(); i++) {
             ParseTask* task = finished[i];
             if (task->runtimeMatches(rt)) {
                 found = true;
                 AutoUnlockHelperThreadState unlock;
-                HelperThreadState().finishParseTask(/* maybecx = */ nullptr, rt, task);
+                HelperThreadState().finishParseTask(/* maybecx = */ nullptr, rt, task->kind,
+                                                    task);
             }
         }
         if (!found)
             break;
     }
 }
 
 bool
@@ -314,17 +361,17 @@ EnsureConstructor(JSContext* cx, Handle<
     MOZ_ASSERT(global->getPrototype(key).toObject().isDelegate(),
                "standard class prototype wasn't a delegate from birth");
     return true;
 }
 
 // Initialize all classes potentially created during parsing for use in parser
 // data structures, template objects, &c.
 static bool
-EnsureParserCreatedClasses(JSContext* cx)
+EnsureParserCreatedClasses(JSContext* cx, ParseTaskKind kind)
 {
     Handle<GlobalObject*> global = cx->global();
 
     if (!EnsureConstructor(cx, global, JSProto_Function))
         return false; // needed by functions, also adds object literals' proto
 
     if (!EnsureConstructor(cx, global, JSProto_Array))
         return false; // needed by array literals
@@ -333,28 +380,25 @@ EnsureParserCreatedClasses(JSContext* cx
         return false; // needed by regular expression literals
 
     if (!EnsureConstructor(cx, global, JSProto_Iterator))
         return false; // needed by ???
 
     if (!GlobalObject::initStarGenerators(cx, global))
         return false; // needed by function*() {} and generator comprehensions
 
+    if (kind == ParseTaskKind::Module && !GlobalObject::ensureModulePrototypesCreated(cx, global))
+        return false;
+
     return true;
 }
 
-bool
-js::StartOffThreadParseScript(JSContext* cx, const ReadOnlyCompileOptions& options,
-                              const char16_t* chars, size_t length,
-                              JS::OffThreadCompileCallback callback, void* callbackData)
+static JSObject*
+CreateGlobalForOffThreadParse(JSContext* cx, ParseTaskKind kind, const gc::AutoSuppressGC& nogc)
 {
-    // Suppress GC so that calls below do not trigger a new incremental GC
-    // which could require barriers on the atoms compartment.
-    gc::AutoSuppressGC suppress(cx);
-
     JSCompartment* currentCompartment = cx->compartment();
 
     JS::CompartmentOptions compartmentOptions(currentCompartment->creationOptions(),
                                               currentCompartment->behaviors());
 
     auto& creationOptions = compartmentOptions.creationOptions();
 
     creationOptions.setInvisibleToDebugger(true)
@@ -362,64 +406,121 @@ js::StartOffThreadParseScript(JSContext*
                    .setZone(JS::FreshZone);
 
     // Don't falsely inherit the host's global trace hook.
     creationOptions.setTrace(nullptr);
 
     JSObject* global = JS_NewGlobalObject(cx, &parseTaskGlobalClass, nullptr,
                                           JS::FireOnNewGlobalHook, compartmentOptions);
     if (!global)
-        return false;
+        return nullptr;
 
     JS_SetCompartmentPrincipals(global->compartment(), currentCompartment->principals());
 
     // Initialize all classes required for parsing while still on the main
     // thread, for both the target and the new global so that prototype
     // pointers can be changed infallibly after parsing finishes.
-    if (!EnsureParserCreatedClasses(cx))
-        return false;
+    if (!EnsureParserCreatedClasses(cx, kind))
+        return nullptr;
     {
         AutoCompartment ac(cx, global);
-        if (!EnsureParserCreatedClasses(cx))
+        if (!EnsureParserCreatedClasses(cx, kind))
+            return nullptr;
+    }
+
+    return global;
+}
+
+static bool
+QueueOffThreadParseTask(JSContext* cx, ParseTask* task)
+{
+    if (OffThreadParsingMustWaitForGC(cx->runtime())) {
+        AutoLockHelperThreadState lock;
+        if (!HelperThreadState().parseWaitingOnGC().append(task)) {
+            ReportOutOfMemory(cx);
+            return false;
+        }
+    } else {
+        AutoLockHelperThreadState lock;
+        if (!HelperThreadState().parseWorklist().append(task)) {
+            ReportOutOfMemory(cx);
             return false;
+        }
+
+        task->activate(cx->runtime());
+        HelperThreadState().notifyOne(GlobalHelperThreadState::PRODUCER);
     }
 
+    return true;
+}
+
+bool
+js::StartOffThreadParseScript(JSContext* cx, const ReadOnlyCompileOptions& options,
+                              const char16_t* chars, size_t length,
+                              JS::OffThreadCompileCallback callback, void* callbackData)
+{
+    // Suppress GC so that calls below do not trigger a new incremental GC
+    // which could require barriers on the atoms compartment.
+    gc::AutoSuppressGC nogc(cx);
+
+    JSObject* global = CreateGlobalForOffThreadParse(cx, ParseTaskKind::Script, nogc);
+    if (!global)
+        return false;
+
     ScopedJSDeletePtr<ExclusiveContext> helpercx(
         cx->new_<ExclusiveContext>(cx->runtime(), (PerThreadData*) nullptr,
                                    ExclusiveContext::Context_Exclusive));
     if (!helpercx)
         return false;
 
     ScopedJSDeletePtr<ParseTask> task(
-        cx->new_<ParseTask>(helpercx.get(), global, cx, chars, length,
-                            callback, callbackData));
+        cx->new_<ScriptParseTask>(helpercx.get(), global, cx, chars, length,
+                                  callback, callbackData));
     if (!task)
         return false;
 
     helpercx.forget();
 
-    if (!task->init(cx, options))
+    if (!task->init(cx, options) || !QueueOffThreadParseTask(cx, task))
         return false;
 
-    if (OffThreadParsingMustWaitForGC(cx->runtime())) {
-        AutoLockHelperThreadState lock;
-        if (!HelperThreadState().parseWaitingOnGC().append(task.get())) {
-            ReportOutOfMemory(cx);
-            return false;
-        }
-    } else {
-        AutoLockHelperThreadState lock;
-        if (!HelperThreadState().parseWorklist().append(task.get())) {
-            ReportOutOfMemory(cx);
-            return false;
-        }
+    task.forget();
+
+    return true;
+}
+
+bool
+js::StartOffThreadParseModule(JSContext* cx, const ReadOnlyCompileOptions& options,
+                              const char16_t* chars, size_t length,
+                              JS::OffThreadCompileCallback callback, void* callbackData)
+{
+    // Suppress GC so that calls below do not trigger a new incremental GC
+    // which could require barriers on the atoms compartment.
+    gc::AutoSuppressGC nogc(cx);
 
-        task->activate(cx->runtime());
-        HelperThreadState().notifyOne(GlobalHelperThreadState::PRODUCER);
-    }
+    JSObject* global = CreateGlobalForOffThreadParse(cx, ParseTaskKind::Module, nogc);
+    if (!global)
+        return false;
+
+    ScopedJSDeletePtr<ExclusiveContext> helpercx(
+        cx->new_<ExclusiveContext>(cx->runtime(), (PerThreadData*) nullptr,
+                                   ExclusiveContext::Context_Exclusive));
+    if (!helpercx)
+        return false;
+
+    ScopedJSDeletePtr<ParseTask> task(
+        cx->new_<ModuleParseTask>(helpercx.get(), global, cx, chars, length,
+                                  callback, callbackData));
+    if (!task)
+        return false;
+
+    helpercx.forget();
+
+    if (!task->init(cx, options) || !QueueOffThreadParseTask(cx, task))
+        return false;
 
     task.forget();
 
     return true;
 }
 
 void
 js::EnqueuePendingParseTasksAfterGC(JSRuntime* rt)
@@ -1010,17 +1111,18 @@ LeaveParseTaskZone(JSRuntime* rt, ParseT
 {
     // Mark the zone as no longer in use by an ExclusiveContext, and available
     // to be collected by the GC.
     task->cx->leaveCompartment(task->cx->compartment());
     rt->clearUsedByExclusiveThread(task->cx->zone());
 }
 
 JSScript*
-GlobalHelperThreadState::finishParseTask(JSContext* maybecx, JSRuntime* rt, void* token)
+GlobalHelperThreadState::finishParseTask(JSContext* maybecx, JSRuntime* rt, ParseTaskKind kind,
+                                         void* token)
 {
     ScopedJSDeletePtr<ParseTask> parseTask;
 
     // The token is a ParseTask* which should be in the finished list.
     // Find and remove its entry.
     {
         AutoLockHelperThreadState lock;
         ParseTaskVector& finished = parseFinishedList();
@@ -1028,29 +1130,30 @@ GlobalHelperThreadState::finishParseTask
             if (finished[i] == token) {
                 parseTask = finished[i];
                 remove(finished, &i);
                 break;
             }
         }
     }
     MOZ_ASSERT(parseTask);
+    MOZ_ASSERT(parseTask->kind == kind);
 
     if (!maybecx) {
         LeaveParseTaskZone(rt, parseTask);
         return nullptr;
     }
 
     JSContext* cx = maybecx;
     MOZ_ASSERT(cx->compartment());
 
     // Make sure we have all the constructors we need for the prototype
     // remapping below, since we can't GC while that's happening.
     Rooted<GlobalObject*> global(cx, &cx->global()->as<GlobalObject>());
-    if (!EnsureParserCreatedClasses(cx)) {
+    if (!EnsureParserCreatedClasses(cx, kind)) {
         LeaveParseTaskZone(rt, parseTask);
         return nullptr;
     }
 
     mergeParseTaskCompartment(rt, parseTask, global, cx->compartment());
 
     if (!parseTask->finish(cx))
         return nullptr;
@@ -1086,16 +1189,44 @@ GlobalHelperThreadState::finishParseTask
     // Update the compressed source table with the result. This is normally
     // called by setCompressedSource when compilation occurs on the main thread.
     if (script->scriptSource()->hasCompressedSource())
         script->scriptSource()->updateCompressedSourceSet(rt);
 
     return script;
 }
 
+JSScript*
+GlobalHelperThreadState::finishScriptParseTask(JSContext* maybecx, JSRuntime* rt, void* token)
+{
+    JSScript* script = finishParseTask(maybecx, rt, ParseTaskKind::Script, token);
+    MOZ_ASSERT_IF(script, script->isGlobalCode());
+    return script;
+}
+
+JSObject*
+GlobalHelperThreadState::finishModuleParseTask(JSContext* maybecx, JSRuntime* rt, void* token)
+{
+    JSScript* script = finishParseTask(maybecx, rt, ParseTaskKind::Module, token);
+    if (!script)
+        return nullptr;
+
+    MOZ_ASSERT(script->module());
+    if (!maybecx)
+        return nullptr;
+
+    JSContext* cx = maybecx;
+    RootedModuleObject module(cx, script->module());
+    module->fixScopesAfterCompartmentMerge(cx);
+    if (!ModuleObject::FreezeArrayProperties(cx, module))
+        return nullptr;
+
+    return module;
+}
+
 JSObject*
 GlobalObject::getStarGeneratorFunctionPrototype()
 {
     const Value& v = getReservedSlot(STAR_GENERATOR_FUNCTION_PROTO);
     return v.isObject() ? &v.toObject() : nullptr;
 }
 
 void
@@ -1113,47 +1244,55 @@ GlobalHelperThreadState::mergeParseTaskC
     LeaveParseTaskZone(rt, parseTask);
 
     {
         gc::ZoneCellIter iter(parseTask->cx->zone(), gc::AllocKind::OBJECT_GROUP);
 
         // Generator functions don't have Function.prototype as prototype but a
         // different function object, so the IdentifyStandardPrototype trick
         // below won't work.  Just special-case it.
-        JSObject* parseTaskStarGenFunctionProto =
-            parseTask->exclusiveContextGlobal->as<GlobalObject>().getStarGeneratorFunctionPrototype();
+        GlobalObject* parseGlobal = &parseTask->exclusiveContextGlobal->as<GlobalObject>();
+        JSObject* parseTaskStarGenFunctionProto = parseGlobal->getStarGeneratorFunctionPrototype();
+
+        // Module objects don't have standard prototypes either.
+        JSObject* moduleProto = parseGlobal->maybeGetModulePrototype();
+        JSObject* importEntryProto = parseGlobal->maybeGetImportEntryPrototype();
+        JSObject* exportEntryProto = parseGlobal->maybeGetExportEntryPrototype();
 
         // Point the prototypes of any objects in the script's compartment to refer
         // to the corresponding prototype in the new compartment. This will briefly
         // create cross compartment pointers, which will be fixed by the
         // MergeCompartments call below.
         for (; !iter.done(); iter.next()) {
             ObjectGroup* group = iter.get<ObjectGroup>();
             TaggedProto proto(group->proto());
             if (!proto.isObject())
                 continue;
 
             JSObject* protoObj = proto.toObject();
 
             JSObject* newProto;
-            if (protoObj == parseTaskStarGenFunctionProto) {
-                newProto = global->getStarGeneratorFunctionPrototype();
-            } else {
-                JSProtoKey key = JS::IdentifyStandardPrototype(protoObj);
-                if (key == JSProto_Null)
-                    continue;
-
+            JSProtoKey key = JS::IdentifyStandardPrototype(protoObj);
+            if (key != JSProto_Null) {
                 MOZ_ASSERT(key == JSProto_Object || key == JSProto_Array ||
                            key == JSProto_Function || key == JSProto_RegExp ||
                            key == JSProto_Iterator);
-
                 newProto = GetBuiltinPrototypePure(global, key);
+            } else if (protoObj == parseTaskStarGenFunctionProto) {
+                newProto = global->getStarGeneratorFunctionPrototype();
+            } else if (protoObj == moduleProto) {
+                newProto = global->getModulePrototype();
+            } else if (protoObj == importEntryProto) {
+                newProto = global->getImportEntryPrototype();
+            } else if (protoObj == exportEntryProto) {
+                newProto = global->getExportEntryPrototype();
+            } else {
+                continue;
             }
 
-            MOZ_ASSERT(newProto);
             group->setProtoUnchecked(TaggedProto(newProto));
         }
     }
 
     // Move the parsed script and all its contents into the desired compartment.
     gc::MergeCompartments(parseTask->cx->compartment(), dest);
 }
 
@@ -1384,35 +1523,17 @@ HelperThread::handleParseWorkload()
     currentTask.emplace(HelperThreadState().parseWorklist().popCopy());
     ParseTask* task = parseTask();
     task->cx->setHelperThread(this);
 
     {
         AutoUnlockHelperThreadState unlock;
         PerThreadData::AutoEnterRuntime enter(threadData.ptr(),
                                               task->exclusiveContextGlobal->runtimeFromAnyThread());
-        SourceBufferHolder srcBuf(task->chars, task->length,
-                                  SourceBufferHolder::NoOwnership);
-
-        // ! WARNING WARNING WARNING !
-        //
-        // See comment in Parser::bindLexical about optimizing global lexical
-        // bindings. If we start optimizing them, passing in task->cx's
-        // global lexical scope would be incorrect!
-        //
-        // ! WARNING WARNING WARNING !
-        ExclusiveContext* parseCx = task->cx;
-        Rooted<ClonedBlockObject*> globalLexical(parseCx, &parseCx->global()->lexicalScope());
-        Rooted<StaticScope*> staticScope(parseCx, &globalLexical->staticBlock());
-        task->script = frontend::CompileScript(parseCx, &task->alloc,
-                                               globalLexical, staticScope, nullptr,
-                                               task->options, srcBuf,
-                                               /* source_ = */ nullptr,
-                                               /* extraSct = */ nullptr,
-                                               /* sourceObjectOut = */ task->sourceObject.address());
+        task->parse();
     }
 
     // The callback is invoked while we are still off the main thread.
     task->callback(task, task->callbackData);
 
     // FinishOffThreadScript will need to be called on the script to
     // migrate it into the correct compartment.
     {
--- a/js/src/vm/HelperThreads.h
+++ b/js/src/vm/HelperThreads.h
@@ -32,16 +32,22 @@ namespace jit {
 } // namespace jit
 namespace wasm {
   class FuncIR;
   class FunctionCompileResults;
   class IonCompileTask;
   typedef Vector<IonCompileTask*, 0, SystemAllocPolicy> IonCompileTaskVector;
 } // namespace wasm
 
+enum class ParseTaskKind
+{
+    Script,
+    Module
+};
+
 // Per-process state for off thread work items.
 class GlobalHelperThreadState
 {
   public:
     // Number of CPUs to treat this machine as having when creating threads.
     // May be accessed without locking.
     size_t cpuCount;
 
@@ -214,28 +220,31 @@ class GlobalHelperThreadState
         MOZ_ASSERT(isLocked());
         numWasmFailedJobs++;
     }
     bool wasmFailed() {
         MOZ_ASSERT(isLocked());
         return bool(numWasmFailedJobs);
     }
 
+    JSScript* finishParseTask(JSContext* maybecx, JSRuntime* rt, ParseTaskKind kind, void* token);
+    void mergeParseTaskCompartment(JSRuntime* rt, ParseTask* parseTask,
+                                   Handle<GlobalObject*> global,
+                                   JSCompartment* dest);
+
   private:
     /*
      * Number of wasm jobs that encountered failure for the active module.
      * Their parent is logically the main thread, and this number serves for harvesting.
      */
     uint32_t numWasmFailedJobs;
 
   public:
-    JSScript* finishParseTask(JSContext* maybecx, JSRuntime* rt, void* token);
-    void mergeParseTaskCompartment(JSRuntime* rt, ParseTask* parseTask,
-                                   Handle<GlobalObject*> global,
-                                   JSCompartment* dest);
+    JSScript* finishScriptParseTask(JSContext* maybecx, JSRuntime* rt, void* token);
+    JSObject* finishModuleParseTask(JSContext* maybecx, JSRuntime* rt, void* token);
     bool compressionInProgress(SourceCompressionTask* task);
     SourceCompressionTask* compressionTaskForSource(ScriptSource* ss);
 
     bool hasActiveThreads();
     void waitForAllThreads();
 
     template <typename T>
     bool checkTaskThreadLimit(size_t maxThreads) const;
@@ -405,16 +414,21 @@ CancelOffThreadParses(JSRuntime* runtime
  * Start a parse/emit cycle for a stream of source. The characters must stay
  * alive until the compilation finishes.
  */
 bool
 StartOffThreadParseScript(JSContext* cx, const ReadOnlyCompileOptions& options,
                           const char16_t* chars, size_t length,
                           JS::OffThreadCompileCallback callback, void* callbackData);
 
+bool
+StartOffThreadParseModule(JSContext* cx, const ReadOnlyCompileOptions& options,
+                          const char16_t* chars, size_t length,
+                          JS::OffThreadCompileCallback callback, void* callbackData);
+
 /*
  * Called at the end of GC to enqueue any Parse tasks that were waiting on an
  * atoms-zone GC to finish.
  */
 void
 EnqueuePendingParseTasksAfterGC(JSRuntime* rt);
 
 struct AutoEnqueuePendingParseTasksAfterGC {
@@ -458,16 +472,17 @@ class MOZ_RAII AutoUnlockHelperThreadSta
     ~AutoUnlockHelperThreadState()
     {
         HelperThreadState().lock();
     }
 };
 
 struct ParseTask
 {
+    ParseTaskKind kind;
     ExclusiveContext* cx;
     OwningCompileOptions options;
     const char16_t* chars;
     size_t length;
     LifoAlloc alloc;
 
     // Rooted pointer to the global object used by 'cx'.
     PersistentRootedObject exclusiveContextGlobal;
@@ -485,29 +500,46 @@ struct ParseTask
     PersistentRooted<ScriptSourceObject*> sourceObject;
 
     // Any errors or warnings produced during compilation. These are reported
     // when finishing the script.
     Vector<frontend::CompileError*> errors;
     bool overRecursed;
     bool outOfMemory;
 
-    ParseTask(ExclusiveContext* cx, JSObject* exclusiveContextGlobal,
+    ParseTask(ParseTaskKind kind, ExclusiveContext* cx, JSObject* exclusiveContextGlobal,
               JSContext* initCx, const char16_t* chars, size_t length,
               JS::OffThreadCompileCallback callback, void* callbackData);
     bool init(JSContext* cx, const ReadOnlyCompileOptions& options);
 
     void activate(JSRuntime* rt);
+    virtual void parse() = 0;
     bool finish(JSContext* cx);
 
     bool runtimeMatches(JSRuntime* rt) {
         return exclusiveContextGlobal->runtimeFromAnyThread() == rt;
     }
 
-    ~ParseTask();
+    virtual ~ParseTask();
+};
+
+struct ScriptParseTask : public ParseTask
+{
+    ScriptParseTask(ExclusiveContext* cx, JSObject* exclusiveContextGlobal,
+                    JSContext* initCx, const char16_t* chars, size_t length,
+                    JS::OffThreadCompileCallback callback, void* callbackData);
+    void parse() override;
+};
+
+struct ModuleParseTask : public ParseTask
+{
+    ModuleParseTask(ExclusiveContext* cx, JSObject* exclusiveContextGlobal,
+                    JSContext* initCx, const char16_t* chars, size_t length,
+                    JS::OffThreadCompileCallback callback, void* callbackData);
+    void parse() override;
 };
 
 // Return whether, if a new parse task was started, it would need to wait for
 // an in-progress GC to complete before starting.
 extern bool
 OffThreadParsingMustWaitForGC(JSRuntime* rt);
 
 // Compression tasks are allocated on the stack by their triggering thread,
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -1016,17 +1016,17 @@ js::UnwindScopeToTryPc(JSScript* script,
         MOZ_ASSERT(*pc == JSOP_TRY);
     }
     return pc;
 }
 
 static bool
 ForcedReturn(JSContext* cx, ScopeIter& si, InterpreterRegs& regs, bool frameOk = true)
 {
-    bool ok = Debugger::onLeaveFrame(cx, regs.fp(), frameOk);
+    bool ok = Debugger::onLeaveFrame(cx, regs.fp(), regs.pc, frameOk);
     UnwindAllScopesInFrame(cx, si);
     // Point the frame to the end of the script, regardless of error. The
     // caller must jump to the correct continuation depending on 'ok'.
     regs.setToEndOfScript();
     return ok;
 }
 
 static bool
@@ -1202,17 +1202,17 @@ HandleError(JSContext* cx, InterpreterRe
             // No need to increment the PCCounts number of execution here, as
             // the interpreter increments any PCCounts if present.
             MOZ_ASSERT_IF(regs.fp()->script()->hasScriptCounts(),
                           regs.fp()->script()->maybeGetPCCounts(regs.pc));
             return res;
         }
 
         ok = HandleClosingGeneratorReturn(cx, regs.fp(), ok);
-        ok = Debugger::onLeaveFrame(cx, regs.fp(), ok);
+        ok = Debugger::onLeaveFrame(cx, regs.fp(), regs.pc, ok);
     } else {
         // We may be propagating a forced return from the interrupt
         // callback, which cannot easily force a return.
         if (MOZ_UNLIKELY(cx->isPropagatingForcedReturn())) {
             cx->clearPropagatingForcedReturn();
             if (!ForcedReturn(cx, si, regs))
                 return ErrorReturnContinuation;
             return SuccessfulReturnContinuation;
@@ -1902,17 +1902,17 @@ CASE(JSOP_RETRVAL)
 
   return_continuation:
     if (activation.entryFrame() != REGS.fp()) {
         // Stop the engine. (No details about which engine exactly, could be
         // interpreter, Baseline or IonMonkey.)
         TraceLogStopEvent(logger, TraceLogger_Engine);
         TraceLogStopEvent(logger, TraceLogger_Scripts);
 
-        interpReturnOK = Debugger::onLeaveFrame(cx, REGS.fp(), interpReturnOK);
+        interpReturnOK = Debugger::onLeaveFrame(cx, REGS.fp(), REGS.pc, interpReturnOK);
 
         REGS.fp()->epilogue(cx);
 
   jit_return_pop_frame:
 
         activation.popInlineFrame(REGS.fp());
         SET_SCRIPT(REGS.fp()->script());
 
@@ -3988,17 +3988,17 @@ DEFAULT()
         cx->clearPendingException();
       }
       ADVANCE_AND_DISPATCH(0);
     }
 
     MOZ_CRASH("Invalid HandleError continuation");
 
   exit:
-    interpReturnOK = Debugger::onLeaveFrame(cx, REGS.fp(), interpReturnOK);
+    interpReturnOK = Debugger::onLeaveFrame(cx, REGS.fp(), REGS.pc, interpReturnOK);
 
     REGS.fp()->epilogue(cx);
 
     gc::MaybeVerifyBarriers(cx, true);
 
     TraceLogStopEvent(logger, TraceLogger_Engine);
     TraceLogStopEvent(logger, scriptEvent);
 
--- a/js/src/vm/Runtime-inl.h
+++ b/js/src/vm/Runtime-inl.h
@@ -65,17 +65,17 @@ NewObjectCache::newObjectFromHit(JSConte
     if (!obj)
         return nullptr;
 
     copyCachedToObject(obj, templateObj, entry->kind);
 
     if (group->clasp()->shouldDelayMetadataCallback())
         cx->compartment()->setObjectPendingMetadata(cx, obj);
     else
-        SetNewObjectMetadata(cx, obj);
+        obj = static_cast<NativeObject*>(SetNewObjectMetadata(cx, obj));
 
     probes::CreateObject(cx, obj);
     gc::TraceCreateObject(obj);
     return obj;
 }
 
 }  /* namespace js */
 
--- a/js/src/vm/SavedStacks.cpp
+++ b/js/src/vm/SavedStacks.cpp
@@ -1408,17 +1408,17 @@ SavedStacks::chooseSamplingProbability(J
         bernoulli.setRandomState(seed[0], seed[1]);
         bernoulliSeeded = true;
     }
 
     bernoulli.setProbability(probability);
 }
 
 JSObject*
-SavedStacksMetadataCallback(JSContext* cx, JSObject* target)
+SavedStacksMetadataCallback(JSContext* cx, HandleObject target)
 {
     RootedObject obj(cx, target);
 
     SavedStacks& stacks = cx->compartment()->savedStacks();
     if (!stacks.bernoulli.trial())
         return nullptr;
 
     AutoEnterOOMUnsafeRegion oomUnsafe;
--- a/js/src/vm/SavedStacks.h
+++ b/js/src/vm/SavedStacks.h
@@ -144,17 +144,17 @@ namespace js {
 // In the case of z, the `SavedFrame` accessors are called with the `SavedFrame`
 // object in the `this` value, and the content compartment as the cx's current
 // compartment. Similar to the case of y, only the B and C frames are exposed
 // because the cx's current compartment's principals do not subsume A's captured
 // principals.
 
 class SavedStacks {
     friend class SavedFrame;
-    friend JSObject* SavedStacksMetadataCallback(JSContext* cx, JSObject* target);
+    friend JSObject* SavedStacksMetadataCallback(JSContext* cx, HandleObject target);
     friend bool JS::ubi::ConstructSavedFrameStackSlow(JSContext* cx,
                                                       JS::ubi::StackFrame& ubiFrame,
                                                       MutableHandleObject outSavedFrameStack);
 
   public:
     SavedStacks()
       : frames(),
         bernoulliSeeded(false),
@@ -295,17 +295,17 @@ class SavedStacks {
     // removes the dead script; the second will clear out the source atom since
     // it is no longer held by the table.
     using PCLocationMap = GCHashMap<PCKey, LocationValue, PCLocationHasher, SystemAllocPolicy>;
     PCLocationMap pcLocationMap;
 
     bool getLocation(JSContext* cx, const FrameIter& iter, MutableHandle<LocationValue> locationp);
 };
 
-JSObject* SavedStacksMetadataCallback(JSContext* cx, JSObject* target);
+JSObject* SavedStacksMetadataCallback(JSContext* cx, HandleObject target);
 
 template <>
 class RootedBase<SavedStacks::LocationValue>
   : public SavedStacks::MutableLocationValueOperations<JS::Rooted<SavedStacks::LocationValue>>
 {};
 
 template <>
 class MutableHandleBase<SavedStacks::LocationValue>
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -831,16 +831,46 @@ intrinsic_IsUint8TypedArray(JSContext* c
 
 static bool
 intrinsic_IsInt8TypedArray(JSContext* cx, unsigned argc, Value* vp)
 {
     return intrinsic_IsSpecificTypedArray(cx, argc, vp, Scalar::Int8);
 }
 
 static bool
+intrinsic_IsUint16TypedArray(JSContext* cx, unsigned argc, Value* vp)
+{
+    return intrinsic_IsSpecificTypedArray(cx, argc, vp, Scalar::Uint16);
+}
+
+static bool
+intrinsic_IsInt16TypedArray(JSContext* cx, unsigned argc, Value* vp)
+{
+    return intrinsic_IsSpecificTypedArray(cx, argc, vp, Scalar::Int16);
+}
+
+static bool
+intrinsic_IsUint32TypedArray(JSContext* cx, unsigned argc, Value* vp)
+{
+    return intrinsic_IsSpecificTypedArray(cx, argc, vp, Scalar::Uint32);
+}
+
+static bool
+intrinsic_IsInt32TypedArray(JSContext* cx, unsigned argc, Value* vp)
+{
+    return intrinsic_IsSpecificTypedArray(cx, argc, vp, Scalar::Int32);
+}
+
+static bool
+intrinsic_IsFloat32TypedArray(JSContext* cx, unsigned argc, Value* vp)
+{
+    return intrinsic_IsSpecificTypedArray(cx, argc, vp, Scalar::Float32);
+}
+
+static bool
 intrinsic_IsPossiblyWrappedTypedArray(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 1);
 
     bool isTypedArray = false;
     if (args[0].isObject()) {
         JSObject* obj = CheckedUnwrap(&args[0].toObject());
@@ -1850,16 +1880,21 @@ static const JSFunctionSpec intrinsic_fu
     JS_FN("IsSharedArrayBuffer",
           intrinsic_IsInstanceOfBuiltin<SharedArrayBufferObject>,       1,0),
 
     JS_FN("ArrayBufferByteLength",   intrinsic_ArrayBufferByteLength,   1,0),
     JS_FN("ArrayBufferCopyData",     intrinsic_ArrayBufferCopyData,     4,0),
 
     JS_FN("IsUint8TypedArray",        intrinsic_IsUint8TypedArray,      1,0),
     JS_FN("IsInt8TypedArray",         intrinsic_IsInt8TypedArray,       1,0),
+    JS_FN("IsUint16TypedArray",       intrinsic_IsUint16TypedArray,     1,0),
+    JS_FN("IsInt16TypedArray",        intrinsic_IsInt16TypedArray,      1,0),
+    JS_FN("IsUint32TypedArray",       intrinsic_IsUint32TypedArray,     1,0),
+    JS_FN("IsInt32TypedArray",        intrinsic_IsInt32TypedArray,      1,0),
+    JS_FN("IsFloat32TypedArray",      intrinsic_IsFloat32TypedArray,    1,0),
     JS_INLINABLE_FN("IsTypedArray",
                     intrinsic_IsInstanceOfBuiltin<TypedArrayObject>,    1,0,
                     IntrinsicIsTypedArray),
     JS_INLINABLE_FN("IsPossiblyWrappedTypedArray",intrinsic_IsPossiblyWrappedTypedArray,1,0,
                     IntrinsicIsPossiblyWrappedTypedArray),
 
     JS_FN("TypedArrayBuffer",        intrinsic_TypedArrayBuffer,        1,0),
     JS_FN("TypedArrayByteOffset",    intrinsic_TypedArrayByteOffset,    1,0),
--- a/js/src/vm/Stack-inl.h
+++ b/js/src/vm/Stack-inl.h
@@ -845,16 +845,22 @@ AbstractFramePtr::popWith(JSContext* cx)
 {
     if (isInterpreterFrame()) {
         asInterpreterFrame()->popWith(cx);
         return;
     }
     asBaselineFrame()->popWith(cx);
 }
 
+inline bool
+AbstractFramePtr::debuggerNeedsCheckPrimitiveReturn() const
+{
+    return script()->isDerivedClassConstructor();
+}
+
 ActivationEntryMonitor::~ActivationEntryMonitor()
 {
     if (entryMonitor_)
         entryMonitor_->Exit(cx_);
 
     cx_->runtime()->entryMonitor = entryMonitor_;
 }
 
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -13,16 +13,17 @@
 #include "asmjs/WasmModule.h"
 #include "gc/Marking.h"
 #include "jit/BaselineFrame.h"
 #include "jit/JitcodeMap.h"
 #include "jit/JitCompartment.h"
 #include "js/GCAPI.h"
 #include "vm/Debugger.h"
 #include "vm/Opcodes.h"
+#include "vm/ScopeObject.h"
 
 #include "jit/JitFrameIterator-inl.h"
 #include "vm/Interpreter-inl.h"
 #include "vm/Probes-inl.h"
 #include "vm/ScopeObject-inl.h"
 
 using namespace js;
 
--- a/js/src/vm/Stack.h
+++ b/js/src/vm/Stack.h
@@ -211,16 +211,18 @@ class AbstractFramePtr
 
     inline JSScript* script() const;
     inline JSFunction* callee() const;
     inline Value calleev() const;
     inline Value& thisArgument() const;
 
     inline Value newTarget() const;
 
+    inline bool debuggerNeedsCheckPrimitiveReturn() const;
+
     inline bool isFunctionFrame() const;
     inline bool isNonStrictDirectEvalFrame() const;
     inline bool isStrictEvalFrame() const;
 
     inline unsigned numActualArgs() const;
     inline unsigned numFormalArgs() const;
 
     inline Value* argv() const;
--- a/js/src/vm/TypeInference-inl.h
+++ b/js/src/vm/TypeInference-inl.h
@@ -278,27 +278,30 @@ struct AutoEnterAnalysis
     gc::AutoSuppressGC suppressGC;
 
     // Allow clearing inference info on OOM during incremental sweeping.
     AutoClearTypeInferenceStateOnOOM oom;
 
     // Pending recompilations to perform before execution of JIT code can resume.
     RecompileInfoVector pendingRecompiles;
 
+    // Prevent us from calling the objectMetadataCallback.
+    js::AutoSuppressObjectMetadataCallback suppressMetadata;
+
     FreeOp* freeOp;
     Zone* zone;
 
     explicit AutoEnterAnalysis(ExclusiveContext* cx)
-      : suppressGC(cx), oom(cx->zone())
+      : suppressGC(cx), oom(cx->zone()), suppressMetadata(cx)
     {
         init(cx->defaultFreeOp(), cx->zone());
     }
 
     AutoEnterAnalysis(FreeOp* fop, Zone* zone)
-      : suppressGC(zone->runtimeFromMainThread()), oom(zone)
+      : suppressGC(zone->runtimeFromMainThread()), oom(zone), suppressMetadata(zone)
     {
         init(fop, zone);
     }
 
     ~AutoEnterAnalysis()
     {
         if (this != zone->types.activeAnalysis)
             return;
--- a/js/src/vm/UnboxedObject.cpp
+++ b/js/src/vm/UnboxedObject.cpp
@@ -629,16 +629,18 @@ UnboxedPlainObject::convertToNative(JSCo
 
     return true;
 }
 
 /* static */
 UnboxedPlainObject*
 UnboxedPlainObject::create(ExclusiveContext* cx, HandleObjectGroup group, NewObjectKind newKind)
 {
+    AutoSetNewObjectMetadata metadata(cx);
+
     MOZ_ASSERT(group->clasp() == &class_);
     gc::AllocKind allocKind = group->unboxedLayout().getAllocKind();
 
     UnboxedPlainObject* res =
         NewObjectWithGroup<UnboxedPlainObject>(cx, group, allocKind, newKind);
     if (!res)
         return nullptr;
 
@@ -911,17 +913,18 @@ UnboxedPlainObject::obj_enumerate(JSCont
 const Class UnboxedExpandoObject::class_ = {
     "UnboxedExpandoObject",
     0
 };
 
 const Class UnboxedPlainObject::class_ = {
     js_Object_str,
     Class::NON_NATIVE |
-    JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
+    JSCLASS_HAS_CACHED_PROTO(JSProto_Object) |
+    JSCLASS_DELAY_METADATA_CALLBACK,
     nullptr,        /* addProperty */
     nullptr,        /* delProperty */
     nullptr,        /* getProperty */
     nullptr,        /* setProperty */
     nullptr,        /* enumerate   */
     nullptr,        /* resolve     */
     nullptr,        /* mayResolve  */
     nullptr,        /* finalize    */
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -4380,23 +4380,27 @@ ScrollFrameHelper::ScrollEvent::ScrollEv
   : mHelper(aHelper)
 {
   mDriver = mHelper->mOuter->PresContext()->RefreshDriver();
   mDriver->AddRefreshObserver(this, Flush_Style);
 }
 
 ScrollFrameHelper::ScrollEvent::~ScrollEvent()
 {
-  mDriver->RemoveRefreshObserver(this, Flush_Style);
-  mDriver = nullptr;
+  if (mDriver) {
+    mDriver->RemoveRefreshObserver(this, Flush_Style);
+    mDriver = nullptr;
+  }
 }
 
 void
 ScrollFrameHelper::ScrollEvent::WillRefresh(mozilla::TimeStamp aTime)
 {
+  mDriver->RemoveRefreshObserver(this, Flush_Style);
+  mDriver = nullptr;
   mHelper->FireScrollEvent();
 }
 
 void
 ScrollFrameHelper::FireScrollEvent()
 {
   MOZ_ASSERT(mScrollEvent);
   mScrollEvent = nullptr;
--- a/mfbt/UniquePtr.h
+++ b/mfbt/UniquePtr.h
@@ -221,17 +221,17 @@ public:
             typename RemoveReference<D>::Type&& aD2)
     : mTuple(aPtr, Move(aD2))
   {
     static_assert(!IsReference<D>::value,
                   "rvalue deleter can't be stored by reference");
   }
 
   UniquePtr(UniquePtr&& aOther)
-    : mTuple(aOther.release(), Forward<DeleterType>(aOther.getDeleter()))
+    : mTuple(aOther.release(), Forward<DeleterType>(aOther.get_deleter()))
   {}
 
   MOZ_IMPLICIT
   UniquePtr(decltype(nullptr))
     : mTuple(nullptr, DeleterType())
   {
     static_assert(!IsPointer<D>::value, "must provide a deleter instance");
     static_assert(!IsReference<D>::value, "must provide a deleter instance");
@@ -242,40 +242,40 @@ public:
   UniquePtr(UniquePtr<U, E>&& aOther,
             typename EnableIf<IsConvertible<typename UniquePtr<U, E>::Pointer,
                                             Pointer>::value &&
                               !IsArray<U>::value &&
                               (IsReference<D>::value
                                ? IsSame<D, E>::value
                                : IsConvertible<E, D>::value),
                               int>::Type aDummy = 0)
-    : mTuple(aOther.release(), Forward<E>(aOther.getDeleter()))
+    : mTuple(aOther.release(), Forward<E>(aOther.get_deleter()))
   {
   }
 
   ~UniquePtr() { reset(nullptr); }
 
   UniquePtr& operator=(UniquePtr&& aOther)
   {
     reset(aOther.release());
-    getDeleter() = Forward<DeleterType>(aOther.getDeleter());
+    get_deleter() = Forward<DeleterType>(aOther.get_deleter());
     return *this;
   }
 
   template<typename U, typename E>
   UniquePtr& operator=(UniquePtr<U, E>&& aOther)
   {
     static_assert(IsConvertible<typename UniquePtr<U, E>::Pointer,
                                 Pointer>::value,
                   "incompatible UniquePtr pointees");
     static_assert(!IsArray<U>::value,
                   "can't assign from UniquePtr holding an array");
 
     reset(aOther.release());
-    getDeleter() = Forward<E>(aOther.getDeleter());
+    get_deleter() = Forward<E>(aOther.get_deleter());
     return *this;
   }
 
   UniquePtr& operator=(decltype(nullptr))
   {
     reset(nullptr);
     return *this;
   }
@@ -286,32 +286,32 @@ public:
     MOZ_ASSERT(get(), "dereferencing a UniquePtr containing nullptr");
     return get();
   }
 
   explicit operator bool() const { return get() != nullptr; }
 
   Pointer get() const { return ptr(); }
 
-  DeleterType& getDeleter() { return del(); }
-  const DeleterType& getDeleter() const { return del(); }
+  DeleterType& get_deleter() { return del(); }
+  const DeleterType& get_deleter() const { return del(); }
 
   MOZ_WARN_UNUSED_RESULT Pointer release()
   {
     Pointer p = ptr();
     ptr() = nullptr;
     return p;
   }
 
   void reset(Pointer aPtr = Pointer())
   {
     Pointer old = ptr();
     ptr() = aPtr;
     if (old != nullptr) {
-      getDeleter()(old);
+      get_deleter()(old);
     }
   }
 
   void swap(UniquePtr& aOther)
   {
     mTuple.swap(aOther.mTuple);
   }
 
@@ -390,49 +390,49 @@ public:
   template<typename U, typename V>
   UniquePtr(U&& aU, V&& aV,
             typename EnableIf<IsPointer<U>::value &&
                               IsConvertible<U, Pointer>::value,
                               int>::Type aDummy = 0)
   = delete;
 
   UniquePtr(UniquePtr&& aOther)
-    : mTuple(aOther.release(), Forward<DeleterType>(aOther.getDeleter()))
+    : mTuple(aOther.release(), Forward<DeleterType>(aOther.get_deleter()))
   {}
 
   MOZ_IMPLICIT
   UniquePtr(decltype(nullptr))
     : mTuple(nullptr, DeleterType())
   {
     static_assert(!IsPointer<D>::value, "must provide a deleter instance");
     static_assert(!IsReference<D>::value, "must provide a deleter instance");
   }
 
   ~UniquePtr() { reset(nullptr); }
 
   UniquePtr& operator=(UniquePtr&& aOther)
   {
     reset(aOther.release());
-    getDeleter() = Forward<DeleterType>(aOther.getDeleter());
+    get_deleter() = Forward<DeleterType>(aOther.get_deleter());
     return *this;
   }
 
   UniquePtr& operator=(decltype(nullptr))
   {
     reset();
     return *this;
   }
 
   explicit operator bool() const { return get() != nullptr; }
 
   T& operator[](decltype(sizeof(int)) aIndex) const { return get()[aIndex]; }
   Pointer get() const { return mTuple.first(); }
 
-  DeleterType& getDeleter() { return mTuple.second(); }
-  const DeleterType& getDeleter() const { return mTuple.second(); }
+  DeleterType& get_deleter() { return mTuple.second(); }
+  const DeleterType& get_deleter() const { return mTuple.second(); }
 
   MOZ_WARN_UNUSED_RESULT Pointer release()
   {
     Pointer p = mTuple.first();
     mTuple.first() = nullptr;
     return p;
   }
 
--- a/mfbt/tests/TestUniquePtr.cpp
+++ b/mfbt/tests/TestUniquePtr.cpp
@@ -287,17 +287,17 @@ static bool
 TestReferenceDeleterGuts()
 {
   DefaultDelete<int> delInt;
   IntDeleterRef id1(new int, delInt);
 
   IntDeleterRef id2(Move(id1));
   CHECK(id1 == nullptr);
   CHECK(nullptr != id2);
-  CHECK(&id1.getDeleter() == &id2.getDeleter());
+  CHECK(&id1.get_deleter() == &id2.get_deleter());
 
   IntDeleterRef id3(Move(id2));
 
   DefaultDelete<A> delA;
   ADeleterRef a1(new A, delA);
   a1.reset(nullptr);
   a1.reset(new B);
   a1 = nullptr;
--- a/mobile/android/base/java/org/mozilla/gecko/gfx/NativePanZoomController.java
+++ b/mobile/android/base/java/org/mozilla/gecko/gfx/NativePanZoomController.java
@@ -24,16 +24,17 @@ import android.view.View;
 class NativePanZoomController extends JNIObject implements PanZoomController {
     private final PanZoomTarget mTarget;
     private final LayerView mView;
     private boolean mDestroyed;
     private Overscroll mOverscroll;
     boolean mNegateWheelScroll;
     private float mPointerScrollFactor;
     private final PrefsHelper.PrefHandler mPrefsObserver;
+    private long mLastDownTime;
     private static final float MAX_SCROLL = 0.075f * GeckoAppShell.getDpi();
 
     @WrapForJNI
     private native boolean handleMotionEvent(
             int action, int actionIndex, long time, int metaState,
             int pointerId[], float x[], float y[], float orientation[], float pressure[],
             float toolMajor[], float toolMinor[]);
 
@@ -46,16 +47,22 @@ class NativePanZoomController extends JN
     private boolean handleMotionEvent(MotionEvent event, boolean keepInViewCoordinates) {
         if (mDestroyed) {
             return false;
         }
 
         final int action = event.getActionMasked();
         final int count = event.getPointerCount();
 
+        if (action == MotionEvent.ACTION_DOWN) {
+            mLastDownTime = event.getDownTime();
+        } else if (mLastDownTime != event.getDownTime()) {
+            return false;
+        }
+
         final int[] pointerId = new int[count];
         final float[] x = new float[count];
         final float[] y = new float[count];
         final float[] orientation = new float[count];
         final float[] pressure = new float[count];
         final float[] toolMajor = new float[count];
         final float[] toolMinor = new float[count];
 
@@ -140,21 +147,22 @@ class NativePanZoomController extends JN
     @Override
     public boolean onTouchEvent(MotionEvent event) {
         return handleMotionEvent(event, /* keepInViewCoordinates */ true);
     }
 
     @Override
     public boolean onMotionEvent(MotionEvent event) {
         final int action = event.getActionMasked();
-        if (action == MotionEvent.ACTION_SCROLL) {
+        if (action == MotionEvent.ACTION_SCROLL && event.getDownTime() >= mLastDownTime) {
+            mLastDownTime = event.getDownTime();
             return handleScrollEvent(event);
-        } else {
-            return false;
         }
+
+        return false;
     }
 
     @Override
     public boolean onKeyEvent(KeyEvent event) {
         // FIXME implement this
         return false;
     }
 
--- a/mobile/android/base/java/org/mozilla/gecko/toolbar/ToolbarEditLayout.java
+++ b/mobile/android/base/java/org/mozilla/gecko/toolbar/ToolbarEditLayout.java
@@ -74,16 +74,18 @@ public class ToolbarEditLayout extends T
         mEditText = (ToolbarEditText) findViewById(R.id.url_edit_text);
 
         mVoiceInput = (ImageButton) findViewById(R.id.mic);
         mQrCode = (ImageButton) findViewById(R.id.qrcode);
     }
 
     @Override
     public void onAttachedToWindow() {
+        super.onAttachedToWindow();
+
         if (HardwareUtils.isTablet()) {
             mSearchIcon.setVisibility(View.VISIBLE);
         }
 
         mEditText.setOnFocusChangeListener(new OnFocusChangeListener() {
             @Override
             public void onFocusChange(View v, boolean hasFocus) {
                 if (mFocusChangeListener != null) {
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -4777,16 +4777,21 @@ pref("dom.vr.enabled", true);
 // Oculus > 0.5
 pref("dom.vr.oculus.enabled", true);
 // Oculus <= 0.5; will only trigger if > 0.5 is not used or found
 pref("dom.vr.oculus050.enabled", true);
 // Cardboard VR device is disabled by default
 pref("dom.vr.cardboard.enabled", false);
 // 0 = never; 1 = only if real devices aren't there; 2 = always
 pref("dom.vr.add-test-devices", 0);
+// Pose prediction reduces latency effects by returning future predicted HMD
+// poses to callers of the WebVR API.  This currently only has an effect for
+// Oculus Rift on SDK 0.8 or greater.  It is disabled by default for now due to
+// frame uniformity issues with e10s.
+pref("dom.vr.poseprediction.enabled", false);
 // true = show the VR textures in our compositing output; false = don't.
 // true might have performance impact
 pref("gfx.vr.mirror-textures", false);
 
 // MMS UA Profile settings
 pref("wap.UAProf.url", "");
 pref("wap.UAProf.tagname", "x-wap-profile");
 
deleted file mode 100644
--- a/netwerk/base/PollableEvent.cpp
+++ /dev/null
@@ -1,163 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et cindent: */
-/* 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 "nsSocketTransportService2.h"
-#include "PollableEvent.h"
-#include "mozilla/Assertions.h"
-#include "mozilla/DebugOnly.h"
-#include "mozilla/Logging.h"
-#include "prerror.h"
-#include "prio.h"
-#include "private/pprio.h"
-
-#ifdef XP_WIN
-#include "ShutdownLayer.h"
-#else
-#include <fcntl.h>
-#define USEPIPE 1
-#endif
-
-namespace mozilla {
-namespace net {
-
-PollableEvent::PollableEvent()
-  : mWriteFD(nullptr)
-  , mReadFD(nullptr)
-  , mSignaled(false)
-{
-  // create pair of prfiledesc that can be used as a poll()ble
-  // signal. on windows use a localhost socket pair, and on
-  // unix use a pipe.
-#ifdef USEPIPE
-  if (PR_CreatePipe(&mReadFD, &mWriteFD) == PR_SUCCESS) {
-    // make the pipe non blocking. NSPR asserts at
-    // trying to use SockOpt here
-    PROsfd fd = PR_FileDesc2NativeHandle(mReadFD);
-    int flags = fcntl(fd, F_GETFL, 0);
-    (void)fcntl(fd, F_SETFL, flags | O_NONBLOCK);
-    fd = PR_FileDesc2NativeHandle(mWriteFD);
-    flags = fcntl(fd, F_GETFL, 0);
-    (void)fcntl(fd, F_SETFL, flags | O_NONBLOCK);
-  } else {
-    mReadFD = nullptr;
-    mWriteFD = nullptr;
-    SOCKET_LOG(("PollableEvent() pipe failed\n"));
-  }
-#else
-  PRFileDesc *fd[2];
-  if (PR_NewTCPSocketPair(fd) == PR_SUCCESS) {
-    mReadFD = fd[0];
-    mWriteFD = fd[1];
-
-    PRSocketOptionData opt;
-    DebugOnly<PRStatus> status;
-    opt.option = PR_SockOpt_NoDelay;
-    opt.value.no_delay = true;
-    PR_SetSocketOption(mWriteFD, &opt);
-    PR_SetSocketOption(mReadFD, &opt);
-    opt.option = PR_SockOpt_Nonblocking;
-    opt.value.non_blocking = true;
-    status = PR_SetSocketOption(mWriteFD, &opt);
-    MOZ_ASSERT(status == PR_SUCCESS);
-    status = PR_SetSocketOption(mReadFD, &opt);
-    MOZ_ASSERT(status == PR_SUCCESS);
-  } else {
-    SOCKET_LOG(("PollableEvent() socketpair failed\n"));
-  }
-#endif
-
-  if (mReadFD && mWriteFD) {
-    // prime the system to deal with races invovled in [dc]tor cycle
-    SOCKET_LOG(("PollableEvent() ctor ok\n"));
-    mSignaled = true;
-    PR_Write(mWriteFD, "I", 1);
-  }
-}
-
-PollableEvent::~PollableEvent()
-{
-  if (mWriteFD) {
-#if defined(XP_WIN)
-    mozilla::net::AttachShutdownLayer(mWriteFD);
-#endif
-    PR_Close(mWriteFD);
-  }
-  if (mReadFD) {
-#if defined(XP_WIN)
-    mozilla::net::AttachShutdownLayer(mReadFD);
-#endif
-    PR_Close(mReadFD);
-  }
-}
-
-// we do not record signals on the socket thread
-// because the socket thread can reliably look at its
-// own runnable queue before selecting a poll time
-// this is the "service the network without blocking" comment in
-// nsSocketTransportService2.cpp
-bool
-PollableEvent::Signal()
-{
-  SOCKET_LOG(("PollableEvent::Signal\n"));
-
-  if (!mWriteFD) {
-    SOCKET_LOG(("PollableEvent::Signal Failed on no FD\n"));
-    return false;
-  }
-  if (PR_GetCurrentThread() == gSocketThread) {
-    SOCKET_LOG(("PollableEvent::Signal OnSocketThread nop\n"));
-    return true;
-  }
-  if (mSignaled) {
-    return true;
-  }
-  mSignaled = true;
-  int32_t status = PR_Write(mWriteFD, "M", 1);
-  if (status != 1) {
-    NS_WARNING("PollableEvent::Signal Failed\n");
-    SOCKET_LOG(("PollableEvent::Signal Failed\n"));
-  }
-  return (status == 1);
-}
-
-bool
-PollableEvent::Clear()
-{
-  // necessary because of the "dont signal on socket thread" optimization
-  MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
-
-  SOCKET_LOG(("PollableEvent::Clear\n"));
-  mSignaled = false;
-  if (!mReadFD) {
-    SOCKET_LOG(("PollableEvent::Clear mReadFD is null\n"));
-    return false;
-  }
-  char buf[2048];
-  int32_t status = PR_Read(mReadFD, buf, 2048);
-
-  if (status == 1) {
-    return true;
-  }
-  if (status == 0) {
-    SOCKET_LOG(("PollableEvent::Clear EOF!\n"));
-    return false;
-  }
-  if (status > 1) {
-    MOZ_ASSERT(false);
-    SOCKET_LOG(("PollableEvent::Clear Unexpected events\n"));
-    Clear();
-    return true;
-  }
-  PRErrorCode code = PR_GetError();
-  if (code == PR_WOULD_BLOCK_ERROR) {
-    return true;
-  }
-  SOCKET_LOG(("PollableEvent::Clear unexpected error %d\n", code));
-  return false;
-}
-
-} // namespace net
-} // namespace mozilla
deleted file mode 100644
--- a/netwerk/base/PollableEvent.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et cindent: */
-/* 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 PollableEvent_h__
-#define PollableEvent_h__
-
-#include "mozilla/Mutex.h"
-
-namespace mozilla {
-namespace net {
-
-// class must be called locked
-class PollableEvent
-{
-public:
-  PollableEvent();
-  ~PollableEvent();
-
-  // Signal/Clear return false only if they fail
-  bool Signal();
-  bool Clear();
-  bool Valid() { return mWriteFD && mReadFD; }
-
-  PRFileDesc *PollableFD() { return mReadFD; }
-
-private:
-  PRFileDesc *mWriteFD;
-  PRFileDesc *mReadFD;
-  bool        mSignaled;
-};
-
-} // namespace net
-} // namespace mozilla
-
-#endif
--- a/netwerk/base/moz.build
+++ b/netwerk/base/moz.build
@@ -244,17 +244,16 @@ UNIFIED_SOURCES += [
     'nsSyncStreamListener.cpp',
     'nsTemporaryFileInputStream.cpp',
     'nsTransportUtils.cpp',
     'nsUDPSocket.cpp',
     'nsUnicharStreamLoader.cpp',
     'nsURLHelper.cpp',
     'nsURLParsers.cpp',
     'OfflineObserver.cpp',
-    'PollableEvent.cpp',
     'Predictor.cpp',
     'ProxyAutoConfig.cpp',
     'RedirectChannelRegistrar.cpp',
     'SchedulingContextService.cpp',
     'SimpleBuffer.cpp',
     'StreamingProtocolService.cpp',
     'Tickler.cpp',
     'TLSServerSocket.cpp',
--- a/netwerk/base/nsILoadGroup.idl
+++ b/netwerk/base/nsILoadGroup.idl
@@ -53,17 +53,17 @@ interface nsILoadGroup : nsIRequest
 
     /**
      * Removes a request from the group.  If this is a foreground request
      * then the groupObserver's onStopRequest will be called.
      *
      * By the time this call ends, aRequest will have been removed from the
      * loadgroup, even if this function throws an exception.
      */
-    void removeRequest(in nsIRequest aRequest, 
+    void removeRequest(in nsIRequest aRequest,
                        in nsISupports aContext,
                        in nsresult aStatus);
 
     /**
      * Returns the requests contained directly in this group.
      * Enumerator element type: nsIRequest.
      */
     readonly attribute nsISimpleEnumerator requests;
@@ -92,9 +92,15 @@ interface nsILoadGroup : nsIRequest
      * to the group - typically via nsIDocShell::defaultLoadFlags on a new
      * docShell.
      * Note that these flags are *not* added to the default request for the
      * load group; it is expected the default request will already have these
      * flags (again, courtesy of setting nsIDocShell::defaultLoadFlags before
      * the docShell has created the default request.)
      */
     attribute nsLoadFlags defaultLoadFlags;
+
+    /**
+     * The cached user agent override created by UserAgentOverrides.jsm. Used
+     * for all sub-resource requests in the loadgroup.
+     */
+    attribute ACString userAgentOverrideCache;
 };
--- a/netwerk/base/nsLoadGroup.cpp
+++ b/netwerk/base/nsLoadGroup.cpp
@@ -167,35 +167,35 @@ NS_IMETHODIMP
 nsLoadGroup::GetName(nsACString &result)
 {
     // XXX is this the right "name" for a load group?
 
     if (!mDefaultLoadRequest) {
         result.Truncate();
         return NS_OK;
     }
-    
+
     return mDefaultLoadRequest->GetName(result);
 }
 
 NS_IMETHODIMP
 nsLoadGroup::IsPending(bool *aResult)
 {
     *aResult = (mForegroundCount > 0) ? true : false;
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsLoadGroup::GetStatus(nsresult *status)
 {
     if (NS_SUCCEEDED(mStatus) && mDefaultLoadRequest)
         return mDefaultLoadRequest->GetStatus(status);
-    
+
     *status = mStatus;
-    return NS_OK; 
+    return NS_OK;
 }
 
 static bool
 AppendRequestsToArray(PLDHashTable* aTable, nsTArray<nsIRequest*> *aArray)
 {
     for (auto iter = aTable->Iter(); !iter.Done(); iter.Next()) {
         auto e = static_cast<RequestMapEntry*>(iter.Get());
         nsIRequest *request = e->mKey;
@@ -475,17 +475,17 @@ nsLoadGroup::AddRequest(nsIRequest *requ
     // request is null, then the load group should inherit its load flags from
     // the request, but also we need to enforce defaultLoadFlags.
     if (mDefaultLoadRequest == request || !mDefaultLoadRequest) {
         rv = MergeDefaultLoadFlags(request, flags);
     } else {
         rv = MergeLoadFlags(request, flags);
     }
     if (NS_FAILED(rv)) return rv;
-    
+
     //
     // Add the request to the list of active requests...
     //
 
     auto entry =
         static_cast<RequestMapEntry*>(mRequests.Add(request, fallible));
     if (!entry) {
         return NS_ERROR_OUT_OF_MEMORY;
@@ -805,20 +805,34 @@ nsLoadGroup::GetDefaultLoadFlags(uint32_
 
 NS_IMETHODIMP
 nsLoadGroup::SetDefaultLoadFlags(uint32_t aFlags)
 {
     mDefaultLoadFlags = aFlags;
     return NS_OK;
 }
 
+NS_IMETHODIMP
+nsLoadGroup::GetUserAgentOverrideCache(nsACString & aUserAgentOverrideCache)
+{
+  aUserAgentOverrideCache = mUserAgentOverrideCache;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLoadGroup::SetUserAgentOverrideCache(const nsACString & aUserAgentOverrideCache)
+{
+  mUserAgentOverrideCache = aUserAgentOverrideCache;
+  return NS_OK;
+}
+
 
 ////////////////////////////////////////////////////////////////////////////////
 
-void 
+void
 nsLoadGroup::TelemetryReport()
 {
     if (mDefaultLoadIsTimed) {
         Telemetry::Accumulate(Telemetry::HTTP_REQUEST_PER_PAGE, mTimedRequests);
         if (mTimedRequests) {
             Telemetry::Accumulate(Telemetry::HTTP_REQUEST_PER_PAGE_FROM_CACHE,
                                   mCachedRequests * 100 / mTimedRequests);
         }
--- a/netwerk/base/nsLoadGroup.h
+++ b/netwerk/base/nsLoadGroup.h
@@ -24,17 +24,17 @@ class nsITimedChannel;
 class nsLoadGroup : public nsILoadGroup,
                     public nsILoadGroupChild,
                     public nsISupportsPriority,
                     public nsSupportsWeakReference,
                     public nsPILoadGroupInternal
 {
 public:
     NS_DECL_AGGREGATED
-    
+
     ////////////////////////////////////////////////////////////////////////////
     // nsIRequest methods:
     NS_DECL_NSIREQUEST
 
     ////////////////////////////////////////////////////////////////////////////
     // nsILoadGroup methods:
     NS_DECL_NSILOADGROUP
     NS_DECL_NSPILOADGROUPINTERNAL
@@ -74,24 +74,26 @@ protected:
     nsCOMPtr<nsISchedulingContext>  mSchedulingContext;
     nsCOMPtr<nsISchedulingContextService> mSchedulingContextService;
 
     nsCOMPtr<nsIRequest>            mDefaultLoadRequest;
     PLDHashTable                    mRequests;
 
     nsWeakPtr                       mObserver;
     nsWeakPtr                       mParentLoadGroup;
-    
+
     nsresult                        mStatus;
     int32_t                         mPriority;
     bool                            mIsCanceling;
 
     /* Telemetry */
     mozilla::TimeStamp              mDefaultRequestCreationTime;
     bool                            mDefaultLoadIsTimed;
     uint32_t                        mTimedRequests;
     uint32_t                        mCachedRequests;
 
     /* For nsPILoadGroupInternal */
     uint32_t                        mTimedNonCachedRequestsUntilOnEndPageLoad;
+
+    nsCString                       mUserAgentOverrideCache;
 };
 
 #endif // nsLoadGroup_h__
--- a/netwerk/base/nsSocketTransportService2.cpp
+++ b/netwerk/base/nsSocketTransportService2.cpp
@@ -84,23 +84,23 @@ DebugMutexAutoLock::~DebugMutexAutoLock(
   SOCKET_LOG(("Released lock on thread %p", PR_GetCurrentThread()));
 }
 
 //-----------------------------------------------------------------------------
 // ctor/dtor (called on the main/UI thread by the service manager)
 
 nsSocketTransportService::nsSocketTransportService()
     : mThread(nullptr)
+    , mThreadEvent(nullptr)
     , mAutodialEnabled(false)
     , mLock("nsSocketTransportService::mLock")
     , mInitialized(false)
     , mShuttingDown(false)
     , mOffline(false)
     , mGoingOffline(false)
-    , mRawThread(nullptr)
     , mActiveListSize(SOCKET_LIMIT_MIN)
     , mIdleListSize(SOCKET_LIMIT_MIN)
     , mActiveCount(0)
     , mIdleCount(0)
     , mSentBytesCount(0)
     , mReceivedBytesCount(0)
     , mEventQueueLock("nsSocketTransportService::mEventQueueLock")
     , mPendingSocketQ(mEventQueueLock)
@@ -128,16 +128,19 @@ nsSocketTransportService::nsSocketTransp
     NS_ASSERTION(!gSocketTransportService, "must not instantiate twice");
     gSocketTransportService = this;
 }
 
 nsSocketTransportService::~nsSocketTransportService()
 {
     NS_ASSERTION(NS_IsMainThread(), "wrong thread");
     NS_ASSERTION(!mInitialized, "not shutdown properly");
+    
+    if (mThreadEvent)
+        PR_DestroyPollableEvent(mThreadEvent);
 
     free(mActiveList);
     free(mIdleList);
     free(mPollList);
     gSocketTransportService = nullptr;
 }
 
 //-----------------------------------------------------------------------------
@@ -429,46 +432,43 @@ nsSocketTransportService::PollTimeout()
         SOCKET_LOG(("poll timeout: none\n"));
         return NS_SOCKET_POLL_TIMEOUT;
     }
     SOCKET_LOG(("poll timeout: %lu\n", minR));
     return PR_SecondsToInterval(minR);
 }
 
 int32_t
-nsSocketTransportService::Poll(uint32_t *interval,
+nsSocketTransportService::Poll(bool wait, uint32_t *interval,
                                TimeDuration *pollDuration)
 {
     PRPollDesc *pollList;
     uint32_t pollCount;
     PRIntervalTime pollTimeout;
     *pollDuration = 0;
 
-    // If there are pending events for this thread then
-    // DoPollIteration() should service the network without blocking.
-    bool pendingEvents = false;
-    mRawThread->HasPendingEvents(&pendingEvents);
-
     if (mPollList[0].fd) {
         mPollList[0].out_flags = 0;
         pollList = mPollList;
         pollCount = mActiveCount + 1;
-        pollTimeout = pendingEvents ? PR_INTERVAL_NO_WAIT : PollTimeout();
+        pollTimeout = PollTimeout();
     }
     else {
         // no pollable event, so busy wait...
         pollCount = mActiveCount;
         if (pollCount)
             pollList = &mPollList[1];
         else
             pollList = nullptr;
-        pollTimeout =
-            pendingEvents ? PR_INTERVAL_NO_WAIT : PR_MillisecondsToInterval(25);
+        pollTimeout = PR_MillisecondsToInterval(25);
     }
 
+    if (!wait)
+        pollTimeout = PR_INTERVAL_NO_WAIT;
+
     PRIntervalTime ts = PR_IntervalNow();
 
     TimeStamp pollStart;
     if (mTelemetryEnabledPref) {
         pollStart = TimeStamp::NowLoRes();
     }
 
     SOCKET_LOG(("    timeout = %i milliseconds\n",
@@ -510,30 +510,29 @@ nsSocketTransportService::Init()
     }
 
     if (mInitialized)
         return NS_OK;
 
     if (mShuttingDown)
         return NS_ERROR_UNEXPECTED;
 
-    if (!mPollableEvent) {
-        mPollableEvent.reset(new PollableEvent());
+    if (!mThreadEvent) {
+        mThreadEvent = PR_NewPollableEvent();
         //
         // NOTE: per bug 190000, this failure could be caused by Zone-Alarm
         // or similar software.
         //
         // NOTE: per bug 191739, this failure could also be caused by lack
-        // of a loopback device on Windows and OS/2 platforms (it creates
+        // of a loopback device on Windows and OS/2 platforms (NSPR creates
         // a loopback socket pair on these platforms to implement a pollable
         // event object).  if we can't create a pollable event, then we'll
         // have to "busy wait" to implement the socket event queue :-(
         //
-        if (!mPollableEvent->Valid()) {
-            mPollableEvent = nullptr;
+        if (!mThreadEvent) {
             NS_WARNING("running socket transport thread without a pollable event");
             SOCKET_LOG(("running socket transport thread without a pollable event"));
         }
     }
 
     nsCOMPtr<nsIThread> thread;
     nsresult rv = NS_NewThread(getter_AddRefs(thread), this);
     if (NS_FAILED(rv)) return rv;
@@ -582,19 +581,19 @@ nsSocketTransportService::Shutdown()
         return NS_ERROR_UNEXPECTED;
 
     {
         DebugMutexAutoLock lock(mLock);
 
         // signal the socket thread to shutdown
         mShuttingDown = true;
 
-        if (mPollableEvent) {
-            mPollableEvent->Signal();
-        }
+        if (mThreadEvent)
+            PR_SetPollableEvent(mThreadEvent);
+        // else wait for Poll timeout
     }
 
     // join with thread
     mThread->Shutdown();
     {
         DebugMutexAutoLock lock(mLock);
         // Drop our reference to mThread and make sure that any concurrent
         // readers are excluded
@@ -635,19 +634,18 @@ nsSocketTransportService::SetOffline(boo
     if (!mOffline && offline) {
         // signal the socket thread to go offline, so it will detach sockets
         mGoingOffline = true;
         mOffline = true;
     }
     else if (mOffline && !offline) {
         mOffline = false;
     }
-    if (mPollableEvent) {
-        mPollableEvent->Signal();
-    }
+    if (mThreadEvent)
+        PR_SetPollableEvent(mThreadEvent);
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSocketTransportService::GetKeepaliveIdleTime(int32_t *aKeepaliveIdleTimeS)
 {
     MOZ_ASSERT(aKeepaliveIdleTimeS);
@@ -760,29 +758,19 @@ nsSocketTransportService::SetAutodialEna
 {
     mAutodialEnabled = value;
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSocketTransportService::OnDispatchedEvent(nsIThreadInternal *thread)
 {
-    if (PR_GetCurrentThread() == gSocketThread) {
-        // this check is redundant to one done inside ::Signal(), but
-        // we can do it here and skip obtaining the lock - given that
-        // this is a relatively common occurance its worth the
-        // redundant code
-        SOCKET_LOG(("OnDispatchedEvent Same Thread Skip Signal\n"));
-        return NS_OK;
-    }
-
     DebugMutexAutoLock lock(mLock);
-    if (mPollableEvent) {
-        mPollableEvent->Signal();
-    }
+    if (mThreadEvent)
+        PR_SetPollableEvent(mThreadEvent);
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSocketTransportService::OnProcessNextEvent(nsIThreadInternal *thread,
                                              bool mayWait)
 {
     return NS_OK;
@@ -819,25 +807,25 @@ nsSocketTransportService::Run()
     SOCKET_LOG(("STS thread init\n"));
 
 #if !defined(MOZILLA_XPCOMRT_API)
     psm::InitializeSSLServerCertVerificationThreads();
 #endif // !defined(MOZILLA_XPCOMRT_API)
 
     gSocketThread = PR_GetCurrentThread();
 
-    // add thread event to poll list (mPollableEvent may be nullptr)
-    mPollList[0].fd = mPollableEvent ? mPollableEvent->PollableFD() : nullptr;
-    mPollList[0].in_flags = PR_POLL_READ | PR_POLL_EXCEPT;
+    // add thread event to poll list (mThreadEvent may be nullptr)
+    mPollList[0].fd = mThreadEvent;
+    mPollList[0].in_flags = PR_POLL_READ;
     mPollList[0].out_flags = 0;
 
-    mRawThread = NS_GetCurrentThread();
+    nsIThread *thread = NS_GetCurrentThread();
 
     // hook ourselves up to observe event processing for this thread
-    nsCOMPtr<nsIThreadInternal> threadInt = do_QueryInterface(mRawThread);
+    nsCOMPtr<nsIThreadInternal> threadInt = do_QueryInterface(thread);
     threadInt->SetObserver(this);
 
     // make sure the pseudo random number generator is seeded on this thread
     srand(static_cast<unsigned>(PR_Now()));
 
     // For the calculation of the duration of the last cycle (i.e. the last for-loop
     // iteration before shutdown).
     TimeStamp startOfCycleForLastCycleCalc;
@@ -856,43 +844,50 @@ nsSocketTransportService::Run()
 
     // If there is too many pending events queued, we will run some poll()
     // between them and the following variable is cumulative time spent
     // blocking in poll().
     TimeDuration pollDuration;
 
     for (;;) {
         bool pendingEvents = false;
+        thread->HasPendingEvents(&pendingEvents);
 
         numberOfPendingEvents = 0;
         numberOfPendingEventsLastCycle = 0;
         if (mTelemetryEnabledPref) {
             startOfCycleForLastCycleCalc = TimeStamp::NowLoRes();
             startOfNextIteration = TimeStamp::NowLoRes();
         }
         pollDuration = 0;
 
         do {
             if (mTelemetryEnabledPref) {
                 pollCycleStart = TimeStamp::NowLoRes();
             }
 
-            DoPollIteration(&singlePollDuration);
+            // If there are pending events for this thread then
+            // DoPollIteration() should service the network without blocking.
+            DoPollIteration(!pendingEvents, &singlePollDuration);
 
             if (mTelemetryEnabledPref && !pollCycleStart.IsNull()) {
                 Telemetry::Accumulate(Telemetry::STS_POLL_BLOCK_TIME,
                                       singlePollDuration.ToMilliseconds());
                 Telemetry::AccumulateTimeDelta(
                     Telemetry::STS_POLL_CYCLE,
                     pollCycleStart + singlePollDuration,
                     TimeStamp::NowLoRes());
                 pollDuration += singlePollDuration;
             }
 
-            mRawThread->HasPendingEvents(&pendingEvents);
+            // If nothing was pending before the poll, it might be now
+            if (!pendingEvents) {
+                thread->HasPendingEvents(&pendingEvents);
+            }
+
             if (pendingEvents) {
                 if (!mServingPendingQueue) {
                     nsresult rv = Dispatch(NS_NewRunnableMethod(this,
                         &nsSocketTransportService::MarkTheLastElementOfPendingQueue),
                         nsIEventTarget::DISPATCH_NORMAL);
                     if (NS_FAILED(rv)) {
                         NS_WARNING("Could not dispatch a new event on the "
                                    "socket thread.");
@@ -906,20 +901,20 @@ nsSocketTransportService::Run()
                         // be served in the next iteration. If no even
                         // arrives, startOfNextIteration will be reset at the
                         // beginning of each for-loop.
                         startOfNextIteration = TimeStamp::NowLoRes();
                     }
                 }
                 TimeStamp eventQueueStart = TimeStamp::NowLoRes();
                 do {
-                    NS_ProcessNextEvent(mRawThread);
+                    NS_ProcessNextEvent(thread);
                     numberOfPendingEvents++;
                     pendingEvents = false;
-                    mRawThread->HasPendingEvents(&pendingEvents);
+                    thread->HasPendingEvents(&pendingEvents);
                 } while (pendingEvents && mServingPendingQueue &&
                          ((TimeStamp::NowLoRes() -
                            eventQueueStart).ToMilliseconds() <
                           mMaxTimePerPollIter));
 
                 if (mTelemetryEnabledPref && !mServingPendingQueue &&
                     !startOfIteration.IsNull()) {
                     Telemetry::AccumulateTimeDelta(
@@ -967,17 +962,17 @@ nsSocketTransportService::Run()
 
     SOCKET_LOG(("STS shutting down thread\n"));
 
     // detach all sockets, including locals
     Reset(false);
 
     // Final pass over the event queue. This makes sure that events posted by
     // socket detach handlers get processed.
-    NS_ProcessPendingEvents(mRawThread);
+    NS_ProcessPendingEvents(thread);
 
     gSocketThread = nullptr;
 
 #if !defined(MOZILLA_XPCOMRT_API)
     psm::StopSSLServerCertVerificationThreads();
 #endif // !defined(MOZILLA_XPCOMRT_API)
 
     SOCKET_LOG(("STS thread exit\n"));
@@ -1008,21 +1003,22 @@ nsSocketTransportService::Reset(bool aGu
         DetachSocketWithGuard(aGuardLocals, mActiveList, i);
     }
     for (i = mIdleCount - 1; i >= 0; --i) {
         DetachSocketWithGuard(aGuardLocals, mIdleList, i);
     }
 }
 
 nsresult
-nsSocketTransportService::DoPollIteration(TimeDuration *pollDuration)
+nsSocketTransportService::DoPollIteration(bool wait, TimeDuration *pollDuration)
 {
-    SOCKET_LOG(("STS poll iter\n"));
+    SOCKET_LOG(("STS poll iter [%d]\n", wait));
 
     int32_t i, count;
+
     //
     // poll loop
     //
     // walk active list backwards to see if any sockets should actually be
     // idle, then walk the idle list backwards to see if any idle sockets
     // should become active.  take care to check only idle sockets that
     // were idle to begin with ;-)
     //
@@ -1067,24 +1063,25 @@ nsSocketTransportService::DoPollIteratio
     //  windows systems have troubles with the higher limit, so actively probe a
     // limit the first time we exceed 30.
     if ((mActiveCount > 30) && !mProbedMaxCount)
         ProbeMaxCount();
 #endif
 
     // Measures seconds spent while blocked on PR_Poll
     uint32_t pollInterval;
+
     int32_t n = 0;
 #if !defined(MOZILLA_XPCOMRT_API)
     if (!gIOService->IsNetTearingDown()) {
         // Let's not do polling during shutdown.
-        n = Poll(&pollInterval, pollDuration);
+        n = Poll(wait, &pollInterval, pollDuration);
     }
 #else
-    n = Poll(&pollInterval, pollDuration);
+    n = Poll(wait, &pollInterval, pollDuration);
 #endif // defined(MOZILLA_XPCOMRT_API)
 
     if (n < 0) {
         SOCKET_LOG(("  PR_Poll error [%d] os error [%d]\n", PR_GetError(),
                     PR_GetOSError()));
     }
     else {
         //
@@ -1130,38 +1127,39 @@ nsSocketTransportService::DoPollIteratio
         // check for "dead" sockets and remove them (need to do this in
         // reverse order obviously).
         //
         for (i=mActiveCount-1; i>=0; --i) {
             if (NS_FAILED(mActiveList[i].mHandler->mCondition))
                 DetachSocket(mActiveList, &mActiveList[i]);
         }
 
-        if (n != 0 && (mPollList[0].out_flags & (PR_POLL_READ | PR_POLL_EXCEPT))) {
-            DebugMutexAutoLock lock(mLock);
-
-            // acknowledge pollable event (should not block)
-            if (mPollableEvent &&
-                ((mPollList[0].out_flags & PR_POLL_EXCEPT) ||
-                 !mPollableEvent->Clear())) {
+        if (n != 0 && mPollList[0].out_flags == PR_POLL_READ) {
+            // acknowledge pollable event (wait should not block)
+            if (PR_WaitForPollableEvent(mThreadEvent) != PR_SUCCESS) {
                 // On Windows, the TCP loopback connection in the
                 // pollable event may become broken when a laptop
                 // switches between wired and wireless networks or
                 // wakes up from hibernation.  We try to create a
                 // new pollable event.  If that fails, we fall back
                 // on "busy wait".
-                NS_WARNING("Trying to repair mPollableEvent");
-                mPollableEvent.reset(new PollableEvent());
-                if (!mPollableEvent->Valid()) {
-                    mPollableEvent = nullptr;
+                {
+                    DebugMutexAutoLock lock(mLock);
+                    PR_DestroyPollableEvent(mThreadEvent);
+                    mThreadEvent = PR_NewPollableEvent();
                 }
-                SOCKET_LOG(("running socket transport thread without "
-                            "a pollable event now valid=%d", mPollableEvent->Valid()));
-                mPollList[0].fd = mPollableEvent ? mPollableEvent->PollableFD() : nullptr;
-                mPollList[0].in_flags = PR_POLL_READ | PR_POLL_EXCEPT;
+                if (!mThreadEvent) {
+                    NS_WARNING("running socket transport thread without "
+                               "a pollable event");
+                    SOCKET_LOG(("running socket transport thread without "
+                         "a pollable event"));
+                }
+                mPollList[0].fd = mThreadEvent;
+                // mPollList[0].in_flags was already set to PR_POLL_READ
+                // in Run().
                 mPollList[0].out_flags = 0;
             }
         }
     }
 
     return NS_OK;
 }
 
--- a/netwerk/base/nsSocketTransportService2.h
+++ b/netwerk/base/nsSocketTransportService2.h
@@ -14,18 +14,16 @@
 #include "prinrval.h"
 #include "mozilla/Logging.h"
 #include "prinit.h"
 #include "nsIObserver.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/net/DashboardTypes.h"
 #include "mozilla/Atomics.h"
 #include "mozilla/TimeStamp.h"
-#include "mozilla/UniquePtr.h"
-#include "PollableEvent.h"
 
 class nsASocketHandler;
 struct PRPollDesc;
 
 //-----------------------------------------------------------------------------
 
 //
 // set NSPR_LOG_MODULES=nsSocketTransport:5
@@ -121,17 +119,24 @@ protected:
 
 private:
 
     //-------------------------------------------------------------------------
     // misc (any thread)
     //-------------------------------------------------------------------------
 
     nsCOMPtr<nsIThread> mThread;    // protected by mLock
-    mozilla::UniquePtr<mozilla::net::PollableEvent> mPollableEvent;
+    PRFileDesc *mThreadEvent;
+                            // protected by mLock.  mThreadEvent may change
+                            // if the old pollable event is broken.  only
+                            // the socket thread may change mThreadEvent;
+                            // it needs to lock mLock only when it changes
+                            // mThreadEvent.  other threads don't change
+                            // mThreadEvent; they need to lock mLock
+                            // whenever they access mThreadEvent.
     bool        mAutodialEnabled;
                             // pref to control autodial code
 
     // Returns mThread, protecting the get-and-addref with mLock
     already_AddRefed<nsIThread> GetThreadSafely();