merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Wed, 03 Jun 2015 13:49:47 +0200
changeset 246895 6c612d7adbf565fdcf899250793a30718c735776
parent 246846 c6ea858e720d3f8e34cd8fa0b2b4441dc7640997 (current diff)
parent 246894 9ad61b614c2df283ccc9aff28207199bf515c212 (diff)
child 246896 83367fdf47b1aae05c09223923e28bbd644c6506
child 246917 1dc1a8a61d2d40aa8d53dea22138ae110228ccc9
child 246951 3f10ceab96e727c22dac31d7556b7ba8aab9553d
child 246989 9fffe48757f6663ae36eb747cd7e9b6f56a3a0ae
push id28843
push usercbook@mozilla.com
push dateWed, 03 Jun 2015 11:50:02 +0000
treeherdermozilla-central@6c612d7adbf5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone41.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-inbound to mozilla-central a=merge
intl/uconv/ucvcn/gbkuniq2b.uf
--- a/browser/themes/tab-svgs.mozbuild
+++ b/browser/themes/tab-svgs.mozbuild
@@ -1,16 +1,16 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
-script = TOPSRCDIR + '/browser/themes/preprocess-tab-svgs.py'
-input = [TOPSRCDIR + '/browser/themes/shared/tab-selected.svg']
+script = '/browser/themes/preprocess-tab-svgs.py'
+input = ['/browser/themes/shared/tab-selected.svg']
 
 # Context variables can't be used inside functions, so hack around that.
 generated_files = GENERATED_FILES
 
 def generate_svg(svg_name, script_function):
     global generated_files
     generated_files += [svg_name]
     svg = generated_files[svg_name]
--- a/config/external/nspr/Makefile.in
+++ b/config/external/nspr/Makefile.in
@@ -19,19 +19,31 @@ ifdef MOZ_FOLD_LIBS
 EXTRA_MAKE_FLAGS := SHARED_LIBRARY= IMPORT_LIBRARY= SHARED_LIB_PDB=
 
 # Work around libVersionPoint conflict between all three libraries.
 # See bug #838566.
 EXTRA_MAKE_FLAGS += XP_DEFINE=-DlibVersionPoint='libVersionPoint$$(LIBRARY_NAME)'
 else
 # nspr's make export compiles and links everything, but linking can't happen
 # during export on platforms where nspr is linked against mozcrt/mozglue.
-export:: EXTRA_MAKE_FLAGS := SHARED_LIBRARY= IMPORT_LIBRARY= SHARED_LIB_PDB=
+export:: EXTRA_MAKE_FLAGS += SHARED_LIBRARY= IMPORT_LIBRARY= SHARED_LIB_PDB=
 endif
 
+MOZ_BUILDID := $(shell cat $(DEPTH)/config/buildid)
+
+# The NSPR build system uses build-time generated dates for public API
+# exposed data structures. Use the buildid as forced date, to avoid
+# having to deal with what changing NSPR itself might mean.
+
+# SH_DATE is a date with the format "%Y-%m-%d %T"
+EXTRA_MAKE_FLAGS += SH_DATE="$(shell $(PYTHON) -c 'd = "$(MOZ_BUILDID)"; print d[0:4]+"-"+d[4:6]+"-"+d[6:8]+" "+d[8:10]+":"+d[10:12]+":"+d[12:14]')"
+
+# SH_NOW is a date as a unix timestamp in ┬Áseconds
+EXTRA_MAKE_FLAGS += SH_NOW="$(shell $(PYTHON) -c 'import time, calendar; print calendar.timegm(time.strptime("$(MOZ_BUILDID)", "%Y%m%d%H%M%S"))')000000"
+
 clean distclean export::
 	$(MAKE) -C $(DEPTH)/nsprpub $@ $(EXTRA_MAKE_FLAGS)
 
 target::
 # nspr's libs and install rule re-export headers, and that can race with other
 # compilations, so use a separate directory here. The headers are exported
 # during export anyways.
 	$(MAKE) -C $(DEPTH)/nsprpub libs $(EXTRA_MAKE_FLAGS) dist_includedir=$(ABS_DIST)/nspr-include
--- a/dom/base/DOMException.cpp
+++ b/dom/base/DOMException.cpp
@@ -719,10 +719,22 @@ DOMException::Create(nsresult aRv)
   nsCString message;
   uint16_t code;
   NSResultToNameAndMessage(aRv, name, message, &code);
   nsRefPtr<DOMException> inst =
     new DOMException(aRv, message, name, code);
   return inst.forget();
 }
 
+/* static */already_AddRefed<DOMException>
+DOMException::Create(nsresult aRv, const nsCString& aMessage)
+{
+  nsCString name;
+  nsCString message;
+  uint16_t code;
+  NSResultToNameAndMessage(aRv, name, message, &code);
+  nsRefPtr<DOMException> inst =
+    new DOMException(aRv, aMessage, name, code);
+  return inst.forget();
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/base/DOMException.h
+++ b/dom/base/DOMException.h
@@ -152,16 +152,19 @@ public:
 
   // Intentionally shadow the nsXPCException version.
   void GetMessageMoz(nsString& retval);
   void GetName(nsString& retval);
 
   static already_AddRefed<DOMException>
   Create(nsresult aRv);
 
+  static already_AddRefed<DOMException>
+  Create(nsresult aRv, const nsCString& aMessage);
+
 protected:
 
   virtual ~DOMException() {}
 
   nsCString mName;
   nsCString mMessage;
 
   uint16_t mCode;
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -112,16 +112,17 @@
 #include "mozilla/Hal.h"
 #endif
 #include "mozilla/dom/ContentChild.h"
 
 #include "mozilla/dom/FeatureList.h"
 
 #ifdef MOZ_EME
 #include "mozilla/EMEUtils.h"
+#include "mozilla/DetailedPromise.h"
 #endif
 
 #ifdef MOZ_WIDGET_GONK
 #include <cutils/properties.h>
 #endif
 
 namespace mozilla {
 namespace dom {
@@ -2740,17 +2741,17 @@ Navigator::GetUserAgent(nsPIDOMWindow* a
 
 #ifdef MOZ_EME
 already_AddRefed<Promise>
 Navigator::RequestMediaKeySystemAccess(const nsAString& aKeySystem,
                                        const Optional<Sequence<MediaKeySystemOptions>>& aOptions,
                                        ErrorResult& aRv)
 {
   nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(mWindow);
-  nsRefPtr<Promise> promise = Promise::Create(go, aRv);
+  nsRefPtr<DetailedPromise> promise = DetailedPromise::Create(go, aRv);
   if (aRv.Failed()) {
     return nullptr;
   }
 
   if (!mMediaKeySystemAccessManager) {
     mMediaKeySystemAccessManager = new MediaKeySystemAccessManager(mWindow);
   }
 
--- a/dom/base/nsContentList.cpp
+++ b/dom/base/nsContentList.cpp
@@ -148,17 +148,17 @@ NS_IMPL_RELEASE_INHERITED(nsSimpleConten
 
 JSObject*
 nsSimpleContentList::WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto)
 {
   return NodeListBinding::Wrap(cx, this, aGivenProto);
 }
 
 // Hashtable for storing nsContentLists
-static PLDHashTable2* gContentListHashTable;
+static PLDHashTable* gContentListHashTable;
 
 #define RECENTLY_USED_CONTENT_LIST_CACHE_SIZE 31
 static nsContentList*
   sRecentlyUsedContentLists[RECENTLY_USED_CONTENT_LIST_CACHE_SIZE] = {};
 
 static MOZ_ALWAYS_INLINE uint32_t
 RecentlyUsedCacheIndex(const nsContentListKey& aKey)
 {
@@ -212,17 +212,17 @@ NS_GetContentList(nsINode* aRootNode,
     ContentListHashtableMatchEntry,
     PL_DHashMoveEntryStub,
     PL_DHashClearEntryStub
   };
 
   // Initialize the hashtable if needed.
   if (!gContentListHashTable) {
     gContentListHashTable =
-      new PLDHashTable2(&hash_table_ops, sizeof(ContentListHashEntry));
+      new PLDHashTable(&hash_table_ops, sizeof(ContentListHashEntry));
   }
 
   ContentListHashEntry *entry = nullptr;
   // First we look in our hashtable.  Then we create a content list if needed
   entry = static_cast<ContentListHashEntry *>
     (PL_DHashTableAdd(gContentListHashTable, &hashKey, fallible));
   if (entry)
     list = entry->mContentList;
@@ -265,17 +265,17 @@ nsCacheableFuncStringNodeList::WrapObjec
 
 JSObject*
 nsCacheableFuncStringHTMLCollection::WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto)
 {
   return HTMLCollectionBinding::Wrap(cx, this, aGivenProto);
 }
 
 // Hashtable for storing nsCacheableFuncStringContentList
-static PLDHashTable2* gFuncStringContentListHashTable;
+static PLDHashTable* gFuncStringContentListHashTable;
 
 struct FuncStringContentListHashEntry : public PLDHashEntryHdr
 {
   nsCacheableFuncStringContentList* mContentList;
 };
 
 static PLDHashNumber
 FuncStringContentListHashtableHashKey(PLDHashTable *table, const void *key)
@@ -316,17 +316,17 @@ GetFuncStringContentList(nsINode* aRootN
     FuncStringContentListHashtableMatchEntry,
     PL_DHashMoveEntryStub,
     PL_DHashClearEntryStub
   };
 
   // Initialize the hashtable if needed.
   if (!gFuncStringContentListHashTable) {
     gFuncStringContentListHashTable =
-      new PLDHashTable2(&hash_table_ops, sizeof(FuncStringContentListHashEntry));
+      new PLDHashTable(&hash_table_ops, sizeof(FuncStringContentListHashEntry));
   }
 
   FuncStringContentListHashEntry *entry = nullptr;
   // First we look in our hashtable.  Then we create a content list if needed
   if (gFuncStringContentListHashTable) {
     nsFuncStringCacheKey hashKey(aRootNode, aFunc, aString);
 
     entry = static_cast<FuncStringContentListHashEntry *>
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -331,17 +331,17 @@ static const nsAttrValue::EnumTable kAut
   { 0 }
 };
 
 namespace {
 
 static NS_DEFINE_CID(kParserServiceCID, NS_PARSERSERVICE_CID);
 static NS_DEFINE_CID(kCParserCID, NS_PARSER_CID);
 
-static PLDHashTable2* sEventListenerManagersHash;
+static PLDHashTable* sEventListenerManagersHash;
 
 class DOMEventListenerManagersHashReporter final : public nsIMemoryReporter
 {
   MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
 
   ~DOMEventListenerManagersHashReporter() {}
 
 public:
@@ -493,17 +493,17 @@ nsContentUtils::Init()
       PL_DHashVoidPtrKeyStub,
       PL_DHashMatchEntryStub,
       PL_DHashMoveEntryStub,
       EventListenerManagerHashClearEntry,
       EventListenerManagerHashInitEntry
     };
 
     sEventListenerManagersHash =
-      new PLDHashTable2(&hash_table_ops, sizeof(EventListenerManagerMapEntry));
+      new PLDHashTable(&hash_table_ops, sizeof(EventListenerManagerMapEntry));
 
     RegisterStrongMemoryReporter(new DOMEventListenerManagersHashReporter());
   }
 
   sBlockedScriptRunners = new nsTArray< nsCOMPtr<nsIRunnable> >;
 
   Preferences::AddBoolVarCache(&sAllowXULXBL_for_file,
                                "dom.allow_XUL_XBL_for_file");
@@ -3334,17 +3334,17 @@ nsContentUtils::LogSimpleConsoleError(co
       console->LogMessage(scriptError);
     }
   }
 }
 
 /* static */ nsresult
 nsContentUtils::ReportToConsole(uint32_t aErrorFlags,
                                 const nsACString& aCategory,
-                                nsIDocument* aDocument,
+                                const nsIDocument* aDocument,
                                 PropertiesFile aFile,
                                 const char *aMessageName,
                                 const char16_t **aParams,
                                 uint32_t aParamsLength,
                                 nsIURI* aURI,
                                 const nsAFlatString& aSourceLine,
                                 uint32_t aLineNumber,
                                 uint32_t aColumnNumber)
@@ -3369,17 +3369,17 @@ nsContentUtils::ReportToConsole(uint32_t
                                      aLineNumber, aColumnNumber);
 }
 
 
 /* static */ nsresult
 nsContentUtils::ReportToConsoleNonLocalized(const nsAString& aErrorText,
                                             uint32_t aErrorFlags,
                                             const nsACString& aCategory,
-                                            nsIDocument* aDocument,
+                                            const nsIDocument* aDocument,
                                             nsIURI* aURI,
                                             const nsAFlatString& aSourceLine,
                                             uint32_t aLineNumber,
                                             uint32_t aColumnNumber)
 {
   uint64_t innerWindowID = 0;
   if (aDocument) {
     if (!aURI) {
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -770,17 +770,17 @@ public:
               containing error.
    *   @param [aColumnNumber=0] (Optional) Column number within resource
               containing error.
               If aURI is null, then aDocument->GetDocumentURI() is used.
    */
   static nsresult ReportToConsoleNonLocalized(const nsAString& aErrorText,
                                               uint32_t aErrorFlags,
                                               const nsACString& aCategory,
-                                              nsIDocument* aDocument,
+                                              const nsIDocument* aDocument,
                                               nsIURI* aURI = nullptr,
                                               const nsAFlatString& aSourceLine
                                                 = EmptyString(),
                                               uint32_t aLineNumber = 0,
                                               uint32_t aColumnNumber = 0);
 
   /**
    * Report a localized error message to the error console.
@@ -815,17 +815,17 @@ public:
     eCOMMON_DIALOG_PROPERTIES,
     eMATHML_PROPERTIES,
     eSECURITY_PROPERTIES,
     eNECKO_PROPERTIES,
     PropertiesFile_COUNT
   };
   static nsresult ReportToConsole(uint32_t aErrorFlags,
                                   const nsACString& aCategory,
-                                  nsIDocument* aDocument,
+                                  const nsIDocument* aDocument,
                                   PropertiesFile aFile,
                                   const char *aMessageName,
                                   const char16_t **aParams = nullptr,
                                   uint32_t aParamsLength = 0,
                                   nsIURI* aURI = nullptr,
                                   const nsAFlatString& aSourceLine
                                     = EmptyString(),
                                   uint32_t aLineNumber = 0,
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -2025,17 +2025,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
     cb.NoteXPCOMChild(tmp->mFrameRequestCallbacks[i].mCallback.GetISupports());
   }
 
   // Traverse animation components
   if (tmp->mAnimationController) {
     tmp->mAnimationController->Traverse(&cb);
   }
 
-  if (tmp->mSubDocuments && tmp->mSubDocuments->IsInitialized()) {
+  if (tmp->mSubDocuments) {
     PL_DHashTableEnumerate(tmp->mSubDocuments, SubDocTraverser, &cb);
   }
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCSSLoader)
 
   // We own only the items in mDOMMediaQueryLists that have listeners;
   // this reference is managed by their AddListener and RemoveListener
   // methods.
@@ -3981,17 +3981,17 @@ nsDocument::SetSubDocumentFor(Element* a
       {
         PL_DHashVoidPtrKeyStub,
         PL_DHashMatchEntryStub,
         PL_DHashMoveEntryStub,
         SubDocClearEntry,
         SubDocInitEntry
       };
 
-      mSubDocuments = new PLDHashTable2(&hash_table_ops, sizeof(SubDocMapEntry));
+      mSubDocuments = new PLDHashTable(&hash_table_ops, sizeof(SubDocMapEntry));
     }
 
     // Add a mapping to the hash table
     SubDocMapEntry *entry = static_cast<SubDocMapEntry*>
       (PL_DHashTableAdd(mSubDocuments, aElement, fallible));
 
     if (!entry) {
       return NS_ERROR_OUT_OF_MEMORY;
@@ -7528,19 +7528,18 @@ nsIDocument::SetDir(const nsAString& aDi
 NS_IMETHODIMP
 nsDocument::GetInputEncoding(nsAString& aInputEncoding)
 {
   nsIDocument::GetInputEncoding(aInputEncoding);
   return NS_OK;
 }
 
 void
-nsIDocument::GetInputEncoding(nsAString& aInputEncoding)
-{
-  // Not const function, because WarnOnceAbout is not a const method
+nsIDocument::GetInputEncoding(nsAString& aInputEncoding) const
+{
   WarnOnceAbout(eInputEncoding);
   if (mHaveInputEncoding) {
     return GetCharacterSet(aInputEncoding);
   }
 
   SetDOMStringToNull(aInputEncoding);
 }
 
@@ -10417,53 +10416,55 @@ static const char* kDeprecationWarnings[
 #define DOCUMENT_WARNING(_op) #_op "Warning",
 static const char* kDocumentWarnings[] = {
 #include "nsDocumentWarningList.h"
   nullptr
 };
 #undef DOCUMENT_WARNING
 
 bool
-nsIDocument::HasWarnedAbout(DeprecatedOperations aOperation)
+nsIDocument::HasWarnedAbout(DeprecatedOperations aOperation) const
 {
   static_assert(eDeprecatedOperationCount <= 64,
                 "Too many deprecated operations");
   return mDeprecationWarnedAbout & (1ull << aOperation);
 }
 
 void
 nsIDocument::WarnOnceAbout(DeprecatedOperations aOperation,
-                           bool asError /* = false */)
-{
+                           bool asError /* = false */) const
+{
+  MOZ_ASSERT(NS_IsMainThread());
   if (HasWarnedAbout(aOperation)) {
     return;
   }
   mDeprecationWarnedAbout |= (1ull << aOperation);
   uint32_t flags = asError ? nsIScriptError::errorFlag
                            : nsIScriptError::warningFlag;
   nsContentUtils::ReportToConsole(flags,
                                   NS_LITERAL_CSTRING("DOM Core"), this,
                                   nsContentUtils::eDOM_PROPERTIES,
                                   kDeprecationWarnings[aOperation]);
 }
 
 bool
-nsIDocument::HasWarnedAbout(DocumentWarnings aWarning)
+nsIDocument::HasWarnedAbout(DocumentWarnings aWarning) const
 {
   static_assert(eDocumentWarningCount <= 64,
                 "Too many document warnings");
   return mDocWarningWarnedAbout & (1ull << aWarning);
 }
 
 void
 nsIDocument::WarnOnceAbout(DocumentWarnings aWarning,
                            bool asError /* = false */,
                            const char16_t **aParams /* = nullptr */,
-                           uint32_t aParamsLength /* = 0 */)
-{
+                           uint32_t aParamsLength /* = 0 */) const
+{
+  MOZ_ASSERT(NS_IsMainThread());
   if (HasWarnedAbout(aWarning)) {
     return;
   }
   mDocWarningWarnedAbout |= (1ull << aWarning);
   uint32_t flags = asError ? nsIScriptError::errorFlag
                            : nsIScriptError::warningFlag;
   nsContentUtils::ReportToConsole(flags,
                                   NS_LITERAL_CSTRING("DOM Core"), this,
--- a/dom/base/nsDocument.h
+++ b/dom/base/nsDocument.h
@@ -1515,17 +1515,17 @@ protected:
   virtual ~nsDocument();
 
   void EnsureOnloadBlocker();
 
   void NotifyStyleSheetApplicableStateChanged();
 
   nsTArray<nsIObserver*> mCharSetObservers;
 
-  PLDHashTable2 *mSubDocuments;
+  PLDHashTable *mSubDocuments;
 
   // Array of owning references to all children
   nsAttrAndChildArray mChildren;
 
   // Pointer to our parser if we're currently in the process of being
   // parsed into.
   nsCOMPtr<nsIParser> mParser;
 
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -1044,17 +1044,17 @@ public:
   {
     nsPIDOMWindow *window = GetWindow();
     return window ? window->WindowID() : 0;
   }
 
   /**
    * Return the inner window ID.
    */
-  uint64_t InnerWindowID()
+  uint64_t InnerWindowID() const
   {
     nsPIDOMWindow *window = GetInnerWindow();
     return window ? window->WindowID() : 0;
   }
 
   /**
    * Get the script loader for this document
    */ 
@@ -2161,30 +2161,31 @@ public:
   void FlushPendingLinkUpdates();
 
 #define DEPRECATED_OPERATION(_op) e##_op,
   enum DeprecatedOperations {
 #include "nsDeprecatedOperationList.h"
     eDeprecatedOperationCount
   };
 #undef DEPRECATED_OPERATION
-  bool HasWarnedAbout(DeprecatedOperations aOperation);
-  void WarnOnceAbout(DeprecatedOperations aOperation, bool asError = false);
+  bool HasWarnedAbout(DeprecatedOperations aOperation) const;
+  void WarnOnceAbout(DeprecatedOperations aOperation,
+                     bool asError = false) const;
 
 #define DOCUMENT_WARNING(_op) e##_op,
   enum DocumentWarnings {
 #include "nsDocumentWarningList.h"
     eDocumentWarningCount
   };
 #undef DOCUMENT_WARNING
-  bool HasWarnedAbout(DocumentWarnings aWarning);
+  bool HasWarnedAbout(DocumentWarnings aWarning) const;
   void WarnOnceAbout(DocumentWarnings aWarning,
                      bool asError = false,
                      const char16_t **aParams = nullptr,
-                     uint32_t aParamsLength = 0);
+                     uint32_t aParamsLength = 0) const;
 
   virtual void PostVisibilityUpdateEvent() = 0;
   
   bool IsSyntheticDocument() const { return mIsSyntheticDocument; }
 
   void SetNeedLayoutFlush() {
     mNeedLayoutFlush = true;
     if (mDisplayDocument) {
@@ -2374,17 +2375,17 @@ public:
   already_AddRefed<mozilla::dom::CDATASection>
     CreateCDATASection(const nsAString& aData, mozilla::ErrorResult& rv);
   already_AddRefed<mozilla::dom::Attr>
     CreateAttribute(const nsAString& aName, mozilla::ErrorResult& rv);
   already_AddRefed<mozilla::dom::Attr>
     CreateAttributeNS(const nsAString& aNamespaceURI,
                       const nsAString& aQualifiedName,
                       mozilla::ErrorResult& rv);
-  void GetInputEncoding(nsAString& aInputEncoding);
+  void GetInputEncoding(nsAString& aInputEncoding) const;
   already_AddRefed<nsLocation> GetLocation() const;
   void GetReferrer(nsAString& aReferrer) const;
   void GetLastModified(nsAString& aLastModified) const;
   void GetReadyState(nsAString& aReadyState) const;
   // Not const because otherwise the compiler can't figure out whether to call
   // this GetTitle or the nsAString version from non-const methods, since
   // neither is an exact match.
   virtual void GetTitle(nsString& aTitle) = 0;
@@ -2419,26 +2420,26 @@ public:
   void MozExitPointerLock()
   {
     UnlockPointer(this);
   }
   bool Hidden() const
   {
     return mVisibilityState != mozilla::dom::VisibilityState::Visible;
   }
-  bool MozHidden() // Not const because of WarnOnceAbout
+  bool MozHidden() const
   {
     WarnOnceAbout(ePrefixedVisibilityAPI);
     return Hidden();
   }
-  mozilla::dom::VisibilityState VisibilityState()
+  mozilla::dom::VisibilityState VisibilityState() const
   {
     return mVisibilityState;
   }
-  mozilla::dom::VisibilityState MozVisibilityState()
+  mozilla::dom::VisibilityState MozVisibilityState() const
   {
     WarnOnceAbout(ePrefixedVisibilityAPI);
     return VisibilityState();
   }
   virtual mozilla::dom::StyleSheetList* StyleSheets() = 0;
   void GetSelectedStyleSheetSet(nsAString& aSheetSet);
   virtual void SetSelectedStyleSheetSet(const nsAString& aSheetSet) = 0;
   virtual void GetLastStyleSheetSet(nsString& aSheetSet) = 0;
@@ -2541,18 +2542,18 @@ public:
   }
 
   // FontFaceSource
   mozilla::dom::FontFaceSet* GetFonts(mozilla::ErrorResult& aRv);
 
   bool DidFireDOMContentLoaded() const { return mDidFireDOMContentLoaded; }
 
 private:
-  uint64_t mDeprecationWarnedAbout;
-  uint64_t mDocWarningWarnedAbout;
+  mutable uint64_t mDeprecationWarnedAbout;
+  mutable uint64_t mDocWarningWarnedAbout;
   SelectorCache mSelectorCache;
 
 protected:
   ~nsIDocument();
   nsPropertyTable* GetExtraPropertyTable(uint16_t aCategory);
 
   // Never ever call this. Only call GetWindow!
   virtual nsPIDOMWindow *GetWindowInternal() const = 0;
--- a/dom/base/nsImageLoadingContent.cpp
+++ b/dom/base/nsImageLoadingContent.cpp
@@ -297,24 +297,52 @@ nsImageLoadingContent::OnLoadComplete(im
   }
 
   nsCOMPtr<nsINode> thisNode = do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
   nsSVGEffects::InvalidateDirectRenderingObservers(thisNode->AsElement());
 
   return NS_OK;
 }
 
+static bool
+ImageIsAnimated(imgIRequest* aRequest)
+{
+  if (!aRequest) {
+    return false;
+  }
+
+  nsCOMPtr<imgIContainer> image;
+  if (NS_SUCCEEDED(aRequest->GetImage(getter_AddRefs(image)))) {
+    bool isAnimated = false;
+    nsresult rv = image->GetAnimated(&isAnimated);
+    if (NS_SUCCEEDED(rv) && isAnimated) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
 void
 nsImageLoadingContent::OnUnlockedDraw()
 {
   if (mVisibleCount > 0) {
     // We should already be marked as visible, there is nothing more we can do.
     return;
   }
 
+  // It's OK for non-animated images to wait until the next image visibility
+  // update to become locked. (And that's preferable, since in the case of
+  // scrolling it keeps memory usage minimal.) For animated images, though, we
+  // want to mark them visible right away so we can call
+  // IncrementAnimationConsumers() on them and they'll start animating.
+  if (!ImageIsAnimated(mCurrentRequest) && !ImageIsAnimated(mPendingRequest)) {
+    return;
+  }
+
   nsPresContext* presContext = GetFramePresContext();
   if (!presContext)
     return;
 
   nsIPresShell* presShell = presContext->PresShell();
   if (!presShell)
     return;
 
--- a/dom/base/nsPropertyTable.cpp
+++ b/dom/base/nsPropertyTable.cpp
@@ -52,17 +52,17 @@ public:
   bool Equals(nsIAtom *aPropertyName)
   {
     return mName == aPropertyName;
   }
 
   size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf);
 
   nsCOMPtr<nsIAtom>  mName;           // property name
-  PLDHashTable2      mObjectValueMap; // map of object/value pairs
+  PLDHashTable       mObjectValueMap; // map of object/value pairs
   NSPropertyDtorFunc mDtorFunc;       // property specific value dtor function
   void*              mDtorData;       // pointer to pass to dtor
   bool               mTransfer;       // whether to transfer in
                                       // TransferOrDeleteAllPropertiesFor
   
   PropertyList*      mNext;
 };
 
@@ -211,21 +211,16 @@ nsPropertyTable::SetPropertyInternal(nsP
         aTransfer != propertyList->mTransfer) {
       NS_WARNING("Destructor/data mismatch while setting property");
       return NS_ERROR_INVALID_ARG;
     }
 
   } else {
     propertyList = new PropertyList(aPropertyName, aPropDtorFunc,
                                     aPropDtorData, aTransfer);
-    if (!propertyList || !propertyList->mObjectValueMap.IsInitialized()) {
-      delete propertyList;
-      return NS_ERROR_OUT_OF_MEMORY;
-    }
-
     propertyList->mNext = mPropertyList;
     mPropertyList = propertyList;
   }
 
   // The current property value (if there is one) is replaced and the current
   // value is destroyed
   nsresult result = NS_OK;
   PropertyListMapEntry *entry = static_cast<PropertyListMapEntry*>
--- a/dom/base/nsScriptNameSpaceManager.h
+++ b/dom/base/nsScriptNameSpaceManager.h
@@ -229,13 +229,13 @@ private:
   nsresult OperateCategoryEntryHash(nsICategoryManager* aCategoryManager,
                                     const char* aCategory,
                                     nsISupports* aEntry,
                                     bool aRemove);
 
   nsGlobalNameStruct* LookupNameInternal(const nsAString& aName,
                                          const char16_t **aClassName = nullptr);
 
-  PLDHashTable2 mGlobalNames;
-  PLDHashTable2 mNavigatorNames;
+  PLDHashTable mGlobalNames;
+  PLDHashTable mNavigatorNames;
 };
 
 #endif /* nsScriptNameSpaceManager_h__ */
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -10452,25 +10452,23 @@ class CGDOMJSProxyHandler_defineProperty
                 """,
                 callSetter=CGProxyIndexedSetter(self.descriptor).define())
         elif self.descriptor.supportsIndexedProperties():
             # We allow untrusted content to prevent Xrays from setting a
             # property if that property is an indexed property and we have no
             # indexed setter.  That's how the object would normally behave if
             # you tried to set the property on it.  That means we don't need to
             # do anything special for Xrays here.
-            set += fill(
+            set += dedent(
                 """
                 if (IsArrayIndex(GetArrayIndexFromId(cx, id))) {
-                  return js::IsInNonStrictPropertySet(cx)
-                         ? opresult.succeed()
-                         : ThrowErrorMessage(cx, MSG_NO_INDEXED_SETTER, "${name}");
+                  *defined = true;
+                  return opresult.failNoIndexedSetter();
                 }
-                """,
-                name=self.descriptor.name)
+                """)
 
         namedSetter = self.descriptor.operations['NamedSetter']
         if namedSetter:
             if self.descriptor.hasUnforgeableMembers:
                 raise TypeError("Can't handle a named setter on an interface "
                                 "that has unforgeables.  Figure out how that "
                                 "should work!")
             if self.descriptor.operations['NamedCreator'] is not namedSetter:
@@ -10493,23 +10491,21 @@ class CGDOMJSProxyHandler_defineProperty
             # means we don't need to do anything special for Xrays here.
             if self.descriptor.supportsNamedProperties():
                 set += fill(
                     """
                     bool found = false;
                     $*{presenceChecker}
 
                     if (found) {
-                      return js::IsInNonStrictPropertySet(cx)
-                             ? opresult.succeed()
-                             : ThrowErrorMessage(cx, MSG_NO_NAMED_SETTER, "${name}");
+                      *defined = true;
+                      return opresult.failNoNamedSetter();
                     }
                     """,
-                    presenceChecker=CGProxyNamedPresenceChecker(self.descriptor, foundVar="found").define(),
-                    name=self.descriptor.name)
+                    presenceChecker=CGProxyNamedPresenceChecker(self.descriptor, foundVar="found").define())
             set += ("return mozilla::dom::DOMProxyHandler::defineProperty(%s);\n" %
                     ", ".join(a.name for a in self.args))
         return set
 
 
 class CGDOMJSProxyHandler_delete(ClassMethod):
     def __init__(self, descriptor):
         args = [Argument('JSContext*', 'cx'),
--- a/dom/bindings/Errors.msg
+++ b/dom/bindings/Errors.msg
@@ -29,18 +29,16 @@ MSG_DEF(MSG_METHOD_THIS_UNWRAPPING_DENIE
 MSG_DEF(MSG_GETTER_THIS_DOES_NOT_IMPLEMENT_INTERFACE, 2, JSEXN_TYPEERR, "'{0}' getter called on an object that does not implement interface {1}.")
 MSG_DEF(MSG_GETTER_THIS_UNWRAPPING_DENIED, 1, JSEXN_TYPEERR, "Permission to call '{0}' getter denied.")
 MSG_DEF(MSG_SETTER_THIS_DOES_NOT_IMPLEMENT_INTERFACE, 2, JSEXN_TYPEERR, "'{0}' setter called on an object that does not implement interface {1}.")
 MSG_DEF(MSG_SETTER_THIS_UNWRAPPING_DENIED, 1, JSEXN_TYPEERR, "Permission to call '{0}' setter denied.")
 MSG_DEF(MSG_THIS_DOES_NOT_IMPLEMENT_INTERFACE, 1, JSEXN_TYPEERR, "\"this\" object does not implement interface {0}.")
 MSG_DEF(MSG_NOT_IN_UNION, 2, JSEXN_TYPEERR, "{0} could not be converted to any of: {1}.")
 MSG_DEF(MSG_ILLEGAL_CONSTRUCTOR, 0, JSEXN_TYPEERR, "Illegal constructor.")
 MSG_DEF(MSG_CONSTRUCTOR_WITHOUT_NEW, 1, JSEXN_TYPEERR, "Constructor {0} requires 'new'")
-MSG_DEF(MSG_NO_INDEXED_SETTER, 1, JSEXN_TYPEERR, "{0} doesn't have an indexed property setter.")
-MSG_DEF(MSG_NO_NAMED_SETTER, 1, JSEXN_TYPEERR, "{0} doesn't have a named property setter.")
 MSG_DEF(MSG_ENFORCE_RANGE_NON_FINITE, 1, JSEXN_TYPEERR, "Non-finite value is out of range for {0}.")
 MSG_DEF(MSG_ENFORCE_RANGE_OUT_OF_RANGE, 1, JSEXN_TYPEERR, "Value is out of range for {0}.")
 MSG_DEF(MSG_NOT_SEQUENCE, 1, JSEXN_TYPEERR, "{0} can't be converted to a sequence.")
 MSG_DEF(MSG_NOT_DICTIONARY, 1, JSEXN_TYPEERR, "{0} can't be converted to a dictionary.")
 MSG_DEF(MSG_OVERLOAD_RESOLUTION_FAILED, 3, JSEXN_TYPEERR, "Argument {0} is not valid for any of the {1}-argument overloads of {2}.")
 MSG_DEF(MSG_GLOBAL_NOT_NATIVE, 0, JSEXN_TYPEERR, "Global is not a native object.")
 MSG_DEF(MSG_ENCODING_NOT_SUPPORTED, 1, JSEXN_RANGEERR, "The given encoding '{0}' is not supported.")
 MSG_DEF(MSG_DOM_ENCODING_NOT_UTF, 0, JSEXN_RANGEERR, "The encoding must be utf-8, utf-16, or utf-16be.")
--- a/dom/encoding/moz.build
+++ b/dom/encoding/moz.build
@@ -20,17 +20,17 @@ UNIFIED_SOURCES += [
 
 FAIL_ON_WARNINGS = True
 
 FINAL_LIBRARY = 'xul'
 LOCAL_INCLUDES += [
     '/intl/locale',
 ]
 
-props2arrays = TOPSRCDIR + '/intl/locale/props2arrays.py'
+props2arrays = '/intl/locale/props2arrays.py'
 prefixes = (
     'domainsfallbacks',
     'encodingsgroups',
     'labelsencodings',
     'localesfallbacks',
     'nonparticipatingdomains',
 )
 
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -4501,45 +4501,48 @@ HTMLMediaElement::SetMediaKeys(mozilla::
   }
 
   nsCOMPtr<nsIGlobalObject> global =
     do_QueryInterface(OwnerDoc()->GetInnerWindow());
   if (!global) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return nullptr;
   }
-  nsRefPtr<Promise> promise = Promise::Create(global, aRv);
+  nsRefPtr<DetailedPromise> promise = DetailedPromise::Create(global, aRv);
   if (aRv.Failed()) {
     return nullptr;
   }
   if (mMediaKeys == aMediaKeys) {
     promise->MaybeResolve(JS::UndefinedHandleValue);
     return promise.forget();
   }
   if (aMediaKeys && aMediaKeys->IsBoundToMediaElement()) {
-    promise->MaybeReject(NS_ERROR_DOM_QUOTA_EXCEEDED_ERR);
+    promise->MaybeReject(NS_ERROR_DOM_QUOTA_EXCEEDED_ERR,
+                         NS_LITERAL_CSTRING("MediaKeys object is already bound to another HTMLMediaElement"));
     return promise.forget();
   }
   if (mMediaKeys) {
     // Existing MediaKeys object. Shut it down.
     mMediaKeys->Shutdown();
     mMediaKeys = nullptr;
   }
   if (mDecoder &&
       !mMediaSource &&
       Preferences::GetBool("media.eme.mse-only", true)) {
     ShutdownDecoder();
-    promise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+    promise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR,
+                         NS_LITERAL_CSTRING("EME not supported on non-MSE streams"));
     return promise.forget();
   }
 
   mMediaKeys = aMediaKeys;
   if (mMediaKeys) {
     if (NS_FAILED(mMediaKeys->Bind(this))) {
-      promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
+      promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
+                           NS_LITERAL_CSTRING("Failed to bind MediaKeys object to HTMLMediaElement"));
       mMediaKeys = nullptr;
       return promise.forget();
     }
     if (mDecoder) {
       mDecoder->SetCDMProxy(mMediaKeys->GetCDMProxy());
     }
   }
   promise->MaybeResolve(JS::UndefinedHandleValue);
--- a/dom/media/eme/CDMCallbackProxy.cpp
+++ b/dom/media/eme/CDMCallbackProxy.cpp
@@ -101,29 +101,29 @@ class RejectPromiseTask : public nsRunna
 public:
   RejectPromiseTask(CDMProxy* aProxy,
                     uint32_t aPromiseId,
                     nsresult aException,
                     const nsCString& aMessage)
     : mProxy(aProxy)
     , mPid(aPromiseId)
     , mException(aException)
-    , mMsg(NS_ConvertUTF8toUTF16(aMessage))
+    , mMsg(aMessage)
   {
   }
 
   NS_IMETHOD Run() {
     mProxy->OnRejectPromise(mPid, mException, mMsg);
     return NS_OK;
   }
 
   nsRefPtr<CDMProxy> mProxy;
   dom::PromiseId mPid;
   nsresult mException;
-  nsString mMsg;
+  nsCString mMsg;
 };
 
 
 void
 CDMCallbackProxy::RejectPromise(uint32_t aPromiseId,
                                 nsresult aException,
                                 const nsCString& aMessage)
 {
--- a/dom/media/eme/CDMProxy.cpp
+++ b/dom/media/eme/CDMProxy.cpp
@@ -51,22 +51,24 @@ CDMProxy::Init(PromiseId aPromiseId,
           NS_ConvertUTF16toUTF8(aTopLevelOrigin).get(),
           (aInPrivateBrowsing ? "PrivateBrowsing" : "NonPrivateBrowsing"));
 
   nsCString pluginVersion;
   if (!mGMPThread) {
     nsCOMPtr<mozIGeckoMediaPluginService> mps =
       do_GetService("@mozilla.org/gecko-media-plugin-service;1");
     if (!mps) {
-      RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
+      RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
+                    NS_LITERAL_CSTRING("Couldn't get MediaPluginService in CDMProxy::Init"));
       return;
     }
     mps->GetThread(getter_AddRefs(mGMPThread));
     if (!mGMPThread) {
-      RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
+      RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
+                    NS_LITERAL_CSTRING("Couldn't get GMP thread CDMProxy::Init"));
       return;
     }
   }
 
   nsAutoPtr<InitData> data(new InitData());
   data->mPromiseId = aPromiseId;
   data->mOrigin = aOrigin;
   data->mTopLevelOrigin = aTopLevelOrigin;
@@ -85,21 +87,27 @@ CDMProxy::IsOnGMPThread()
   return NS_GetCurrentThread() == mGMPThread;
 }
 #endif
 
 void
 CDMProxy::gmp_InitDone(GMPDecryptorProxy* aCDM, nsAutoPtr<InitData>&& aData)
 {
   EME_LOG("CDMProxy::gmp_InitDone");
-  if (!aCDM || mShutdownCalled) {
+  if (mShutdownCalled) {
     if (aCDM) {
       aCDM->Close();
     }
-    RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
+    RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
+                  NS_LITERAL_CSTRING("CDMProxy was shut down before init could complete"));
+    return;
+  }
+  if (!aCDM) {
+    RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
+                  NS_LITERAL_CSTRING("GetGMPDecryptor failed to return a CDM"));
     return;
   }
 
   mCDM = aCDM;
   mCallback = new CDMCallbackProxy(this);
   mCDM->Init(mCallback);
   nsCOMPtr<nsIRunnable> task(
     NS_NewRunnableMethodWithArg<uint32_t>(this,
@@ -151,85 +159,91 @@ private:
 void
 CDMProxy::gmp_Init(nsAutoPtr<InitData>&& aData)
 {
   MOZ_ASSERT(IsOnGMPThread());
 
   nsCOMPtr<mozIGeckoMediaPluginService> mps =
     do_GetService("@mozilla.org/gecko-media-plugin-service;1");
   if (!mps) {
-    RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
+    RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
+                  NS_LITERAL_CSTRING("Couldn't get MediaPluginService in CDMProxy::gmp_Init"));
     return;
   }
 
   // Make a copy before we transfer ownership of aData to the
   // gmp_InitGetGMPDecryptorCallback.
   InitData data(*aData);
   UniquePtr<GetNodeIdCallback> callback(
     new gmp_InitGetGMPDecryptorCallback(this, Move(aData)));
   nsresult rv = mps->GetNodeId(data.mOrigin,
                                data.mTopLevelOrigin,
                                data.mInPrivateBrowsing,
                                Move(callback));
   if (NS_FAILED(rv)) {
-    RejectPromise(data.mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
+    RejectPromise(data.mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
+                  NS_LITERAL_CSTRING("Call to GetNodeId() failed early"));
   }
 }
 
 void
 CDMProxy::gmp_InitGetGMPDecryptor(nsresult aResult,
                                   const nsACString& aNodeId,
                                   nsAutoPtr<InitData>&& aData)
 {
   uint32_t promiseID = aData->mPromiseId;
   if (NS_FAILED(aResult)) {
-    RejectPromise(promiseID, NS_ERROR_DOM_INVALID_STATE_ERR);
+    RejectPromise(promiseID, NS_ERROR_DOM_INVALID_STATE_ERR,
+                  NS_LITERAL_CSTRING("GetNodeId() called back, but with a failure result"));
     return;
   }
 
   mNodeId = aNodeId;
   MOZ_ASSERT(!GetNodeId().IsEmpty());
 
   nsCOMPtr<mozIGeckoMediaPluginService> mps =
     do_GetService("@mozilla.org/gecko-media-plugin-service;1");
   if (!mps) {
-    RejectPromise(promiseID, NS_ERROR_DOM_INVALID_STATE_ERR);
+    RejectPromise(promiseID, NS_ERROR_DOM_INVALID_STATE_ERR,
+                  NS_LITERAL_CSTRING("Couldn't get MediaPluginService in CDMProxy::gmp_InitGetGMPDecryptor"));
     return;
   }
 
   EME_LOG("CDMProxy::gmp_Init (%s, %s) %s NodeId=%s",
           NS_ConvertUTF16toUTF8(aData->mOrigin).get(),
           NS_ConvertUTF16toUTF8(aData->mTopLevelOrigin).get(),
           (aData->mInPrivateBrowsing ? "PrivateBrowsing" : "NonPrivateBrowsing"),
           GetNodeId().get());
 
   nsTArray<nsCString> tags;
   tags.AppendElement(NS_ConvertUTF16toUTF8(mKeySystem));
 
   UniquePtr<GetGMPDecryptorCallback> callback(new gmp_InitDoneCallback(this,
                                                                        Move(aData)));
   nsresult rv = mps->GetGMPDecryptor(&tags, GetNodeId(), Move(callback));
   if (NS_FAILED(rv)) {
-    RejectPromise(promiseID, NS_ERROR_DOM_INVALID_STATE_ERR);
+    RejectPromise(promiseID, NS_ERROR_DOM_INVALID_STATE_ERR,
+                  NS_LITERAL_CSTRING("Call to GetGMPDecryptor() failed early"));
   }
 }
 
 void
 CDMProxy::OnCDMCreated(uint32_t aPromiseId)
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (mKeys.IsNull()) {
     return;
   }
   MOZ_ASSERT(!GetNodeId().IsEmpty());
   if (mCDM) {
     mKeys->OnCDMCreated(aPromiseId, GetNodeId(), mCDM->GetPluginId());
   } else {
     // No CDM? Just reject the promise.
-    mKeys->RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
+    mKeys->RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
+                         NS_LITERAL_CSTRING("Null CDM in OnCDMCreated()"));
   }
 }
 
 void
 CDMProxy::CreateSession(uint32_t aCreateSessionToken,
                         dom::SessionType aSessionType,
                         PromiseId aPromiseId,
                         const nsAString& aInitDataType,
@@ -259,17 +273,18 @@ ToGMPSessionType(dom::SessionType aSessi
   };
 };
 
 void
 CDMProxy::gmp_CreateSession(nsAutoPtr<CreateSessionData> aData)
 {
   MOZ_ASSERT(IsOnGMPThread());
   if (!mCDM) {
-    RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
+    RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
+                  NS_LITERAL_CSTRING("Null CDM in gmp_CreateSession"));
     return;
   }
   mCDM->CreateSession(aData->mCreateSessionToken,
                       aData->mPromiseId,
                       aData->mInitDataType,
                       aData->mInitData,
                       ToGMPSessionType(aData->mSessionType));
 }
@@ -290,17 +305,18 @@ CDMProxy::LoadSession(PromiseId aPromise
 }
 
 void
 CDMProxy::gmp_LoadSession(nsAutoPtr<SessionOpData> aData)
 {
   MOZ_ASSERT(IsOnGMPThread());
 
   if (!mCDM) {
-    RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
+    RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
+                  NS_LITERAL_CSTRING("Null CDM in gmp_LoadSession"));
     return;
   }
   mCDM->LoadSession(aData->mPromiseId, aData->mSessionId);
 }
 
 void
 CDMProxy::SetServerCertificate(PromiseId aPromiseId,
                                nsTArray<uint8_t>& aCert)
@@ -316,17 +332,18 @@ CDMProxy::SetServerCertificate(PromiseId
   mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
 }
 
 void
 CDMProxy::gmp_SetServerCertificate(nsAutoPtr<SetServerCertificateData> aData)
 {
   MOZ_ASSERT(IsOnGMPThread());
   if (!mCDM) {
-    RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
+    RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
+                  NS_LITERAL_CSTRING("Null CDM in gmp_SetServerCertificate"));
     return;
   }
   mCDM->SetServerCertificate(aData->mPromiseId, aData->mCert);
 }
 
 void
 CDMProxy::UpdateSession(const nsAString& aSessionId,
                         PromiseId aPromiseId,
@@ -345,17 +362,18 @@ CDMProxy::UpdateSession(const nsAString&
   mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
 }
 
 void
 CDMProxy::gmp_UpdateSession(nsAutoPtr<UpdateSessionData> aData)
 {
   MOZ_ASSERT(IsOnGMPThread());
   if (!mCDM) {
-    RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
+    RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
+                  NS_LITERAL_CSTRING("Null CDM in gmp_UpdateSession"));
     return;
   }
   mCDM->UpdateSession(aData->mPromiseId,
                       aData->mSessionId,
                       aData->mResponse);
 }
 
 void
@@ -373,17 +391,18 @@ CDMProxy::CloseSession(const nsAString& 
   mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
 }
 
 void
 CDMProxy::gmp_CloseSession(nsAutoPtr<SessionOpData> aData)
 {
   MOZ_ASSERT(IsOnGMPThread());
   if (!mCDM) {
-    RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
+    RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
+                  NS_LITERAL_CSTRING("Null CDM in gmp_CloseSession"));
     return;
   }
   mCDM->CloseSession(aData->mPromiseId, aData->mSessionId);
 }
 
 void
 CDMProxy::RemoveSession(const nsAString& aSessionId,
                         PromiseId aPromiseId)
@@ -399,17 +418,18 @@ CDMProxy::RemoveSession(const nsAString&
   mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
 }
 
 void
 CDMProxy::gmp_RemoveSession(nsAutoPtr<SessionOpData> aData)
 {
   MOZ_ASSERT(IsOnGMPThread());
   if (!mCDM) {
-    RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
+    RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
+                  NS_LITERAL_CSTRING("Null CDM in gmp_RemoveSession"));
     return;
   }
   mCDM->RemoveSession(aData->mPromiseId, aData->mSessionId);
 }
 
 void
 CDMProxy::Shutdown()
 {
@@ -438,24 +458,26 @@ CDMProxy::gmp_Shutdown()
 
   if (mCDM) {
     mCDM->Close();
     mCDM = nullptr;
   }
 }
 
 void
-CDMProxy::RejectPromise(PromiseId aId, nsresult aCode)
+CDMProxy::RejectPromise(PromiseId aId, nsresult aCode,
+                        const nsCString& aReason)
 {
   if (NS_IsMainThread()) {
     if (!mKeys.IsNull()) {
-      mKeys->RejectPromise(aId, aCode);
+      mKeys->RejectPromise(aId, aCode, aReason);
     }
   } else {
-    nsCOMPtr<nsIRunnable> task(new RejectPromiseTask(this, aId, aCode));
+    nsCOMPtr<nsIRunnable> task(new RejectPromiseTask(this, aId, aCode,
+                                                     aReason));
     NS_DispatchToMainThread(task);
   }
 }
 
 void
 CDMProxy::ResolvePromise(PromiseId aId)
 {
   if (NS_IsMainThread()) {
@@ -592,21 +614,20 @@ CDMProxy::OnSessionError(const nsAString
     session->DispatchKeyError(aSystemCode);
   }
   LogToConsole(aMsg);
 }
 
 void
 CDMProxy::OnRejectPromise(uint32_t aPromiseId,
                           nsresult aDOMException,
-                          const nsAString& aMsg)
+                          const nsCString& aMsg)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  RejectPromise(aPromiseId, aDOMException);
-  LogToConsole(aMsg);
+  RejectPromise(aPromiseId, aDOMException, aMsg);
 }
 
 const nsString&
 CDMProxy::KeySystem() const
 {
   return mKeySystem;
 }
 
--- a/dom/media/eme/CDMProxy.h
+++ b/dom/media/eme/CDMProxy.h
@@ -133,26 +133,27 @@ public:
   void OnSessionError(const nsAString& aSessionId,
                       nsresult aException,
                       uint32_t aSystemCode,
                       const nsAString& aMsg);
 
   // Main thread only.
   void OnRejectPromise(uint32_t aPromiseId,
                        nsresult aDOMException,
-                       const nsAString& aMsg);
+                       const nsCString& aMsg);
 
   // Threadsafe.
   void Decrypt(MediaRawData* aSample,
                DecryptionClient* aSink,
                MediaTaskQueue* aTaskQueue);
 
   // Reject promise with DOMException corresponding to aExceptionCode.
   // Can be called from any thread.
-  void RejectPromise(PromiseId aId, nsresult aExceptionCode);
+  void RejectPromise(PromiseId aId, nsresult aExceptionCode,
+                     const nsCString& aReason);
 
   // Resolves promise with "undefined".
   // Can be called from any thread.
   void ResolvePromise(PromiseId aId);
 
   // Threadsafe.
   const nsString& KeySystem() const;
 
@@ -264,30 +265,33 @@ private:
   };
   // GMP thread only.
   void gmp_Decrypt(nsRefPtr<DecryptJob> aJob);
 
   class RejectPromiseTask : public nsRunnable {
   public:
     RejectPromiseTask(CDMProxy* aProxy,
                       PromiseId aId,
-                      nsresult aCode)
+                      nsresult aCode,
+                      const nsCString& aReason)
       : mProxy(aProxy)
       , mId(aId)
       , mCode(aCode)
+      , mReason(aReason)
     {
     }
     NS_METHOD Run() {
-      mProxy->RejectPromise(mId, mCode);
+      mProxy->RejectPromise(mId, mCode, mReason);
       return NS_OK;
     }
   private:
     nsRefPtr<CDMProxy> mProxy;
     PromiseId mId;
     nsresult mCode;
+    nsCString mReason;
   };
 
   ~CDMProxy();
 
   // Helper to enforce that a raw pointer is only accessed on the main thread.
   template<class Type>
   class MainThreadOnlyRawPtr {
   public:
new file mode 100644
--- /dev/null
+++ b/dom/media/eme/DetailedPromise.cpp
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "DetailedPromise.h"
+#include "mozilla/dom/DOMException.h"
+
+namespace mozilla {
+namespace dom {
+
+DetailedPromise::DetailedPromise(nsIGlobalObject* aGlobal)
+  : Promise(aGlobal)
+  , mResponded(false)
+{
+}
+
+DetailedPromise::~DetailedPromise()
+{
+  MOZ_ASSERT(mResponded == IsPending());
+}
+
+static void
+LogToConsole(const nsAString& aMsg)
+{
+  nsCOMPtr<nsIConsoleService> console(
+    do_GetService("@mozilla.org/consoleservice;1"));
+  if (!console) {
+    NS_WARNING("Failed to log message to console.");
+    return;
+  }
+  nsAutoString msg(aMsg);
+  console->LogStringMessage(msg.get());
+}
+
+void
+DetailedPromise::MaybeReject(nsresult aArg, const nsCString& aReason)
+{
+  mResponded = true;
+
+  LogToConsole(NS_ConvertUTF8toUTF16(aReason));
+
+  nsRefPtr<DOMException> exception =
+    DOMException::Create(aArg, aReason);
+  Promise::MaybeRejectBrokenly(exception);
+}
+
+void
+DetailedPromise::MaybeReject(ErrorResult&, const nsCString& aReason)
+{
+  NS_NOTREACHED("nsresult expected in MaybeReject()");
+}
+
+/* static */ already_AddRefed<DetailedPromise>
+DetailedPromise::Create(nsIGlobalObject* aGlobal, ErrorResult& aRv)
+{
+  nsRefPtr<DetailedPromise> promise = new DetailedPromise(aGlobal);
+  promise->CreateWrapper(aRv);
+  return aRv.Failed() ? nullptr : promise.forget();
+}
+
+}
+}
new file mode 100644
--- /dev/null
+++ b/dom/media/eme/DetailedPromise.h
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef __DetailedPromise_h__
+#define __DetailedPromise_h__
+
+#include "mozilla/dom/Promise.h"
+
+namespace mozilla {
+namespace dom {
+
+/*
+ * This is pretty horrible; bug 1160445.
+ * Extend Promise to add custom DOMException messages on rejection.
+ * Get rid of this once we've ironed out EME errors in the wild.
+ */
+class DetailedPromise : public Promise
+{
+public:
+  static already_AddRefed<DetailedPromise>
+  Create(nsIGlobalObject* aGlobal, ErrorResult& aRv);
+
+  template <typename T>
+  void MaybeResolve(const T& aArg)
+  {
+    mResponded = true;
+    Promise::MaybeResolve<T>(aArg);
+  }
+
+  void MaybeReject(nsresult aArg) = delete;
+  void MaybeReject(nsresult aArg, const nsCString& aReason);
+
+  void MaybeReject(ErrorResult& aArg) = delete;
+  void MaybeReject(ErrorResult&, const nsCString& aReason);
+
+private:
+  explicit DetailedPromise(nsIGlobalObject* aGlobal);
+  virtual ~DetailedPromise();
+
+  bool mResponded;
+};
+
+}
+}
+
+#endif // __DetailedPromise_h__
--- a/dom/media/eme/MediaKeySession.cpp
+++ b/dom/media/eme/MediaKeySession.cpp
@@ -164,34 +164,36 @@ MediaKeySession::KeyStatuses() const
   return mKeyStatusMap;
 }
 
 already_AddRefed<Promise>
 MediaKeySession::GenerateRequest(const nsAString& aInitDataType,
                                  const ArrayBufferViewOrArrayBuffer& aInitData,
                                  ErrorResult& aRv)
 {
-  nsRefPtr<Promise> promise(MakePromise(aRv));
+  nsRefPtr<DetailedPromise> promise(MakePromise(aRv));
   if (aRv.Failed()) {
     return nullptr;
   }
 
   if (!mUninitialized) {
     EME_LOG("MediaKeySession[%p,'%s'] GenerateRequest() failed, uninitialized",
             this, NS_ConvertUTF16toUTF8(mSessionId).get());
-    promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR);
+    promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR,
+                         NS_LITERAL_CSTRING("Session is already initialized in MediaKeySession.generateRequest()"));
     return promise.forget();
   }
 
   mUninitialized = false;
 
   nsTArray<uint8_t> data;
   if (aInitDataType.IsEmpty() ||
       !CopyArrayBufferViewOrArrayBufferData(aInitData, data)) {
-    promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR);
+    promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR,
+                         NS_LITERAL_CSTRING("Bad arguments to MediaKeySession.generateRequest()"));
     EME_LOG("MediaKeySession[%p,'%s'] GenerateRequest() failed, "
             "invalid initData or initDataType",
       this, NS_ConvertUTF16toUTF8(mSessionId).get());
     return promise.forget();
   }
 
   // Convert initData to base64 for easier logging.
   // Note: UpdateSession() Move()s the data out of the array, so we have
@@ -219,30 +221,32 @@ MediaKeySession::GenerateRequest(const n
           base64InitData.get());
 
   return promise.forget();
 }
 
 already_AddRefed<Promise>
 MediaKeySession::Load(const nsAString& aSessionId, ErrorResult& aRv)
 {
-  nsRefPtr<Promise> promise(MakePromise(aRv));
+  nsRefPtr<DetailedPromise> promise(MakePromise(aRv));
   if (aRv.Failed()) {
     return nullptr;
   }
 
   if (aSessionId.IsEmpty()) {
-    promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR);
+    promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR,
+                         NS_LITERAL_CSTRING("Trying to load a session with empty session ID"));
     // "The sessionId parameter is empty."
     EME_LOG("MediaKeySession[%p,''] Load() failed, no sessionId", this);
     return promise.forget();
   }
 
   if (!mUninitialized) {
-    promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR);
+    promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR,
+                         NS_LITERAL_CSTRING("Session is already initialized in MediaKeySession.load()"));
     EME_LOG("MediaKeySession[%p,'%s'] Load() failed, uninitialized",
       this, NS_ConvertUTF16toUTF8(aSessionId).get());
     return promise.forget();
   }
 
   mUninitialized = false;
 
   // We now know the sessionId being loaded into this session. Remove the
@@ -260,25 +264,31 @@ MediaKeySession::Load(const nsAString& a
     this, NS_ConvertUTF16toUTF8(mSessionId).get(), pid);
 
   return promise.forget();
 }
 
 already_AddRefed<Promise>
 MediaKeySession::Update(const ArrayBufferViewOrArrayBuffer& aResponse, ErrorResult& aRv)
 {
-  nsRefPtr<Promise> promise(MakePromise(aRv));
+  nsRefPtr<DetailedPromise> promise(MakePromise(aRv));
   if (aRv.Failed()) {
     return nullptr;
   }
   nsTArray<uint8_t> data;
-  if (IsClosed() ||
-      !mKeys->GetCDMProxy() ||
-      !CopyArrayBufferViewOrArrayBufferData(aResponse, data)) {
-    promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR);
+  if (IsClosed() || !mKeys->GetCDMProxy()) {
+    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
+                         NS_LITERAL_CSTRING("Session is closed or was not properly initialized"));
+    EME_LOG("MediaKeySession[%p,'%s'] Update() failed, session is closed or was not properly initialised.",
+            this, NS_ConvertUTF16toUTF8(mSessionId).get());
+    return promise.forget();
+  }
+  if (!CopyArrayBufferViewOrArrayBufferData(aResponse, data)) {
+    promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR,
+                         NS_LITERAL_CSTRING("Invalid response buffer"));
     EME_LOG("MediaKeySession[%p,'%s'] Update() failed, invalid response buffer",
             this, NS_ConvertUTF16toUTF8(mSessionId).get());
     return promise.forget();
   }
 
 
   // Convert response to base64 for easier logging.
   // Note: UpdateSession() Move()s the data out of the array, so we have
@@ -305,17 +315,17 @@ MediaKeySession::Update(const ArrayBuffe
            base64Response.get());
 
   return promise.forget();
 }
 
 already_AddRefed<Promise>
 MediaKeySession::Close(ErrorResult& aRv)
 {
-  nsRefPtr<Promise> promise(MakePromise(aRv));
+  nsRefPtr<DetailedPromise> promise(MakePromise(aRv));
   if (aRv.Failed()) {
     return nullptr;
   }
   if (IsClosed() || !mKeys->GetCDMProxy()) {
     EME_LOG("MediaKeySession[%p,'%s'] Close() already closed",
             this, NS_ConvertUTF16toUTF8(mSessionId).get());
     promise->MaybeResolve(JS::UndefinedHandleValue);
     return promise.forget();
@@ -347,29 +357,31 @@ bool
 MediaKeySession::IsClosed() const
 {
   return mIsClosed;
 }
 
 already_AddRefed<Promise>
 MediaKeySession::Remove(ErrorResult& aRv)
 {
-  nsRefPtr<Promise> promise(MakePromise(aRv));
+  nsRefPtr<DetailedPromise> promise(MakePromise(aRv));
   if (aRv.Failed()) {
     return nullptr;
   }
   if (mSessionType != SessionType::Persistent) {
-    promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR);
+    promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR,
+                         NS_LITERAL_CSTRING("Calling MediaKeySession.remove() on non-persistent session"));
     // "The operation is not supported on session type sessions."
     EME_LOG("MediaKeySession[%p,'%s'] Remove() failed, sesion not persisrtent.",
             this, NS_ConvertUTF16toUTF8(mSessionId).get());
     return promise.forget();
   }
   if (IsClosed() || !mKeys->GetCDMProxy()) {
-    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
+    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
+                         NS_LITERAL_CSTRING("MediaKeySesison.remove() called but session is not active"));
     // "The session is closed."
     EME_LOG("MediaKeySession[%p,'%s'] Remove() failed, already session closed.",
             this, NS_ConvertUTF16toUTF8(mSessionId).get());
     return promise.forget();
   }
   PromiseId pid = mKeys->StorePromise(promise);
   mKeys->GetCDMProxy()->RemoveSession(mSessionId, pid);
   EME_LOG("MediaKeySession[%p,'%s'] Remove() sent to CDM, promiseId=%d.",
@@ -429,22 +441,22 @@ MediaKeySession::DispatchKeyStatusesChan
 }
 
 uint32_t
 MediaKeySession::Token() const
 {
   return mToken;
 }
 
-already_AddRefed<Promise>
+already_AddRefed<DetailedPromise>
 MediaKeySession::MakePromise(ErrorResult& aRv)
 {
   nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject());
   if (!global) {
     NS_WARNING("Passed non-global to MediaKeys ctor!");
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return nullptr;
   }
-  return Promise::Create(global, aRv);
+  return DetailedPromise::Create(global, aRv);
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/media/eme/MediaKeySession.h
+++ b/dom/media/eme/MediaKeySession.h
@@ -92,19 +92,19 @@ public:
 
   // Process-unique identifier.
   uint32_t Token() const;
 
 private:
   ~MediaKeySession();
 
   void UpdateKeyStatusMap();
-  already_AddRefed<Promise> MakePromise(ErrorResult& aRv);
+  already_AddRefed<DetailedPromise> MakePromise(ErrorResult& aRv);
 
-  nsRefPtr<Promise> mClosed;
+  nsRefPtr<DetailedPromise> mClosed;
 
   nsRefPtr<MediaKeyError> mMediaKeyError;
   nsRefPtr<MediaKeys> mKeys;
   const nsString mKeySystem;
   nsString mSessionId;
   const SessionType mSessionType;
   const uint32_t mToken;
   bool mIsClosed;
--- a/dom/media/eme/MediaKeySystemAccessManager.cpp
+++ b/dom/media/eme/MediaKeySystemAccessManager.cpp
@@ -4,16 +4,17 @@
 
 #include "MediaKeySystemAccessManager.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/EMEUtils.h"
 #include "nsServiceManagerUtils.h"
 #include "nsComponentManagerUtils.h"
 #include "nsIObserverService.h"
 #include "mozilla/Services.h"
+#include "mozilla/DetailedPromise.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MediaKeySystemAccessManager)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
   NS_INTERFACE_MAP_ENTRY(nsIObserver)
 NS_INTERFACE_MAP_END
@@ -21,17 +22,17 @@ NS_INTERFACE_MAP_END
 NS_IMPL_CYCLE_COLLECTING_ADDREF(MediaKeySystemAccessManager)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(MediaKeySystemAccessManager)
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(MediaKeySystemAccessManager)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(MediaKeySystemAccessManager)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
   for (size_t i = 0; i < tmp->mRequests.Length(); i++) {
-    tmp->mRequests[i].RejectPromise();
+    tmp->mRequests[i].RejectPromise(NS_LITERAL_CSTRING("Promise still outstanding at MediaKeySystemAccessManager GC"));
     tmp->mRequests[i].CancelTimer();
     NS_IMPL_CYCLE_COLLECTION_UNLINK(mRequests[i].mPromise)
   }
   tmp->mRequests.Clear();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(MediaKeySystemAccessManager)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
@@ -47,56 +48,59 @@ MediaKeySystemAccessManager::MediaKeySys
 }
 
 MediaKeySystemAccessManager::~MediaKeySystemAccessManager()
 {
   Shutdown();
 }
 
 void
-MediaKeySystemAccessManager::Request(Promise* aPromise,
+MediaKeySystemAccessManager::Request(DetailedPromise* aPromise,
                                      const nsAString& aKeySystem,
                                      const Optional<Sequence<MediaKeySystemOptions>>& aOptions)
 {
   if (aKeySystem.IsEmpty() || (aOptions.WasPassed() && aOptions.Value().IsEmpty())) {
-    aPromise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR);
+    aPromise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR,
+                          NS_LITERAL_CSTRING("Invalid keysystem type or invalid options sequence"));
     return;
   }
   Sequence<MediaKeySystemOptions> optionsNotPassed;
   const auto& options = aOptions.WasPassed() ? aOptions.Value() : optionsNotPassed;
   Request(aPromise, aKeySystem, options, RequestType::Initial);
 }
 
 void
-MediaKeySystemAccessManager::Request(Promise* aPromise,
+MediaKeySystemAccessManager::Request(DetailedPromise* aPromise,
                                      const nsAString& aKeySystem,
                                      const Sequence<MediaKeySystemOptions>& aOptions,
                                      RequestType aType)
 {
   EME_LOG("MediaKeySystemAccessManager::Request %s", NS_ConvertUTF16toUTF8(aKeySystem).get());
   if (!Preferences::GetBool("media.eme.enabled", false)) {
     // EME disabled by user, send notification to chrome so UI can
     // inform user.
     MediaKeySystemAccess::NotifyObservers(mWindow,
                                           aKeySystem,
                                           MediaKeySystemStatus::Api_disabled);
-    aPromise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+    aPromise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR,
+                          NS_LITERAL_CSTRING("EME has been preffed off"));
     return;
   }
 
   // Parse keysystem, split it out into keySystem prefix, and version suffix.
   nsAutoString keySystem;
   int32_t minCdmVersion = NO_CDM_VERSION;
   if (!ParseKeySystem(aKeySystem,
                       keySystem,
                       minCdmVersion)) {
     // Invalid keySystem string, or unsupported keySystem. Send notification
     // to chrome to show a failure notice.
     MediaKeySystemAccess::NotifyObservers(mWindow, aKeySystem, MediaKeySystemStatus::Cdm_not_supported);
-    aPromise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+    aPromise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR,
+                          NS_LITERAL_CSTRING("Key system string is invalid, or key system is unsupported"));
     return;
   }
 
   MediaKeySystemStatus status = MediaKeySystemAccess::GetKeySystemStatus(keySystem, minCdmVersion);
   if ((status == MediaKeySystemStatus::Cdm_not_installed ||
        status == MediaKeySystemStatus::Cdm_insufficient_version) &&
       keySystem.EqualsLiteral("com.adobe.primetime")) {
     // These are cases which could be resolved by downloading a new(er) CDM.
@@ -112,42 +116,48 @@ MediaKeySystemAccessManager::Request(Pro
       // Note: If we're re-trying, we don't re-send the notificaiton,
       // as chrome is already displaying the "we can't play, updating"
       // notification.
       MediaKeySystemAccess::NotifyObservers(mWindow, keySystem, status);
     } else {
       // We waited or can't wait for an update and we still can't service
       // the request. Give up. Chrome will still be showing a "I can't play,
       // updating" notification.
-      aPromise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+      aPromise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR,
+                            NS_LITERAL_CSTRING("Gave up while waiting for a CDM update"));
     }
     return;
   }
   if (status != MediaKeySystemStatus::Available) {
     if (status != MediaKeySystemStatus::Error) {
       // Failed due to user disabling something, send a notification to
       // chrome, so we can show some UI to explain how the user can rectify
       // the situation.
       MediaKeySystemAccess::NotifyObservers(mWindow, keySystem, status);
+      aPromise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR,
+                            NS_LITERAL_CSTRING("The key system has been disabled by the user"));
+      return;
     }
-    aPromise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+    aPromise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
+                          NS_LITERAL_CSTRING("GetKeySystemAccess failed"));
     return;
   }
 
   if (aOptions.IsEmpty() ||
       MediaKeySystemAccess::IsSupported(keySystem, aOptions)) {
     nsRefPtr<MediaKeySystemAccess> access(new MediaKeySystemAccess(mWindow, keySystem));
     aPromise->MaybeResolve(access);
     return;
   }
 
-  aPromise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+  aPromise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR,
+                        NS_LITERAL_CSTRING("Key system is not supported"));
 }
 
-MediaKeySystemAccessManager::PendingRequest::PendingRequest(Promise* aPromise,
+MediaKeySystemAccessManager::PendingRequest::PendingRequest(DetailedPromise* aPromise,
                                                             const nsAString& aKeySystem,
                                                             const Sequence<MediaKeySystemOptions>& aOptions,
                                                             nsITimer* aTimer)
   : mPromise(aPromise)
   , mKeySystem(aKeySystem)
   , mOptions(aOptions)
   , mTimer(aTimer)
 {
@@ -172,25 +182,25 @@ void
 MediaKeySystemAccessManager::PendingRequest::CancelTimer()
 {
   if (mTimer) {
     mTimer->Cancel();
   }
 }
 
 void
-MediaKeySystemAccessManager::PendingRequest::RejectPromise()
+MediaKeySystemAccessManager::PendingRequest::RejectPromise(const nsCString& aReason)
 {
   if (mPromise) {
-    mPromise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR);
+    mPromise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR, aReason);
   }
 }
 
 bool
-MediaKeySystemAccessManager::AwaitInstall(Promise* aPromise,
+MediaKeySystemAccessManager::AwaitInstall(DetailedPromise* aPromise,
                                           const nsAString& aKeySystem,
                                           const Sequence<MediaKeySystemOptions>& aOptions)
 {
   EME_LOG("MediaKeySystemAccessManager::AwaitInstall %s", NS_ConvertUTF16toUTF8(aKeySystem).get());
 
   if (!EnsureObserversAdded()) {
     NS_WARNING("Failed to add pref observer");
     return false;
@@ -260,17 +270,17 @@ MediaKeySystemAccessManager::EnsureObser
 void
 MediaKeySystemAccessManager::Shutdown()
 {
   EME_LOG("MediaKeySystemAccessManager::Shutdown");
   nsTArray<PendingRequest> requests(Move(mRequests));
   for (PendingRequest& request : requests) {
     // Cancel all requests; we're shutting down.
     request.CancelTimer();
-    request.RejectPromise();
+    request.RejectPromise(NS_LITERAL_CSTRING("Promise still outstanding at MediaKeySystemAccessManager shutdown"));
   }
   if (mAddedObservers) {
     nsCOMPtr<nsIObserverService> obsService = mozilla::services::GetObserverService();
     if (obsService) {
       obsService->RemoveObserver(this, "gmp-path-added");
       mAddedObservers = false;
     }
   }
--- a/dom/media/eme/MediaKeySystemAccessManager.h
+++ b/dom/media/eme/MediaKeySystemAccessManager.h
@@ -9,65 +9,67 @@
 #include "nsIObserver.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsISupportsImpl.h"
 #include "nsITimer.h"
 
 namespace mozilla {
 namespace dom {
 
+class DetailedPromise;
+
 class MediaKeySystemAccessManager final : public nsIObserver
 {
 public:
 
   explicit MediaKeySystemAccessManager(nsPIDOMWindow* aWindow);
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(MediaKeySystemAccessManager, nsIObserver)
   NS_DECL_NSIOBSERVER
 
-  void Request(Promise* aPromise,
+  void Request(DetailedPromise* aPromise,
                const nsAString& aKeySystem,
                const Optional<Sequence<MediaKeySystemOptions>>& aOptions);
 
   void Shutdown();
 
   struct PendingRequest {
-    PendingRequest(Promise* aPromise,
+    PendingRequest(DetailedPromise* aPromise,
       const nsAString& aKeySystem,
       const Sequence<MediaKeySystemOptions>& aOptions,
       nsITimer* aTimer);
     PendingRequest(const PendingRequest& aOther);
     ~PendingRequest();
     void CancelTimer();
-    void RejectPromise();
+    void RejectPromise(const nsCString& aReason);
 
-    nsRefPtr<Promise> mPromise;
+    nsRefPtr<DetailedPromise> mPromise;
     const nsString mKeySystem;
     const Sequence<MediaKeySystemOptions> mOptions;
     nsCOMPtr<nsITimer> mTimer;
   };
 
 private:
 
   enum RequestType {
     Initial,
     Subsequent
   };
 
-  void Request(Promise* aPromise,
+  void Request(DetailedPromise* aPromise,
                const nsAString& aKeySystem,
                const Sequence<MediaKeySystemOptions>& aOptions,
                RequestType aType);
 
   ~MediaKeySystemAccessManager();
 
   bool EnsureObserversAdded();
 
-  bool AwaitInstall(Promise* aPromise,
+  bool AwaitInstall(DetailedPromise* aPromise,
                     const nsAString& aKeySystem,
                     const Sequence<MediaKeySystemOptions>& aOptions);
 
   void RetryRequest(PendingRequest& aRequest);
 
   nsTArray<PendingRequest> mRequests;
 
   nsCOMPtr<nsPIDOMWindow> mWindow;
--- a/dom/media/eme/MediaKeys.cpp
+++ b/dom/media/eme/MediaKeys.cpp
@@ -55,20 +55,21 @@ MediaKeys::MediaKeys(nsPIDOMWindow* aPar
   , mCreatePromiseId(0)
 {
   EME_LOG("MediaKeys[%p] constructed keySystem=%s",
           this, NS_ConvertUTF16toUTF8(mKeySystem).get());
 }
 
 static PLDHashOperator
 RejectPromises(const uint32_t& aKey,
-               nsRefPtr<dom::Promise>& aPromise,
+               nsRefPtr<dom::DetailedPromise>& aPromise,
                void* aClosure)
 {
-  aPromise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
+  aPromise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
+                        NS_LITERAL_CSTRING("Promise still outstanding at MediaKeys shutdown"));
   ((MediaKeys*)aClosure)->Release();
   return PL_DHASH_NEXT;
 }
 
 MediaKeys::~MediaKeys()
 {
   Shutdown();
   EME_LOG("MediaKeys[%p] destroyed", this);
@@ -140,101 +141,105 @@ MediaKeys::WrapObject(JSContext* aCx, JS
 }
 
 void
 MediaKeys::GetKeySystem(nsString& retval) const
 {
   retval = mKeySystem;
 }
 
-already_AddRefed<Promise>
+already_AddRefed<DetailedPromise>
 MediaKeys::SetServerCertificate(const ArrayBufferViewOrArrayBuffer& aCert, ErrorResult& aRv)
 {
-  nsRefPtr<Promise> promise(MakePromise(aRv));
+  nsRefPtr<DetailedPromise>
+    promise(MakePromise(aRv));
   if (aRv.Failed()) {
     return nullptr;
   }
 
   if (!mProxy) {
     NS_WARNING("Tried to use a MediaKeys without a CDM");
-    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
+    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
+                         NS_LITERAL_CSTRING("Null CDM in MediaKeys.setServerCertificate()"));
     return promise.forget();
   }
 
   nsTArray<uint8_t> data;
   if (!CopyArrayBufferViewOrArrayBufferData(aCert, data)) {
-    promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR);
+    promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR,
+                         NS_LITERAL_CSTRING("Invalid argument to MediaKeys.setServerCertificate()"));
     return promise.forget();
   }
 
   mProxy->SetServerCertificate(StorePromise(promise), data);
   return promise.forget();
 }
 
-already_AddRefed<Promise>
+already_AddRefed<DetailedPromise>
 MediaKeys::MakePromise(ErrorResult& aRv)
 {
   nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject());
   if (!global) {
     NS_WARNING("Passed non-global to MediaKeys ctor!");
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return nullptr;
   }
-  return Promise::Create(global, aRv);
+  return DetailedPromise::Create(global, aRv);
 }
 
 PromiseId
-MediaKeys::StorePromise(Promise* aPromise)
+MediaKeys::StorePromise(DetailedPromise* aPromise)
 {
   static uint32_t sEMEPromiseCount = 1;
   MOZ_ASSERT(aPromise);
   uint32_t id = sEMEPromiseCount++;
 
   EME_LOG("MediaKeys[%p]::StorePromise() id=%d", this, id);
 
   // Keep MediaKeys alive for the lifetime of its promises. Any still-pending
   // promises are rejected in Shutdown().
   AddRef();
 
   mPromises.Put(id, aPromise);
   return id;
 }
 
-already_AddRefed<Promise>
+already_AddRefed<DetailedPromise>
 MediaKeys::RetrievePromise(PromiseId aId)
 {
   if (!mPromises.Contains(aId)) {
     NS_WARNING(nsPrintfCString("Tried to retrieve a non-existent promise id=%d", aId).get());
     return nullptr;
   }
-  nsRefPtr<Promise> promise;
+  nsRefPtr<DetailedPromise> promise;
   mPromises.Remove(aId, getter_AddRefs(promise));
   Release();
   return promise.forget();
 }
 
 void
-MediaKeys::RejectPromise(PromiseId aId, nsresult aExceptionCode)
+MediaKeys::RejectPromise(PromiseId aId, nsresult aExceptionCode,
+                         const nsCString& aReason)
 {
   EME_LOG("MediaKeys[%p]::RejectPromise(%d, 0x%x)", this, aId, aExceptionCode);
 
-  nsRefPtr<Promise> promise(RetrievePromise(aId));
+  nsRefPtr<DetailedPromise> promise(RetrievePromise(aId));
   if (!promise) {
     return;
   }
   if (mPendingSessions.Contains(aId)) {
     // This promise could be a createSession or loadSession promise,
     // so we might have a pending session waiting to be resolved into
     // the promise on success. We've been directed to reject to promise,
     // so we can throw away the corresponding session object.
     mPendingSessions.Remove(aId);
   }
 
   MOZ_ASSERT(NS_FAILED(aExceptionCode));
-  promise->MaybeReject(aExceptionCode);
+  promise->MaybeReject(aExceptionCode, aReason);
 
   if (mCreatePromiseId == aId) {
     // Note: This will probably destroy the MediaKeys object!
     Release();
   }
 }
 
 void
@@ -259,98 +264,102 @@ MediaKeys::OnSessionIdReady(MediaKeySess
   mKeySessions.Put(aSession->GetSessionId(), aSession);
 }
 
 void
 MediaKeys::ResolvePromise(PromiseId aId)
 {
   EME_LOG("MediaKeys[%p]::ResolvePromise(%d)", this, aId);
 
-  nsRefPtr<Promise> promise(RetrievePromise(aId));
+  nsRefPtr<DetailedPromise> promise(RetrievePromise(aId));
   if (!promise) {
     return;
   }
   if (mPendingSessions.Contains(aId)) {
     // We should only resolve LoadSession calls via this path,
     // not CreateSession() promises.
     nsRefPtr<MediaKeySession> session;
     if (!mPendingSessions.Get(aId, getter_AddRefs(session)) ||
         !session ||
         session->GetSessionId().IsEmpty()) {
       NS_WARNING("Received activation for non-existent session!");
-      promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR);
+      promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR,
+                           NS_LITERAL_CSTRING("CDM LoadSession() returned a different session ID than requested"));
       mPendingSessions.Remove(aId);
       return;
     }
     mPendingSessions.Remove(aId);
     mKeySessions.Put(session->GetSessionId(), session);
     promise->MaybeResolve(session);
   } else {
     promise->MaybeResolve(JS::UndefinedHandleValue);
   }
 }
 
-already_AddRefed<Promise>
+already_AddRefed<DetailedPromise>
 MediaKeys::Init(ErrorResult& aRv)
 {
-  nsRefPtr<Promise> promise(MakePromise(aRv));
+  nsRefPtr<DetailedPromise>
+    promise(MakePromise(aRv));
   if (aRv.Failed()) {
     return nullptr;
   }
 
   mProxy = new CDMProxy(this, mKeySystem);
 
   // Determine principal (at creation time) of the MediaKeys object.
   nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(GetParentObject());
   if (!sop) {
-    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
+    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
+                         NS_LITERAL_CSTRING("Couldn't get script principal in MediaKeys::Init"));
     return promise.forget();
   }
   mPrincipal = sop->GetPrincipal();
 
   // Determine principal of the "top-level" window; the principal of the
   // page that will display in the URL bar.
   nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(GetParentObject());
   if (!window) {
-    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
+    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
+                         NS_LITERAL_CSTRING("Couldn't get top-level window in MediaKeys::Init"));
     return promise.forget();
   }
   nsCOMPtr<nsIDOMWindow> topWindow;
   window->GetTop(getter_AddRefs(topWindow));
   nsCOMPtr<nsPIDOMWindow> top = do_QueryInterface(topWindow);
   if (!top || !top->GetExtantDoc()) {
-    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
+    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
+                         NS_LITERAL_CSTRING("Couldn't get document in MediaKeys::Init"));
     return promise.forget();
   }
 
   mTopLevelPrincipal = top->GetExtantDoc()->NodePrincipal();
 
   if (!mPrincipal || !mTopLevelPrincipal) {
     NS_WARNING("Failed to get principals when creating MediaKeys");
-    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
+    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
+                         NS_LITERAL_CSTRING("Couldn't get principal(s) in MediaKeys::Init"));
     return promise.forget();
   }
 
   nsAutoString origin;
   nsresult rv = nsContentUtils::GetUTFOrigin(mPrincipal, origin);
   if (NS_FAILED(rv)) {
-    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
+    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
+                         NS_LITERAL_CSTRING("Couldn't get principal origin string in MediaKeys::Init"));
     return promise.forget();
   }
   nsAutoString topLevelOrigin;
   rv = nsContentUtils::GetUTFOrigin(mTopLevelPrincipal, topLevelOrigin);
   if (NS_FAILED(rv)) {
-    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
+    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
+                         NS_LITERAL_CSTRING("Couldn't get top-level principal origin string in MediaKeys::Init"));
     return promise.forget();
   }
 
-  if (!window) {
-    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
-    return promise.forget();
-  }
   nsIDocument* doc = window->GetExtantDoc();
   const bool inPrivateBrowsing = nsContentUtils::IsInPrivateBrowsing(doc);
 
   EME_LOG("MediaKeys[%p]::Create() (%s, %s), %s",
           this,
           NS_ConvertUTF16toUTF8(origin).get(),
           NS_ConvertUTF16toUTF8(topLevelOrigin).get(),
           (inPrivateBrowsing ? "PrivateBrowsing" : "NonPrivateBrowsing"));
@@ -449,17 +458,17 @@ private:
   uint32_t mPluginId;
   nsWeakPtr mParentWindowWeakPtr;
   nsWeakPtr mDocumentWeakPtr;
 };
 
 void
 MediaKeys::OnCDMCreated(PromiseId aId, const nsACString& aNodeId, const uint32_t aPluginId)
 {
-  nsRefPtr<Promise> promise(RetrievePromise(aId));
+  nsRefPtr<DetailedPromise> promise(RetrievePromise(aId));
   if (!promise) {
     return;
   }
   mNodeId = aNodeId;
   nsRefPtr<MediaKeys> keys(this);
   EME_LOG("MediaKeys[%p]::OnCDMCreated() resolve promise id=%d", this, aId);
   promise->MaybeResolve(keys);
   if (mCreatePromiseId == aId) {
@@ -518,17 +527,17 @@ MediaKeys::CreateSession(JSContext* aCx,
   mPendingSessions.Put(session->Token(), session);
 
   return session.forget();
 }
 
 void
 MediaKeys::OnSessionLoaded(PromiseId aId, bool aSuccess)
 {
-  nsRefPtr<Promise> promise(RetrievePromise(aId));
+  nsRefPtr<DetailedPromise> promise(RetrievePromise(aId));
   if (!promise) {
     return;
   }
   EME_LOG("MediaKeys[%p]::OnSessionLoaded() resolve promise id=%d", this, aId);
 
   promise->MaybeResolve(aSuccess);
 }
 
--- a/dom/media/eme/MediaKeys.h
+++ b/dom/media/eme/MediaKeys.h
@@ -13,29 +13,30 @@
 #include "mozilla/Attributes.h"
 #include "mozilla/RefPtr.h"
 #include "nsCOMPtr.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsRefPtrHashtable.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/MediaKeysBinding.h"
 #include "mozIGeckoMediaPluginService.h"
+#include "mozilla/DetailedPromise.h"
 
 namespace mozilla {
 
 class CDMProxy;
 
 namespace dom {
 
 class ArrayBufferViewOrArrayBuffer;
 class MediaKeySession;
 class HTMLMediaElement;
 
 typedef nsRefPtrHashtable<nsStringHashKey, MediaKeySession> KeySessionHashMap;
-typedef nsRefPtrHashtable<nsUint32HashKey, dom::Promise> PromiseHashMap;
+typedef nsRefPtrHashtable<nsUint32HashKey, dom::DetailedPromise> PromiseHashMap;
 typedef nsRefPtrHashtable<nsUint32HashKey, MediaKeySession> PendingKeySessionsHashMap;
 typedef uint32_t PromiseId;
 
 // Helper function to extract data coming in from JS in an
 // (ArrayBuffer or ArrayBufferView) IDL typed function argument.
 bool
 CopyArrayBufferViewOrArrayBufferData(const ArrayBufferViewOrArrayBuffer& aBufferOrView,
                                      nsTArray<uint8_t>& aOutData);
@@ -48,35 +49,36 @@ class MediaKeys final : public nsISuppor
   ~MediaKeys();
 
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(MediaKeys)
 
   MediaKeys(nsPIDOMWindow* aParentWindow, const nsAString& aKeySystem);
 
-  already_AddRefed<Promise> Init(ErrorResult& aRv);
+  already_AddRefed<DetailedPromise> Init(ErrorResult& aRv);
 
   nsPIDOMWindow* GetParentObject() const;
 
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   nsresult Bind(HTMLMediaElement* aElement);
 
   // Javascript: readonly attribute DOMString keySystem;
   void GetKeySystem(nsString& retval) const;
 
   // JavaScript: MediaKeys.createSession()
   already_AddRefed<MediaKeySession> CreateSession(JSContext* aCx,
                                                   SessionType aSessionType,
                                                   ErrorResult& aRv);
 
   // JavaScript: MediaKeys.SetServerCertificate()
-  already_AddRefed<Promise> SetServerCertificate(const ArrayBufferViewOrArrayBuffer& aServerCertificate,
-                                                 ErrorResult& aRv);
+  already_AddRefed<DetailedPromise>
+    SetServerCertificate(const ArrayBufferViewOrArrayBuffer& aServerCertificate,
+                         ErrorResult& aRv);
 
   already_AddRefed<MediaKeySession> GetSession(const nsAString& aSessionId);
 
   // Removes and returns MediaKeySession from the set of sessions awaiting
   // their sessionId to be assigned.
   already_AddRefed<MediaKeySession> GetPendingSession(uint32_t aToken);
 
   // Called once a Init() operation succeeds.
@@ -92,24 +94,25 @@ public:
   void OnSessionLoaded(PromiseId aId, bool aSuccess);
 
   // Called once a session has closed.
   void OnSessionClosed(MediaKeySession* aSession);
 
   CDMProxy* GetCDMProxy() { return mProxy; }
 
   // Makes a new promise, or nullptr on failure.
-  already_AddRefed<Promise> MakePromise(ErrorResult& aRv);
+  already_AddRefed<DetailedPromise> MakePromise(ErrorResult& aRv);
   // Stores promise in mPromises, returning an ID that can be used to retrieve
   // it later. The ID is passed to the CDM, so that it can signal specific
   // promises to be resolved.
-  PromiseId StorePromise(Promise* aPromise);
+  PromiseId StorePromise(DetailedPromise* aPromise);
 
   // Reject promise with DOMException corresponding to aExceptionCode.
-  void RejectPromise(PromiseId aId, nsresult aExceptionCode);
+  void RejectPromise(PromiseId aId, nsresult aExceptionCode,
+                     const nsCString& aReason);
   // Resolves promise with "undefined".
   void ResolvePromise(PromiseId aId);
 
   const nsCString& GetNodeId() const;
 
   void Shutdown();
 
   // Called by CDMProxy when CDM crashes or shuts down. It is different from
@@ -119,17 +122,17 @@ public:
   // Returns true if this MediaKeys has been bound to a media element.
   bool IsBoundToMediaElement() const;
 
 private:
 
   bool IsInPrivateBrowsing();
 
   // Removes promise from mPromises, and returns it.
-  already_AddRefed<Promise> RetrievePromise(PromiseId aId);
+  already_AddRefed<DetailedPromise> RetrievePromise(PromiseId aId);
 
   // Owning ref to proxy. The proxy has a weak reference back to the MediaKeys,
   // and the MediaKeys destructor clears the proxy's reference to the MediaKeys.
   nsRefPtr<CDMProxy> mProxy;
 
   nsRefPtr<HTMLMediaElement> mElement;
 
   nsCOMPtr<nsPIDOMWindow> mParent;
--- a/dom/media/eme/moz.build
+++ b/dom/media/eme/moz.build
@@ -14,23 +14,25 @@ EXPORTS.mozilla.dom += [
     'MediaKeySystemAccess.h',
     'MediaKeySystemAccessManager.h',
 ]
 
 EXPORTS.mozilla += [
     'CDMCallbackProxy.h',
     'CDMCaps.h',
     'CDMProxy.h',
-    'EMEUtils.h'
+    'DetailedPromise.h',
+    'EMEUtils.h',
 ]
 
 UNIFIED_SOURCES += [
     'CDMCallbackProxy.cpp',
     'CDMCaps.cpp',
     'CDMProxy.cpp',
+    'DetailedPromise.cpp',
     'EMEUtils.cpp',
     'MediaEncryptedEvent.cpp',
     'MediaKeyError.cpp',
     'MediaKeyMessageEvent.cpp',
     'MediaKeys.cpp',
     'MediaKeySession.cpp',
     'MediaKeyStatusMap.cpp',
     'MediaKeySystemAccess.cpp',
--- a/dom/media/test/mochitest.ini
+++ b/dom/media/test/mochitest.ini
@@ -347,24 +347,26 @@ skip-if = (toolkit == 'android' && proce
 [test_bug495300.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
 [test_bug654550.html]
 [test_bug686942.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
 [test_bug726904.html]
 [test_bug874897.html]
 [test_bug879717.html]
+tags=capturestream
 skip-if = os == 'win' && !debug # bug 1140675
 [test_bug883173.html]
 [test_bug895091.html]
 [test_bug895305.html]
 [test_bug919265.html]
 [test_bug957847.html]
 [test_bug1018933.html]
 [test_bug1113600.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
@@ -397,17 +399,17 @@ skip-if = toolkit == 'android' # bug 104
 # in future however, so I'm not removing the test, just disabling it.
 [test_eme_persistent_sessions.html]
 skip-if = toolkit == 'android' # bug 1043403
 [test_eme_playback.html]
 skip-if = toolkit == 'android' # bug 1043403
 [test_eme_requestKeySystemAccess.html]
 skip-if = toolkit == 'android' # bug 1043403
 [test_eme_stream_capture_blocked.html]
-tags=msg
+tags=msg capturestream
 skip-if = toolkit == 'android' || (os == 'win' && !debug) # bug 1043403, bug 1140675
 [test_empty_resource.html]
 [test_error_in_video_document.html]
 skip-if = toolkit == 'android' || (os == 'win' && !debug) || (os == 'mac' && !debug) # bug 608634
 [test_error_on_404.html]
 [test_fastSeek.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
 [test_fastSeek-forwards.html]
@@ -427,55 +429,55 @@ skip-if = (toolkit == 'android' && proce
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
 [test_media_selection.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
 [test_media_sniffer.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
 [test_mediarecorder_avoid_recursion.html]
 tags=msg
 [test_mediarecorder_creation.html]
-tags=msg
+tags=msg capturestream
 [test_mediarecorder_creation_fail.html]
 tags=msg
 [test_mediarecorder_getencodeddata.html]
 tags=msg
 [test_mediarecorder_record_4ch_audiocontext.html]
 tags=msg
 [test_mediarecorder_record_audiocontext.html]
 tags=msg
 [test_mediarecorder_record_audiocontext_mlk.html]
 tags=msg
 [test_mediarecorder_record_audionode.html]
 tags=msg
 [test_mediarecorder_record_gum_video_timeslice.html]
 tags=msg
 skip-if = buildapp == 'b2g' || toolkit == 'android' # mimetype check, bug 969289
 [test_mediarecorder_record_immediate_stop.html]
-tags=msg
+tags=msg capturestream
 [test_mediarecorder_record_no_timeslice.html]
-tags=msg
+tags=msg capturestream
 skip-if = toolkit == 'gonk' && debug
 [test_mediarecorder_record_nosrc.html]
-tags=msg
+tags=msg capturestream
 [test_mediarecorder_record_session.html]
-tags=msg
+tags=msg capturestream
 [test_mediarecorder_record_startstopstart.html]
 tags=msg
 skip-if = toolkit == 'gonk' && debug
 [test_mediarecorder_record_stopms.html]
 tags=msg
 [test_mediarecorder_record_timeslice.html]
-tags=msg
+tags=msg capturestream
 skip-if = toolkit == 'gonk' && debug
 [test_mediarecorder_reload_crash.html]
-tags=msg
+tags=msg capturestream
 [test_mediarecorder_unsupported_src.html]
 tags=msg
 [test_mediarecorder_record_getdata_afterstart.html]
-tags=msg
+tags=msg capturestream
 skip-if = toolkit == 'gonk' && debug
 [test_mediatrack_consuming_mediaresource.html]
 [test_mediatrack_consuming_mediastream.html]
 tags=msg
 [test_mediatrack_events.html]
 skip-if = toolkit == 'gonk' && debug # bug 1065924
 [test_mediatrack_parsing_ogg.html]
 [test_mediatrack_replay_from_end.html]
@@ -518,16 +520,17 @@ skip-if = toolkit == 'gonk' || (toolkit 
 [test_readyState.html]
 [test_referer.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only
 [test_replay_metadata.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
 [test_reset_events_async.html]
 [test_reset_src.html]
 [test_video_dimensions.html]
+tags=capturestream
 [test_resume.html]
 skip-if = true # bug 1021673
 [test_seek_out_of_range.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
 [test_seek-1.html]
 skip-if = android_version == '10' # bug 1059116
 [test_seek-2.html]
 [test_seek-3.html]
@@ -548,37 +551,37 @@ skip-if = (toolkit == 'android' && proce
 [test_seekLies.html]
 [test_source.html]
 [test_source_media.html]
 [test_source_null.html]
 [test_source_write.html]
 [test_standalone.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
 [test_streams_autoplay.html]
-tags=msg
+tags=msg capturestream
 [test_streams_element_capture.html]
 #x86 only bug 914439, b2g desktop bug 752796
 skip-if = (toolkit == 'android' && processor == 'x86') || (buildapp == 'b2g' && toolkit != 'gonk')
-tags=msg
+tags=msg capturestream
 [test_streams_element_capture_createObjectURL.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
-tags=msg
+tags=msg capturestream
 [test_streams_element_capture_playback.html]
-tags=msg
+tags=msg capturestream
 [test_streams_element_capture_reset.html]
-tags=msg
+tags=msg capturestream
 [test_streams_gc.html]
 skip-if = buildapp == 'b2g' && toolkit != 'gonk' # bug 1096270
-tags=msg
+tags=msg capturestream
 [test_streams_individual_pause.html]
 tags=msg
 [test_streams_srcObject.html]
-tags=msg
+tags=msg capturestream
 [test_streams_tracks.html]
-tags=msg
+tags=msg capturestream
 [test_texttrack.html]
 [test_texttrackcue.html]
 [test_texttracklist.html]
 [test_texttrackregion.html]
 [test_timeupdate_small_files.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
 [test_trackelementevent.html]
 [test_trackevent.html]
--- a/dom/media/tests/mochitest/mochitest.ini
+++ b/dom/media/tests/mochitest/mochitest.ini
@@ -100,16 +100,17 @@ skip-if = toolkit == 'gonk' || buildapp 
 skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_peerConnection_bug834153.html]
 skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867)
 [test_peerConnection_bug1013809.html]
 skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g emulator seems to be too slow (Bug 1016498 and 1008080)
 [test_peerConnection_bug1042791.html]
 skip-if = buildapp == 'b2g' || buildapp == 'mulet' || os == 'android' # bug 1043403 # Bug 1141029 Mulet parity with B2G Desktop for TC
 [test_peerConnection_capturedVideo.html]
+tags=capturestream
 skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_peerConnection_captureStream_canvas_2d.html]
 skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_peerConnection_captureStream_canvas_webgl.html]
 skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_peerConnection_close.html]
 skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867)
 [test_peerConnection_errorCallbacks.html]
--- a/dom/media/webaudio/test/mochitest.ini
+++ b/dom/media/webaudio/test/mochitest.ini
@@ -44,16 +44,17 @@ skip-if = (toolkit == 'android' && (proc
 [test_audioBufferSourceNodeNullBuffer.html]
 [test_audioBufferSourceNodeOffset.html]
 skip-if = (toolkit == 'gonk') || (toolkit == 'android') || debug #bug 906752
 [test_audioBufferSourceNodePassThrough.html]
 [test_audioBufferSourceNodeRate.html]
 [test_AudioContext.html]
 skip-if = android_version == '10' # bug 1138462
 [test_audioContextSuspendResumeClose.html]
+tags=capturestream
 [test_audioDestinationNode.html]
 [test_AudioListener.html]
 [test_audioParamExponentialRamp.html]
 [test_audioParamGain.html]
 [test_audioParamLinearRamp.html]
 [test_audioParamSetCurveAtTime.html]
 [test_audioParamSetCurveAtTimeZeroDuration.html]
 [test_audioParamSetTargetAtTime.html]
@@ -75,16 +76,17 @@ skip-if = android_version == '10' # bug 
 [test_bug867174.html]
 [test_bug867203.html]
 [test_bug875221.html]
 [test_bug875402.html]
 [test_bug894150.html]
 [test_bug956489.html]
 [test_bug964376.html]
 [test_bug966247.html]
+tags=capturestream
 [test_bug972678.html]
 [test_bug1056032.html]
 skip-if = toolkit == 'android' # bug 1056706
 [test_channelMergerNode.html]
 [test_channelMergerNodeWithVolume.html]
 [test_channelSplitterNode.html]
 [test_channelSplitterNodeWithVolume.html]
 skip-if = (android_version == '18' && debug) # bug 1158417
@@ -111,25 +113,30 @@ skip-if = toolkit == 'android' # bug 105
 [test_dynamicsCompressorNode.html]
 [test_dynamicsCompressorNodePassThrough.html]
 [test_gainNode.html]
 [test_gainNodeInLoop.html]
 [test_gainNodePassThrough.html]
 [test_maxChannelCount.html]
 [test_mediaDecoding.html]
 [test_mediaElementAudioSourceNode.html]
+tags=capturestream
 [test_mediaElementAudioSourceNodePassThrough.html]
+tags=capturestream
 skip-if = toolkit == 'android' # bug 1145816
 [test_mediaElementAudioSourceNodeCrossOrigin.html]
+tags=capturestream
 skip-if = toolkit == 'android' # bug 1145816
 [test_mediaStreamAudioDestinationNode.html]
 [test_mediaStreamAudioSourceNode.html]
 [test_mediaStreamAudioSourceNodeCrossOrigin.html]
+tags=capturestream
 [test_mediaStreamAudioSourceNodePassThrough.html]
 [test_mediaStreamAudioSourceNodeResampling.html]
+tags=capturestream
 [test_mixingRules.html]
 skip-if = android_version == '10' || android_version == '18' # bug 1091965
 [test_mozaudiochannel.html]
 # Android: bug 1061675; OSX 10.6: bug 1097721
 skip-if = (toolkit == 'gonk' && !debug) || android_version == '10' || android_version == '18' || (os == 'mac' && os_version == '10.6')
 [test_nodeToParamConnection.html]
 [test_OfflineAudioContext.html]
 [test_offlineDestinationChannelCountLess.html]
--- a/dom/media/webspeech/recognition/test/mochitest.ini
+++ b/dom/media/webspeech/recognition/test/mochitest.ini
@@ -6,16 +6,17 @@ support-files =
   hello.ogg^headers^
   silence.ogg
   silence.ogg^headers^
 
 [test_abort.html]
 skip-if = toolkit == 'android' || toolkit == 'gonk' # bug 1037287
 [test_audio_capture_error.html]
 [test_call_start_from_end_handler.html]
+tags=capturestream
 skip-if = (android_version == '18' && debug) # bug 967606
 [test_nested_eventloop.html]
 skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' || e10s # b2g(showmodaldialog)
 [test_preference_enable.html]
 [test_recognition_service_error.html]
 skip-if = buildapp == 'b2g' # b2g(timed out)
 [test_success_without_recognition_service.html]
 [test_timeout.html]
--- a/dom/network/TCPSocket.js
+++ b/dom/network/TCPSocket.js
@@ -685,24 +685,26 @@ TCPSocket.prototype = {
 
   send: function ts_send(data, byteOffset, byteLength) {
     if (this._readyState !== kOPEN) {
       throw new Error("Socket not open.");
     }
 
     if (this._binaryType === "arraybuffer") {
       byteLength = byteLength || data.byteLength;
+    } else {
+      data = data.toString();
+      byteLength = data.length;
     }
 
     if (this._inChild) {
       this._socketBridge.sendSend(data, byteOffset, byteLength, ++this._trackingNumber);
     }
 
-    let length = this._binaryType === "arraybuffer" ? byteLength : data.length;
-    let newBufferedAmount = this.bufferedAmount + length;
+    let newBufferedAmount = this.bufferedAmount + byteLength;
     let bufferFull = newBufferedAmount >= BUFFER_SIZE;
 
     if (bufferFull) {
       // If we buffered more than some arbitrary amount of data,
       // (65535 right now) we should tell the caller so they can
       // wait until ondrain is called if they so desire. Once all the
       // buffered data has been written to the socket, ondrain is
       // called.
@@ -717,32 +719,32 @@ TCPSocket.prototype = {
     }
 
     let new_stream;
     if (this._binaryType === "arraybuffer") {
       new_stream = new ArrayBufferInputStream();
       new_stream.setData(data, byteOffset, byteLength);
     } else {
       new_stream = new StringInputStream();
-      new_stream.setData(data, length);
+      new_stream.setData(data, byteLength);
     }
 
     if (this._waitingForStartTLS) {
       // When we are waiting for starttls, new_stream is added to pendingData
       // and will be appended to multiplexStream after tls had been set up.
       this._pendingDataAfterStartTLS.push(new_stream);
     } else {
       this._multiplexStream.appendStream(new_stream);
     }
 
     this._ensureCopying();
 
 #ifdef MOZ_WIDGET_GONK
     // Collect transmitted amount for network statistics.
-    this._txBytes += length;
+    this._txBytes += byteLength;
     this._saveNetworkStats(false);
 #endif
 
     return !bufferFull;
   },
 
   suspend: function ts_suspend() {
     if (this._inChild) {
--- a/dom/network/tests/test_tcpsocket_client_and_server_basics.js
+++ b/dom/network/tests/test_tcpsocket_client_and_server_basics.js
@@ -341,16 +341,40 @@ function* test_basics() {
     bigUint8Array.length);
   assertUint8ArraysEqual(serverReceived, bigUint8Array,
                          'server received/client sent');
   // And a close.
   is((yield serverQueue.waitForEvent()).type, 'close',
      'The drain event should fire after a large send that returned true.');
 
 
+  // -- Re-establish connection
+  connectedPromise = waitForConnection(listeningServer);
+  clientSocket = TCPSocket.open('127.0.0.1', serverPort,
+                                { binaryType: 'string' });
+  clientQueue = listenForEventsOnSocket(clientSocket, 'client');
+  is((yield clientQueue.waitForEvent()).type, 'open', 'got open event');
+
+  connectedResult = yield connectedPromise;
+  // destructuring assignment is not yet ES6 compliant, must manually unpack
+  serverSocket = connectedResult.socket;
+  serverQueue = connectedResult.queue;
+
+  // -- Attempt to send non-string data.
+  is(clientSocket.send(bigUint8Array), true,
+     'Client sending a large non-string should only send a small string.');
+  clientSocket.close();
+  // The server will get its data
+  serverReceived = yield serverQueue.waitForDataWithAtLeastLength(
+    bigUint8Array.toString().length);
+  // Then we'll get a close
+  is((yield clientQueue.waitForEvent()).type, 'close',
+     'The close event should fire after the drain event.');
+
+
   // -- Close the listening server (and try to connect)
   // We want to verify that the server actually closes / stops listening when
   // we tell it to.
   listeningServer.close();
 
   // - try and connect, get an error
   clientSocket = TCPSocket.open('127.0.0.1', serverPort,
                                 { binaryType: 'arraybuffer' });
--- a/dom/plugins/base/nsJSNPRuntime.cpp
+++ b/dom/plugins/base/nsJSNPRuntime.cpp
@@ -78,17 +78,17 @@ typedef js::HashMap<nsJSObjWrapperKey,
                     js::SystemAllocPolicy> JSObjWrapperTable;
 static JSObjWrapperTable sJSObjWrappers;
 
 // Whether it's safe to iterate sJSObjWrappers.  Set to true when sJSObjWrappers
 // has been initialized and is not currently being enumerated.
 static bool sJSObjWrappersAccessible = false;
 
 // Hash of NPObject wrappers that wrap NPObjects as JSObjects.
-static PLDHashTable2* sNPObjWrappers;
+static PLDHashTable* sNPObjWrappers;
 
 // Global wrapper count. This includes JSObject wrappers *and*
 // NPObject wrappers. When this count goes to zero, there are no more
 // wrappers and we can kill off hash tables etc.
 static int32_t sWrapperCount;
 
 // The runtime service used to register/unregister GC callbacks.
 nsCOMPtr<nsIJSRuntimeService> sCallbackRuntime;
@@ -403,17 +403,17 @@ CreateNPObjWrapperTable()
 {
   MOZ_ASSERT(!sNPObjWrappers);
 
   if (!RegisterGCCallbacks()) {
     return false;
   }
 
   sNPObjWrappers =
-    new PLDHashTable2(PL_DHashGetStubOps(), sizeof(NPObjWrapperHashEntry));
+    new PLDHashTable(PL_DHashGetStubOps(), sizeof(NPObjWrapperHashEntry));
   return true;
 }
 
 static void
 DestroyNPObjWrapperTable()
 {
   MOZ_ASSERT(sNPObjWrappers->EntryCount() == 0);
 
@@ -1965,17 +1965,17 @@ NPObjWrapperPluginDestroyedCallback(PLDH
                                     uint32_t number, void *arg)
 {
   NPObjWrapperHashEntry *entry = (NPObjWrapperHashEntry *)hdr;
   NppAndCx *nppcx = reinterpret_cast<NppAndCx *>(arg);
 
   if (entry->mNpp == nppcx->npp) {
     // HACK: temporarily hide the hash we're enumerating so that invalidate()
     // and deallocate() don't touch it.
-    PLDHashTable2 *tmp = static_cast<PLDHashTable2*>(table);
+    PLDHashTable *tmp = static_cast<PLDHashTable*>(table);
     sNPObjWrappers = nullptr;
 
     NPObject *npobj = entry->mNPObj;
 
     if (npobj->_class && npobj->_class->invalidate) {
       npobj->_class->invalidate(npobj);
     }
 
--- a/dom/promise/Promise.cpp
+++ b/dom/promise/Promise.cpp
@@ -5,16 +5,17 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/Promise.h"
 
 #include "jsfriendapi.h"
 #include "js/Debug.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/DOMError.h"
+#include "mozilla/dom/DOMException.h"
 #include "mozilla/dom/OwningNonNull.h"
 #include "mozilla/dom/PromiseBinding.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/MediaStreamError.h"
 #include "mozilla/Atomics.h"
 #include "mozilla/CycleCollectedJSRuntime.h"
 #include "mozilla/Preferences.h"
 #include "PromiseCallback.h"
@@ -1690,16 +1691,20 @@ PromiseWorkerProxy::CleanUp(JSContext* a
 }
 
 // Specializations of MaybeRejectBrokenly we actually support.
 template<>
 void Promise::MaybeRejectBrokenly(const nsRefPtr<DOMError>& aArg) {
   MaybeSomething(aArg, &Promise::MaybeReject);
 }
 template<>
+void Promise::MaybeRejectBrokenly(const nsRefPtr<DOMException>& aArg) {
+  MaybeSomething(aArg, &Promise::MaybeReject);
+}
+template<>
 void Promise::MaybeRejectBrokenly(const nsAString& aArg) {
   MaybeSomething(aArg, &Promise::MaybeReject);
 }
 
 uint64_t
 Promise::GetID() {
   if (mID != 0) {
     return mID;
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -785,16 +785,21 @@ public:
     callback->UpdateFailed(aError);
     FailCommon(NS_ERROR_DOM_JS_EXCEPTION);
   }
 
   // Public so our error handling code can continue with a successful worker.
   void
   ContinueInstall()
   {
+    // mRegistration will be null if we have already Fail()ed.
+    if (!mRegistration) {
+      return;
+    }
+
     // Even if we are canceled, ensure integrity of mSetOfScopesBeingUpdated
     // first.
     nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
     MOZ_ASSERT(swm->mSetOfScopesBeingUpdated.Contains(mRegistration->mScope));
     swm->mSetOfScopesBeingUpdated.Remove(mRegistration->mScope);
     // This is effectively the end of Step 4.3 of the [[Update]] algorithm.
     // The invocation of [[Install]] is not part of the atomic block.
 
--- a/dom/workers/test/serviceworkers/mochitest.ini
+++ b/dom/workers/test/serviceworkers/mochitest.ini
@@ -101,16 +101,17 @@ support-files =
   app-protocol/*
   force_refresh_worker.js
   sw_clients/refresher.html
   sw_clients/refresher_compressed.html
   sw_clients/refresher_compressed.html^headers^
   sw_clients/refresher_cached.html
   sw_clients/refresher_cached_compressed.html
   sw_clients/refresher_cached_compressed.html^headers^
+  strict_mode_error.js
 
 [test_unregister.html]
 [test_installation_simple.html]
 [test_fetch_event.html]
 [test_https_fetch.html]
 [test_https_fetch_cloned_response.html]
 [test_https_synth_fetch_from_cached_sw.html]
 [test_match_all.html]
@@ -141,8 +142,9 @@ support-files =
 [test_claim.html]
 [test_periodic_https_update.html]
 [test_sanitize.html]
 [test_sanitize_domain.html]
 [test_service_worker_allowed.html]
 [test_app_protocol.html]
 [test_claim_fetch.html]
 [test_force_refresh.html]
+[test_strict_mode_error.html]
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/strict_mode_error.js
@@ -0,0 +1,4 @@
+function f() {
+  return 1;
+  return 2;
+}
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_strict_mode_error.html
@@ -0,0 +1,39 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Bug 1170550 - test registration of service worker scripts with a strict mode error</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+  function runTest() {
+    navigator.serviceWorker
+      .register("strict_mode_error.js", {scope: "strict_mode_error"})
+      .catch(() => {
+        ok(true, "Registration failed as expected");
+        SimpleTest.finish();
+      });
+  }
+
+  SimpleTest.waitForExplicitFinish();
+  onload = function() {
+    SpecialPowers.pushPrefEnv({"set": [
+      ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+      ["dom.serviceWorkers.enabled", true],
+      ["dom.serviceWorkers.testing.enabled", true],
+    ]}, runTest);
+  };
+</script>
+</pre>
+</body>
+</html>
--- a/dom/xul/XULDocument.cpp
+++ b/dom/xul/XULDocument.cpp
@@ -760,17 +760,17 @@ XULDocument::AddBroadcastListenerFor(Ele
         PL_DHashVoidPtrKeyStub,
         PL_DHashMatchEntryStub,
         PL_DHashMoveEntryStub,
         ClearBroadcasterMapEntry,
         nullptr
     };
 
     if (! mBroadcasterMap) {
-        mBroadcasterMap = new PLDHashTable2(&gOps, sizeof(BroadcasterMapEntry));
+        mBroadcasterMap = new PLDHashTable(&gOps, sizeof(BroadcasterMapEntry));
     }
 
     BroadcasterMapEntry* entry =
         static_cast<BroadcasterMapEntry*>
                    (PL_DHashTableSearch(mBroadcasterMap, &aBroadcaster));
 
     if (!entry) {
         entry = static_cast<BroadcasterMapEntry*>
--- a/dom/xul/XULDocument.h
+++ b/dom/xul/XULDocument.h
@@ -714,17 +714,17 @@ protected:
         NS_DECL_NSIREQUESTOBSERVER
     };
 
     friend class ParserObserver;
 
     /**
      * A map from a broadcaster element to a list of listener elements.
      */
-    PLDHashTable2* mBroadcasterMap;
+    PLDHashTable* mBroadcasterMap;
 
     nsAutoPtr<nsInterfaceHashtable<nsURIHashKey,nsIObserver> > mOverlayLoadObservers;
     nsAutoPtr<nsInterfaceHashtable<nsURIHashKey,nsIObserver> > mPendingOverlayLoadNotifications;
 
     bool mInitialLayoutComplete;
 
     class nsDelayedBroadcastUpdate
     {
--- a/dom/xul/templates/nsContentSupportMap.cpp
+++ b/dom/xul/templates/nsContentSupportMap.cpp
@@ -1,24 +1,18 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* 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 "nsContentSupportMap.h"
 #include "nsXULElement.h"
 
-nsresult
+void
 nsContentSupportMap::Remove(nsIContent* aElement)
 {
-    if (!mMap.IsInitialized())
-        return NS_ERROR_NOT_INITIALIZED;
-
     nsIContent* child = aElement;
     do {
         PL_DHashTableRemove(&mMap, child);
         child = child->GetNextNode(aElement);
     } while(child);
-
-    return NS_OK;
 }
 
-
--- a/dom/xul/templates/nsContentSupportMap.h
+++ b/dom/xul/templates/nsContentSupportMap.h
@@ -42,22 +42,22 @@ public:
         if (!hdr)
             return false;
 
         Entry* entry = static_cast<Entry*>(hdr);
         *aMatch = entry->mMatch;
         return true;
     }
 
-    nsresult Remove(nsIContent* aElement);
+    void Remove(nsIContent* aElement);
 
     void Clear() { mMap.Clear(); }
 
 protected:
-    PLDHashTable2 mMap;
+    PLDHashTable mMap;
 
     struct Entry : public PLDHashEntryHdr {
         nsIContent*      mContent;
         nsTemplateMatch* mMatch;
     };
 };
 
 #endif
--- a/dom/xul/templates/nsTemplateMap.h
+++ b/dom/xul/templates/nsTemplateMap.h
@@ -11,17 +11,17 @@
 
 class nsTemplateMap {
 protected:
     struct Entry : public PLDHashEntryHdr {
         nsIContent*     mContent;
         nsIContent*     mTemplate;
     };
 
-    PLDHashTable2 mTable;
+    PLDHashTable mTable;
 
 public:
     nsTemplateMap() : mTable(PL_DHashGetStubOps(), sizeof(Entry)) { }
 
     ~nsTemplateMap() { }
 
     void
     Put(nsIContent* aContent, nsIContent* aTemplate) {
--- a/embedding/components/commandhandler/nsCommandParams.h
+++ b/embedding/components/commandhandler/nsCommandParams.h
@@ -119,14 +119,14 @@ protected:
   static bool HashMatchEntry(PLDHashTable* aTable,
                              const PLDHashEntryHdr* aEntry, const void* aKey);
 
   static void HashMoveEntry(PLDHashTable* aTable, const PLDHashEntryHdr* aFrom,
                             PLDHashEntryHdr* aTo);
 
   static void HashClearEntry(PLDHashTable* aTable, PLDHashEntryHdr* aEntry);
 
-  PLDHashTable2 mValuesHash;
+  PLDHashTable mValuesHash;
 
   static const PLDHashTableOps sHashOps;
 };
 
 #endif // nsCommandParams_h__
--- a/gfx/ipc/GfxMessageUtils.h
+++ b/gfx/ipc/GfxMessageUtils.h
@@ -729,16 +729,17 @@ struct ParamTraits<mozilla::layers::Fram
     WriteParam(aMsg, aParam.mBackgroundColor);
     WriteParam(aMsg, aParam.mDoSmoothScroll);
     WriteParam(aMsg, aParam.mSmoothScrollOffset);
     WriteParam(aMsg, aParam.GetLineScrollAmount());
     WriteParam(aMsg, aParam.GetPageScrollAmount());
     WriteParam(aMsg, aParam.AllowVerticalScrollWithWheel());
     WriteParam(aMsg, aParam.mClipRect);
     WriteParam(aMsg, aParam.mIsLayersIdRoot);
+    WriteParam(aMsg, aParam.mUsesContainerScrolling);
     WriteParam(aMsg, aParam.GetContentDescription());
   }
 
   static bool ReadContentDescription(const Message* aMsg, void** aIter, paramType* aResult)
   {
     nsCString str;
     if (!ReadParam(aMsg, aIter, &str)) {
       return false;
@@ -773,16 +774,17 @@ struct ParamTraits<mozilla::layers::Fram
             ReadParam(aMsg, aIter, &aResult->mBackgroundColor) &&
             ReadParam(aMsg, aIter, &aResult->mDoSmoothScroll) &&
             ReadParam(aMsg, aIter, &aResult->mSmoothScrollOffset) &&
             ReadParam(aMsg, aIter, &aResult->mLineScrollAmount) &&
             ReadParam(aMsg, aIter, &aResult->mPageScrollAmount) &&
             ReadParam(aMsg, aIter, &aResult->mAllowVerticalScrollWithWheel) &&
             ReadParam(aMsg, aIter, &aResult->mClipRect) &&
             ReadParam(aMsg, aIter, &aResult->mIsLayersIdRoot) &&
+            ReadParam(aMsg, aIter, &aResult->mUsesContainerScrolling) &&
             ReadContentDescription(aMsg, aIter, aResult));
   }
 };
 
 template<>
 struct ParamTraits<mozilla::layers::TextureFactoryIdentifier>
 {
   typedef mozilla::layers::TextureFactoryIdentifier paramType;
--- a/gfx/layers/FrameMetrics.h
+++ b/gfx/layers/FrameMetrics.h
@@ -9,16 +9,17 @@
 #include <stdint.h>                     // for uint32_t, uint64_t
 #include "Units.h"                      // for CSSRect, CSSPixel, etc
 #include "mozilla/Maybe.h"
 #include "mozilla/gfx/BasePoint.h"      // for BasePoint
 #include "mozilla/gfx/Rect.h"           // for RoundedIn
 #include "mozilla/gfx/ScaleFactor.h"    // for ScaleFactor
 #include "mozilla/gfx/Logging.h"        // for Log
 #include "gfxColor.h"
+#include "gfxPrefs.h"                   // for LayoutUseContainersForRootFrames
 #include "nsString.h"
 
 namespace IPC {
 template <typename T> struct ParamTraits;
 } // namespace IPC
 
 namespace mozilla {
 namespace layers {
@@ -63,16 +64,17 @@ public:
     , mPresShellId(-1)
     , mViewport(0, 0, 0, 0)
     , mExtraResolution()
     , mBackgroundColor(0, 0, 0, 0)
     , mLineScrollAmount(0, 0)
     , mPageScrollAmount(0, 0)
     , mAllowVerticalScrollWithWheel(false)
     , mIsLayersIdRoot(false)
+    , mUsesContainerScrolling(false)
   {
   }
 
   // Default copy ctor and operator= are fine
 
   bool operator==(const FrameMetrics& aOther) const
   {
     return mCompositionBounds.IsEqualEdges(aOther.mCompositionBounds) &&
@@ -97,17 +99,18 @@ public:
            mScrollGeneration == aOther.mScrollGeneration &&
            mExtraResolution == aOther.mExtraResolution &&
            mBackgroundColor == aOther.mBackgroundColor &&
            mDoSmoothScroll == aOther.mDoSmoothScroll &&
            mLineScrollAmount == aOther.mLineScrollAmount &&
            mPageScrollAmount == aOther.mPageScrollAmount &&
            mAllowVerticalScrollWithWheel == aOther.mAllowVerticalScrollWithWheel &&
            mClipRect == aOther.mClipRect &&
-           mIsLayersIdRoot == aOther.mIsLayersIdRoot;
+           mIsLayersIdRoot == aOther.mIsLayersIdRoot &&
+		   mUsesContainerScrolling == aOther.mUsesContainerScrolling;
   }
   bool operator!=(const FrameMetrics& aOther) const
   {
     return !operator==(aOther);
   }
 
   bool IsDefault() const
   {
@@ -529,16 +532,24 @@ public:
 
   void SetIsLayersIdRoot(bool aValue) {
     mIsLayersIdRoot = aValue;
   }
   bool IsLayersIdRoot() const {
     return mIsLayersIdRoot;
   }
 
+  void SetUsesContainerScrolling(bool aValue) {
+    MOZ_ASSERT_IF(aValue, gfxPrefs::LayoutUseContainersForRootFrames());
+    mUsesContainerScrolling = aValue;
+  }
+  bool UsesContainerScrolling() const {
+    return mUsesContainerScrolling;
+  }
+
 private:
 
   // The pres-shell resolution that has been induced on the document containing
   // this scroll frame as a result of zooming this scroll frame (whether via
   // user action, or choosing an initial zoom level on page load). This can
   // only be different from 1.0 for frames that are zoomable, which currently
   // is just the root content document's root scroll frame (mIsRoot = true).
   // This is a plain float rather than a ScaleFactor because in and of itself
@@ -707,16 +718,20 @@ private:
 
   // The clip rect to use when compositing a layer with this FrameMetrics.
   Maybe<ParentLayerIntRect> mClipRect;
 
   // Whether these framemetrics are for the root scroll frame (root element if
   // we don't have a root scroll frame) for its layers id.
   bool mIsLayersIdRoot;
 
+  // True if scrolling using containers, false otherwise. This can be removed
+  // when containerful scrolling is eliminated.
+  bool mUsesContainerScrolling;
+
   // WARNING!!!!
   //
   // When adding new fields to FrameMetrics, the following places should be
   // updated to include them (as needed):
   //    FrameMetrics::operator ==
   //    AsyncPanZoomController::NotifyLayersUpdated
   //    The ParamTraits specialization in GfxMessageUtils.h
   //
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -924,16 +924,37 @@ Layer::GetVisibleRegionRelativeToRootLay
     // positioning code.
     offset += currentLayerOffset;
   }
 
   *aLayerOffset = nsIntPoint(offset.x, offset.y);
   return true;
 }
 
+Maybe<ParentLayerIntRect>
+Layer::GetCombinedClipRect() const
+{
+  Maybe<ParentLayerIntRect> clip = GetClipRect();
+
+  for (size_t i = 0; i < mFrameMetrics.Length(); i++) {
+    if (!mFrameMetrics[i].HasClipRect()) {
+      continue;
+    }
+
+    const ParentLayerIntRect& other = mFrameMetrics[i].ClipRect();
+    if (clip) {
+      clip = Some(clip.value().Intersect(other));
+    } else {
+      clip = Some(other);
+    }
+  }
+
+  return clip;
+}
+
 ContainerLayer::ContainerLayer(LayerManager* aManager, void* aImplData)
   : Layer(aManager, aImplData),
     mFirstChild(nullptr),
     mLastChild(nullptr),
     mPreXScale(1.0f),
     mPreYScale(1.0f),
     mInheritedXScale(1.0f),
     mInheritedYScale(1.0f),
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -1250,16 +1250,22 @@ public:
   const LayerRect& GetStickyScrollRangeOuter() { return mStickyPositionData->mOuter; }
   const LayerRect& GetStickyScrollRangeInner() { return mStickyPositionData->mInner; }
   FrameMetrics::ViewID GetScrollbarTargetContainerId() { return mScrollbarTargetId; }
   ScrollDirection GetScrollbarDirection() { return mScrollbarDirection; }
   float GetScrollbarThumbRatio() { return mScrollbarThumbRatio; }
   bool IsScrollbarContainer() { return mIsScrollbarContainer; }
   Layer* GetMaskLayer() const { return mMaskLayer; }
 
+  /*
+   * Get the combined clip rect of the Layer clip and all clips on FrameMetrics.
+   * This is intended for use in Layout. The compositor needs to apply async
+   * transforms to find the combined clip.
+   */
+  Maybe<ParentLayerIntRect> GetCombinedClipRect() const;
 
   /**
    * Retrieve the root level visible region for |this| taking into account
    * clipping applied to parent layers of |this| as well as subtracting
    * visible regions of higher siblings of this layer and each ancestor.
    *
    * Note translation values for offsets of visible regions and accumulated
    * aLayerOffset are integer rounded using Point's RoundedToInt.
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -2898,16 +2898,17 @@ void AsyncPanZoomController::NotifyLayer
     mFrameMetrics.SetRootCompositionSize(aLayerMetrics.GetRootCompositionSize());
     mFrameMetrics.SetPresShellResolution(aLayerMetrics.GetPresShellResolution());
     mFrameMetrics.SetCumulativeResolution(aLayerMetrics.GetCumulativeResolution());
     mFrameMetrics.SetHasScrollgrab(aLayerMetrics.GetHasScrollgrab());
     mFrameMetrics.SetLineScrollAmount(aLayerMetrics.GetLineScrollAmount());
     mFrameMetrics.SetPageScrollAmount(aLayerMetrics.GetPageScrollAmount());
     mFrameMetrics.SetClipRect(aLayerMetrics.GetClipRect());
     mFrameMetrics.SetIsLayersIdRoot(aLayerMetrics.IsLayersIdRoot());
+    mFrameMetrics.SetUsesContainerScrolling(aLayerMetrics.UsesContainerScrolling());
 
     if (scrollOffsetUpdated) {
       APZC_LOG("%p updating scroll offset from %s to %s\n", this,
         ToString(mFrameMetrics.GetScrollOffset()).c_str(),
         ToString(aLayerMetrics.GetScrollOffset()).c_str());
 
       mFrameMetrics.CopyScrollInfoFrom(aLayerMetrics);
 
--- a/gfx/layers/composite/AsyncCompositionManager.cpp
+++ b/gfx/layers/composite/AsyncCompositionManager.cpp
@@ -575,31 +575,40 @@ AsyncCompositionManager::ApplyAsyncConte
   }
 
   Matrix4x4 oldTransform = aLayer->GetTransform();
 
   Matrix4x4 combinedAsyncTransformWithoutOverscroll;
   Matrix4x4 combinedAsyncTransform;
   bool hasAsyncTransform = false;
   LayerMargin fixedLayerMargins(0, 0, 0, 0);
-  Maybe<ParentLayerIntRect> clipRect = aLayer->AsLayerComposite()->GetShadowClipRect();
+
+  // Each layer has multiple clips. Its local clip, which must move with async
+  // transforms, and its scrollframe clips, which are the clips between each
+  // scrollframe and its ancestor scrollframe. Scrollframe clips include the
+  // composition bounds and any other clips induced by layout.
+  //
+  // The final clip for the layer is the intersection of these clips.
+  Maybe<ParentLayerIntRect> asyncClip = aLayer->GetClipRect();
 
   for (uint32_t i = 0; i < aLayer->GetFrameMetricsCount(); i++) {
     AsyncPanZoomController* controller = aLayer->GetAsyncPanZoomController(i);
     if (!controller) {
       continue;
     }
 
     hasAsyncTransform = true;
 
     ViewTransform asyncTransformWithoutOverscroll;
     ParentLayerPoint scrollOffset;
     controller->SampleContentTransformForFrame(&asyncTransformWithoutOverscroll,
                                                scrollOffset);
     Matrix4x4 overscrollTransform = controller->GetOverscrollTransform();
+    Matrix4x4 asyncTransform =
+      Matrix4x4(asyncTransformWithoutOverscroll) * overscrollTransform;
 
     if (!aLayer->IsScrollInfoLayer()) {
       controller->MarkAsyncTransformAppliedToContent();
     }
 
     const FrameMetrics& metrics = aLayer->GetFrameMetrics(i);
     ScreenPoint offset(0, 0);
     // TODO: When we enable APZ on Fennec, we'll need to call SyncFrameMetrics here.
@@ -618,54 +627,44 @@ AsyncCompositionManager::ApplyAsyncConte
 #endif
 
     mIsFirstPaint = false;
     mLayersUpdated = false;
 
     // Apply the render offset
     mLayerManager->GetCompositor()->SetScreenRenderOffset(offset);
 
-    // See the comment below - the first FrameMetrics has the clip computed
-    // by layout (currently, effectively the composition bounds), which we
-    // intersect here to include the layer clip.
-    if (i == 0 && metrics.HasClipRect()) {
-      if (clipRect) {
-        clipRect = Some(clipRect.value().Intersect(metrics.ClipRect()));
+    // Transform the current local clip by this APZC's async transform. If we're
+    // using containerful scrolling, then the clip is not part of the scrolled
+    // frame and should not be transformed.
+    if (asyncClip && !metrics.UsesContainerScrolling()) {
+      asyncClip = Some(TransformTo<ParentLayerPixel>(asyncTransform, *asyncClip));
+    }
+
+    // Combine the local clip with the ancestor scrollframe clip. This is not
+    // included in the async transform above, since the ancestor clip should not
+    // move with this APZC.
+    if (metrics.HasClipRect()) {
+      ParentLayerIntRect clip = metrics.ClipRect();
+      if (asyncClip) {
+        asyncClip = Some(clip.Intersect(*asyncClip));
       } else {
-        clipRect = Some(metrics.ClipRect());
+        asyncClip = Some(clip);
       }
     }
 
     combinedAsyncTransformWithoutOverscroll *= asyncTransformWithoutOverscroll;
-    combinedAsyncTransform *= (Matrix4x4(asyncTransformWithoutOverscroll) * overscrollTransform);
-    if (i > 0 && clipRect) {
-      // The clip rect Layout calculates is the intersection of the composition
-      // bounds of all the scroll frames at the time of the paint (when there
-      // are no async transforms).
-      // An async transform on a scroll frame does not affect the composition
-      // bounds of *that* scroll frame, but it does affect the composition
-      // bounds of the scroll frames *below* it.
-      // Therefore, if we have multiple scroll frames associated with this
-      // layer, the clip rect needs to be adjusted for the async transforms of
-      // the scroll frames other than the bottom-most one.
-      // To make this adjustment, we start with the Layout-provided clip rect,
-      // and at each level other than the bottom, transform it by the async
-      // transform at that level, and then re-intersect it with the composition
-      // bounds at that level.
-      ParentLayerRect transformed = TransformTo<ParentLayerPixel>(
-        (Matrix4x4(asyncTransformWithoutOverscroll) * overscrollTransform),
-        ParentLayerRect(*clipRect));
-      clipRect = Some(RoundedOut(transformed.Intersect(metrics.GetCompositionBounds())));
-    }
+    combinedAsyncTransform *= asyncTransform;
   }
 
   if (hasAsyncTransform) {
-    if (clipRect) {
-      aLayer->AsLayerComposite()->SetShadowClipRect(clipRect);
+    if (asyncClip) {
+      aLayer->AsLayerComposite()->SetShadowClipRect(asyncClip);
     }
+
     // Apply the APZ transform on top of GetLocalTransform() here (rather than
     // GetTransform()) in case the OMTA code in SampleAnimations already set a
     // shadow transform; in that case we want to apply ours on top of that one
     // rather than clobber it.
     SetShadowTransform(aLayer,
         aLayer->GetLocalTransform() * AdjustForClip(combinedAsyncTransform, aLayer));
 
     const FrameMetrics& bottom = LayerMetricsWrapper::BottommostScrollableMetrics(aLayer);
--- a/gfx/thebes/gfxFT2FontList.cpp
+++ b/gfx/thebes/gfxFT2FontList.cpp
@@ -612,55 +612,56 @@ FT2FontFamily::AddFacesToFontList(Infall
  * find their attributes), leading to significantly quicker startup.
  */
 
 #define CACHE_KEY "font.cached-list"
 
 class FontNameCache {
 public:
     FontNameCache()
-        : mWriteNeeded(false)
+        : mMap(&mOps, sizeof(FNCMapEntry), 0)
+        , mWriteNeeded(false)
     {
+        // HACK ALERT: it's weird to assign |mOps| after we passed a pointer to
+        // it to |mMap|'s constructor. A more normal approach here would be to
+        // have a static |sOps| member. Unfortunately, this mysteriously but
+        // consistently makes Fennec start-up slower, so we take this
+        // unorthodox approach instead. It's safe because PLDHashTable's
+        // constructor doesn't dereference the pointer; it just makes a copy of
+        // it.
         mOps = (PLDHashTableOps) {
             StringHash,
             HashMatchEntry,
             MoveEntry,
             PL_DHashClearEntryStub,
             nullptr
         };
 
-        PL_DHashTableInit(&mMap, &mOps, sizeof(FNCMapEntry), 0);
-
         MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default,
                    "StartupCacheFontNameCache should only be used in chrome "
                    "process");
         mCache = mozilla::scache::StartupCache::GetSingleton();
 
         Init();
     }
 
     ~FontNameCache()
     {
-        if (!mMap.IsInitialized()) {
-            return;
-        }
         if (!mWriteNeeded || !mCache) {
-            PL_DHashTableFinish(&mMap);
             return;
         }
 
         nsAutoCString buf;
         PL_DHashTableEnumerate(&mMap, WriteOutMap, &buf);
-        PL_DHashTableFinish(&mMap);
         mCache->PutBuffer(CACHE_KEY, buf.get(), buf.Length() + 1);
     }
 
     void Init()
     {
-        if (!mMap.IsInitialized() || !mCache) {
+        if (!mCache) {
             return;
         }
         uint32_t size;
         char* buf;
         if (NS_FAILED(mCache->GetBuffer(CACHE_KEY, &buf, &size))) {
             return;
         }
 
@@ -705,19 +706,16 @@ public:
         // Should we use free() or delete[] here? See bug 684700.
         free(buf);
     }
 
     virtual void
     GetInfoForFile(const nsCString& aFileName, nsCString& aFaceList,
                    uint32_t *aTimestamp, uint32_t *aFilesize)
     {
-        if (!mMap.IsInitialized()) {
-            return;
-        }
         FNCMapEntry *entry =
             static_cast<FNCMapEntry*>(PL_DHashTableSearch(&mMap,
                                                           aFileName.get()));
         if (entry) {
             *aTimestamp = entry->mTimestamp;
             *aFilesize = entry->mFilesize;
             aFaceList.Assign(entry->mFaces);
             // this entry does correspond to an existing file
@@ -726,19 +724,16 @@ public:
             entry->mFileExists = true;
         }
     }
 
     virtual void
     CacheFileInfo(const nsCString& aFileName, const nsCString& aFaceList,
                   uint32_t aTimestamp, uint32_t aFilesize)
     {
-        if (!mMap.IsInitialized()) {
-            return;
-        }
         FNCMapEntry* entry = static_cast<FNCMapEntry*>
             (PL_DHashTableAdd(&mMap, aFileName.get(), fallible));
         if (entry) {
             entry->mFilename.Assign(aFileName);
             entry->mTimestamp = aTimestamp;
             entry->mFilesize = aFilesize;
             entry->mFaces.Assign(aFaceList);
             entry->mFileExists = true;
--- a/gfx/thebes/gfxPlatformMac.cpp
+++ b/gfx/thebes/gfxPlatformMac.cpp
@@ -571,17 +571,26 @@ static CVReturn VsyncCallback(CVDisplayL
 {
   // Executed on OS X hardware vsync thread
   OSXVsyncSource::OSXDisplay* display = (OSXVsyncSource::OSXDisplay*) aDisplayLinkContext;
   int64_t nextVsyncTimestamp = aOutputTime->hostTime;
   mozilla::TimeStamp nextVsync = mozilla::TimeStamp::FromSystemTime(nextVsyncTimestamp);
 
   mozilla::TimeStamp previousVsync = display->mPreviousTimestamp;
   display->mPreviousTimestamp = nextVsync;
-  MOZ_ASSERT(TimeStamp::Now() > previousVsync);
+  mozilla::TimeStamp now = TimeStamp::Now();
+  MOZ_ASSERT(nextVsync > previousVsync);
+
+  // Bug 1158321 - The VsyncCallback can sometimes execute before the reported
+  // vsync time. In those cases, normalize the timestamp to Now() as sending
+  // timestamps in the future has undefined behavior. See the comment above
+  // OSXDisplay::mPreviousTimestamp
+  if (now < previousVsync) {
+    previousVsync = now;
+  }
 
   display->NotifyVsync(previousVsync);
   return kCVReturnSuccess;
 }
 
 already_AddRefed<mozilla::gfx::VsyncSource>
 gfxPlatformMac::CreateHardwareVsyncSource()
 {
--- a/image/SurfaceCache.cpp
+++ b/image/SurfaceCache.cpp
@@ -388,16 +388,17 @@ public:
                    uint32_t aSurfaceCacheSize)
     : mExpirationTracker(aSurfaceCacheExpirationTimeMS)
     , mMemoryPressureObserver(new MemoryPressureObserver)
     , mMutex("SurfaceCache")
     , mDiscardFactor(aSurfaceCacheDiscardFactor)
     , mMaxCost(aSurfaceCacheSize)
     , mAvailableCost(aSurfaceCacheSize)
     , mLockedCost(0)
+    , mOverflowCount(0)
   {
     nsCOMPtr<nsIObserverService> os = services::GetObserverService();
     if (os) {
       os->AddObserver(mMemoryPressureObserver, "memory-pressure", false);
     }
   }
 
 private:
@@ -425,16 +426,17 @@ public:
     // If this is a duplicate surface, refuse to replace the original.
     if (MOZ_UNLIKELY(Lookup(aImageKey, aSurfaceKey))) {
       return InsertOutcome::FAILURE_ALREADY_PRESENT;
     }
 
     // If this is bigger than we can hold after discarding everything we can,
     // refuse to cache it.
     if (MOZ_UNLIKELY(!CanHoldAfterDiscarding(aCost))) {
+      mOverflowCount++;
       return InsertOutcome::FAILURE;
     }
 
     // Remove elements in order of cost until we can fit this in the cache. Note
     // that locked surfaces aren't in mCosts, so we never remove them here.
     while (aCost > mAvailableCost) {
       MOZ_ASSERT(!mCosts.IsEmpty(),
                  "Removed everything and it still won't fit");
@@ -792,16 +794,24 @@ public:
 
     rv = MOZ_COLLECT_REPORT("imagelib-surface-cache-estimated-locked",
                             KIND_OTHER, UNITS_BYTES,
                             mLockedCost,
                             "Estimated memory used by locked surfaces in the "
                             "imagelib surface cache.");
     NS_ENSURE_SUCCESS(rv, rv);
 
+    rv = MOZ_COLLECT_REPORT("imagelib-surface-cache-overflow-count",
+                            KIND_OTHER, UNITS_COUNT,
+                            mOverflowCount,
+                            "Count of how many times the surface cache has hit "
+                            "its capacity and been unable to insert a new "
+                            "surface.");
+    NS_ENSURE_SUCCESS(rv, rv);
+
     return NS_OK;
   }
 
   void CollectSizeOfSurfaces(const ImageKey                  aImageKey,
                              nsTArray<SurfaceMemoryCounter>& aCounters,
                              MallocSizeOf                    aMallocSizeOf)
   {
     nsRefPtr<ImageSurfaceCache> cache = GetImageCache(aImageKey);
@@ -881,16 +891,17 @@ private:
     ImageSurfaceCache> mImageCaches;
   SurfaceTracker                          mExpirationTracker;
   nsRefPtr<MemoryPressureObserver>        mMemoryPressureObserver;
   Mutex                                   mMutex;
   const uint32_t                          mDiscardFactor;
   const Cost                              mMaxCost;
   Cost                                    mAvailableCost;
   Cost                                    mLockedCost;
+  size_t                                  mOverflowCount;
 };
 
 NS_IMPL_ISUPPORTS(SurfaceCacheImpl, nsIMemoryReporter)
 NS_IMPL_ISUPPORTS(SurfaceCacheImpl::MemoryPressureObserver, nsIObserver)
 
 ///////////////////////////////////////////////////////////////////////////////
 // Public API
 ///////////////////////////////////////////////////////////////////////////////
copy from intl/uconv/tests/unit/test_encode_gbk.js
copy to intl/uconv/tests/unit/test_encode_gb18030.js
--- a/intl/uconv/tests/unit/test_encode_gbk.js
+++ b/intl/uconv/tests/unit/test_encode_gb18030.js
@@ -1,15 +1,15 @@
-// Tests conversion from Unicode to gbk
+// Tests conversion from Unicode to gb18030
 // This is a sniff test which doesn't cover the full gbk range: the test string
 // includes only the ASCII range and the first 63 double byte characters
 
 load('CharsetConversionTests.js');
 	
-const inString = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u4E02\u4E04\u4E05\u4E06\u4E0F\u4E12\u4E17\u4E1F\u4E20\u4E21\u4E23\u4E26\u4E29\u4E2E\u4E2F\u4E31\u4E33\u4E35\u4E37\u4E3C\u4E40\u4E41\u4E42\u4E44\u4E46\u4E4A\u4E51\u4E55\u4E57\u4E5A\u4E5B\u4E62\u4E63\u4E64\u4E65\u4E67\u4E68\u4E6A\u4E6B\u4E6C\u4E6D\u4E6E\u4E6F\u4E72\u4E74\u4E75\u4E76\u4E77\u4E78\u4E79\u4E7A\u4E7B\u4E7C\u4E7D\u4E7F\u4E80\u4E81\u4E82\u4E83\u4E84\u4E85\u4E87\u4E8A";
+const inString = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u20AC\u4E02\u4E04\u4E05\u4E06\u4E0F\u4E12\u4E17\u4E1F\u4E20\u4E21\u4E23\u4E26\u4E29\u4E2E\u4E2F\u4E31\u4E33\u4E35\u4E37\u4E3C\u4E40\u4E41\u4E42\u4E44\u4E46\u4E4A\u4E51\u4E55\u4E57\u4E5A\u4E5B\u4E62\u4E63\u4E64\u4E65\u4E67\u4E68\u4E6A\u4E6B\u4E6C\u4E6D\u4E6E\u4E6F\u4E72\u4E74\u4E75\u4E76\u4E77\u4E78\u4E79\u4E7A\u4E7B\u4E7C\u4E7D\u4E7F\u4E80\u4E81\u4E82\u4E83\u4E84\u4E85\u4E87\u4E8A\uFFFD";
 
-const expectedString = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x81@\x81A\x81B\x81C\x81D\x81E\x81F\x81G\x81H\x81I\x81J\x81K\x81L\x81M\x81N\x81O\x81P\x81Q\x81R\x81S\x81T\x81U\x81V\x81W\x81X\x81Y\x81Z\x81[\x81\\\x81]\x81^\x81_\x81`\x81a\x81b\x81c\x81d\x81e\x81f\x81g\x81h\x81i\x81j\x81k\x81l\x81m\x81n\x81o\x81p\x81q\x81r\x81s\x81t\x81u\x81v\x81w\x81x\x81y\x81z\x81{\x81|\x81}\x81~";
+const expectedString = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\xa2\xe3\x81@\x81A\x81B\x81C\x81D\x81E\x81F\x81G\x81H\x81I\x81J\x81K\x81L\x81M\x81N\x81O\x81P\x81Q\x81R\x81S\x81T\x81U\x81V\x81W\x81X\x81Y\x81Z\x81[\x81\\\x81]\x81^\x81_\x81`\x81a\x81b\x81c\x81d\x81e\x81f\x81g\x81h\x81i\x81j\x81k\x81l\x81m\x81n\x81o\x81p\x81q\x81r\x81s\x81t\x81u\x81v\x81w\x81x\x81y\x81z\x81{\x81|\x81}\x81~\x84\x31\xa4\x37";
     
-const aliases = [ "gbk", "x-gbk" ];
+const aliases = [ "gb18030" ];
 
 function run_test() {
   testEncodeAliases();
 }
--- a/intl/uconv/tests/unit/test_encode_gbk.js
+++ b/intl/uconv/tests/unit/test_encode_gbk.js
@@ -1,15 +1,15 @@
 // Tests conversion from Unicode to gbk
 // This is a sniff test which doesn't cover the full gbk range: the test string
 // includes only the ASCII range and the first 63 double byte characters
 
 load('CharsetConversionTests.js');
 	
-const inString = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u4E02\u4E04\u4E05\u4E06\u4E0F\u4E12\u4E17\u4E1F\u4E20\u4E21\u4E23\u4E26\u4E29\u4E2E\u4E2F\u4E31\u4E33\u4E35\u4E37\u4E3C\u4E40\u4E41\u4E42\u4E44\u4E46\u4E4A\u4E51\u4E55\u4E57\u4E5A\u4E5B\u4E62\u4E63\u4E64\u4E65\u4E67\u4E68\u4E6A\u4E6B\u4E6C\u4E6D\u4E6E\u4E6F\u4E72\u4E74\u4E75\u4E76\u4E77\u4E78\u4E79\u4E7A\u4E7B\u4E7C\u4E7D\u4E7F\u4E80\u4E81\u4E82\u4E83\u4E84\u4E85\u4E87\u4E8A";
+const inString = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u20AC\u4E02\u4E04\u4E05\u4E06\u4E0F\u4E12\u4E17\u4E1F\u4E20\u4E21\u4E23\u4E26\u4E29\u4E2E\u4E2F\u4E31\u4E33\u4E35\u4E37\u4E3C\u4E40\u4E41\u4E42\u4E44\u4E46\u4E4A\u4E51\u4E55\u4E57\u4E5A\u4E5B\u4E62\u4E63\u4E64\u4E65\u4E67\u4E68\u4E6A\u4E6B\u4E6C\u4E6D\u4E6E\u4E6F\u4E72\u4E74\u4E75\u4E76\u4E77\u4E78\u4E79\u4E7A\u4E7B\u4E7C\u4E7D\u4E7F\u4E80\u4E81\u4E82\u4E83\u4E84\u4E85\u4E87\u4E8A\uFFFD";
 
-const expectedString = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x81@\x81A\x81B\x81C\x81D\x81E\x81F\x81G\x81H\x81I\x81J\x81K\x81L\x81M\x81N\x81O\x81P\x81Q\x81R\x81S\x81T\x81U\x81V\x81W\x81X\x81Y\x81Z\x81[\x81\\\x81]\x81^\x81_\x81`\x81a\x81b\x81c\x81d\x81e\x81f\x81g\x81h\x81i\x81j\x81k\x81l\x81m\x81n\x81o\x81p\x81q\x81r\x81s\x81t\x81u\x81v\x81w\x81x\x81y\x81z\x81{\x81|\x81}\x81~";
+const expectedString = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x80\x81@\x81A\x81B\x81C\x81D\x81E\x81F\x81G\x81H\x81I\x81J\x81K\x81L\x81M\x81N\x81O\x81P\x81Q\x81R\x81S\x81T\x81U\x81V\x81W\x81X\x81Y\x81Z\x81[\x81\\\x81]\x81^\x81_\x81`\x81a\x81b\x81c\x81d\x81e\x81f\x81g\x81h\x81i\x81j\x81k\x81l\x81m\x81n\x81o\x81p\x81q\x81r\x81s\x81t\x81u\x81v\x81w\x81x\x81y\x81z\x81{\x81|\x81}\x81~";
     
 const aliases = [ "gbk", "x-gbk" ];
 
 function run_test() {
   testEncodeAliases();
 }
--- a/intl/uconv/tests/unit/xpcshell.ini
+++ b/intl/uconv/tests/unit/xpcshell.ini
@@ -96,16 +96,17 @@ support-files =
 [test_encode_CP1252.js]
 [test_encode_CP1253.js]
 [test_encode_CP1254.js]
 [test_encode_CP1255.js]
 [test_encode_CP1256.js]
 [test_encode_CP1257.js]
 [test_encode_CP1258.js]
 [test_encode_CP874.js]
+[test_encode_gb18030.js]
 [test_encode_gbk.js]
 [test_encode_x_mac_arabic.js]
 [test_encode_x_mac_ce.js]
 [test_encode_x_mac_croatian.js]
 [test_encode_x_mac_cyrillic.js]
 [test_encode_x_mac_devanagari.js]
 [test_encode_x_mac_farsi.js]
 [test_encode_x_mac_greek.js]
--- a/intl/uconv/tools/gengb18030tables.pl
+++ b/intl/uconv/tools/gengb18030tables.pl
@@ -196,21 +196,21 @@ sub printgb18030table()
 sub genufut()
 {
  print ( "umaptable -uf < gb18030uniq.txt > gb18030uniq2b.uf\n");
  system( "umaptable -uf < gb18030uniq.txt > gb18030uniq2b.uf");
 
  print ( "umaptable -ut < gb18030uniq.txt > gb18030uniq2b.ut\n");
  system( "umaptable -ut < gb18030uniq.txt > gb18030uniq2b.ut");
 
- print ( "umaptable -uf < cp936uniq.txt > gbkuniq2b.uf\n") ;
- system( "umaptable -uf < cp936uniq.txt > gbkuniq2b.uf") ;
+ print ( "umaptable -uf < cp936uniq.txt > gbkuniq.uf\n") ;
+ system( "umaptable -uf < cp936uniq.txt > gbkuniq.uf") ;
 
- print ( "umaptable -ut < cp936uniq.txt > gbkuniq2b.ut\n") ;
- system( "umaptable -ut < cp936uniq.txt > gbkuniq2b.ut") ;
+ print ( "umaptable -ut < cp936uniq.txt > gbkuniq.ut\n") ;
+ system( "umaptable -ut < cp936uniq.txt > gbkuniq.ut") ;
 
  print ( "umaptable -uf < gb180304b.txt > gb180304bytes.uf\n")  ;
  system( "umaptable -uf < gb180304b.txt > gb180304bytes.uf")  ;
 
  print ( "umaptable -ut < gb180304b.txt > gb180304bytes.ut\n")  ;
  system( "umaptable -ut < gb180304b.txt > gb180304bytes.ut")  ;
 
  print ( "perl cp936tocdx.pl > cp936map.h\n");
rename from intl/uconv/ucvcn/gbkuniq2b.uf
rename to intl/uconv/ucvcn/gbkuniq.uf
--- a/intl/uconv/ucvcn/nsGBKConvUtil.cpp
+++ b/intl/uconv/ucvcn/nsGBKConvUtil.cpp
@@ -35,16 +35,18 @@ bool nsGBKConvUtil::UnicodeToGBKChar(
     if(item != 0) 
     {
       *aOutByte1 = item >> 8;
       *aOutByte2 = item & 0x00FF;
       found = true;
     } else {
       return false;
     }
+  } else if (aChar == UCS2_NO_MAPPING) {
+    return false;
   } else {
     // ugly linear search
     for( int32_t i = 0; i < MAX_GBK_LENGTH; i++ )
     {
       if( aChar == gGBKToUnicodeTable[i])
       {
         *aOutByte1 = (i /  0x00BF + 0x0081) ;
         *aOutByte2 = (i %  0x00BF + 0x0040) ;
--- a/intl/uconv/ucvcn/nsUnicodeToGBK.cpp
+++ b/intl/uconv/ucvcn/nsUnicodeToGBK.cpp
@@ -49,27 +49,27 @@ class nsUnicodeTo4BytesGB18030 : public 
 public: 
   nsUnicodeTo4BytesGB18030()
     : nsTableEncoderSupport(u4BytesGB18030Charset, 
                              (uMappingTable*) &g_uf_gb18030_4bytes, 4) {}
 protected: 
 };
 //-----------------------------------------------------------------------
 //  Private class used by nsUnicodeToGBK
-//    nsUnicodeToGBKUniq2Bytes
+//    nsUnicodeToGBKUniq
 //-----------------------------------------------------------------------
-static const uint16_t g_uf_gbk_2bytes[] = {
-#include "gbkuniq2b.uf"
+static const uint16_t g_uf_gbk[] = {
+#include "gbkuniq.uf"
 };
-class nsUnicodeToGBKUniq2Bytes : public nsTableEncoderSupport
+class nsUnicodeToGBKUniq : public nsTableEncoderSupport
 {
 public: 
-  nsUnicodeToGBKUniq2Bytes()
-    : nsTableEncoderSupport(u2BytesCharset, 
-                             (uMappingTable*) &g_uf_gbk_2bytes, 2) {}
+  nsUnicodeToGBKUniq()
+    : nsTableEncoderSupport(u1ByteCharset,
+                             (uMappingTable*) &g_uf_gbk, 1) {}
 protected: 
 };
 //-----------------------------------------------------------------------
 //  nsUnicodeToGB18030
 //-----------------------------------------------------------------------
 void nsUnicodeToGB18030::CreateExtensionEncoder()
 {
   mExtensionEncoder = new nsUnicodeToGB18030Uniq2Bytes();
@@ -111,17 +111,17 @@ nsUnicodeToGBK::nsUnicodeToGBK(uint32_t 
   nsEncoderSupport(aMaxLength)
 {
   mExtensionEncoder = nullptr;
   m4BytesEncoder = nullptr;
   mSurrogateHigh = 0;
 }
 void nsUnicodeToGBK::CreateExtensionEncoder()
 {
-  mExtensionEncoder = new nsUnicodeToGBKUniq2Bytes();
+  mExtensionEncoder = new nsUnicodeToGBKUniq();
 }
 void nsUnicodeToGBK::Create4BytesEncoder()
 {
   m4BytesEncoder = nullptr;
 }
 bool nsUnicodeToGBK::TryExtensionEncoder(
   char16_t aChar,
   char* aOut,
--- a/js/public/Class.h
+++ b/js/public/Class.h
@@ -137,16 +137,18 @@ class ObjectOpResult
     JS_PUBLIC_API(bool) failGetterOnly();
     JS_PUBLIC_API(bool) failCantDelete();
 
     JS_PUBLIC_API(bool) failCantSetInterposed();
     JS_PUBLIC_API(bool) failCantDefineWindowElement();
     JS_PUBLIC_API(bool) failCantDeleteWindowElement();
     JS_PUBLIC_API(bool) failCantDeleteWindowNamedProperty();
     JS_PUBLIC_API(bool) failCantPreventExtensions();
+    JS_PUBLIC_API(bool) failNoNamedSetter();
+    JS_PUBLIC_API(bool) failNoIndexedSetter();
 
     uint32_t failureCode() const {
         MOZ_ASSERT(!ok());
         return uint32_t(code_);
     }
 
     /*
      * Report an error or warning if necessary; return true to proceed and
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -838,20 +838,16 @@ HasChild(JSContext* cx, unsigned argc, j
     }
 
     HasChildTracer trc(cx->runtime(), child);
     TraceChildren(&trc, parent.toGCThing(), parent.traceKind());
     args.rval().setBoolean(trc.found());
     return true;
 }
 
-// Stolen from jsmath.cpp
-static const uint64_t RNG_MULTIPLIER = 0x5DEECE66DLL;
-static const uint64_t RNG_MASK = (1LL << 48) - 1;
-
 static bool
 SetSavedStacksRNGState(JSContext* cx, unsigned argc, jsval* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     if (!args.requireAtLeast(cx, "setSavedStacksRNGState", 1))
         return false;
 
     int32_t seed;
--- a/js/src/devtools/automation/autospider.sh
+++ b/js/src/devtools/automation/autospider.sh
@@ -86,18 +86,36 @@ elif [ "$OSTYPE" = "linux-gnu" ]; then
   if [ -n "$AUTOMATION" ]; then
       GCCDIR="${GCCDIR:-/tools/gcc-4.7.2-0moz1}"
       CONFIGURE_ARGS="$CONFIGURE_ARGS --with-ccache"
   fi
   UNAME_M=$(uname -m)
   MAKEFLAGS=-j4
   if [ "$VARIANT" = "arm-sim" ]; then
     USE_64BIT=false
-  elif [ "$UNAME_M" = "x86_64" ]; then
-    USE_64BIT=true
+  else
+    case "$platform" in
+    linux64)
+      USE_64BIT=true
+      ;;
+    linux64-debug)
+      USE_64BIT=true
+      ;;
+    linux)
+      USE_64BIT=false
+      ;;
+    linux-debug)
+      USE_64BIT=false
+      ;;
+    *)
+      if [ "$UNAME_M" = "x86_64" ]; then
+        USE_64BIT=true
+      fi
+      ;;
+    esac
   fi
 
   if [ "$UNAME_M" != "arm" ] && [ -n "$AUTOMATION" ]; then
     export CC=$GCCDIR/bin/gcc
     export CXX=$GCCDIR/bin/g++
     if $USE_64BIT; then
       export LD_LIBRARY_PATH=$GCCDIR/lib64
     else
@@ -126,16 +144,21 @@ if $USE_64BIT; then
   fi
 else
   NSPR64=""
   if [ "$OSTYPE" != "msys" ]; then
     export CC="${CC:-/usr/bin/gcc} -m32"
     export CXX="${CXX:-/usr/bin/g++} -m32"
     export AR=ar
   fi
+  if [ "$OSTYPE" = "linux-gnu" ]; then
+    if [ "$UNAME_M" != "arm" ] && [ -n "$AUTOMATION" ]; then
+      CONFIGURE_ARGS="$CONFIGURE_ARGS --target=i686-pc-linux --host=i686-pc-linux"
+    fi
+  fi
 fi
 
 $SOURCE/js/src/configure $CONFIGURE_ARGS --enable-nspr-build --prefix=$OBJDIR/dist || exit 2
 $MAKE -s -w -j4 || exit 2
 cp -p $SOURCE/build/unix/run-mozilla.sh $OBJDIR/dist/bin
 
 COMMAND_PREFIX=''
 
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -1993,42 +1993,38 @@ BytecodeEmitter::checkSideEffects(ParseN
          * We can't easily prove that neither operand ever denotes an
          * object with a toString or valueOf method.
          */
         *answer = true;
         return true;
 
       case PN_UNARY:
         switch (pn->getKind()) {
-          case PNK_DELETE:
-          {
-            ParseNode* pn2 = pn->pn_kid;
-            switch (pn2->getKind()) {
-              case PNK_NAME:
-                if (!bindNameToSlot(pn2))
-                    return false;
-                if (pn2->isConst()) {
-                    MOZ_ASSERT(*answer == false);
-                    return true;
-                }
-                /* FALL THROUGH */
-              case PNK_DOT:
-              case PNK_CALL:
-              case PNK_ELEM:
-              case PNK_SUPERELEM:
-                /* All these delete addressing modes have effects too. */
-                *answer = true;
-                return true;
-              default:
-                return checkSideEffects(pn2, answer);
-            }
-            MOZ_CRASH("We have a returning default case");
+          case PNK_DELETENAME: {
+            ParseNode* nameExpr = pn->pn_kid;
+            MOZ_ASSERT(nameExpr->isKind(PNK_NAME));
+            if (!bindNameToSlot(nameExpr))
+                return false;
+            *answer = !nameExpr->isConst();
+            return true;
           }
 
-          case PNK_TYPEOF:
+          case PNK_DELETEPROP:
+          case PNK_DELETESUPERPROP:
+          case PNK_DELETEELEM:
+          case PNK_DELETESUPERELEM:
+            // All these delete addressing modes have effects, too.
+            *answer = true;
+            return true;
+
+          case PNK_DELETEEXPR:
+            return checkSideEffects(pn->pn_kid, answer);
+
+          case PNK_TYPEOFNAME:
+          case PNK_TYPEOFEXPR:
           case PNK_VOID:
           case PNK_NOT:
           case PNK_BITNOT:
             if (pn->isOp(JSOP_NOT)) {
                 /* ! does not convert its operand via toString or valueOf. */
                 return checkSideEffects(pn->pn_kid, answer);
             }
             /* FALL THROUGH */
@@ -6152,92 +6148,120 @@ BytecodeEmitter::emitStatement(ParseNode
                 return false;
         }
     }
 
     return true;
 }
 
 bool
-BytecodeEmitter::emitDelete(ParseNode* pn)
-{
-    /*
-     * Under ECMA 3, deleting a non-reference returns true -- but alas we
-     * must evaluate the operand if it appears it might have side effects.
-     */
-    ParseNode* pn2 = pn->pn_kid;
-    switch (pn2->getKind()) {
-      case PNK_NAME:
-        if (!bindNameToSlot(pn2))
-            return false;
-        if (!emitAtomOp(pn2, pn2->getOp()))
-            return false;
-        break;
-      case PNK_DOT:
-      {
-        JSOp delOp = sc->strict() ? JSOP_STRICTDELPROP : JSOP_DELPROP;
-        if (!emitPropOp(pn2, delOp))
-            return false;
-        break;
-      }
-      case PNK_SUPERPROP:
-        // Still have to calculate the base, even though we are are going
-        // to throw unconditionally, as calculating the base could also
-        // throw.
-        if (!emit1(JSOP_SUPERBASE))
-            return false;
-        if (!emitUint16Operand(JSOP_THROWMSG, JSMSG_CANT_DELETE_SUPER))
-            return false;
-        break;
-      case PNK_ELEM:
-      {
-        JSOp delOp = sc->strict() ? JSOP_STRICTDELELEM : JSOP_DELELEM;
-        if (!emitElemOp(pn2, delOp))
-            return false;
-        break;
-      }
-      case PNK_SUPERELEM:
-        // Still have to calculate everything, even though we're gonna throw
-        // since it may have side effects
-        if (!emitTree(pn2->pn_kid))
-            return false;
-        if (!emit1(JSOP_SUPERBASE))
-            return false;
-        if (!emitUint16Operand(JSOP_THROWMSG, JSMSG_CANT_DELETE_SUPER))
-            return false;
-
-        // Another wrinkle: Balance the stack from the emitter's point of view.
-        // Execution will not reach here, as the last bytecode threw.
+BytecodeEmitter::emitDeleteName(ParseNode* node)
+{
+    MOZ_ASSERT(node->isKind(PNK_DELETENAME));
+    MOZ_ASSERT(node->isArity(PN_UNARY));
+
+    ParseNode* nameExpr = node->pn_kid;
+    MOZ_ASSERT(nameExpr->isKind(PNK_NAME));
+
+    if (!bindNameToSlot(nameExpr))
+        return false;
+
+    MOZ_ASSERT(nameExpr->isOp(JSOP_DELNAME));
+    return emitAtomOp(nameExpr, JSOP_DELNAME);
+}
+
+bool
+BytecodeEmitter::emitDeleteProperty(ParseNode* node)
+{
+    MOZ_ASSERT(node->isKind(PNK_DELETEPROP));
+    MOZ_ASSERT(node->isArity(PN_UNARY));
+
+    ParseNode* propExpr = node->pn_kid;
+    MOZ_ASSERT(propExpr->isKind(PNK_DOT));
+
+    JSOp delOp = sc->strict() ? JSOP_STRICTDELPROP : JSOP_DELPROP;
+    return emitPropOp(propExpr, delOp);
+}
+
+bool
+BytecodeEmitter::emitDeleteSuperProperty(ParseNode* node)
+{
+    MOZ_ASSERT(node->isKind(PNK_DELETESUPERPROP));
+    MOZ_ASSERT(node->isArity(PN_UNARY));
+    MOZ_ASSERT(node->pn_kid->isKind(PNK_SUPERPROP));
+
+    // Still have to calculate the base, even though we are are going
+    // to throw unconditionally, as calculating the base could also
+    // throw.
+    if (!emit1(JSOP_SUPERBASE))
+        return false;
+
+    return emitUint16Operand(JSOP_THROWMSG, JSMSG_CANT_DELETE_SUPER);
+}
+
+bool
+BytecodeEmitter::emitDeleteElement(ParseNode* node)
+{
+    MOZ_ASSERT(node->isKind(PNK_DELETEELEM));
+    MOZ_ASSERT(node->isArity(PN_UNARY));
+
+    ParseNode* elemExpr = node->pn_kid;
+    MOZ_ASSERT(elemExpr->isKind(PNK_ELEM));
+
+    JSOp delOp = sc->strict() ? JSOP_STRICTDELELEM : JSOP_DELELEM;
+    return emitElemOp(elemExpr, delOp);
+}
+
+bool
+BytecodeEmitter::emitDeleteSuperElement(ParseNode* node)
+{
+    MOZ_ASSERT(node->isKind(PNK_DELETESUPERELEM));
+    MOZ_ASSERT(node->isArity(PN_UNARY));
+
+    ParseNode* superElemExpr = node->pn_kid;
+    MOZ_ASSERT(superElemExpr->isKind(PNK_SUPERELEM));
+
+    // Still have to calculate everything, even though we're gonna throw
+    // since it may have side effects
+    MOZ_ASSERT(superElemExpr->isArity(PN_UNARY));
+    if (!emitTree(superElemExpr->pn_kid))
+        return false;
+    if (!emit1(JSOP_SUPERBASE))
+        return false;
+    if (!emitUint16Operand(JSOP_THROWMSG, JSMSG_CANT_DELETE_SUPER))
+        return false;
+
+    // Another wrinkle: Balance the stack from the emitter's point of view.
+    // Execution will not reach here, as the last bytecode threw.
+    return emit1(JSOP_POP);
+}
+
+bool
+BytecodeEmitter::emitDeleteExpression(ParseNode* node)
+{
+    MOZ_ASSERT(node->isKind(PNK_DELETEEXPR));
+    MOZ_ASSERT(node->isArity(PN_UNARY));
+
+    ParseNode* expression = node->pn_kid;
+
+    // If useless, just emit JSOP_TRUE; otherwise convert |delete <expr>| to
+    // effectively |<expr>, true|.
+    bool useful = false;
+    if (!checkSideEffects(expression, &useful))
+        return false;
+
+    if (useful) {
+        MOZ_ASSERT_IF(expression->isKind(PNK_CALL), !(expression->pn_xflags & PNX_SETCALL));
+        if (!emitTree(expression))
+            return false;
         if (!emit1(JSOP_POP))
             return false;
-        break;
-      default:
-      {
-        /*
-         * If useless, just emit JSOP_TRUE; otherwise convert delete foo()
-         * to foo(), true (a comma expression).
-         */
-        bool useful = false;
-        if (!checkSideEffects(pn2, &useful))
-            return false;
-
-        if (useful) {
-            MOZ_ASSERT_IF(pn2->isKind(PNK_CALL), !(pn2->pn_xflags & PNX_SETCALL));
-            if (!emitTree(pn2))
-                return false;
-            if (!emit1(JSOP_POP))
-                return false;
-        }
-
-        if (!emit1(JSOP_TRUE))
-            return false;
-      }
-    }
-
-    return true;
+    }
+
+    return emit1(JSOP_TRUE);
 }
 
 bool
 BytecodeEmitter::emitSelfHostedCallFunction(ParseNode* pn)
 {
     // Special-casing of callFunction to emit bytecode that directly
     // invokes the callee with the correct |this| object and arguments.
     // callFunction(fun, thisArg, arg0, arg1) thus becomes:
@@ -6959,22 +6983,36 @@ BytecodeEmitter::emitUnary(ParseNode* pn
 {
     if (!updateSourceCoordNotes(pn->pn_pos.begin))
         return false;
 
     /* Unary op, including unary +/-. */
     JSOp op = pn->getOp();
     ParseNode* pn2 = pn->pn_kid;
 
-    if (op == JSOP_TYPEOF && !pn2->isKind(PNK_NAME))
-        op = JSOP_TYPEOFEXPR;
+    bool oldEmittingForInit = emittingForInit;
+    emittingForInit = false;
+    if (!emitTree(pn2))
+        return false;
+
+    emittingForInit = oldEmittingForInit;
+    return emit1(op);
+}
+
+bool
+BytecodeEmitter::emitTypeof(ParseNode* node, JSOp op)
+{
+    MOZ_ASSERT(op == JSOP_TYPEOF || op == JSOP_TYPEOFEXPR);
+
+    if (!updateSourceCoordNotes(node->pn_pos.begin))
+        return false;
 
     bool oldEmittingForInit = emittingForInit;
     emittingForInit = false;
-    if (!emitTree(pn2))
+    if (!emitTree(node->pn_kid))
         return false;
 
     emittingForInit = oldEmittingForInit;
     return emit1(op);
 }
 
 bool
 BytecodeEmitter::emitDefaultsAndDestructuring(ParseNode* pn)
@@ -7439,35 +7477,62 @@ BytecodeEmitter::emitTree(ParseNode* pn)
             if (!emitTree(subexpr))
                 return false;
             if (!emit1(op))
                 return false;
         }
         break;
       }
 
+      case PNK_TYPEOFNAME:
+        ok = emitTypeof(pn, JSOP_TYPEOF);
+        break;
+
+      case PNK_TYPEOFEXPR:
+        ok = emitTypeof(pn, JSOP_TYPEOFEXPR);
+        break;
+
       case PNK_THROW:
-      case PNK_TYPEOF:
       case PNK_VOID:
       case PNK_NOT:
       case PNK_BITNOT:
       case PNK_POS:
       case PNK_NEG:
         ok = emitUnary(pn);
         break;
 
       case PNK_PREINCREMENT:
       case PNK_PREDECREMENT:
       case PNK_POSTINCREMENT:
       case PNK_POSTDECREMENT:
         ok = emitIncOrDec(pn);
         break;
 
-      case PNK_DELETE:
-        ok = emitDelete(pn);
+      case PNK_DELETENAME:
+        ok = emitDeleteName(pn);
+        break;
+
+      case PNK_DELETEPROP:
+        ok = emitDeleteProperty(pn);
+        break;
+
+      case PNK_DELETESUPERPROP:
+        ok = emitDeleteSuperProperty(pn);
+        break;
+
+      case PNK_DELETEELEM:
+        ok = emitDeleteElement(pn);
+        break;
+
+      case PNK_DELETESUPERELEM:
+        ok = emitDeleteSuperElement(pn);
+        break;
+
+      case PNK_DELETEEXPR:
+        ok = emitDeleteExpression(pn);
         break;
 
       case PNK_DOT:
         ok = emitPropOp(pn, JSOP_GETPROP);
         break;
 
       case PNK_SUPERPROP:
         if (!emitSuperPropOp(pn, JSOP_GETPROP_SUPER))
@@ -7500,16 +7565,17 @@ BytecodeEmitter::emitTree(ParseNode* pn)
 
       case PNK_CONST:
       case PNK_LET:
         ok = emitVariables(pn, InitializeVars);
         break;
 
       case PNK_IMPORT:
       case PNK_EXPORT:
+      case PNK_EXPORT_DEFAULT:
       case PNK_EXPORT_FROM:
        // TODO: Implement emitter support for modules
        reportError(nullptr, JSMSG_MODULES_NOT_IMPLEMENTED);
        return false;
 
       case PNK_ARRAYPUSH: {
         /*
          * The array object's stack index is in arrayCompDepth. See below
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -540,17 +540,26 @@ struct BytecodeEmitter
     bool emitCallSiteObject(ParseNode* pn);
     bool emitTemplateString(ParseNode* pn);
     bool emitAssignment(ParseNode* lhs, JSOp op, ParseNode* rhs);
 
     bool emitReturn(ParseNode* pn);
     bool emitStatement(ParseNode* pn);
     bool emitStatementList(ParseNode* pn, ptrdiff_t top);
 
-    bool emitDelete(ParseNode* pn);
+    bool emitDeleteName(ParseNode* pn);
+    bool emitDeleteProperty(ParseNode* pn);
+    bool emitDeleteSuperProperty(ParseNode* pn);
+    bool emitDeleteElement(ParseNode* pn);
+    bool emitDeleteSuperElement(ParseNode* pn);
+    bool emitDeleteExpression(ParseNode* pn);
+
+    // |op| must be JSOP_TYPEOF or JSOP_TYPEOFEXPR.
+    bool emitTypeof(ParseNode* node, JSOp op);
+
     bool emitLogical(ParseNode* pn);
     bool emitUnary(ParseNode* pn);
 
     MOZ_NEVER_INLINE bool emitIncOrDec(ParseNode* pn);
 
     bool emitConditionalExpression(ConditionalExpression& conditional);
 
     bool emitCallOrNew(ParseNode* pn);
--- a/js/src/frontend/FoldConstants.cpp
+++ b/js/src/frontend/FoldConstants.cpp
@@ -316,21 +316,27 @@ ContainsHoistedDeclaration(ExclusiveCont
       // method, because some parent component should have asserted itself.
       case PNK_OBJECT_PROPERTY_NAME:
       case PNK_COMPUTED_NAME:
       case PNK_SPREAD:
       case PNK_MUTATEPROTO:
       case PNK_COLON:
       case PNK_SHORTHAND:
       case PNK_CONDITIONAL:
-      case PNK_TYPEOF:
+      case PNK_TYPEOFNAME:
+      case PNK_TYPEOFEXPR:
       case PNK_VOID:
       case PNK_NOT:
       case PNK_BITNOT:
-      case PNK_DELETE:
+      case PNK_DELETENAME:
+      case PNK_DELETEPROP:
+      case PNK_DELETESUPERPROP:
+      case PNK_DELETEELEM:
+      case PNK_DELETESUPERELEM:
+      case PNK_DELETEEXPR:
       case PNK_POS:
       case PNK_NEG:
       case PNK_PREINCREMENT:
       case PNK_POSTINCREMENT:
       case PNK_PREDECREMENT:
       case PNK_POSTDECREMENT:
       case PNK_OR:
       case PNK_AND:
@@ -695,33 +701,21 @@ Fold(ExclusiveContext* cx, ParseNode** p
                     return false;
             }
         }
         pn1 = pn->pn_left;
         pn2 = pn->pn_right;
         break;
 
       case PN_UNARY:
-        /*
-         * Kludge to deal with typeof expressions: because constant folding
-         * can turn an expression into a name node, we have to check here,
-         * before folding, to see if we should throw undefined name errors.
-         *
-         * NB: We know that if pn->pn_op is JSOP_TYPEOF, pn1 will not be
-         * null. This assumption does not hold true for other unary
-         * expressions.
-         */
-        if (pn->isKind(PNK_TYPEOF) && !pn->pn_kid->isKind(PNK_NAME))
-            pn->setOp(JSOP_TYPEOFEXPR);
-
         if (pn->pn_kid) {
             SyntacticContext kidsc =
                 pn->isKind(PNK_NOT)
                 ? SyntacticContext::Condition
-                : pn->isKind(PNK_DELETE)
+                : IsDeleteKind(pn->getKind())
                 ? SyntacticContext::Delete
                 : SyntacticContext::Other;
             if (!Fold(cx, &pn->pn_kid, handler, options, inGenexpLambda, kidsc))
                 return false;
         }
         pn1 = pn->pn_kid;
         break;
 
@@ -741,17 +735,17 @@ Fold(ExclusiveContext* cx, ParseNode** p
             pn1 = *lhsp;
         }
         break;
 
       case PN_NULLARY:
         break;
     }
 
-    // The immediate child of a PNK_DELETE node should not be replaced
+    // The immediate child of a PNK_DELETE* node should not be replaced
     // with node indicating a different syntactic form; |delete x| is not
     // the same as |delete (true && x)|. See bug 888002.
     //
     // pn is the immediate child in question. Its descendants were already
     // constant-folded above, so we're done.
     if (sc == SyntacticContext::Delete)
         return true;
 
@@ -991,17 +985,18 @@ Fold(ExclusiveContext* cx, ParseNode** p
             while ((pn2 = pn3) != nullptr) {
                 pn3 = pn2->pn_next;
                 if (!FoldBinaryNumeric(cx, op, pn, pn2, pn))
                     return false;
             }
         }
         break;
 
-      case PNK_TYPEOF:
+      case PNK_TYPEOFNAME:
+      case PNK_TYPEOFEXPR:
       case PNK_VOID:
       case PNK_NOT:
       case PNK_BITNOT:
       case PNK_POS:
       case PNK_NEG:
         if (pn1->isKind(PNK_NUMBER)) {
             double d;
 
@@ -1095,17 +1090,17 @@ Fold(ExclusiveContext* cx, ParseNode** p
 
             // Supposing we're replacing |obj["prop"]| with |obj.prop|, we now
             // can free the |"prop"| node and |obj["prop"]| nodes -- but not
             // the |obj| node now a sub-node of |expr|.  Mutate |pn| into a
             // node with |"prop"| as its child so that our |pn| doesn't have a
             // necessarily-weird structure (say, by nulling out |pn->pn_left|
             // only) that would fail AST sanity assertions performed by
             // |handler.freeTree(pn)|.
-            pn->setKind(PNK_TYPEOF);
+            pn->setKind(PNK_TYPEOFEXPR);
             pn->setArity(PN_UNARY);
             pn->pn_kid = pn2;
             handler.freeTree(pn);
 
             pn = expr;
         }
         break;
       }
--- a/js/src/frontend/FullParseHandler.h
+++ b/js/src/frontend/FullParseHandler.h
@@ -67,33 +67,50 @@ class FullParseHandler
     Parser<SyntaxParseHandler>* syntaxParser;
 
     /* new_ methods for creating parse nodes. These report OOM on context. */
     JS_DECLARE_NEW_METHODS(new_, allocParseNode, inline)
 
     typedef ParseNode* Node;
     typedef Definition* DefinitionNode;
 
+    bool isPropertyAccess(ParseNode* node) {
+        if (node->isKind(PNK_DOT) || node->isKind(PNK_ELEM))
+            return true;
+        return node->isKind(PNK_SUPERPROP) || node->isKind(PNK_SUPERELEM);
+    }
+
+    bool isFunctionCall(ParseNode* node) {
+        // Note: super() is a special form, *not* a function call.
+        return node->isKind(PNK_CALL);
+    }
+
+    bool isDestructuringTarget(ParseNode* node) {
+        return node->isKind(PNK_OBJECT) || node->isKind(PNK_ARRAY);
+    }
+
     FullParseHandler(ExclusiveContext* cx, LifoAlloc& alloc,
                      TokenStream& tokenStream, Parser<SyntaxParseHandler>* syntaxParser,
                      LazyScript* lazyOuterFunction)
       : allocator(cx, alloc),
         tokenStream(tokenStream),
         lazyOuterFunction_(lazyOuterFunction),
         lazyInnerFunctionIndex(0),
         syntaxParser(syntaxParser)
     {}
 
     static ParseNode* null() { return nullptr; }
 
     ParseNode* freeTree(ParseNode* pn) { return allocator.freeTree(pn); }
     void prepareNodeForMutation(ParseNode* pn) { return allocator.prepareNodeForMutation(pn); }
     const Token& currentToken() { return tokenStream.currentToken(); }
 
-    ParseNode* newName(PropertyName* name, uint32_t blockid, const TokenPos& pos) {
+    ParseNode* newName(PropertyName* name, uint32_t blockid, const TokenPos& pos,
+                       ExclusiveContext* cx)
+    {
         return new_<NameNode>(PNK_NAME, JSOP_GETNAME, name, blockid, pos);
     }
 
     ParseNode* newComputedName(ParseNode* expr, uint32_t begin, uint32_t end) {
         TokenPos pos(begin, end);
         return new_<UnaryNode>(PNK_COMPUTED_NAME, JSOP_NOP, pos, expr);
     }
 
@@ -182,21 +199,39 @@ class FullParseHandler
         return new_<ConditionalExpression>(cond, thenExpr, elseExpr);
     }
 
     void markAsSetCall(ParseNode* pn) {
         pn->pn_xflags |= PNX_SETCALL;
     }
 
     ParseNode* newDelete(uint32_t begin, ParseNode* expr) {
-        if (expr->getKind() == PNK_NAME) {
+        if (expr->isKind(PNK_NAME)) {
             expr->pn_dflags |= PND_DEOPTIMIZED;
             expr->setOp(JSOP_DELNAME);
+            return newUnary(PNK_DELETENAME, JSOP_NOP, begin, expr);
         }
-        return newUnary(PNK_DELETE, JSOP_NOP, begin, expr);
+
+        if (expr->isKind(PNK_DOT))
+            return newUnary(PNK_DELETEPROP, JSOP_NOP, begin, expr);
+        if (expr->isKind(PNK_SUPERPROP))
+            return newUnary(PNK_DELETESUPERPROP, JSOP_NOP, begin, expr);
+
+        if (expr->isKind(PNK_ELEM))
+            return newUnary(PNK_DELETEELEM, JSOP_NOP, begin, expr);
+        if (expr->isKind(PNK_SUPERELEM))
+            return newUnary(PNK_DELETESUPERELEM, JSOP_NOP, begin, expr);
+
+        return newUnary(PNK_DELETEEXPR, JSOP_NOP, begin, expr);
+    }
+
+    ParseNode* newTypeof(uint32_t begin, ParseNode* kid) {
+        TokenPos pos(begin, kid->pn_pos.end);
+        ParseNodeKind kind = kid->isKind(PNK_NAME) ? PNK_TYPEOFNAME : PNK_TYPEOFEXPR;
+        return new_<UnaryNode>(kind, JSOP_NOP, pos, kid);
     }
 
     ParseNode* newNullary(ParseNodeKind kind, JSOp op, const TokenPos& pos) {
         return new_<NullaryNode>(kind, op, pos);
     }
 
     ParseNode* newUnary(ParseNodeKind kind, JSOp op, uint32_t begin, ParseNode* kid) {
         TokenPos pos(begin, kid ? kid->pn_pos.end : begin + 1);
@@ -271,16 +306,24 @@ class FullParseHandler
     }
 
     void addArrayElement(ParseNode* literal, ParseNode* element) {
         if (!element->isConstant())
             literal->pn_xflags |= PNX_NONCONST;
         literal->append(element);
     }
 
+    ParseNode* newCall() {
+        return newList(PNK_CALL, JSOP_CALL);
+    }
+
+    ParseNode* newTaggedTemplate() {
+        return newList(PNK_TAGGED_TEMPLATE, JSOP_CALL);
+    }
+
     ParseNode* newObjectLiteral(uint32_t begin) {
         ParseNode* literal = new_<ListNode>(PNK_OBJECT, TokenPos(begin, begin + 1));
         // Later in this stack: remove dependency on this opcode.
         if (literal)
             literal->setOp(JSOP_NEWINIT);
         return literal;
     }
 
@@ -722,33 +765,42 @@ class FullParseHandler
     }
     void setPrologue(ParseNode* pn) {
         pn->pn_prologue = true;
     }
 
     bool isConstant(ParseNode* pn) {
         return pn->isConstant();
     }
-    PropertyName* isName(ParseNode* pn) {
+    PropertyName* maybeName(ParseNode* pn) {
         return pn->isKind(PNK_NAME) ? pn->pn_atom->asPropertyName() : nullptr;
     }
     bool isCall(ParseNode* pn) {
         return pn->isKind(PNK_CALL);
     }
-    PropertyName* isGetProp(ParseNode* pn) {
+    PropertyName* maybeDottedProperty(ParseNode* pn) {
         return pn->is<PropertyAccess>() ? &pn->as<PropertyAccess>().name() : nullptr;
     }
     JSAtom* isStringExprStatement(ParseNode* pn, TokenPos* pos) {
         if (JSAtom* atom = pn->isStringExprStatement()) {
             *pos = pn->pn_kid->pn_pos;
             return atom;
         }
         return nullptr;
     }
 
+    void markAsAssigned(ParseNode* node) { node->markAsAssigned(); }
+    void adjustGetToSet(ParseNode* node) {
+        node->setOp(node->isOp(JSOP_GETLOCAL) ? JSOP_SETLOCAL : JSOP_SETNAME);
+    }
+    void maybeDespecializeSet(ParseNode* node) {
+        if (!(js_CodeSpec[node->getOp()].format & JOF_SET))
+            node->setOp(JSOP_SETNAME);
+    }
+
     inline ParseNode* makeAssignment(ParseNode* pn, ParseNode* rhs);
 
     static Definition* getDefinitionNode(Definition* dn) {
         return dn;
     }
     static Definition::Kind getDefinitionKind(Definition* dn) {
         return dn->kind();
     }
--- a/js/src/frontend/NameFunctions.cpp
+++ b/js/src/frontend/NameFunctions.cpp
@@ -372,23 +372,34 @@ class NameResolver
           case PNK_DEBUGGER:
           case PNK_EXPORT_BATCH_SPEC:
           case PNK_FRESHENBLOCK:
           case PNK_SUPERPROP:
           case PNK_OBJECT_PROPERTY_NAME:
             MOZ_ASSERT(cur->isArity(PN_NULLARY));
             break;
 
+          case PNK_TYPEOFNAME:
+            MOZ_ASSERT(cur->isArity(PN_UNARY));
+            MOZ_ASSERT(cur->pn_kid->isKind(PNK_NAME));
+            MOZ_ASSERT(!cur->pn_kid->maybeExpr());
+            break;
+
           // Nodes with a single non-null child requiring name resolution.
-          case PNK_TYPEOF:
+          case PNK_TYPEOFEXPR:
           case PNK_VOID:
           case PNK_NOT:
           case PNK_BITNOT:
           case PNK_THROW:
-          case PNK_DELETE:
+          case PNK_DELETENAME:
+          case PNK_DELETEPROP:
+          case PNK_DELETESUPERPROP:
+          case PNK_DELETEELEM:
+          case PNK_DELETESUPERELEM:
+          case PNK_DELETEEXPR:
           case PNK_NEG:
           case PNK_POS:
           case PNK_PREINCREMENT:
           case PNK_POSTINCREMENT:
           case PNK_PREDECREMENT:
           case PNK_POSTDECREMENT:
           case PNK_COMPUTED_NAME:
           case PNK_ARRAYPUSH:
--- a/js/src/frontend/ParseNode.cpp
+++ b/js/src/frontend/ParseNode.cpp
@@ -216,22 +216,28 @@ PushNodeChildren(ParseNode* pn, NodeStac
       case PNK_FRESHENBLOCK:
       case PNK_SUPERPROP:
         MOZ_ASSERT(pn->isArity(PN_NULLARY));
         MOZ_ASSERT(!pn->isUsed(), "handle non-trivial cases separately");
         MOZ_ASSERT(!pn->isDefn(), "handle non-trivial cases separately");
         return PushResult::Recyclable;
 
       // Nodes with a single non-null child.
-      case PNK_TYPEOF:
+      case PNK_TYPEOFNAME:
+      case PNK_TYPEOFEXPR:
       case PNK_VOID:
       case PNK_NOT:
       case PNK_BITNOT:
       case PNK_THROW:
-      case PNK_DELETE:
+      case PNK_DELETENAME:
+      case PNK_DELETEPROP:
+      case PNK_DELETESUPERPROP:
+      case PNK_DELETEELEM:
+      case PNK_DELETESUPERELEM:
+      case PNK_DELETEEXPR:
       case PNK_POS:
       case PNK_NEG:
       case PNK_PREINCREMENT:
       case PNK_POSTINCREMENT:
       case PNK_PREDECREMENT:
       case PNK_POSTDECREMENT:
       case PNK_COMPUTED_NAME:
       case PNK_ARRAYPUSH:
--- a/js/src/frontend/ParseNode.h
+++ b/js/src/frontend/ParseNode.h
@@ -113,17 +113,23 @@ class UpvarCookie
     F(BREAK) \
     F(CONTINUE) \
     F(VAR) \
     F(CONST) \
     F(GLOBALCONST) \
     F(WITH) \
     F(RETURN) \
     F(NEW) \
-    F(DELETE) \
+    /* Delete operations.  These must be sequential. */ \
+    F(DELETENAME) \
+    F(DELETEPROP) \
+    F(DELETESUPERPROP) \
+    F(DELETEELEM) \
+    F(DELETESUPERELEM) \
+    F(DELETEEXPR) \
     F(TRY) \
     F(CATCH) \
     F(CATCHLIST) \
     F(THROW) \
     F(DEBUGGER) \
     F(GENERATOR) \
     F(YIELD) \
     F(YIELD_STAR) \
@@ -152,17 +158,18 @@ class UpvarCookie
     F(CLASS) \
     F(CLASSMETHOD) \
     F(CLASSMETHODLIST) \
     F(CLASSNAMES) \
     F(SUPERPROP) \
     F(SUPERELEM) \
     \
     /* Unary operators. */ \
-    F(TYPEOF) \
+    F(TYPEOFNAME) \
+    F(TYPEOFEXPR) \
     F(VOID) \
     F(NOT) \
     F(BITNOT) \
     \
     /* \
      * Binary operators. \
      * These must be in the same order as TOK_OR and friends in TokenStream.h. \
      */ \
@@ -222,16 +229,22 @@ enum ParseNodeKind
 #undef EMIT_ENUM
     PNK_LIMIT, /* domain size */
     PNK_BINOP_FIRST = PNK_OR,
     PNK_BINOP_LAST = PNK_MOD,
     PNK_ASSIGNMENT_START = PNK_ASSIGN,
     PNK_ASSIGNMENT_LAST = PNK_MODASSIGN
 };
 
+inline bool
+IsDeleteKind(ParseNodeKind kind)
+{
+    return PNK_DELETENAME <= kind && kind <= PNK_DELETEEXPR;
+}
+
 /*
  * Label        Variant     Members
  * -----        -------     -------
  * <Definitions>
  * PNK_FUNCTION name        pn_funbox: ptr to js::FunctionBox holding function
  *                            object containing arg and var properties.  We
  *                            create the function object at parse (not emit)
  *                            time to specialize arg and var bytecodes early.
@@ -377,28 +390,38 @@ enum ParseNodeKind
  * PNK_URSH
  * PNK_ADD,     binary      pn_left: left-assoc ADD expr, pn_right: MUL expr
  * PNK_SUB
  * PNK_STAR,    binary      pn_left: left-assoc MUL expr, pn_right: UNARY expr
  * PNK_DIV,                 pn_op: JSOP_MUL, JSOP_DIV, JSOP_MOD
  * PNK_MOD
  * PNK_POS,     unary       pn_kid: UNARY expr
  * PNK_NEG
- * PNK_TYPEOF,  unary       pn_kid: UNARY expr
- * PNK_VOID,
+ * PNK_VOID,    unary       pn_kid: UNARY expr
  * PNK_NOT,
  * PNK_BITNOT
+ * PNK_TYPEOFNAME, unary    pn_kid: UNARY expr
+ * PNK_TYPEOFEXPR
  * PNK_PREINCREMENT, unary  pn_kid: MEMBER expr
  * PNK_POSTINCREMENT,
  * PNK_PREDECREMENT,
  * PNK_POSTDECREMENT
  * PNK_NEW      list        pn_head: list of ctor, arg1, arg2, ... argN
  *                          pn_count: 1 + N (where N is number of args)
  *                          ctor is a MEMBER expr
- * PNK_DELETE   unary       pn_kid: MEMBER expr
+ * PNK_DELETENAME unary     pn_kid: PNK_NAME expr
+ * PNK_DELETEPROP unary     pn_kid: PNK_DOT expr
+ * PNK_DELETESUPERPROP unary pn_kid: PNK_SUPERPROP expr
+ * PNK_DELETEELEM unary     pn_kid: PNK_ELEM expr
+ * PNK_DELETESUPERELEM unary pn_kid: PNK_SUPERELEM expr
+ * PNK_DELETEEXPR unary     pn_kid: MEMBER expr that's evaluated, then the
+ *                          overall delete evaluates to true; can't be a kind
+ *                          for a more-specific PNK_DELETE* unless constant
+ *                          folding (or a similar parse tree manipulation) has
+ *                          occurred
  * PNK_DOT      name        pn_expr: MEMBER expr to left of .
  *                          pn_atom: name to right of .
  * PNK_ELEM     binary      pn_left: MEMBER expr to left of [
  *                          pn_right: expr between [ and ]
  * PNK_CALL     list        pn_head: list of call, arg1, arg2, ... argN
  *                          pn_count: 1 + N (where N is number of args)
  *                          call is a MEMBER expr naming a callable object
  * PNK_GENEXP   list        Exactly like PNK_CALL, used for the implicit call
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -738,42 +738,16 @@ Parser<ParseHandler>::reportBadReturn(No
             return false;
     } else {
         errnum = anonerrnum;
     }
     return report(kind, pc->sc->strict(), pn, errnum, name.ptr());
 }
 
 /*
- * Check that assigning to lhs is permitted.  Assigning to 'eval' or
- * 'arguments' is banned in strict mode.
- */
-template <typename ParseHandler>
-bool
-Parser<ParseHandler>::checkStrictAssignment(Node lhs)
-{
-    if (!pc->sc->needStrictChecks())
-        return true;
-
-    JSAtom* atom = handler.isName(lhs);
-    if (!atom)
-        return true;
-
-    if (atom == context->names().eval || atom == context->names().arguments) {
-        JSAutoByteString name;
-        if (!AtomToPrintableString(context, atom, &name))
-            return false;
-
-        if (!report(ParseStrictError, pc->sc->strict(), lhs, JSMSG_BAD_STRICT_ASSIGN, name.ptr()))
-            return false;
-    }
-    return true;
-}
-
-/*
  * Check that it is permitted to introduce a binding for atom.  Strict mode
  * forbids introducing new definitions for 'eval', 'arguments', or for any
  * strict mode reserved keyword.  Use pn for reporting error locations, or use
  * pc's token stream if pn is nullptr.
  */
 template <typename ParseHandler>
 bool
 Parser<ParseHandler>::checkStrictBinding(PropertyName* name, Node pn)
@@ -3282,30 +3256,30 @@ Parser<ParseHandler>::bindVarOrGlobalCon
             }
         }
     }
 
     parser->handler.linkUseToDef(pn, dn);
     return true;
 }
 
-template <>
+template <typename ParseHandler>
 bool
-Parser<FullParseHandler>::makeSetCall(ParseNode* pn, unsigned msg)
-{
-    MOZ_ASSERT(pn->isKind(PNK_CALL));
-    MOZ_ASSERT(pn->isArity(PN_LIST));
-    MOZ_ASSERT(pn->isOp(JSOP_CALL) || pn->isOp(JSOP_SPREADCALL) ||
-               pn->isOp(JSOP_EVAL) || pn->isOp(JSOP_STRICTEVAL) ||
-               pn->isOp(JSOP_SPREADEVAL) || pn->isOp(JSOP_STRICTSPREADEVAL) ||
-               pn->isOp(JSOP_FUNCALL) || pn->isOp(JSOP_FUNAPPLY));
-
-    if (!report(ParseStrictError, pc->sc->strict(), pn, msg))
+Parser<ParseHandler>::makeSetCall(Node target, unsigned msg)
+{
+    MOZ_ASSERT(handler.isFunctionCall(target));
+
+    // Assignment to function calls is forbidden in ES6.  We're still somewhat
+    // concerned about sites using this in dead code, so forbid it only in
+    // strict mode code (or if the werror option has been set), and otherwise
+    // warn.
+    if (!report(ParseStrictError, pc->sc->strict(), target, msg))
         return false;
-    handler.markAsSetCall(pn);
+
+    handler.markAsSetCall(target);
     return true;
 }
 
 template <typename ParseHandler>
 bool
 Parser<ParseHandler>::noteNameUse(HandlePropertyName name, Node pn)
 {
     /*
@@ -4518,16 +4492,20 @@ Parser<FullParseHandler>::exportDeclarat
             kid = functionStmt(YieldIsKeyword, AllowDefaultName);
             break;
           case TOK_CLASS:
             kid = classDefinition(YieldIsKeyword, ClassStatement, AllowDefaultName);
             break;
           default:
             tokenStream.ungetToken();
             kid = assignExpr(InAllowed, YieldIsKeyword);
+            if (kid) {
+                if (!MatchOrInsertSemicolon(tokenStream))
+                    return null();
+            }
             break;
         }
         if (!kid)
             return null();
 
         return handler.newExportDefaultDeclaration(kid, TokenPos(begin, pos().end));
       }
 
@@ -5128,19 +5106,18 @@ Parser<SyntaxParseHandler>::forStatement
             return null();
     }
     if (isForIn || isForOf) {
         /* Parse the rest of the for/in or for/of head. */
         forStmt.type = isForOf ? STMT_FOR_OF_LOOP : STMT_FOR_IN_LOOP;
 
         /* Check that the left side of the 'in' or 'of' is valid. */
         if (!isForDecl &&
-            lhsNode != SyntaxParseHandler::NodeName &&
-            lhsNode != SyntaxParseHandler::NodeGetProp &&
-            lhsNode != SyntaxParseHandler::NodeLValue)
+            !handler.maybeName(lhsNode) &&
+            !handler.isPropertyAccess(lhsNode))
         {
             JS_ALWAYS_FALSE(abortIfSyntaxParser());
             return null();
         }
 
         if (!simpleForDecl) {
             JS_ALWAYS_FALSE(abortIfSyntaxParser());
             return null();
@@ -6415,84 +6392,66 @@ Parser<ParseHandler>::condExpr1(InHandli
 
     // Advance to the next token; the caller is responsible for interpreting it.
     TokenKind ignored;
     if (!tokenStream.getToken(&ignored))
         return null();
     return handler.newConditional(condition, thenExpr, elseExpr);
 }
 
-template <>
+template <typename ParseHandler>
 bool
-Parser<FullParseHandler>::checkAndMarkAsAssignmentLhs(ParseNode* pn, AssignmentFlavor flavor)
-{
-    switch (pn->getKind()) {
-      case PNK_NAME:
-        if (!checkStrictAssignment(pn))
-            return false;
-        if (flavor == KeyedDestructuringAssignment) {
-            /*
-             * We may be called on a name node that has already been
-             * specialized, in the very weird "for (var [x] = i in o) ..."
-             * case. See bug 558633.
-             */
-            if (!(js_CodeSpec[pn->getOp()].format & JOF_SET))
-                pn->setOp(JSOP_SETNAME);
-        } else {
-            pn->setOp(pn->isOp(JSOP_GETLOCAL) ? JSOP_SETLOCAL : JSOP_SETNAME);
-        }
-        pn->markAsAssigned();
-        break;
-
-      case PNK_DOT:
-      case PNK_ELEM:
-      case PNK_SUPERPROP:
-      case PNK_SUPERELEM:
-        break;
-
-      case PNK_ARRAY:
-      case PNK_OBJECT:
+Parser<ParseHandler>::checkAndMarkAsAssignmentLhs(Node target, AssignmentFlavor flavor)
+{
+    // Handle destructuring object/array patterns specially.
+    if (handler.isDestructuringTarget(target)) {
         if (flavor == CompoundAssignment) {
             report(ParseError, false, null(), JSMSG_BAD_DESTRUCT_ASS);
             return false;
         }
-        if (!checkDestructuring(nullptr, pn))
-            return false;
-        break;
-
-      case PNK_CALL:
-        if (flavor == KeyedDestructuringAssignment) {
-            report(ParseError, false, pn, JSMSG_BAD_DESTRUCT_TARGET);
-            return false;
-        }
-        if (!makeSetCall(pn, JSMSG_BAD_LEFTSIDE_OF_ASS))
+
+        return checkDestructuring(nullptr, target);
+    }
+
+    // All other permitted targets are simple.
+    if (!reportIfNotValidSimpleAssignmentTarget(target, flavor))
+        return false;
+
+    if (handler.isPropertyAccess(target))
+        return true;
+
+    if (handler.maybeName(target)) {
+        // The arguments/eval identifiers are simple in non-strict mode code,
+        // but warn to discourage use nonetheless.
+        if (!reportIfArgumentsEvalTarget(target))
             return false;
-        break;
-
-      default:
-        unsigned errnum = (flavor == KeyedDestructuringAssignment) ? JSMSG_BAD_DESTRUCT_TARGET :
-            JSMSG_BAD_LEFTSIDE_OF_ASS;
-        report(ParseError, false, pn, errnum);
+
+        if (flavor == KeyedDestructuringAssignment) {
+            // We may be called on a name node that has already been
+            // specialized, in the very weird "for (var [x] = i in o) ..."
+            // case. See bug 558633.
+            //
+            // XXX Is this necessary with the changes in bug 1164741?  This is
+            //     likely removable now.
+            handler.maybeDespecializeSet(target);
+        } else {
+            handler.adjustGetToSet(target);
+        }
+        handler.markAsAssigned(target);
+        return true;
+    }
+
+    MOZ_ASSERT(handler.isFunctionCall(target));
+
+    if (flavor == KeyedDestructuringAssignment) {
+        report(ParseError, false, target, JSMSG_BAD_DESTRUCT_TARGET);
         return false;
     }
-    return true;
-}
-
-template <>
-bool
-Parser<SyntaxParseHandler>::checkAndMarkAsAssignmentLhs(Node pn, AssignmentFlavor flavor)
-{
-    /* Full syntax checking of valid assignment LHS terms requires a parse tree. */
-    if (pn != SyntaxParseHandler::NodeName &&
-        pn != SyntaxParseHandler::NodeGetProp &&
-        pn != SyntaxParseHandler::NodeLValue)
-    {
-        return abortIfSyntaxParser();
-    }
-    return checkStrictAssignment(pn);
+
+    return makeSetCall(target, JSMSG_BAD_LEFTSIDE_OF_ASS);
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandling,
                                  InvokedPrediction invoked)
 {
     JS_CHECK_RECURSION(context, return null());
@@ -6601,151 +6560,226 @@ Parser<ParseHandler>::assignExpr(InHandl
     Node rhs = assignExpr(inHandling, yieldHandling);
     pc->inDeclDestructuring = saved;
     if (!rhs)
         return null();
 
     return handler.newAssignment(kind, lhs, rhs, pc, op);
 }
 
-static const char incop_name_str[][10] = {"increment", "decrement"};
-
-template <>
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::isValidSimpleAssignmentTarget(Node node)
+{
+    if (PropertyName* name = handler.maybeName(node)) {
+        // Note that we implement *exactly* the ES6 semantics here.  Warning
+        // for arguments/eval when extraWarnings is set isn't handled here.
+        if (!pc->sc->strict())
+            return true;
+
+        return name != context->names().arguments && name != context->names().eval;
+    }
+
+    if (handler.isPropertyAccess(node))
+        return true;
+    return handler.isFunctionCall(node);
+}
+
+template <typename ParseHandler>
 bool
-Parser<FullParseHandler>::checkAndMarkAsIncOperand(ParseNode* kid, TokenKind tt, bool preorder)
+Parser<ParseHandler>::reportIfArgumentsEvalTarget(Node target)
+{
+    PropertyName* name = handler.maybeName(target);
+    if (!name)
+        return true;
+
+    const char* chars = (name == context->names().arguments)
+                        ? js_arguments_str
+                        : (name == context->names().eval)
+                        ? js_eval_str
+                        : nullptr;
+    if (!chars)
+        return true;
+
+    if (!report(ParseStrictError, pc->sc->strict(), target, JSMSG_BAD_STRICT_ASSIGN, chars))
+        return false;
+
+    MOZ_ASSERT(!pc->sc->strict(), "in strict mode an error should have been reported");
+    return true;
+}
+
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::reportIfNotValidSimpleAssignmentTarget(Node target,
+                                                             AssignmentFlavor flavor)
+{
+    if (isValidSimpleAssignmentTarget(target))
+        return true;
+
+    // Use a special error if the target is arguments/eval, as a nicety.
+    if (!reportIfArgumentsEvalTarget(target))
+        return false;
+
+    unsigned errnum;
+    const char* extra = nullptr;
+
+    switch (flavor) {
+      case IncrementAssignment:
+        errnum = JSMSG_BAD_OPERAND;
+        extra = "increment";
+        break;
+
+      case DecrementAssignment:
+        errnum = JSMSG_BAD_OPERAND;
+        extra = "decrement";
+        break;
+
+      case KeyedDestructuringAssignment:
+        errnum = JSMSG_BAD_DESTRUCT_TARGET;
+        break;
+
+      case PlainAssignment:
+      case CompoundAssignment:
+        errnum = JSMSG_BAD_LEFTSIDE_OF_ASS;
+        break;
+    }
+
+    report(ParseError, pc->sc->strict(), target, errnum, extra);
+    return false;
+}
+
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::checkAndMarkAsIncOperand(Node target, AssignmentFlavor flavor)
 {
     // Check.
-    if (!kid->isKind(PNK_NAME) &&
-        !kid->isKind(PNK_DOT) &&
-        !kid->isKind(PNK_SUPERPROP) &&
-        !kid->isKind(PNK_SUPERELEM) &&
-        !kid->isKind(PNK_ELEM) &&
-        !(kid->isKind(PNK_CALL) &&
-          (kid->isOp(JSOP_CALL) || kid->isOp(JSOP_SPREADCALL) ||
-           kid->isOp(JSOP_EVAL) || kid->isOp(JSOP_STRICTEVAL) ||
-           kid->isOp(JSOP_SPREADEVAL) || kid->isOp(JSOP_STRICTSPREADEVAL) ||
-           kid->isOp(JSOP_FUNCALL) ||
-           kid->isOp(JSOP_FUNAPPLY))))
-    {
-        report(ParseError, false, null(), JSMSG_BAD_OPERAND, incop_name_str[tt == TOK_DEC]);
+    if (!reportIfNotValidSimpleAssignmentTarget(target, flavor))
         return false;
-    }
-
-    if (!checkStrictAssignment(kid))
+
+    // Assignment to arguments/eval is allowed outside strict mode code,
+    // but it's dodgy.  Report a strict warning (error, if werror was set).
+    if (!reportIfArgumentsEvalTarget(target))
         return false;
 
     // Mark.
-    if (kid->isKind(PNK_NAME)) {
-        kid->markAsAssigned();
-    } else if (kid->isKind(PNK_CALL)) {
-        if (!makeSetCall(kid, JSMSG_BAD_INCOP_OPERAND))
+    if (handler.maybeName(target)) {
+        handler.markAsAssigned(target);
+    } else if (handler.isFunctionCall(target)) {
+        if (!makeSetCall(target, JSMSG_BAD_INCOP_OPERAND))
             return false;
     }
     return true;
 }
 
-template <>
-bool
-Parser<SyntaxParseHandler>::checkAndMarkAsIncOperand(Node kid, TokenKind tt, bool preorder)
-{
-    // To the extent of what we support in syntax-parse mode, the rules for
-    // inc/dec operands are the same as for assignment. There are differences,
-    // such as destructuring; but if we hit any of those cases, we'll abort and
-    // reparse in full mode.
-    return checkAndMarkAsAssignmentLhs(kid, IncDecAssignment);
-}
-
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::unaryOpExpr(YieldHandling yieldHandling, ParseNodeKind kind, JSOp op,
                                   uint32_t begin)
 {
     Node kid = unaryExpr(yieldHandling);
     if (!kid)
         return null();
     return handler.newUnary(kind, op, begin, kid);
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::unaryExpr(YieldHandling yieldHandling, InvokedPrediction invoked)
 {
-    Node pn, pn2;
-
     JS_CHECK_RECURSION(context, return null());
 
     TokenKind tt;
     if (!tokenStream.getToken(&tt, TokenStream::Operand))
         return null();
     uint32_t begin = pos().begin;
     switch (tt) {
-      case TOK_TYPEOF:
-        return unaryOpExpr(yieldHandling, PNK_TYPEOF, JSOP_TYPEOF, begin);
       case TOK_VOID:
         return unaryOpExpr(yieldHandling, PNK_VOID, JSOP_VOID, begin);
       case TOK_NOT:
         return unaryOpExpr(yieldHandling, PNK_NOT, JSOP_NOT, begin);
       case TOK_BITNOT:
         return unaryOpExpr(yieldHandling, PNK_BITNOT, JSOP_BITNOT, begin);
       case TOK_ADD:
         return unaryOpExpr(yieldHandling, PNK_POS, JSOP_POS, begin);
       case TOK_SUB:
         return unaryOpExpr(yieldHandling, PNK_NEG, JSOP_NEG, begin);
 
+      case TOK_TYPEOF: {
+        // The |typeof| operator is specially parsed to distinguish its
+        // application to a name, from its application to a non-name
+        // expression:
+        //
+        //   // Looks up the name, doesn't find it and so evaluates to
+        //   // "undefined".
+        //   assertEq(typeof nonExistentName, "undefined");
+        //
+        //   // Evaluates expression, triggering a runtime ReferenceError for
+        //   // the undefined name.
+        //   typeof (1, nonExistentName);
+        Node kid = unaryExpr(yieldHandling);
+        if (!kid)
+            return null();
+
+        return handler.newTypeof(begin, kid);
+      }
+
       case TOK_INC:
       case TOK_DEC:
       {
         TokenKind tt2;
         if (!tokenStream.getToken(&tt2, TokenStream::Operand))
             return null();
-        pn2 = memberExpr(yieldHandling, tt2, true);
+        Node pn2 = memberExpr(yieldHandling, tt2, true);
         if (!pn2)
             return null();
-        if (!checkAndMarkAsIncOperand(pn2, tt, true))
+        AssignmentFlavor flavor = (tt == TOK_INC) ? IncrementAssignment : DecrementAssignment;
+        if (!checkAndMarkAsIncOperand(pn2, flavor))
             return null();
         return handler.newUnary((tt == TOK_INC) ? PNK_PREINCREMENT : PNK_PREDECREMENT,
                                 JSOP_NOP,
                                 begin,
                                 pn2);
       }
 
       case TOK_DELETE: {
         Node expr = unaryExpr(yieldHandling);
         if (!expr)
             return null();
 
         // Per spec, deleting any unary expression is valid -- it simply
         // returns true -- except for one case that is illegal in strict mode.
-        if (handler.isName(expr)) {
+        if (handler.maybeName(expr)) {
             if (!report(ParseStrictError, pc->sc->strict(), expr, JSMSG_DEPRECATED_DELETE_OPERAND))
                 return null();
             pc->sc->setBindingsAccessedDynamically();
         }
 
         return handler.newDelete(begin, expr);
       }
 
-      default:
-        pn = memberExpr(yieldHandling, tt, /* allowCallSyntax = */ true, invoked);
+      default: {
+        Node pn = memberExpr(yieldHandling, tt, /* allowCallSyntax = */ true, invoked);
         if (!pn)
             return null();
 
         /* Don't look across a newline boundary for a postfix incop. */
         if (!tokenStream.peekTokenSameLine(&tt, TokenStream::Operand))
             return null();
         if (tt == TOK_INC || tt == TOK_DEC) {
             tokenStream.consumeKnownToken(tt);
-            if (!checkAndMarkAsIncOperand(pn, tt, false))
+            AssignmentFlavor flavor = (tt == TOK_INC) ? IncrementAssignment : DecrementAssignment;
+            if (!checkAndMarkAsIncOperand(pn, flavor))
                 return null();
             return handler.newUnary((tt == TOK_INC) ? PNK_POSTINCREMENT : PNK_POSTDECREMENT,
                                     JSOP_NOP,
                                     begin,
                                     pn);
         }
         return pn;
+      }
     }
 }
 
 /*
  * A dedicated helper for transplanting the legacy comprehension expression E in
  *
  *   [E for (V in I)]   // legacy array comprehension
  *   (E for (V in I))   // legacy generator expression
@@ -7917,23 +7951,23 @@ Parser<ParseHandler>::memberExpr(YieldHa
                    tt == TOK_NO_SUBS_TEMPLATE)
         {
             if (isSuper) {
                 // For now...
                 report(ParseError, false, null(), JSMSG_BAD_SUPER);
                 return null();
             }
 
-            JSOp op = JSOP_CALL;
-            nextMember = handler.newList(tt == TOK_LP ? PNK_CALL : PNK_TAGGED_TEMPLATE, JSOP_CALL);
+            nextMember = tt == TOK_LP ? handler.newCall() : handler.newTaggedTemplate();
             if (!nextMember)
                 return null();
 
-            if (JSAtom* atom = handler.isName(lhs)) {
-                if (tt == TOK_LP && atom == context->names().eval) {
+            JSOp op = JSOP_CALL;
+            if (PropertyName* name = handler.maybeName(lhs)) {
+                if (tt == TOK_LP && name == context->names().eval) {
                     /* Select JSOP_EVAL and flag pc as heavyweight. */
                     op = pc->sc->strict() ? JSOP_STRICTEVAL : JSOP_EVAL;
                     pc->sc->setBindingsAccessedDynamically();
                     pc->sc->setHasDirectEval();
 
                     /*
                      * In non-strict mode code, direct calls to eval can add
                      * variables to the call object.
@@ -7942,23 +7976,24 @@ Parser<ParseHandler>::memberExpr(YieldHa
                         pc->sc->asFunctionBox()->setHasExtensibleScope();
 
                     // If we're in a method, mark the method as requiring
                     // support for 'super', since direct eval code can use it.
                     // (If we're not in a method, that's fine, so ignore the
                     // return value.)
                     checkAndMarkSuperScope();
                 }
-            } else if (JSAtom* atom = handler.isGetProp(lhs)) {
-                /* Select JSOP_FUNAPPLY given foo.apply(...). */
-                if (atom == context->names().apply) {
+            } else if (PropertyName* prop = handler.maybeDottedProperty(lhs)) {
+                // Use the JSOP_FUN{APPLY,CALL} optimizations given the right
+                // syntax.
+                if (prop == context->names().apply) {
                     op = JSOP_FUNAPPLY;
                     if (pc->sc->isFunctionBox())
                         pc->sc->asFunctionBox()->usesApply = true;
-                } else if (atom == context->names().call) {
+                } else if (prop == context->names().call) {
                     op = JSOP_FUNCALL;
                 }
             }
 
             handler.setBeginPosition(nextMember, lhs);
             handler.addList(nextMember, lhs);
 
             if (tt == TOK_LP) {
@@ -7997,17 +8032,17 @@ Parser<ParseHandler>::memberExpr(YieldHa
 
     return lhs;
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::newName(PropertyName* name)
 {
-    return handler.newName(name, pc->blockid(), pos());
+    return handler.newName(name, pc->blockid(), pos(), context);
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::identifierName(YieldHandling yieldHandling)
 {
     RootedPropertyName name(context, tokenStream.currentName());
     if (yieldHandling == YieldIsKeyword && name == context->names().yield) {
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -651,34 +651,45 @@ class Parser : private JS::AutoGCRooter,
         return versionNumber() >= JSVERSION_1_6;
 #endif
     }
 
     enum AssignmentFlavor {
         PlainAssignment,
         CompoundAssignment,
         KeyedDestructuringAssignment,
-        IncDecAssignment
+        IncrementAssignment,
+        DecrementAssignment
     };
 
     bool checkAndMarkAsAssignmentLhs(Node pn, AssignmentFlavor flavor);
     bool matchInOrOf(bool* isForInp, bool* isForOfp);
 
     bool checkFunctionArguments();
     bool makeDefIntoUse(Definition* dn, Node pn, JSAtom* atom);
     bool checkFunctionDefinition(HandlePropertyName funName, Node* pn, FunctionSyntaxKind kind,
                                  bool* pbodyProcessed);
     bool finishFunctionDefinition(Node pn, FunctionBox* funbox, Node body);
     bool addFreeVariablesFromLazyFunction(JSFunction* fun, ParseContext<ParseHandler>* pc);
 
     bool isValidForStatementLHS(Node pn1, JSVersion version, bool forDecl, bool forEach,
                                 ParseNodeKind headKind);
     bool checkForHeadConstInitializers(Node pn1);
-    bool checkAndMarkAsIncOperand(Node kid, TokenKind tt, bool preorder);
+
+    bool isValidSimpleAssignmentTarget(Node node);
+
+    // Invalid assignment targets are handled differently in different places.
+    // Select the desired semantics using |flavor|.
+    bool reportIfArgumentsEvalTarget(Node target);
+    bool reportIfNotValidSimpleAssignmentTarget(Node target, AssignmentFlavor flavor);
+
+    bool checkAndMarkAsIncOperand(Node kid, AssignmentFlavor flavor);
+
     bool checkStrictAssignment(Node lhs);
+
     bool checkStrictBinding(PropertyName* name, Node pn);
     bool defineArg(Node funcpn, HandlePropertyName name,
                    bool disallowDuplicateArgs = false, Node* duplicatedArg = nullptr);
     Node pushLexicalScope(StmtInfoPC* stmt);
     Node pushLexicalScope(Handle<StaticBlockObject*> blockObj, StmtInfoPC* stmt);
     Node pushLetScope(Handle<StaticBlockObject*> blockObj, StmtInfoPC* stmt);
     bool noteNameUse(HandlePropertyName name, Node pn);
     Node computedPropertyName(YieldHandling yieldHandling, Node literal);
@@ -691,17 +702,17 @@ class Parser : private JS::AutoGCRooter,
     bool checkAndPrepareLexical(bool isConst, const TokenPos& errorPos);
     Node makeInitializedLexicalBinding(HandlePropertyName name, bool isConst, const TokenPos& pos);
 
     Node newBindingNode(PropertyName* name, bool functionScope, VarContext varContext = HoistVars);
     bool checkDestructuring(BindData<ParseHandler>* data, Node left);
     bool checkDestructuringObject(BindData<ParseHandler>* data, Node objectPattern);
     bool checkDestructuringArray(BindData<ParseHandler>* data, Node arrayPattern);
     bool bindInitialized(BindData<ParseHandler>* data, Node pn);
-    bool makeSetCall(Node pn, unsigned msg);
+    bool makeSetCall(Node node, unsigned errnum);
     Node cloneDestructuringDefault(Node opn);
     Node cloneLeftHandSide(Node opn);
     Node cloneParseTree(Node opn);
 
     Node newNumber(const Token& tok) {
         return handler.newNumber(tok.number(), tok.decimalPoint(), tok.pos);
     }
 
@@ -731,26 +742,16 @@ class Parser : private JS::AutoGCRooter,
     bool asmJS(Node list);
 
     void addTelemetry(JSCompartment::DeprecatedLanguageExtension e);
 
     friend class LegacyCompExprTransplanter;
     friend struct BindData<ParseHandler>;
 };
 
-/* Declare some required template specializations. */
-
-template <>
-bool
-Parser<FullParseHandler>::checkAndMarkAsAssignmentLhs(ParseNode* pn, AssignmentFlavor flavor);
-
-template <>
-bool
-Parser<SyntaxParseHandler>::checkAndMarkAsAssignmentLhs(Node pn, AssignmentFlavor flavor);
-
 } /* namespace frontend */
 } /* namespace js */
 
 /*
  * Convenience macro to access Parser.tokenStream as a pointer.
  */
 #define TS(p) (&(p)->tokenStream)
 
--- a/js/src/frontend/SyntaxParseHandler.h
+++ b/js/src/frontend/SyntaxParseHandler.h
@@ -34,25 +34,44 @@ class SyntaxParseHandler
     JSAtom* lastAtom;
     TokenPos lastStringPos;
     TokenStream& tokenStream;
 
   public:
     enum Node {
         NodeFailure = 0,
         NodeGeneric,
-        NodeName,
         NodeGetProp,
         NodeStringExprStatement,
-        NodeLValue,
         NodeReturn,
         NodeHoistableDeclaration,
         NodeBreak,
         NodeThrow,
 
+        // This is needed for proper assignment-target handling.  ES6 formally
+        // requires function calls *not* pass IsValidSimpleAssignmentTarget,
+        // but at last check there were still sites with |f() = 5| and similar
+        // in code not actually executed (or at least not executed enough to be
+        // noticed).
+        NodeFunctionCall,
+
+        // Nodes representing names.  These *must* be sequential per |isName|.
+        NodeArgumentsName,
+        NodeEvalName,
+        NodeName,
+
+        NodeDottedProperty,
+        NodeElement,
+        NodeSuperProperty,
+        NodeSuperElement,
+
+        // Valuable for recognizing potential destructuring patterns.
+        NodeArray,
+        NodeObject,
+
         // In rare cases a parenthesized |node| doesn't have the same semantics
         // as |node|.  Each such node has a special Node value, and we use a
         // different Node value to represent the parenthesized form.  See also
         // isUnparenthesized*(Node), newExprStatement(Node, uint32_t),
         // parenthesize(Node), and meaningMightChangeIfParenthesized(Node).
 
         // The directive prologue at the start of a FunctionBody or ScriptBody
         // is the longest sequence (possibly empty) of string literal
@@ -85,16 +104,30 @@ class SyntaxParseHandler
         // (Technically this isn't needed, as these are *only* extraWarnings
         // warnings, and parsing with that option disables syntax parsing.  But
         // it seems best to be consistent, and perhaps the syntax parser will
         // eventually enforce extraWarnings and will require this then.)
         NodeUnparenthesizedAssignment
     };
     typedef Definition::Kind DefinitionNode;
 
+    bool isPropertyAccess(Node node) {
+        return node == NodeDottedProperty || node == NodeElement ||
+               node == NodeSuperProperty || node == NodeSuperElement;
+    }
+
+    bool isFunctionCall(Node node) {
+        // Note: super() is a special form, *not* a function call.
+        return node == NodeFunctionCall;
+    }
+
+    bool isDestructuringTarget(Node node) {
+        return node == NodeArray || node == NodeObject;
+    }
+
   private:
     static bool meaningMightChangeIfParenthesized(Node node) {
         return node == NodeUnparenthesizedString ||
                node == NodeUnparenthesizedCommaExpr ||
                node == NodeUnparenthesizedYieldExpr ||
                node == NodeUnparenthesizedAssignment;
     }
 
@@ -106,18 +139,22 @@ class SyntaxParseHandler
       : lastAtom(nullptr),
         tokenStream(tokenStream)
     {}
 
     static Node null() { return NodeFailure; }
 
     void trace(JSTracer* trc) {}
 
-    Node newName(PropertyName* name, uint32_t blockid, const TokenPos& pos) {
+    Node newName(PropertyName* name, uint32_t blockid, const TokenPos& pos, ExclusiveContext* cx) {
         lastAtom = name;
+        if (name == cx->names().arguments)
+            return NodeArgumentsName;
+        if (name == cx->names().eval)
+            return NodeEvalName;
         return NodeName;
     }
 
     Node newComputedName(Node expr, uint32_t start, uint32_t end) {
         return NodeName;
     }
 
     DefinitionNode newPlaceholder(JSAtom* atom, uint32_t blockid, const TokenPos& pos) {
@@ -154,17 +191,27 @@ class SyntaxParseHandler
 
     template <class Boxer>
     Node newRegExp(RegExpObject* reobj, const TokenPos& pos, Boxer& boxer) { return NodeGeneric; }
 
     Node newConditional(Node cond, Node thenExpr, Node elseExpr) { return NodeGeneric; }
 
     Node newElision() { return NodeGeneric; }
 
-    Node newDelete(uint32_t begin, Node expr) { return NodeGeneric; }
+    void markAsSetCall(Node node) {
+        MOZ_ASSERT(node == NodeFunctionCall);
+    }
+
+    Node newDelete(uint32_t begin, Node expr) {
+        return NodeGeneric;
+    }
+
+    Node newTypeof(uint32_t begin, Node kid) {
+        return NodeGeneric;
+    }
 
     Node newUnary(ParseNodeKind kind, JSOp op, uint32_t begin, Node kid) {
         return NodeGeneric;
     }
 
     Node newBinary(ParseNodeKind kind, JSOp op = JSOP_NOP) { return NodeGeneric; }
     Node newBinary(ParseNodeKind kind, Node left, JSOp op = JSOP_NOP) { return NodeGeneric; }
     Node newBinary(ParseNodeKind kind, Node left, Node right, JSOp op = JSOP_NOP) {
@@ -179,25 +226,35 @@ class SyntaxParseHandler
         return NodeGeneric;
     }
 
     // Expressions
 
     Node newArrayComprehension(Node body, unsigned blockid, const TokenPos& pos) {
         return NodeGeneric;
     }
-    Node newArrayLiteral(uint32_t begin, unsigned blockid) { return NodeGeneric; }
+    Node newArrayLiteral(uint32_t begin, unsigned blockid) { return NodeArray; }
     bool addElision(Node literal, const TokenPos& pos) { return true; }
     bool addSpreadElement(Node literal, uint32_t begin, Node inner) { return true; }
     void addArrayElement(Node literal, Node element) { }
 
-    Node newObjectLiteral(uint32_t begin) { return NodeGeneric; }
+    Node newCall() { return NodeFunctionCall; }
+    Node newTaggedTemplate() { return NodeGeneric; }
+
+    Node newObjectLiteral(uint32_t begin) { return NodeObject; }
     Node newClassMethodList(uint32_t begin) { return NodeGeneric; }
-    Node newSuperProperty(JSAtom* atom, const TokenPos& pos) { return NodeGeneric; }
-    Node newSuperElement(Node expr, const TokenPos& pos) { return NodeGeneric; }
+
+    Node newSuperProperty(PropertyName* prop, const TokenPos& pos) {
+        return NodeSuperProperty;
+    }
+
+    Node newSuperElement(Node expr, const TokenPos& pos) {
+        return NodeSuperElement;
+    }
+
     bool addPrototypeMutation(Node literal, uint32_t begin, Node expr) { return true; }
     bool addPropertyDefinition(Node literal, Node name, Node expr) { return true; }
     bool addShorthand(Node literal, Node name, Node expr) { return true; }
     bool addObjectMethodDefinition(Node literal, Node name, Node fn, JSOp op) { return true; }
     bool addClassMethodDefinition(Node literal, Node name, Node fn, JSOp op, bool isStatic) { return true; }
     Node newYieldExpression(uint32_t begin, Node value, Node gen) { return NodeUnparenthesizedYieldExpr; }
     Node newYieldStarExpression(uint32_t begin, Node value, Node gen) { return NodeGeneric; }
 
@@ -228,20 +285,20 @@ class SyntaxParseHandler
     Node newThrowStatement(Node expr, const TokenPos& pos) { return NodeThrow; }
     Node newTryStatement(uint32_t begin, Node body, Node catchList, Node finallyBlock) {
         return NodeGeneric;
     }
     Node newDebuggerStatement(const TokenPos& pos) { return NodeGeneric; }
 
     Node newPropertyAccess(Node pn, PropertyName* name, uint32_t end) {
         lastAtom = name;
-        return NodeGetProp;
+        return NodeDottedProperty;
     }
 
-    Node newPropertyByValue(Node pn, Node kid, uint32_t end) { return NodeLValue; }
+    Node newPropertyByValue(Node pn, Node kid, uint32_t end) { return NodeElement; }
 
     bool addCatchBlock(Node catchList, Node letBlock,
                        Node catchName, Node catchGuard, Node catchBody) { return true; }
 
     bool setLastFunctionArgumentDefault(Node funcpn, Node pn) { return true; }
     void setLastFunctionArgumentDestructuring(Node funcpn, Node pn) {}
     Node newFunctionDefinition() { return NodeHoistableDeclaration; }
     void setFunctionBody(Node pn, Node kid) {}
@@ -300,18 +357,22 @@ class SyntaxParseHandler
         return newList(PNK_CATCHLIST, JSOP_NOP);
     }
 
     Node newCommaExpressionList(Node kid) {
         return NodeUnparenthesizedCommaExpr;
     }
 
     void addList(Node list, Node kid) {
-        MOZ_ASSERT(list == NodeGeneric || list == NodeUnparenthesizedCommaExpr ||
-                   list == NodeHoistableDeclaration);
+        MOZ_ASSERT(list == NodeGeneric ||
+                   list == NodeArray ||
+                   list == NodeObject ||
+                   list == NodeUnparenthesizedCommaExpr ||
+                   list == NodeHoistableDeclaration ||
+                   list == NodeFunctionCall);
     }
 
     Node newAssignment(ParseNodeKind kind, Node lhs, Node rhs,
                        ParseContext<SyntaxParseHandler>* pc, JSOp op)
     {
         if (kind == PNK_ASSIGN)
             return NodeUnparenthesizedAssignment;
         return newBinary(kind, lhs, rhs, op);
@@ -350,30 +411,45 @@ class SyntaxParseHandler
         return node;
     }
     MOZ_WARN_UNUSED_RESULT Node setLikelyIIFE(Node pn) {
         return pn; // Remain in syntax-parse mode.
     }
     void setPrologue(Node pn) {}
 
     bool isConstant(Node pn) { return false; }
-    PropertyName* isName(Node pn) {
-        return (pn == NodeName) ? lastAtom->asPropertyName() : nullptr;
+    PropertyName* maybeName(Node pn) {
+        if (pn == NodeName || pn == NodeArgumentsName || pn == NodeEvalName)
+            return lastAtom->asPropertyName();
+        return nullptr;
     }
-    PropertyName* isGetProp(Node pn) {
-        return (pn == NodeGetProp) ? lastAtom->asPropertyName() : nullptr;
+
+    PropertyName* maybeDottedProperty(Node node) {
+        // Note: |super.apply(...)| is a special form that calls an "apply"
+        // method retrieved from one value, but using a *different* value as
+        // |this|.  It's not really eligible for the funapply/funcall
+        // optimizations as they're currently implemented (assuming a single
+        // value is used for both retrieval and |this|).
+        if (node != NodeDottedProperty)
+            return nullptr;
+        return lastAtom->asPropertyName();
     }
+
     JSAtom* isStringExprStatement(Node pn, TokenPos* pos) {
         if (pn == NodeStringExprStatement) {
             *pos = lastStringPos;
             return lastAtom;
         }
         return nullptr;
     }
 
+    void markAsAssigned(Node node) {}
+    void adjustGetToSet(Node node) {}
+    void maybeDespecializeSet(Node node) {}
+
     Node makeAssignment(Node pn, Node rhs) { return NodeGeneric; }
 
     static Node getDefinitionNode(DefinitionNode dn) { return NodeGeneric; }
     static Definition::Kind getDefinitionKind(DefinitionNode dn) { return dn; }
     static bool isPlaceholderDefinition(DefinitionNode dn) { return dn == Definition::PLACEHOLDER; }
     void linkUseToDef(Node pn, DefinitionNode dn) {}
     DefinitionNode resolve(DefinitionNode dn) { return dn; }
     void deoptimizeUsesWithin(DefinitionNode dn, const TokenPos& pos) {}
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/modules/bug1169850.js
@@ -0,0 +1,9 @@
+// export-default should throw SyntaxError until it's implemented.
+
+var caught = false;
+try {
+  eval("export default 1;");
+} catch (e) {
+  caught = true;
+}
+assertEq(caught, true);
--- a/js/src/jit-test/tests/modules/export-declaration.js
+++ b/js/src/jit-test/tests/modules/export-declaration.js
@@ -318,16 +318,20 @@ program([
     exportDeclaration(
         lit(1234),
         null,
         null,
         true
     )
 ]).assert(Reflect.parse("export default 1234"));
 
+assertThrowsInstanceOf(function () {
+   Reflect.parse("export default 1234 5678");
+}, SyntaxError);
+
 var loc = Reflect.parse("export { a as b } from 'c'", {
     loc: true
 }).body[0].loc;
 
 assertEq(loc.start.line, 1);
 assertEq(loc.start.column, 0);
 assertEq(loc.start.line, 1);
 assertEq(loc.end.column, 26);
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -5151,31 +5151,16 @@ CodeGenerator::visitPowD(LPowD* ins)
     masm.passABIArg(value, MoveOp::DOUBLE);
     masm.passABIArg(power, MoveOp::DOUBLE);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, ecmaPow), MoveOp::DOUBLE);
 
     MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg);
 }
 
 void
-CodeGenerator::visitRandom(LRandom* ins)
-{
-    Register temp = ToRegister(ins->temp());
-    Register temp2 = ToRegister(ins->temp2());
-
-    masm.loadJSContext(temp);
-
-    masm.setupUnalignedABICall(1, temp2);
-    masm.passABIArg(temp);
-    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, math_random_no_outparam), MoveOp::DOUBLE);
-
-    MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg);
-}
-
-void
 CodeGenerator::visitMathFunctionD(LMathFunctionD* ins)
 {
     Register temp = ToRegister(ins->temp());
     FloatRegister input = ToFloatRegister(ins->input());
     MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg);
 
     const MathCache* mathCache = ins->mir()->cache();
 
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -210,17 +210,16 @@ class CodeGenerator : public CodeGenerat
     void visitSetPropertyPolymorphicV(LSetPropertyPolymorphicV* ins);
     void visitArraySplice(LArraySplice* splice);
     void visitSetPropertyPolymorphicT(LSetPropertyPolymorphicT* ins);
     void visitAbsI(LAbsI* lir);
     void visitAtan2D(LAtan2D* lir);
     void visitHypot(LHypot* lir);
     void visitPowI(LPowI* lir);
     void visitPowD(LPowD* lir);
-    void visitRandom(LRandom* lir);
     void visitMathFunctionD(LMathFunctionD* ins);
     void visitMathFunctionF(LMathFunctionF* ins);
     void visitModD(LModD* ins);
     void visitMinMaxI(LMinMaxI* lir);
     void visitBinaryV(LBinaryV* lir);
     void emitCompareS(LInstruction* lir, JSOp op, Register left, Register right, Register output);
     void visitCompareS(LCompareS* lir);
     void visitCompareStrictS(LCompareStrictS* lir);
--- a/js/src/jit/LIR-Common.h
+++ b/js/src/jit/LIR-Common.h
@@ -3121,33 +3121,16 @@ class LPowD : public LCallInstructionHel
     const LAllocation* power() {
         return getOperand(1);
     }
     const LDefinition* temp() {
         return getTemp(0);
     }
 };
 
-// Math.random().
-class LRandom : public LCallInstructionHelper<1, 0, 2>
-{
-  public:
-    LIR_HEADER(Random)
-    LRandom(const LDefinition& temp, const LDefinition& temp2) {
-        setTemp(0, temp);
-        setTemp(1, temp2);
-    }
-    const LDefinition* temp() {
-        return getTemp(0);
-    }
-    const LDefinition* temp2() {
-        return getTemp(1);
-    }
-};
-
 class LMathFunctionD : public LCallInstructionHelper<1, 1, 1>
 {
   public:
     LIR_HEADER(MathFunctionD)
     LMathFunctionD(const LAllocation& input, const LDefinition& temp) {
         setOperand(0, input);
         setTemp(0, temp);
     }
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -1395,23 +1395,16 @@ LIRGenerator::visitPow(MPow* ins)
     } else {
         lir = new(alloc()) LPowD(useRegisterAtStart(input), useRegisterAtStart(power),
                                  tempFixed(CallTempReg0));
     }
     defineReturn(lir, ins);
 }
 
 void
-LIRGenerator::visitRandom(MRandom* ins)
-{
-    LRandom* lir = new(alloc()) LRandom(tempFixed(CallTempReg0), tempFixed(CallTempReg1));
-    defineReturn(lir, ins);
-}
-
-void
 LIRGenerator::visitMathFunction(MMathFunction* ins)
 {
     MOZ_ASSERT(IsFloatingPointType(ins->type()));
     MOZ_ASSERT(ins->type() == ins->input()->type());
 
     LInstruction* lir;
     if (ins->type() == MIRType_Double) {
         // Note: useRegisterAtStart is safe here, the temp is not a FP register.
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -125,17 +125,16 @@ class LIRGenerator : public LIRGenerator
     void visitRound(MRound* ins);
     void visitMinMax(MMinMax* ins);
     void visitAbs(MAbs* ins);
     void visitClz(MClz* ins);
     void visitSqrt(MSqrt* ins);
     void visitAtan2(MAtan2* ins);
     void visitHypot(MHypot* ins);
     void visitPow(MPow* ins);
-    void visitRandom(MRandom* ins);
     void visitMathFunction(MMathFunction* ins);
     void visitAdd(MAdd* ins);
     void visitSub(MSub* ins);
     void visitMul(MMul* ins);
     void visitDiv(MDiv* ins);
     void visitMod(MMod* ins);
     void visitConcat(MConcat* ins);
     void visitCharCodeAt(MCharCodeAt* ins);
--- a/js/src/jit/arm/CodeGenerator-arm.cpp
+++ b/js/src/jit/arm/CodeGenerator-arm.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "jit/arm/CodeGenerator-arm.h"
 
 #include "mozilla/MathAlgorithms.h"
 
 #include "jscntxt.h"
 #include "jscompartment.h"
+#include "jsmath.h"
 #include "jsnum.h"
 
 #include "jit/CodeGenerator.h"
 #include "jit/JitCompartment.h"
 #include "jit/JitFrames.h"
 #include "jit/MIR.h"
 #include "jit/MIRGraph.h"
 #include "js/Conversions.h"
@@ -2315,8 +2316,23 @@ CodeGeneratorARM::memoryBarrier(MemoryBa
         masm.ma_dmb();
 }
 
 void
 CodeGeneratorARM::visitMemoryBarrier(LMemoryBarrier* ins)
 {
     memoryBarrier(ins->type());
 }
+
+void
+CodeGeneratorARM::visitRandom(LRandom* ins)
+{
+    Register temp = ToRegister(ins->temp());
+    Register temp2 = ToRegister(ins->temp2());
+
+    masm.loadJSContext(temp);
+
+    masm.setupUnalignedABICall(1, temp2);
+    masm.passABIArg(temp);
+    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, math_random_no_outparam), MoveOp::DOUBLE);
+
+    MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg);
+}
--- a/js/src/jit/arm/CodeGenerator-arm.h
+++ b/js/src/jit/arm/CodeGenerator-arm.h
@@ -211,16 +211,18 @@ class CodeGeneratorARM : public CodeGene
     void visitAsmJSLoadFuncPtr(LAsmJSLoadFuncPtr* ins);
     void visitAsmJSLoadFFIFunc(LAsmJSLoadFFIFunc* ins);
     void visitAsmJSPassStackArg(LAsmJSPassStackArg* ins);
 
     void visitMemoryBarrier(LMemoryBarrier* ins);
 
     void generateInvalidateEpilogue();
 
+    void visitRandom(LRandom* ins);
+
   protected:
     void visitEffectiveAddress(LEffectiveAddress* ins);
     void visitUDiv(LUDiv* ins);
     void visitUMod(LUMod* ins);
     void visitSoftUDivOrMod(LSoftUDivOrMod* ins);
 
   public:
     // Unimplemented SIMD instructions
--- a/js/src/jit/arm/LIR-arm.h
+++ b/js/src/jit/arm/LIR-arm.h
@@ -507,12 +507,29 @@ class LAsmJSAtomicBinopCallout : public 
         return getOperand(1);
     }
 
     const MAsmJSAtomicBinopHeap* mir() const {
         return mir_->toAsmJSAtomicBinopHeap();
     }
 };
 
+// Math.random().
+class LRandom : public LCallInstructionHelper<1, 0, 2>
+{
+  public:
+    LIR_HEADER(Random)
+    LRandom(const LDefinition& temp, const LDefinition& temp2) {
+        setTemp(0, temp);
+        setTemp(1, temp2);
+    }
+    const LDefinition* temp() {
+        return getTemp(0);
+    }
+    const LDefinition* temp2() {
+        return getTemp(1);
+    }
+};
+
 } // namespace jit
 } // namespace js
 
 #endif /* jit_arm_LIR_arm_h */
--- a/js/src/jit/arm/Lowering-arm.cpp
+++ b/js/src/jit/arm/Lowering-arm.cpp
@@ -710,8 +710,15 @@ LIRGeneratorARM::visitSubstr(MSubstr* in
                                          useRegister(ins->begin()),
                                          useRegister(ins->length()),
                                          temp(),
                                          temp(),
                                          tempByteOpRegister());
     define(lir, ins);
     assignSafepoint(lir, ins);
 }
+
+void
+LIRGeneratorARM::visitRandom(MRandom* ins)
+{
+    LRandom* lir = new(alloc()) LRandom(tempFixed(CallTempReg0), tempFixed(CallTempReg1));
+    defineReturn(lir, ins);
+}
--- a/js/src/jit/arm/Lowering-arm.h
+++ b/js/src/jit/arm/Lowering-arm.h
@@ -100,16 +100,17 @@ class LIRGeneratorARM : public LIRGenera
     void visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic* ins);
     void visitSimdBinaryArith(MSimdBinaryArith* ins);
     void visitSimdSelect(MSimdSelect* ins);
     void visitSimdSplatX4(MSimdSplatX4* ins);
     void visitSimdValueX4(MSimdValueX4* ins);
     void visitCompareExchangeTypedArrayElement(MCompareExchangeTypedArrayElement* ins);
     void visitAtomicTypedArrayElementBinop(MAtomicTypedArrayElementBinop* ins);
     void visitSubstr(MSubstr* ins);
+    void visitRandom(MRandom* ins);
 };
 
 typedef LIRGeneratorARM LIRGeneratorSpecific;
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_arm_Lowering_arm_h */
--- a/js/src/jit/mips/CodeGenerator-mips.cpp
+++ b/js/src/jit/mips/CodeGenerator-mips.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "jit/mips/CodeGenerator-mips.h"
 
 #include "mozilla/MathAlgorithms.h"
 
 #include "jscntxt.h"
 #include "jscompartment.h"
+#include "jsmath.h"
 #include "jsnum.h"
 
 #include "jit/CodeGenerator.h"
 #include "jit/JitCompartment.h"
 #include "jit/JitFrames.h"
 #include "jit/MIR.h"
 #include "jit/MIRGraph.h"
 #include "js/Conversions.h"
@@ -2147,8 +2148,23 @@ CodeGeneratorMIPS::visitNegD(LNegD* ins)
 void
 CodeGeneratorMIPS::visitNegF(LNegF* ins)
 {
     FloatRegister input = ToFloatRegister(ins->input());
     FloatRegister output = ToFloatRegister(ins->output());
 
     masm.as_negs(output, input);
 }
+
+void
+CodeGeneratorMIPS::visitRandom(LRandom* ins)
+{
+    Register temp = ToRegister(ins->temp());
+    Register temp2 = ToRegister(ins->temp2());
+
+    masm.loadJSContext(temp);
+
+    masm.setupUnalignedABICall(1, temp2);
+    masm.passABIArg(temp);
+    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, math_random_no_outparam), MoveOp::DOUBLE);
+
+    MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg);
+}
--- a/js/src/jit/mips/CodeGenerator-mips.h
+++ b/js/src/jit/mips/CodeGenerator-mips.h
@@ -240,16 +240,18 @@ class CodeGeneratorMIPS : public CodeGen
     void visitAsmJSStoreGlobalVar(LAsmJSStoreGlobalVar* ins);
     void visitAsmJSLoadFuncPtr(LAsmJSLoadFuncPtr* ins);
     void visitAsmJSLoadFFIFunc(LAsmJSLoadFFIFunc* ins);
 
     void visitAsmJSPassStackArg(LAsmJSPassStackArg* ins);
 
     void generateInvalidateEpilogue();
 
+    void visitRandom(LRandom* ins);
+
   protected:
     void visitEffectiveAddress(LEffectiveAddress* ins);
     void visitUDivOrMod(LUDivOrMod* ins);
 
   public:
     // Unimplemented SIMD instructions
     void visitSimdSplatX4(LSimdSplatX4* lir) { MOZ_CRASH("NYI"); }
     void visitInt32x4(LInt32x4* ins) { MOZ_CRASH("NYI"); }
--- a/js/src/jit/mips/LIR-mips.h
+++ b/js/src/jit/mips/LIR-mips.h
@@ -382,12 +382,29 @@ class LAsmJSLoadFuncPtr : public LInstru
     const MAsmJSLoadFuncPtr* mir() const {
         return mir_->toAsmJSLoadFuncPtr();
     }
     const LAllocation* index() {
         return getOperand(0);
     }
 };
 
+// Math.random().
+class LRandom : public LCallInstructionHelper<1, 0, 2>
+{
+  public:
+    LIR_HEADER(Random)
+    LRandom(const LDefinition& temp, const LDefinition& temp2) {
+        setTemp(0, temp);
+        setTemp(1, temp2);
+    }
+    const LDefinition* temp() {
+        return getTemp(0);
+    }
+    const LDefinition* temp2() {
+        return getTemp(1);
+    }
+};
+
 } // namespace jit
 } // namespace js
 
 #endif /* jit_mips_LIR_mips_h */
--- a/js/src/jit/mips/Lowering-mips.cpp
+++ b/js/src/jit/mips/Lowering-mips.cpp
@@ -579,8 +579,15 @@ LIRGeneratorMIPS::visitAsmJSAtomicBinopH
     MOZ_CRASH("NYI");
 }
 
 void
 LIRGeneratorMIPS::visitAtomicTypedArrayElementBinop(MAtomicTypedArrayElementBinop* ins)
 {
     MOZ_CRASH("NYI");
 }
+
+void
+LIRGeneratorMIPS::visitRandom(MRandom* ins)
+{
+    LRandom* lir = new(alloc()) LRandom(tempFixed(CallTempReg0), tempFixed(CallTempReg1));
+    defineReturn(lir, ins);
+}
--- a/js/src/jit/mips/Lowering-mips.h
+++ b/js/src/jit/mips/Lowering-mips.h
@@ -100,16 +100,17 @@ class LIRGeneratorMIPS : public LIRGener
     void visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic* ins);
     void visitSimdBinaryArith(MSimdBinaryArith* ins);
     void visitSimdSelect(MSimdSelect* ins);
     void visitSimdSplatX4(MSimdSplatX4* ins);
     void visitSimdValueX4(MSimdValueX4* ins);
     void visitCompareExchangeTypedArrayElement(MCompareExchangeTypedArrayElement* ins);
     void visitAtomicTypedArrayElementBinop(MAtomicTypedArrayElementBinop* ins);
     void visitSubstr(MSubstr* ins);
+    void visitRandom(MRandom* ins);
 };
 
 typedef LIRGeneratorMIPS LIRGeneratorSpecific;
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_mips_Lowering_mips_h */
--- a/js/src/jit/x64/Assembler-x64.h
+++ b/js/src/jit/x64/Assembler-x64.h
@@ -560,16 +560,23 @@ class Assembler : public AssemblerX86Sha
     }
     void xorq(Register src, Register dest) {
         masm.xorq_rr(src.encoding(), dest.encoding());
     }
     void xorq(Imm32 imm, Register dest) {
         masm.xorq_ir(imm.value, dest.encoding());
     }
 
+    void imulq(Register src, Register dest) {
+        masm.imulq_rr(src.encoding(), dest.encoding());
+    }
+    void vcvtsi2sdq(Register src, FloatRegister dest) {
+        masm.vcvtsi2sdq_rr(src.encoding(), dest.encoding());
+    }
+
     void mov(ImmWord word, Register dest) {
         // Use xor for setting registers to zero, as it is specially optimized
         // for this purpose on modern hardware. Note that it does clobber FLAGS
         // though. Use xorl instead of xorq since they are functionally
         // equivalent (32-bit instructions zero-extend their results to 64 bits)
         // and xorl has a smaller encoding.
         if (word.value == 0)
             xorl(dest, dest);
--- a/js/src/jit/x64/CodeGenerator-x64.cpp
+++ b/js/src/jit/x64/CodeGenerator-x64.cpp
@@ -814,8 +814,134 @@ CodeGeneratorX64::visitTruncateFToInt32(
     FloatRegister input = ToFloatRegister(ins->input());
     Register output = ToRegister(ins->output());
 
     // On x64, branchTruncateFloat32 uses vcvttss2sq. Unlike the x86
     // implementation, this should handle most floats and we can just
     // call a stub if it fails.
     emitTruncateFloat32(input, output, ins->mir());
 }
+
+namespace js {
+namespace jit {
+
+// Out-of-line math_random_no_outparam call for LRandom.
+class OutOfLineRandom : public OutOfLineCodeBase<CodeGeneratorX64>
+{
+    LRandom* lir_;
+
+  public:
+    explicit OutOfLineRandom(LRandom* lir)
+      : lir_(lir)
+    { }
+
+    void accept(CodeGeneratorX64* codegen) {
+        codegen->visitOutOfLineRandom(this);
+    }
+
+    LRandom* lir() const {
+        return lir_;
+    }
+};
+
+} // namespace jit
+} // namespace js
+
+static const double RNG_DSCALE_INV = 1 / RNG_DSCALE;
+
+void
+CodeGeneratorX64::visitRandom(LRandom* ins)
+{
+    FloatRegister output = ToFloatRegister(ins->output());
+
+    Register JSCompartmentReg = ToRegister(ins->temp());
+    Register rngStateReg = ToRegister(ins->temp2());
+    Register highReg = ToRegister(ins->temp3());
+    Register lowReg = ToRegister(ins->temp4());
+    Register rngMaskReg = ToRegister(ins->temp5());
+
+    // rngState = cx->compartment()->rngState
+    masm.loadJSContext(JSCompartmentReg);
+    masm.loadPtr(Address(JSCompartmentReg, JSContext::offsetOfCompartment()), JSCompartmentReg);
+    masm.loadPtr(Address(JSCompartmentReg, JSCompartment::offsetOfRngState()), rngStateReg);
+
+    // if rngState == 0, escape from inlined code and call
+    // math_random_no_outparam.
+    OutOfLineRandom* ool = new(alloc()) OutOfLineRandom(ins);
+    addOutOfLineCode(ool, ins->mir());
+    masm.branchTestPtr(Assembler::Zero, rngStateReg, rngStateReg, ool->entry());
+
+    // nextstate = rngState * RNG_MULTIPLIER;
+    Register& rngMultiplierReg = lowReg;
+    masm.movq(ImmWord(RNG_MULTIPLIER), rngMultiplierReg);
+    masm.imulq(rngMultiplierReg, rngStateReg);
+
+    // nextstate += RNG_ADDEND;
+    masm.addq(Imm32(RNG_ADDEND), rngStateReg);
+
+    // nextstate &= RNG_MASK;
+    masm.movq(ImmWord(RNG_MASK), rngMaskReg);
+    masm.andq(rngMaskReg, rngStateReg);
+
+    // rngState = nextstate
+
+    // if rngState == 0, escape from inlined code and call
+    // math_random_no_outparam.
+    masm.j(Assembler::Zero, ool->entry());
+
+    // high = (nextstate >> (RNG_STATE_WIDTH - RNG_HIGH_BITS)) << RNG_LOW_BITS;
+    masm.movq(rngStateReg, highReg);
+    masm.shrq(Imm32(RNG_STATE_WIDTH - RNG_HIGH_BITS), highReg);
+    masm.shlq(Imm32(RNG_LOW_BITS), highReg);
+
+    // nextstate = rngState * RNG_MULTIPLIER;
+    masm.imulq(rngMultiplierReg, rngStateReg);
+
+    // nextstate += RNG_ADDEND;
+    masm.addq(Imm32(RNG_ADDEND), rngStateReg);
+
+    // nextstate &= RNG_MASK;
+    masm.andq(rngMaskReg, rngStateReg);
+
+    // low = nextstate >> (RNG_STATE_WIDTH - RNG_LOW_BITS);
+    masm.movq(rngStateReg, lowReg);
+    masm.shrq(Imm32(RNG_STATE_WIDTH - RNG_LOW_BITS), lowReg);
+
+    // output = double(high | low);
+    masm.orq(highReg, lowReg);
+    masm.vcvtsi2sdq(lowReg, output);
+
+    // output = output * RNG_DSCALE_INV;
+    Register& rngDscaleInvReg = lowReg;
+    masm.movq(ImmPtr(&RNG_DSCALE_INV), rngDscaleInvReg);
+    masm.vmulsd(Operand(rngDscaleInvReg, 0), output, output);
+
+    // cx->compartment()->rngState = nextstate
+    masm.storePtr(rngStateReg, Address(JSCompartmentReg, JSCompartment::offsetOfRngState()));
+
+    masm.bind(ool->rejoin());
+}
+
+void
+CodeGeneratorX64::visitOutOfLineRandom(OutOfLineRandom* ool)
+{
+    LRandom* ins = ool->lir();
+    Register temp = ToRegister(ins->temp());
+    Register temp2 = ToRegister(ins->temp2());
+    MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg);
+
+    LiveRegisterSet regs;
+    regs.add(ReturnFloat32Reg);
+    regs.add(ReturnDoubleReg);
+    regs.add(ReturnInt32x4Reg);
+    regs.add(ReturnFloat32x4Reg);
+    saveVolatile(regs);
+
+    masm.loadJSContext(temp);
+
+    masm.setupUnalignedABICall(1, temp2);
+    masm.passABIArg(temp);
+    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, math_random_no_outparam), MoveOp::DOUBLE);
+
+    restoreVolatile(regs);
+
+    masm.jump(ool->rejoin());
+}
--- a/js/src/jit/x64/CodeGenerator-x64.h
+++ b/js/src/jit/x64/CodeGenerator-x64.h
@@ -7,16 +7,18 @@
 #ifndef jit_x64_CodeGenerator_x64_h
 #define jit_x64_CodeGenerator_x64_h
 
 #include "jit/x86-shared/CodeGenerator-x86-shared.h"
 
 namespace js {
 namespace jit {
 
+class OutOfLineRandom;
+
 class CodeGeneratorX64 : public CodeGeneratorX86Shared
 {
     CodeGeneratorX64* thisFromCtor() {
         return this;
     }
 
   protected:
     ValueOperand ToValue(LInstruction* ins, size_t pos);
@@ -53,16 +55,18 @@ class CodeGeneratorX64 : public CodeGene
     void visitAsmJSAtomicBinopHeap(LAsmJSAtomicBinopHeap* ins);
     void visitAsmJSAtomicBinopHeapForEffect(LAsmJSAtomicBinopHeapForEffect* ins);
     void visitAsmJSLoadGlobalVar(LAsmJSLoadGlobalVar* ins);
     void visitAsmJSStoreGlobalVar(LAsmJSStoreGlobalVar* ins);
     void visitAsmJSLoadFuncPtr(LAsmJSLoadFuncPtr* ins);
     void visitAsmJSLoadFFIFunc(LAsmJSLoadFFIFunc* ins);
     void visitAsmJSUInt32ToDouble(LAsmJSUInt32ToDouble* lir);
     void visitAsmJSUInt32ToFloat32(LAsmJSUInt32ToFloat32* lir);
+    void visitRandom(LRandom* ins);
+    void visitOutOfLineRandom(OutOfLineRandom* ool);
 };
 
 typedef CodeGeneratorX64 CodeGeneratorSpecific;
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_x64_CodeGenerator_x64_h */
--- a/js/src/jit/x64/LIR-x64.h
+++ b/js/src/jit/x64/LIR-x64.h
@@ -116,12 +116,47 @@ class LAsmJSLoadFuncPtr : public LInstru
     const LAllocation* index() {
         return getOperand(0);
     }
     const LDefinition* temp() {
         return getTemp(0);
     }
 };
 
+// Math.random().
+class LRandom : public LInstructionHelper<1, 0, 5>
+{
+  public:
+    LIR_HEADER(Random)
+    LRandom(const LDefinition &temp, const LDefinition &temp2, const LDefinition &temp3,
+            const LDefinition &temp4, const LDefinition &temp5)
+    {
+        setTemp(0, temp);
+        setTemp(1, temp2);
+        setTemp(2, temp3);
+        setTemp(3, temp4);
+        setTemp(4, temp5);
+    }
+    const LDefinition* temp() {
+        return getTemp(0);
+    }
+    const LDefinition* temp2() {
+        return getTemp(1);
+    }
+    const LDefinition *temp3() {
+        return getTemp(2);
+    }
+    const LDefinition *temp4() {
+        return getTemp(3);
+    }
+    const LDefinition *temp5() {
+        return getTemp(4);
+    }
+
+    MRandom* mir() const {
+        return mir_->toRandom();
+    }
+};
+
 } // namespace jit
 } // namespace js
 
 #endif /* jit_x64_LIR_x64_h */
--- a/js/src/jit/x64/Lowering-x64.cpp
+++ b/js/src/jit/x64/Lowering-x64.cpp
@@ -304,8 +304,19 @@ LIRGeneratorX64::visitSubstr(MSubstr* in
     assignSafepoint(lir, ins);
 }
 
 void
 LIRGeneratorX64::visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic* ins)
 {
     MOZ_CRASH("NYI");
 }
+
+void
+LIRGeneratorX64::visitRandom(MRandom* ins)
+{
+    LRandom *lir = new(alloc()) LRandom(temp(),
+                                        temp(),
+                                        temp(),
+                                        temp(),
+                                        temp());
+    defineFixed(lir, ins, LFloatReg(ReturnDoubleReg));
+}
--- a/js/src/jit/x64/Lowering-x64.h
+++ b/js/src/jit/x64/Lowering-x64.h
@@ -46,16 +46,17 @@ class LIRGeneratorX64 : public LIRGenera
     void visitAsmJSUnsignedToFloat32(MAsmJSUnsignedToFloat32* ins);
     void visitAsmJSLoadHeap(MAsmJSLoadHeap* ins);
     void visitAsmJSStoreHeap(MAsmJSStoreHeap* ins);
     void visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr* ins);
     void visitAsmJSCompareExchangeHeap(MAsmJSCompareExchangeHeap* ins);
     void visitAsmJSAtomicBinopHeap(MAsmJSAtomicBinopHeap* ins);
     void visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic* ins);
     void visitSubstr(MSubstr* ins);
+    void visitRandom(MRandom* ins);
 };
 
 typedef LIRGeneratorX64 LIRGeneratorSpecific;
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_x64_Lowering_x64_h */
--- a/js/src/jit/x86-shared/BaseAssembler-x86-shared.h
+++ b/js/src/jit/x86-shared/BaseAssembler-x86-shared.h
@@ -1306,16 +1306,24 @@ public:
             m_formatter.oneByteOp(OP_IMUL_GvEvIb, src, dst);
             m_formatter.immediate8s(value);
         } else {
             m_formatter.oneByteOp(OP_IMUL_GvEvIz, src, dst);
             m_formatter.immediate32(value);
         }
     }
 
+#ifdef JS_CODEGEN_X64
+    void imulq_rr(RegisterID src, RegisterID dst)
+    {
+        spew("imulq      %s, %s", GPReg64Name(src), GPReg64Name(dst));
+        m_formatter.twoByteOp64(OP2_IMUL_GvEv, src, dst);
+    }
+#endif
+
     void idivl_r(RegisterID divisor)
     {
         spew("idivl      %s", GPReg32Name(divisor));
         m_formatter.oneByteOp(OP_GROUP3_Ev, divisor, GROUP3_OP_IDIV);
     }
 
     void divl_r(RegisterID divisor)
     {
@@ -2766,16 +2774,23 @@ public:
 
 #ifdef JS_CODEGEN_X86
     void vcvtsi2sd_mr(const void* address, XMMRegisterID src0, XMMRegisterID dst)
     {
         twoByteOpSimd("vcvtsi2sd", VEX_SD, OP2_CVTSI2SD_VsdEd, address, src0, dst);
     }
 #endif
 
+#ifdef JS_CODEGEN_X64
+    void vcvtsi2sdq_rr(RegisterID src, XMMRegisterID dst)
+    {
+        twoByteOpInt64Simd("vcvtsi2sdq", VEX_SD, OP2_CVTSI2SD_VsdEd, src, invalid_xmm, dst);
+    }
+#endif
+
     void vcvttsd2si_rr(XMMRegisterID src, RegisterID dst)
     {
         twoByteOpSimdInt32("vcvttsd2si", VEX_SD, OP2_CVTTSD2SI_GdWsd, src, dst);
     }
 
     void vcvttss2si_rr(XMMRegisterID src, RegisterID dst)
     {
         twoByteOpSimdInt32("vcvttss2si", VEX_SS, OP2_CVTTSD2SI_GdWsd, src, dst);
--- a/js/src/jit/x86/CodeGenerator-x86.cpp
+++ b/js/src/jit/x86/CodeGenerator-x86.cpp
@@ -4,16 +4,17 @@
  * 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 "jit/x86/CodeGenerator-x86.h"
 
 #include "mozilla/Casting.h"
 #include "mozilla/DebugOnly.h"
 
+#include "jsmath.h"
 #include "jsnum.h"
 
 #include "jit/IonCaches.h"
 #include "jit/MIR.h"
 #include "jit/MIRGraph.h"
 #include "js/Conversions.h"
 #include "vm/Shape.h"
 
@@ -1107,8 +1108,23 @@ CodeGeneratorX86::visitOutOfLineTruncate
         masm.storeCallResult(output);
         masm.pop(input);
 
         restoreVolatile(output);
     }
 
     masm.jump(ool->rejoin());
 }
+
+void
+CodeGeneratorX86::visitRandom(LRandom* ins)
+{
+    Register temp = ToRegister(ins->temp());
+    Register temp2 = ToRegister(ins->temp2());
+
+    masm.loadJSContext(temp);
+
+    masm.setupUnalignedABICall(1, temp2);
+    masm.passABIArg(temp);
+    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, math_random_no_outparam), MoveOp::DOUBLE);
+
+    MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg);
+}
--- a/js/src/jit/x86/CodeGenerator-x86.h
+++ b/js/src/jit/x86/CodeGenerator-x86.h
@@ -66,16 +66,18 @@ class CodeGeneratorX86 : public CodeGene
     void visitAsmJSLoadGlobalVar(LAsmJSLoadGlobalVar* ins);
     void visitAsmJSStoreGlobalVar(LAsmJSStoreGlobalVar* ins);
     void visitAsmJSLoadFuncPtr(LAsmJSLoadFuncPtr* ins);
     void visitAsmJSLoadFFIFunc(LAsmJSLoadFFIFunc* ins);
 
     void visitOutOfLineTruncate(OutOfLineTruncate* ool);
     void visitOutOfLineTruncateFloat32(OutOfLineTruncateFloat32* ool);
 
+    void visitRandom(LRandom* ins);
+
   private:
     void asmJSAtomicComputeAddress(Register addrTemp, Register ptrReg, bool boundsCheck,
                                    int32_t offset, int32_t endOffset, Register out,
                                    Label& rejoin);
 };
 
 typedef CodeGeneratorX86 CodeGeneratorSpecific;
 
--- a/js/src/jit/x86/LIR-x86.h
+++ b/js/src/jit/x86/LIR-x86.h
@@ -138,12 +138,29 @@ class LAsmJSLoadFuncPtr : public LInstru
     MAsmJSLoadFuncPtr* mir() const {
         return mir_->toAsmJSLoadFuncPtr();
     }
     const LAllocation* index() {
         return getOperand(0);
     }
 };
 
+// Math.random().
+class LRandom : public LCallInstructionHelper<1, 0, 2>
+{
+  public:
+    LIR_HEADER(Random)
+    LRandom(const LDefinition& temp, const LDefinition& temp2) {
+        setTemp(0, temp);
+        setTemp(1, temp2);
+    }
+    const LDefinition* temp() {
+        return getTemp(0);
+    }
+    const LDefinition* temp2() {
+        return getTemp(1);
+    }
+};
+
 } // namespace jit
 } // namespace js
 
 #endif /* jit_x86_LIR_x86_h */
--- a/js/src/jit/x86/Lowering-x86.cpp
+++ b/js/src/jit/x86/Lowering-x86.cpp
@@ -408,8 +408,15 @@ LIRGeneratorX86::visitSubstr(MSubstr* in
                                          useRegister(ins->begin()),
                                          useRegister(ins->length()),
                                          temp(),
                                          LDefinition::BogusTemp(),
                                          tempByteOpRegister());
     define(lir, ins);
     assignSafepoint(lir, ins);
 }
+
+void
+LIRGeneratorX86::visitRandom(MRandom* ins)
+{
+    LRandom* lir = new(alloc()) LRandom(tempFixed(CallTempReg0), tempFixed(CallTempReg1));
+    defineReturn(lir, ins);
+}
--- a/js/src/jit/x86/Lowering-x86.h
+++ b/js/src/jit/x86/Lowering-x86.h
@@ -52,16 +52,17 @@ class LIRGeneratorX86 : public LIRGenera
     void visitAsmJSUnsignedToFloat32(MAsmJSUnsignedToFloat32* ins);
     void visitAsmJSLoadHeap(MAsmJSLoadHeap* ins);
     void visitAsmJSStoreHeap(MAsmJSStoreHeap* ins);
     void visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr* ins);
     void visitAsmJSCompareExchangeHeap(MAsmJSCompareExchangeHeap* ins);
     void visitAsmJSAtomicBinopHeap(MAsmJSAtomicBinopHeap* ins);
     void visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic* ins);
     void visitSubstr(MSubstr* ins);
+    void visitRandom(MRandom* ins);
     void lowerPhi(MPhi* phi);
 
     static bool allowTypedElementHoleCheck() {
         return true;
     }
 
     static bool allowStaticTypedArrayAccesses() {
         return true;
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -482,16 +482,18 @@ MSG_DEF(JSMSG_SYMBOL_TO_STRING,        0
 MSG_DEF(JSMSG_SYMBOL_TO_NUMBER,        0, JSEXN_TYPEERR, "can't convert symbol to number")
 
 // Atomics and futexes
 MSG_DEF(JSMSG_ATOMICS_BAD_ARRAY,         0, JSEXN_TYPEERR, "invalid array type for the operation")
 MSG_DEF(JSMSG_ATOMICS_TOO_LONG,          0, JSEXN_RANGEERR, "timeout value too large")
 MSG_DEF(JSMSG_ATOMICS_WAIT_NOT_ALLOWED,  0, JSEXN_ERR, "waiting is not allowed on this thread")
 
 // XPConnect wrappers and DOM bindings
-MSG_DEF(JSMSG_CANT_SET_INTERPOSED,      1, JSEXN_TYPEERR, "unable to set interposed data property '{0}'")
+MSG_DEF(JSMSG_CANT_SET_INTERPOSED,       1, JSEXN_TYPEERR, "unable to set interposed data property '{0}'")
 MSG_DEF(JSMSG_CANT_DEFINE_WINDOW_ELEMENT, 0, JSEXN_TYPEERR, "can't define elements on a Window object")
 MSG_DEF(JSMSG_CANT_DELETE_WINDOW_ELEMENT, 0, JSEXN_TYPEERR, "can't delete elements from a Window object")
 MSG_DEF(JSMSG_CANT_DELETE_WINDOW_NAMED_PROPERTY, 1, JSEXN_TYPEERR, "can't delete property {0} from window's named properties object")
-MSG_DEF(JSMSG_CANT_PREVENT_EXTENSIONS,  0, JSEXN_TYPEERR, "can't prevent extensions on this proxy object")
+MSG_DEF(JSMSG_CANT_PREVENT_EXTENSIONS,   0, JSEXN_TYPEERR, "can't prevent extensions on this proxy object")
+MSG_DEF(JSMSG_NO_NAMED_SETTER,           2, JSEXN_TYPEERR, "{0} doesn't have a named property setter for '{1}'")
+MSG_DEF(JSMSG_NO_INDEXED_SETTER,         2, JSEXN_TYPEERR, "{0} doesn't have an indexed property setter for '{1}'")
 
 // Super
 MSG_DEF(JSMSG_CANT_DELETE_SUPER, 0, JSEXN_REFERENCEERR, "invalid delete involving 'super'")
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -125,22 +125,31 @@ JS::CallArgs::requireAtLeast(JSContext* 
                              fnname, numArgsStr, required == 2 ? "" : "s");
         return false;
     }
 
     return true;
 }
 
 static bool
-ErrorTakesIdArgument(unsigned msg)
+ErrorTakesArguments(unsigned msg)
 {
     MOZ_ASSERT(msg < JSErr_Limit);
     unsigned argCount = js_ErrorFormatString[msg].argCount;
-    MOZ_ASSERT(argCount <= 1);
-    return argCount == 1;
+    MOZ_ASSERT(argCount <= 2);
+    return argCount == 1 || argCount == 2;
+}
+
+static bool
+ErrorTakesObjectArgument(unsigned msg)
+{
+    MOZ_ASSERT(msg < JSErr_Limit);
+    unsigned argCount = js_ErrorFormatString[msg].argCount;
+    MOZ_ASSERT(argCount <= 2);
+    return argCount == 2;
 }
 
 JS_PUBLIC_API(bool)
 JS::ObjectOpResult::reportStrictErrorOrWarning(JSContext* cx, HandleObject obj, HandleId id,
                                                bool strict)
 {
     static_assert(unsigned(OkCode) == unsigned(JSMSG_NOT_AN_ERROR),
                   "unsigned value of OkCode must not be an error code");
@@ -148,38 +157,43 @@ JS::ObjectOpResult::reportStrictErrorOrW
     MOZ_ASSERT(!ok());
 
     unsigned flags = strict ? JSREPORT_ERROR : (JSREPORT_WARNING | JSREPORT_STRICT);
     if (code_ == JSMSG_OBJECT_NOT_EXTENSIBLE || code_ == JSMSG_SET_NON_OBJECT_RECEIVER) {
         RootedValue val(cx, ObjectValue(*obj));
         return ReportValueErrorFlags(cx, flags, code_, JSDVG_IGNORE_STACK, val,
                                      nullptr, nullptr, nullptr);
     }
-    if (ErrorTakesIdArgument(code_)) {
+    if (ErrorTakesArguments(code_)) {
         RootedValue idv(cx, IdToValue(id));
         RootedString str(cx, ValueToSource(cx, idv));
         if (!str)
             return false;
 
         JSAutoByteString propName(cx, str);
         if (!propName)
             return false;
 
+        if (ErrorTakesObjectArgument(code_)) {
+            return JS_ReportErrorFlagsAndNumber(cx, flags, GetErrorMessage, nullptr, code_,
+                                                obj->getClass()->name, propName.ptr());
+        }
+
         return JS_ReportErrorFlagsAndNumber(cx, flags, GetErrorMessage, nullptr, code_,
                                             propName.ptr());
     }
     return JS_ReportErrorFlagsAndNumber(cx, flags, GetErrorMessage, nullptr, code_);
 }
 
 JS_PUBLIC_API(bool)
 JS::ObjectOpResult::reportStrictErrorOrWarning(JSContext* cx, HandleObject obj, bool strict)
 {
     MOZ_ASSERT(code_ != Uninitialized);
     MOZ_ASSERT(!ok());
-    MOZ_ASSERT(!ErrorTakesIdArgument(code_));
+    MOZ_ASSERT(!ErrorTakesArguments(code_));
 
     unsigned flags = strict ? JSREPORT_ERROR : (JSREPORT_WARNING | JSREPORT_STRICT);
     return JS_ReportErrorFlagsAndNumber(cx, flags, GetErrorMessage, nullptr, code_);
 }
 
 JS_PUBLIC_API(bool)
 JS::ObjectOpResult::failCantRedefineProp()
 {
@@ -229,16 +243,28 @@ JS::ObjectOpResult::failCantDeleteWindow
 }
 
 JS_PUBLIC_API(bool)
 JS::ObjectOpResult::failCantPreventExtensions()
 {
     return fail(JSMSG_CANT_PREVENT_EXTENSIONS);
 }
 
+JS_PUBLIC_API(bool)
+JS::ObjectOpResult::failNoNamedSetter()
+{
+    return fail(JSMSG_NO_NAMED_SETTER);
+}
+
+JS_PUBLIC_API(bool)
+JS::ObjectOpResult::failNoIndexedSetter()
+{
+    return fail(JSMSG_NO_INDEXED_SETTER);
+}
+
 JS_PUBLIC_API(int64_t)
 JS_Now()
 {
     return PRMJ_Now();
 }
 
 JS_PUBLIC_API(jsval)
 JS_GetNaNValue(JSContext* cx)
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -287,16 +287,19 @@ struct JSContext : public js::ExclusiveC
     ~JSContext();
 
     JSRuntime* runtime() const { return runtime_; }
     js::PerThreadData& mainThread() const { return runtime()->mainThread; }
 
     static size_t offsetOfRuntime() {
         return offsetof(JSContext, runtime_);
     }
+    static size_t offsetOfCompartment() {
+        return offsetof(JSContext, compartment_);
+    }
 
     friend class js::ExclusiveContext;
     friend class JS::AutoSaveExceptionState;
     friend class js::jit::DebugModeOSRVolatileJitFrameIterator;
     friend void js::ReportOverRecursed(JSContext*);
 
   private:
     /* Exception state -- the exception member is a GC root by definition. */
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -450,16 +450,20 @@ struct JSCompartment
 
     void findOutgoingEdges(js::gc::ComponentFinder<JS::Zone>& finder);
 
     js::DtoaCache dtoaCache;
 
     /* Random number generator state, used by jsmath.cpp. */
     uint64_t rngState;
 
+    static size_t offsetOfRngState() {
+        return offsetof(JSCompartment, rngState);
+    }
+
   private:
     JSCompartment* thisForCtor() { return this; }
 
   public:
     //
     // The Debugger observes execution on a frame-by-frame basis. The
     // invariants of JSCompartment's debug mode bits, JSScript::isDebuggee,
     // InterpreterFrame::isDebuggee, and BaselineFrame::isDebuggee are
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -304,24 +304,16 @@ js::IsAtomsCompartment(JSCompartment* co
 
 JS_FRIEND_API(bool)
 js::IsAtomsZone(JS::Zone* zone)
 {
     return zone->runtimeFromAnyThread()->isAtomsZone(zone);
 }
 
 JS_FRIEND_API(bool)
-js::IsInNonStrictPropertySet(JSContext* cx)
-{
-    jsbytecode* pc;
-    JSScript* script = cx->currentScript(&pc, JSContext::ALLOW_CROSS_COMPARTMENT);
-    return script && !IsStrictSetPC(pc) && (js_CodeSpec[*pc].format & JOF_SET);
-}
-
-JS_FRIEND_API(bool)
 js::IsFunctionObject(JSObject* obj)
 {
     return obj->is<JSFunction>();
 }
 
 JS_FRIEND_API(bool)
 js::IsScopeObject(JSObject* obj)
 {
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -476,25 +476,16 @@ extern JS_FRIEND_API(bool)
 IsSystemZone(JS::Zone* zone);
 
 extern JS_FRIEND_API(bool)
 IsAtomsCompartment(JSCompartment* comp);
 
 extern JS_FRIEND_API(bool)
 IsAtomsZone(JS::Zone* zone);
 
-/*
- * Returns whether we're in a non-strict property set (in that we're in a
- * non-strict script and the bytecode we're on is a property set).  The return
- * value does NOT indicate any sort of exception was thrown: it's just a
- * boolean.
- */
-extern JS_FRIEND_API(bool)
-IsInNonStrictPropertySet(JSContext* cx);
-
 struct WeakMapTracer
 {
     JSRuntime* runtime;
 
     explicit WeakMapTracer(JSRuntime* rt) : runtime(rt) {}
 
     // Weak map tracer callback, called once for every binding of every
     // weak map that was live at the time of the last garbage collection.
--- a/js/src/jsmath.cpp
+++ b/js/src/jsmath.cpp
@@ -760,47 +760,43 @@ random_generateSeed()
 #else
 # error "Platform needs to implement random_generateSeed()"
 #endif
 
     seed.u64 ^= PRMJ_Now();
     return seed.u64;
 }
 
-static const uint64_t RNG_MULTIPLIER = 0x5DEECE66DLL;
-static const uint64_t RNG_ADDEND = 0xBLL;
-static const uint64_t RNG_MASK = (1LL << 48) - 1;
-
 /*
  * Math.random() support, lifted from java.util.Random.java.
  */
 void
 js::random_initState(uint64_t* rngState)
 {
     /* Our PRNG only uses 48 bits, so squeeze our entropy into those bits. */
     uint64_t seed = random_generateSeed();
     seed ^= (seed >> 16);
     *rngState = (seed ^ RNG_MULTIPLIER) & RNG_MASK;
 }
 
 uint64_t
 js::random_next(uint64_t* rngState, int bits)
 {
     MOZ_ASSERT((*rngState & 0xffff000000000000ULL) == 0, "Bad rngState");
-    MOZ_ASSERT(bits > 0 && bits <= 48, "bits is out of range");
+    MOZ_ASSERT(bits > 0 && bits <= RNG_STATE_WIDTH, "bits is out of range");
 
     if (*rngState == 0) {
         random_initState(rngState);
     }
 
     uint64_t nextstate = *rngState * RNG_MULTIPLIER;
     nextstate += RNG_ADDEND;
     nextstate &= RNG_MASK;
     *rngState = nextstate;
-    return nextstate >> (48 - bits);
+    return nextstate >> (RNG_STATE_WIDTH - bits);
 }
 
 double
 js::math_random_no_outparam(JSContext* cx)
 {
     /* Calculate random without memory traffic, for use in the JITs. */
     return random_nextDouble(&cx->compartment()->rngState);
 }
--- a/js/src/jsmath.h
+++ b/js/src/jsmath.h
@@ -92,21 +92,28 @@ InitMathClass(JSContext* cx, HandleObjec
 
 extern void
 random_initState(uint64_t* rngState);
 
 extern uint64_t
 random_next(uint64_t* rngState, int bits);
 
 static const double RNG_DSCALE = double(1LL << 53);
+static const int RNG_STATE_WIDTH = 48;
+static const int RNG_HIGH_BITS = 26;
+static const int RNG_LOW_BITS = 27;
+static const uint64_t RNG_MULTIPLIER = 0x5DEECE66DLL;
+static const uint64_t RNG_ADDEND = 0xBLL;
+static const uint64_t RNG_MASK = (1LL << RNG_STATE_WIDTH) - 1;
 
 inline double
 random_nextDouble(uint64_t* rng)
 {
-    return double((random_next(rng, 26) << 27) + random_next(rng, 27)) / RNG_DSCALE;
+    return double((random_next(rng, RNG_HIGH_BITS) << RNG_LOW_BITS) +
+                  random_next(rng, RNG_LOW_BITS)) / RNG_DSCALE;
 }
 
 extern double
 math_random_no_outparam(JSContext* cx);
 
 extern bool
 math_random(JSContext* cx, unsigned argc, js::Value* vp);
 
--- a/js/src/jsreflect.cpp
+++ b/js/src/jsreflect.cpp
@@ -1905,31 +1905,31 @@ ASTSerializer::aop(JSOp op)
       default:
         return AOP_ERR;
     }
 }
 
 UnaryOperator
 ASTSerializer::unop(ParseNodeKind kind, JSOp op)
 {
-    if (kind == PNK_DELETE)
+    if (IsDeleteKind(kind))
         return UNOP_DELETE;
 
+    if (kind == PNK_TYPEOFNAME || kind == PNK_TYPEOFEXPR)
+        return UNOP_TYPEOF;
+
     switch (op) {
       case JSOP_NEG:
         return UNOP_NEG;
       case JSOP_POS:
         return UNOP_POS;
       case JSOP_NOT:
         return UNOP_NOT;
       case JSOP_BITNOT:
         return UNOP_BITNOT;
-      case JSOP_TYPEOF:
-      case JSOP_TYPEOFEXPR:
-        return UNOP_TYPEOF;
       case JSOP_VOID:
         return UNOP_VOID;
       default:
         return UNOP_ERR;
     }
 }
 
 BinaryOperator
@@ -2921,18 +2921,24 @@ ASTSerializer::expression(ParseNode* pn,
       case PNK_MOD:
       case PNK_BITOR:
       case PNK_BITXOR:
       case PNK_BITAND:
       case PNK_IN:
       case PNK_INSTANCEOF:
         return leftAssociate(pn, dst);
 
-      case PNK_DELETE:
-      case PNK_TYPEOF:
+      case PNK_DELETENAME:
+      case PNK_DELETEPROP:
+      case PNK_DELETESUPERPROP:
+      case PNK_DELETEELEM:
+      case PNK_DELETESUPERELEM:
+      case PNK_DELETEEXPR:
+      case PNK_TYPEOFNAME:
+      case PNK_TYPEOFEXPR:
       case PNK_VOID:
       case PNK_NOT:
       case PNK_BITNOT:
       case PNK_POS:
       case PNK_NEG: {
         MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_kid->pn_pos));
 
         UnaryOperator op = unop(pn->getKind(), pn->getOp());
--- a/js/src/jswrapper.h
+++ b/js/src/jswrapper.h
@@ -200,20 +200,20 @@ class JS_FRIEND_API(OpaqueCrossCompartme
 
     /* SpiderMonkey extensions. */
     virtual bool getPropertyDescriptor(JSContext* cx, HandleObject wrapper, HandleId id,
                                        MutableHandle<JSPropertyDescriptor> desc) const override;
     virtual bool hasOwn(JSContext* cx, HandleObject wrapper, HandleId id,
                         bool* bp) const override;
     virtual bool getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject wrapper,
                                               AutoIdVector& props) const override;
-    virtual bool objectClassIs(HandleObject obj, ESClassValue classValue, JSContext* cx) const;
+    virtual bool objectClassIs(HandleObject obj, ESClassValue classValue, JSContext* cx) const override;
     virtual const char* className(JSContext* cx, HandleObject wrapper) const override;
-    virtual JSString* fun_toString(JSContext* cx, HandleObject proxy, unsigned indent) const;
-    virtual bool defaultValue(JSContext* cx, HandleObject obj, JSType hint, MutableHandleValue vp) const;
+    virtual JSString* fun_toString(JSContext* cx, HandleObject proxy, unsigned indent) const override;
+    virtual bool defaultValue(JSContext* cx, HandleObject obj, JSType hint, MutableHandleValue vp) const override;
 
     static const OpaqueCrossCompartmentWrapper singleton;
 };
 
 /*
  * Base class for security wrappers. A security wrapper is potentially hiding
  * all or part of some wrapped object thus SecurityWrapper defaults to denying
  * access to the wrappee. This is the opposite of Wrapper which tries to be
--- a/js/src/vm/Xdr.h
+++ b/js/src/vm/Xdr.h
@@ -24,21 +24,21 @@ namespace js {
  * versions.  If deserialization fails, the data should be invalidated if
  * possible.
  *
  * When you change this, run make_opcode_doc.py and copy the new output into
  * this wiki page:
  *
  *  https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode
  */
-static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 287;
+static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 288;
 static const uint32_t XDR_BYTECODE_VERSION =
     uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND);
 
-static_assert(JSErr_Limit == 398,
+static_assert(JSErr_Limit == 400,
               "GREETINGS, POTENTIAL SUBTRAHEND INCREMENTER! If you added or "
               "removed MSG_DEFs from js.msg, you should increment "
               "XDR_BYTECODE_VERSION_SUBTRAHEND and update this assertion's "
               "expected JSErr_Limit value.");
 
 class XDRBuffer {
   public:
     explicit XDRBuffer(JSContext* cx)
--- a/js/xpconnect/src/XPCMaps.cpp
+++ b/js/xpconnect/src/XPCMaps.cpp
@@ -169,17 +169,17 @@ JSObject2WrappedJSMap::SizeOfWrappedJS(m
 Native2WrappedNativeMap*
 Native2WrappedNativeMap::newMap(int length)
 {
     return new Native2WrappedNativeMap(length);
 }
 
 Native2WrappedNativeMap::Native2WrappedNativeMap(int length)
 {
-    mTable = new PLDHashTable2(PL_DHashGetStubOps(), sizeof(Entry), length);
+    mTable = new PLDHashTable(PL_DHashGetStubOps(), sizeof(Entry), length);
 }
 
 Native2WrappedNativeMap::~Native2WrappedNativeMap()
 {
     delete mTable;
 }
 
 size_t
@@ -217,17 +217,17 @@ IID2WrappedJSClassMap::newMap(int length
     if (map && map->mTable)
         return map;
     delete map;
     return nullptr;
 }
 
 IID2WrappedJSClassMap::IID2WrappedJSClassMap(int length)
 {
-    mTable = new PLDHashTable2(&Entry::sOps, sizeof(Entry), length);
+    mTable = new PLDHashTable(&Entry::sOps, sizeof(Entry), length);
 }
 
 IID2WrappedJSClassMap::~IID2WrappedJSClassMap()
 {
     delete mTable;
 }
 
 
@@ -250,17 +250,17 @@ IID2NativeInterfaceMap::newMap(int lengt
     if (map && map->mTable)
         return map;
     delete map;
     return nullptr;
 }
 
 IID2NativeInterfaceMap::IID2NativeInterfaceMap(int length)
 {
-    mTable = new PLDHashTable2(&Entry::sOps, sizeof(Entry), length);
+    mTable = new PLDHashTable(&Entry::sOps, sizeof(Entry), length);
 }
 
 IID2NativeInterfaceMap::~IID2NativeInterfaceMap()
 {
     delete mTable;
 }
 
 size_t
@@ -291,17 +291,17 @@ ClassInfo2NativeSetMap::newMap(int lengt
     if (map && map->mTable)
         return map;
     delete map;
     return nullptr;
 }
 
 ClassInfo2NativeSetMap::ClassInfo2NativeSetMap(int length)
 {
-    mTable = new PLDHashTable2(PL_DHashGetStubOps(), sizeof(Entry), length);
+    mTable = new PLDHashTable(PL_DHashGetStubOps(), sizeof(Entry), length);
 }
 
 ClassInfo2NativeSetMap::~ClassInfo2NativeSetMap()
 {
     delete mTable;
 }
 
 size_t
@@ -321,17 +321,17 @@ ClassInfo2NativeSetMap::ShallowSizeOfInc
 ClassInfo2WrappedNativeProtoMap*
 ClassInfo2WrappedNativeProtoMap::newMap(int length)
 {
     return new ClassInfo2WrappedNativeProtoMap(length);
 }
 
 ClassInfo2WrappedNativeProtoMap::ClassInfo2WrappedNativeProtoMap(int length)
 {
-    mTable = new PLDHashTable2(PL_DHashGetStubOps(), sizeof(Entry), length);
+    mTable = new PLDHashTable(PL_DHashGetStubOps(), sizeof(Entry), length);
 }
 
 ClassInfo2WrappedNativeProtoMap::~ClassInfo2WrappedNativeProtoMap()
 {
     delete mTable;
 }
 
 size_t
@@ -441,17 +441,17 @@ NativeSetMap::newMap(int length)
     if (map && map->mTable)
         return map;
     delete map;
     return nullptr;
 }
 
 NativeSetMap::NativeSetMap(int length)
 {
-    mTable = new PLDHashTable2(&Entry::sOps, sizeof(Entry), length);
+    mTable = new PLDHashTable(&Entry::sOps, sizeof(Entry), length);
 }
 
 NativeSetMap::~NativeSetMap()
 {
     delete mTable;
 }
 
 size_t
@@ -504,17 +504,17 @@ IID2ThisTranslatorMap::newMap(int length
     if (map && map->mTable)
         return map;
     delete map;
     return nullptr;
 }
 
 IID2ThisTranslatorMap::IID2ThisTranslatorMap(int length)
 {
-    mTable = new PLDHashTable2(&Entry::sOps, sizeof(Entry), length);
+    mTable = new PLDHashTable(&Entry::sOps, sizeof(Entry), length);
 }
 
 IID2ThisTranslatorMap::~IID2ThisTranslatorMap()
 {
     delete mTable;
 }
 
 /***************************************************************************/
@@ -580,17 +580,17 @@ XPCNativeScriptableSharedMap::newMap(int
     if (map && map->mTable)
         return map;
     delete map;
     return nullptr;
 }
 
 XPCNativeScriptableSharedMap::XPCNativeScriptableSharedMap(int length)
 {
-    mTable = new PLDHashTable2(&Entry::sOps, sizeof(Entry), length);
+    mTable = new PLDHashTable(&Entry::sOps, sizeof(Entry), length);
 }
 
 XPCNativeScriptableSharedMap::~XPCNativeScriptableSharedMap()
 {
     delete mTable;
 }
 
 bool
@@ -631,18 +631,18 @@ XPCWrappedNativeProtoMap::newMap(int len
     if (map && map->mTable)
         return map;
     delete map;
     return nullptr;
 }
 
 XPCWrappedNativeProtoMap::XPCWrappedNativeProtoMap(int length)
 {
-    mTable = new PLDHashTable2(PL_DHashGetStubOps(),
-                               sizeof(PLDHashEntryStub), length);
+    mTable = new PLDHashTable(PL_DHashGetStubOps(),
+                              sizeof(PLDHashEntryStub), length);
 }
 
 XPCWrappedNativeProtoMap::~XPCWrappedNativeProtoMap()
 {
     delete mTable;
 }
 
 /***************************************************************************/
--- a/js/xpconnect/src/XPCMaps.h
+++ b/js/xpconnect/src/XPCMaps.h
@@ -153,17 +153,17 @@ public:
     ~Native2WrappedNativeMap();
 private:
     Native2WrappedNativeMap();    // no implementation
     explicit Native2WrappedNativeMap(int size);
 
     static size_t SizeOfEntryExcludingThis(PLDHashEntryHdr* hdr, mozilla::MallocSizeOf mallocSizeOf, void*);
 
 private:
-    PLDHashTable2* mTable;
+    PLDHashTable* mTable;
 };
 
 /*************************/
 
 class IID2WrappedJSClassMap
 {
 public:
     struct Entry : public PLDHashEntryHdr
@@ -207,17 +207,17 @@ public:
     inline uint32_t Enumerate(PLDHashEnumerator f, void* arg)
         {return PL_DHashTableEnumerate(mTable, f, arg);}
 
     ~IID2WrappedJSClassMap();
 private:
     IID2WrappedJSClassMap();    // no implementation
     explicit IID2WrappedJSClassMap(int size);
 private:
-    PLDHashTable2* mTable;
+    PLDHashTable* mTable;
 };
 
 /*************************/
 
 class IID2NativeInterfaceMap
 {
 public:
     struct Entry : public PLDHashEntryHdr
@@ -266,17 +266,17 @@ public:
     ~IID2NativeInterfaceMap();
 private:
     IID2NativeInterfaceMap();    // no implementation
     explicit IID2NativeInterfaceMap(int size);
 
     static size_t SizeOfEntryExcludingThis(PLDHashEntryHdr* hdr, mozilla::MallocSizeOf mallocSizeOf, void*);
 
 private:
-    PLDHashTable2* mTable;
+    PLDHashTable* mTable;
 };
 
 /*************************/
 
 class ClassInfo2NativeSetMap
 {
 public:
     struct Entry : public PLDHashEntryHdr
@@ -323,17 +323,17 @@ public:
     // pointers to *all* XPCNativeSets).  Hence the "Shallow".
     size_t ShallowSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
 
     ~ClassInfo2NativeSetMap();
 private:
     ClassInfo2NativeSetMap();    // no implementation
     explicit ClassInfo2NativeSetMap(int size);
 private:
-    PLDHashTable2* mTable;
+    PLDHashTable* mTable;
 };
 
 /*************************/
 
 class ClassInfo2WrappedNativeProtoMap
 {
 public:
     struct Entry : public PLDHashEntryHdr
@@ -379,17 +379,17 @@ public:
     ~ClassInfo2WrappedNativeProtoMap();
 private:
     ClassInfo2WrappedNativeProtoMap();    // no implementation
     explicit ClassInfo2WrappedNativeProtoMap(int size);
 
     static size_t SizeOfEntryExcludingThis(PLDHashEntryHdr* hdr, mozilla::MallocSizeOf mallocSizeOf, void*);
 
 private:
-    PLDHashTable2* mTable;
+    PLDHashTable* mTable;
 };
 
 /*************************/
 
 class NativeSetMap
 {
 public:
     struct Entry : public PLDHashEntryHdr
@@ -449,17 +449,17 @@ public:
     ~NativeSetMap();
 private:
     NativeSetMap();    // no implementation
     explicit NativeSetMap(int size);
 
     static size_t SizeOfEntryExcludingThis(PLDHashEntryHdr* hdr, mozilla::MallocSizeOf mallocSizeOf, void*);
 
 private:
-    PLDHashTable2* mTable;
+    PLDHashTable* mTable;
 };
 
 /***************************************************************************/
 
 class IID2ThisTranslatorMap
 {
 public:
     struct Entry : public PLDHashEntryHdr
@@ -507,17 +507,17 @@ public:
     inline uint32_t Enumerate(PLDHashEnumerator f, void* arg)
         {return PL_DHashTableEnumerate(mTable, f, arg);}
 
     ~IID2ThisTranslatorMap();
 private:
     IID2ThisTranslatorMap();    // no implementation
     explicit IID2ThisTranslatorMap(int size);
 private:
-    PLDHashTable2* mTable;
+    PLDHashTable* mTable;
 };
 
 /***************************************************************************/
 
 class XPCNativeScriptableSharedMap
 {
 public:
     struct Entry : public PLDHashEntryHdr
@@ -543,17 +543,17 @@ public:
     inline uint32_t Enumerate(PLDHashEnumerator f, void* arg)
         {return PL_DHashTableEnumerate(mTable, f, arg);}
 
     ~XPCNativeScriptableSharedMap();
 private:
     XPCNativeScriptableSharedMap();    // no implementation
     explicit XPCNativeScriptableSharedMap(int size);
 private:
-    PLDHashTable2* mTable;
+    PLDHashTable* mTable;
 };
 
 /***************************************************************************/
 
 class XPCWrappedNativeProtoMap
 {
 public:
     static XPCWrappedNativeProtoMap* newMap(int length);
@@ -581,17 +581,17 @@ public:
     inline uint32_t Enumerate(PLDHashEnumerator f, void* arg)
         {return PL_DHashTableEnumerate(mTable, f, arg);}
 
     ~XPCWrappedNativeProtoMap();
 private:
     XPCWrappedNativeProtoMap();    // no implementation
     explicit XPCWrappedNativeProtoMap(int size);
 private:
-    PLDHashTable2* mTable;
+    PLDHashTable* mTable;
 };
 
 /***************************************************************************/
 
 class JSObject2JSObjectMap
 {
     typedef js::HashMap<JSObject*, JS::Heap<JSObject*>, js::PointerHasher<JSObject*, 3>,
                         js::SystemAllocPolicy> Map;
--- a/js/xpconnect/wrappers/XrayWrapper.cpp
+++ b/js/xpconnect/wrappers/XrayWrapper.cpp
@@ -454,17 +454,17 @@ JSXrayTraits::resolveOwnProperty(JSConte
         return true;
     }
 
     // The non-HasPrototypes semantics implemented by traditional Xrays are kind
     // of broken with respect to |own|-ness and the holder. The common code
     // muddles through by only checking the holder for non-|own| lookups, but
     // that doesn't work for us. So we do an explicit holder check here, and hope
     // that this mess gets fixed up soon.
-    if (!JS_GetPropertyDescriptorById(cx, holder, id, desc))
+    if (!JS_GetOwnPropertyDescriptorById(cx, holder, id, desc))
         return false;
     if (desc.object()) {
         desc.object().set(wrapper);
         return true;
     }
 
     // Handle the 'constructor' property.
     if (id == GetRTIdByIndex(cx, XPCJSRuntime::IDX_CONSTRUCTOR)) {
@@ -516,23 +516,23 @@ JSXrayTraits::resolveOwnProperty(JSConte
         if (fsMatch->selfHostedName) {
             fun = JS::GetSelfHostedFunction(cx, fsMatch->selfHostedName, id, fsMatch->nargs);
         } else {
             fun = JS_NewFunctionById(cx, fsMatch->call.op, fsMatch->nargs, 0, id);
         }
         if (!fun)
             return false;
 
-        // The generic Xray machinery only defines non-own properties on the holder.
-        // This is broken, and will be fixed at some point, but for now we need to
-        // cache the value explicitly. See the corresponding call to
-        // JS_GetPropertyById at the top of this function.
+        // The generic Xray machinery only defines non-own properties of the target on
+        // the holder. This is broken, and will be fixed at some point, but for now we
+        // need to cache the value explicitly. See the corresponding call to
+        // JS_GetOwnPropertyDescriptorById at the top of this function.
         RootedObject funObj(cx, JS_GetFunctionObject(fun));
         return JS_DefinePropertyById(cx, holder, id, funObj, 0) &&
-               JS_GetPropertyDescriptorById(cx, holder, id, desc);
+               JS_GetOwnPropertyDescriptorById(cx, holder, id, desc);
     }
 
     // Scan through the properties.
     const JSPropertySpec* psMatch = nullptr;
     for (const JSPropertySpec* ps = clasp->spec.prototypeProperties(); ps && ps->name; ++ps) {
         if (PropertySpecNameEqualsId(ps->name, id)) {
             psMatch = ps;
             break;
@@ -575,17 +575,17 @@ JSXrayTraits::resolveOwnProperty(JSConte
                                      UndefinedHandleValue,
                                      // This particular descriptor, unlike most,
                                      // actually stores JSNatives directly,
                                      // since we just set it up.  Do NOT pass
                                      // JSPROP_PROPOP_ACCESSORS here!
                                      desc.attributes(),
                                      JS_PROPERTYOP_GETTER(desc.getter()),
                                      JS_PROPERTYOP_SETTER(desc.setter())) &&
-               JS_GetPropertyDescriptorById(cx, holder, id, desc);
+               JS_GetOwnPropertyDescriptorById(cx, holder, id, desc);
     }
 
     return true;
 }
 
 bool
 JSXrayTraits::delete_(JSContext* cx, HandleObject wrapper, HandleId id, ObjectOpResult& result)
 {
@@ -1237,17 +1237,17 @@ XPCWrappedNativeXrayTraits::resolveNativ
         JSFunction* toString = JS_NewFunction(cx, XrayToString, 0, 0, "toString");
         if (!toString)
             return false;
 
         FillPropertyDescriptor(desc, wrapper, 0,
                                ObjectValue(*JS_GetFunctionObject(toString)));
 
         return JS_DefinePropertyById(cx, holder, id, desc) &&
-               JS_GetPropertyDescriptorById(cx, holder, id, desc);
+               JS_GetOwnPropertyDescriptorById(cx, holder, id, desc);
     }
 
     desc.object().set(holder);
     desc.setAttributes(JSPROP_ENUMERATE);
     desc.setGetter(nullptr);
     desc.setSetter(nullptr);
     desc.value().set(JSVAL_VOID);
 
@@ -1330,17 +1330,17 @@ XrayTraits::resolveOwnProperty(JSContext
     if (!getExpandoObject(cx, target, wrapper, &expando))
         return false;
 
     // Check for expando properties first. Note that the expando object lives
     // in the target compartment.
     bool found = false;
     if (expando) {
         JSAutoCompartment ac(cx, expando);
-        if (!JS_GetPropertyDescriptorById(cx, expando, id, desc))
+        if (!JS_GetOwnPropertyDescriptorById(cx, expando, id, desc))
             return false;
         found = !!desc.object();
     }
 
     // Next, check for ES builtins.
     if (!found && JS_IsGlobalObject(target)) {
         JSProtoKey key = JS_IdToProtoKey(cx, id);
         JSAutoCompartment ac(cx, target);
@@ -1376,17 +1376,17 @@ XrayTraits::resolveOwnProperty(JSContext
     {
         if (!JS_AlreadyHasOwnPropertyById(cx, holder, id, &found))
             return false;
         if (!found && !JS_DefinePropertyById(cx, holder, id, UndefinedHandleValue,
                                              JSPROP_ENUMERATE | JSPROP_SHARED,
                                              wrappedJSObject_getter)) {
             return false;
         }
-        if (!JS_GetPropertyDescriptorById(cx, holder, id, desc))
+        if (!JS_GetOwnPropertyDescriptorById(cx, holder, id, desc))
             return false;
         desc.object().set(wrapper);
         return true;
     }
 
     return true;
 }
 
@@ -1424,17 +1424,17 @@ XPCWrappedNativeXrayTraits::resolveOwnPr
             }
         }
     }
 
     // Xray wrappers don't use the regular wrapper hierarchy, so we should be
     // in the wrapper's compartment here, not the wrappee.
     MOZ_ASSERT(js::IsObjectInContextCompartment(wrapper, cx));
 
-    return JS_GetPropertyDescriptorById(cx, holder, id, desc);
+    return JS_GetOwnPropertyDescriptorById(cx, holder, id, desc);
 }
 
 bool
 XPCWrappedNativeXrayTraits::defineProperty(JSContext* cx, HandleObject wrapper, HandleId id,
                                            Handle<JSPropertyDescriptor> desc,
                                            Handle<JSPropertyDescriptor> existingDesc,
                                            JS::ObjectOpResult& result, bool* defined)
 {
@@ -1567,17 +1567,17 @@ DOMXrayTraits::resolveOwnProperty(JSCont
                 }
                 desc.value().setObject(*obj);
                 FillPropertyDescriptor(desc, wrapper, true);
                 return JS_WrapPropertyDescriptor(cx, desc);
             }
         }
     }
 
-    if (!JS_GetPropertyDescriptorById(cx, holder, id, desc))
+    if (!JS_GetOwnPropertyDescriptorById(cx, holder, id, desc))
         return false;
     if (desc.object()) {
         desc.object().set(wrapper);
         return true;
     }
 
     RootedObject obj(cx, getTargetObject(wrapper));
     bool cacheOnHolder;
@@ -1585,17 +1585,17 @@ DOMXrayTraits::resolveOwnProperty(JSCont
         return false;
 
     MOZ_ASSERT(!desc.object() || desc.object() == wrapper, "What did we resolve this on?");
 
     if (!desc.object() || !cacheOnHolder)
         return true;
 
     return JS_DefinePropertyById(cx, holder, id, desc) &&
-           JS_GetPropertyDescriptorById(cx, holder, id, desc);
+           JS_GetOwnPropertyDescriptorById(cx, holder, id, desc);
 }
 
 bool
 DOMXrayTraits::defineProperty(JSContext* cx, HandleObject wrapper, HandleId id,
                               Handle<JSPropertyDescriptor> desc,
                               Handle<JSPropertyDescriptor> existingDesc,
                               JS::ObjectOpResult& result, bool* defined)
 {
@@ -1861,17 +1861,17 @@ XrayWrapper<Base, Traits>::getPropertyDe
     // Finally, we call resolveNativeProperty, which checks non-own properties,
     // and unconditionally caches what it finds on the holder.
 
     // Check resolveOwnProperty.
     if (!Traits::singleton.resolveOwnProperty(cx, *this, wrapper, holder, id, desc))
         return false;
 
     // Check the holder.
-    if (!desc.object() && !JS_GetPropertyDescriptorById(cx, holder, id, desc))
+    if (!desc.object() && !JS_GetOwnPropertyDescriptorById(cx, holder, id, desc))
         return false;
     if (desc.object()) {
         desc.object().set(wrapper);
         return true;
     }
 
     // Nothing in the cache. Call through, and cache the result.
     if (!Traits::singleton.resolveNativeProperty(cx, wrapper, holder, id, desc))
@@ -1903,17 +1903,17 @@ XrayWrapper<Base, Traits>::getPropertyDe
         }
     }
 
     // If we still have nothing, we're done.
     if (!desc.object())
         return true;
 
     if (!JS_DefinePropertyById(cx, holder, id, desc) ||
-        !JS_GetPropertyDescriptorById(cx, holder, id, desc))
+        !JS_GetOwnPropertyDescriptorById(cx, holder, id, desc))
     {
         return false;
     }
     MOZ_ASSERT(desc.object());
     desc.object().set(wrapper);
     return true;
 }
 
--- a/layout/base/FrameLayerBuilder.cpp
+++ b/layout/base/FrameLayerBuilder.cpp
@@ -4403,16 +4403,25 @@ InvalidateVisibleBoundsChangesForScrolle
         printf_stderr("Invalidating layer %p: %s\n", aLayer, str.get());
       }
 #endif
     }
     data->mIgnoreInvalidationsOutsideRect = Nothing();
   }
 }
 
+static inline const Maybe<ParentLayerIntRect>&
+GetStationaryClipInContainer(Layer* aLayer)
+{
+  if (size_t metricsCount = aLayer->GetFrameMetricsCount()) {
+    return aLayer->GetFrameMetrics(metricsCount - 1).GetClipRect();
+  }
+  return aLayer->GetClipRect();
+}
+
 void
 ContainerState::PostprocessRetainedLayers(nsIntRegion* aOpaqueRegionForContainer)
 {
   nsAutoTArray<OpaqueRegionEntry,4> opaqueRegions;
   bool hideAll = false;
   int32_t opaqueRegionForContainer = -1;
 
   for (int32_t i = mNewChildLayers.Length() - 1; i >= 0; --i) {
@@ -4429,17 +4438,17 @@ ContainerState::PostprocessRetainedLayer
     OpaqueRegionEntry* data = FindOpaqueRegionEntry(opaqueRegions,
         animatedGeometryRootForOpaqueness, e->mFixedPosFrameForLayerData);
 
     SetupScrollingMetadata(e);
 
     if (hideAll) {
       e->mVisibleRegion.SetEmpty();
     } else if (!e->mLayer->IsScrollbarContainer()) {
-      const Maybe<ParentLayerIntRect>& clipRect = e->mLayer->GetClipRect();
+      const Maybe<ParentLayerIntRect>& clipRect = GetStationaryClipInContainer(e->mLayer);
       if (clipRect && opaqueRegionForContainer >= 0 &&
           opaqueRegions[opaqueRegionForContainer].mOpaqueRegion.Contains(ParentLayerIntRect::ToUntyped(*clipRect))) {
         e->mVisibleRegion.SetEmpty();
       } else if (data) {
         e->mVisibleRegion.Sub(e->mVisibleRegion, data->mOpaqueRegion);
       }
     }
 
@@ -4469,17 +4478,17 @@ ContainerState::PostprocessRetainedLayer
           opaqueRegionForContainer = opaqueRegions.Length();
         }
         data = opaqueRegions.AppendElement();
         data->mAnimatedGeometryRoot = animatedGeometryRootToCover;
         data->mFixedPosFrameForLayerData = e->mFixedPosFrameForLayerData;
       }
 
       nsIntRegion clippedOpaque = e->mOpaqueRegion;
-      const Maybe<ParentLayerIntRect>& clipRect = e->mLayer->GetClipRect();
+      Maybe<ParentLayerIntRect> clipRect = e->mLayer->GetCombinedClipRect();
       if (clipRect) {
         clippedOpaque.AndWith(ParentLayerIntRect::ToUntyped(*clipRect));
       }
       data->mOpaqueRegion.Or(data->mOpaqueRegion, clippedOpaque);
       if (e->mHideAllLayersBelow) {
         hideAll = true;
       }
     }
--- a/layout/base/nsFrameManager.cpp
+++ b/layout/base/nsFrameManager.cpp
@@ -159,23 +159,21 @@ nsFrameManager::Destroy()
 //----------------------------------------------------------------------
 
 // Placeholder frame functions
 nsPlaceholderFrame*
 nsFrameManager::GetPlaceholderFrameFor(const nsIFrame* aFrame)
 {
   NS_PRECONDITION(aFrame, "null param unexpected");
 
-  if (mPlaceholderMap.IsInitialized()) {
-    PlaceholderMapEntry *entry = static_cast<PlaceholderMapEntry*>
-                                            (PL_DHashTableSearch(const_cast<PLDHashTable2*>(&mPlaceholderMap),
-                                aFrame));
-    if (entry) {
-      return entry->placeholderFrame;
-    }
+  PlaceholderMapEntry *entry = static_cast<PlaceholderMapEntry*>
+                                          (PL_DHashTableSearch(const_cast<PLDHashTable*>(&mPlaceholderMap),
+                              aFrame));
+  if (entry) {
+    return entry->placeholderFrame;
   }
 
   return nullptr;
 }
 
 nsresult
 nsFrameManager::RegisterPlaceholderFrame(nsPlaceholderFrame* aPlaceholderFrame)
 {
@@ -196,38 +194,34 @@ nsFrameManager::RegisterPlaceholderFrame
 
 void
 nsFrameManager::UnregisterPlaceholderFrame(nsPlaceholderFrame* aPlaceholderFrame)
 {
   NS_PRECONDITION(aPlaceholderFrame, "null param unexpected");
   NS_PRECONDITION(nsGkAtoms::placeholderFrame == aPlaceholderFrame->GetType(),
                   "unexpected frame type");
 
-  if (mPlaceholderMap.IsInitialized()) {
-    PL_DHashTableRemove(&mPlaceholderMap,
-                        aPlaceholderFrame->GetOutOfFlowFrame());
-  }
+  PL_DHashTableRemove(&mPlaceholderMap,
+                      aPlaceholderFrame->GetOutOfFlowFrame());
 }
 
 static PLDHashOperator
 UnregisterPlaceholders(PLDHashTable* table, PLDHashEntryHdr* hdr,
                        uint32_t number, void* arg)
 {
   PlaceholderMapEntry* entry = static_cast<PlaceholderMapEntry*>(hdr);
   entry->placeholderFrame->SetOutOfFlowFrame(nullptr);
   return PL_DHASH_NEXT;
 }
 
 void
 nsFrameManager::ClearPlaceholderFrameMap()
 {
-  if (mPlaceholderMap.IsInitialized()) {
-    PL_DHashTableEnumerate(&mPlaceholderMap, UnregisterPlaceholders, nullptr);
-    mPlaceholderMap.Clear();
-  }
+  PL_DHashTableEnumerate(&mPlaceholderMap, UnregisterPlaceholders, nullptr);
+  mPlaceholderMap.Clear();
 }
 
 //----------------------------------------------------------------------
 
 /* static */ nsStyleContext*
 nsFrameManager::GetStyleContextInMap(UndisplayedMap* aMap, nsIContent* aContent)
 {
   if (!aContent) {
--- a/layout/base/nsFrameManagerBase.h
+++ b/layout/base/nsFrameManagerBase.h
@@ -51,17 +51,17 @@ public:
 protected:
   class UndisplayedMap;
 
   // weak link, because the pres shell owns us
   nsIPresShell* MOZ_NON_OWNING_REF mPresShell;
   // the pres shell owns the style set
   nsStyleSet*                     mStyleSet;
   nsIFrame*                       mRootFrame;
-  PLDHashTable2                   mPlaceholderMap;
+  PLDHashTable                    mPlaceholderMap;
   UndisplayedMap*                 mUndisplayedMap;
   UndisplayedMap*                 mDisplayContentsMap;
   bool                            mIsDestroyingFrames;  // The frame manager is destroying some frame(s).
 
   // The frame tree generation number
   // We use this to avoid unnecessary screenshotting
   // on Android. Unfortunately, this is static to match
   // the single consumer which is also static. Keeping
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -8214,16 +8214,18 @@ nsLayoutUtils::ComputeFrameMetrics(nsIFr
       LayoutDeviceIntSize::FromAppUnitsRounded(pageScrollAmount, presContext->AppUnitsPerDevPixel());
     metrics.SetPageScrollAmount(pageScrollAmountInDevPixels);
 
     if (!aScrollFrame->GetParent() ||
         EventStateManager::CanVerticallyScrollFrameWithWheel(aScrollFrame->GetParent()))
     {
       metrics.SetAllowVerticalScrollWithWheel();
     }
+
+    metrics.SetUsesContainerScrolling(scrollableFrame->UsesContainerScrolling());
   }
 
   // If we have the scrollparent being the same as the scroll id, the
   // compositor-side code could get into an infinite loop while building the
   // overscroll handoff chain.
   MOZ_ASSERT(aScrollParentId == FrameMetrics::NULL_SCROLL_ID || scrollId != aScrollParentId);
   metrics.SetScrollId(scrollId);
   metrics.SetIsRoot(aIsRoot);
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -3060,23 +3060,16 @@ ScrollFrameHelper::ComputeFrameMetrics(L
   }
 
   bool needsParentLayerClip = true;
   if (gfxPrefs::LayoutUseContainersForRootFrames() && !mAddClipRectToLayer) {
     // For containerful frames, the clip is on the container frame.
     needsParentLayerClip = false;
   }
 
-  // Note: Do not apply clips to the scroll ports of metrics above the first
-  // one on a given layer. They will be applied by the compositor instead,
-  // with async transforms for the scrollframes interspersed between them.
-  if (aOutput->Length() > 0) {
-    needsParentLayerClip = false;
-  }
-
   nsPoint toReferenceFrame = mOuter->GetOffsetToCrossDoc(aContainerReferenceFrame);
   bool isRoot = mIsRoot && mOuter->PresContext()->IsRootContentDocument();
 
   Maybe<nsRect> parentLayerClip;
   if (needsParentLayerClip) {
     nsRect clip = nsRect(mScrollPort.TopLeft() + toReferenceFrame,
                          nsLayoutUtils::CalculateCompositionSizeForFrame(mOuter));
     if (isRoot) {
@@ -5588,8 +5581,17 @@ ScrollFrameHelper::GetSnapPointForDestin
   } else {
     snapped = true;
   }
   if (snapped) {
     aDestination = finalPos;
   }
   return snapped;
 }
+
+bool
+ScrollFrameHelper::UsesContainerScrolling() const
+{
+  if (gfxPrefs::LayoutUseContainersForRootFrames()) {
+    return mIsRoot;
+  }
+  return false;
+}
--- a/layout/generic/nsGfxScrollFrame.h
+++ b/layout/generic/nsGfxScrollFrame.h
@@ -354,16 +354,17 @@ public:
       // If the block has some text-overflow stuff we should kick off a paint
       // because we have special behaviour for it when APZ scrolling is active.
       mOuter->SchedulePaint();
     }
   }
   bool IsTransformingByAPZ() const {
     return mTransformingByAPZ;
   }
+  bool UsesContainerScrolling() const;
 
   void ScheduleSyntheticMouseMove();
   static void ScrollActivityCallback(nsITimer *aTimer, void* anInstance);
 
   void HandleScrollbarStyleSwitching();
 
   nsIAtom* LastScrollOrigin() const { return mLastScrollOrigin; }
   nsIAtom* LastSmoothScrollOrigin() const { return mLastSmoothScrollOrigin; }
@@ -840,16 +841,19 @@ public:
                                 aParameters, aOutput);
   }
   virtual bool IsIgnoringViewportClipping() const override {
     return mHelper.IsIgnoringViewportClipping();
   }
   virtual void MarkScrollbarsDirtyForReflow() const override {
     mHelper.MarkScrollbarsDirtyForReflow();
   }
+  virtual bool UsesContainerScrolling() const override {
+    return mHelper.UsesContainerScrolling();
+  }
 
   // nsIStatefulFrame
   NS_IMETHOD SaveState(nsPresState** aState) override {
     NS_ENSURE_ARG_POINTER(aState);
     *aState = mHelper.SaveState();
     return NS_OK;
   }
   NS_IMETHOD RestoreState(nsPresState* aState) override {
@@ -1301,16 +1305,19 @@ public:
 
   virtual bool IsScrollbarOnRight() const override {
     return mHelper.IsScrollbarOnRight();
   }
 
   virtual void SetTransformingByAPZ(bool aTransforming) override {
     mHelper.SetTransformingByAPZ(aTransforming);
   }
+  virtual bool UsesContainerScrolling() const override {
+    return mHelper.UsesContainerScrolling();
+  }
   bool IsTransformingByAPZ() const override {
     return mHelper.IsTransformingByAPZ();
   }
 
 #ifdef DEBUG_FRAME_DUMP
   virtual nsresult GetFrameName(nsAString& aResult) const override;
 #endif
 
--- a/layout/generic/nsIScrollableFrame.h
+++ b/layout/generic/nsIScrollableFrame.h
@@ -427,11 +427,16 @@ public:
 
   /**
    * Mark the scrollbar frames for reflow.
    */
   virtual void MarkScrollbarsDirtyForReflow() const = 0;
 
   virtual void SetTransformingByAPZ(bool aTransforming) = 0;
   virtual bool IsTransformingByAPZ() const = 0;
+
+  /**
+   * Whether or not this frame uses containerful scrolling.
+   */
+  virtual bool UsesContainerScrolling() const = 0;
 };
 
 #endif
--- a/layout/reftests/image-element/reftest.list
+++ b/layout/reftests/image-element/reftest.list
@@ -1,9 +1,9 @@
-== bug-364968.html bug-364968-ref.html
+random == bug-364968.html bug-364968-ref.html
 == bug-463204.html bug-463204-ref.html
 fails-if(Android&&AndroidVersion<15&&AndroidVersion!=10) == canvas-outside-document.html canvas-inside-document.html
 == mozsetimageelement-01.html mozsetimageelement-01-ref.html
 skip-if(B2G||Mulet) == mozsetimageelement-02.html about:blank # bug 773482 # Initial mulet triage: parity with B2G/B2G Desktop
 skip-if(B2G||Mulet) == image-outside-document-invalidate.html about:blank # bug 773482 # Initial mulet triage: parity with B2G/B2G Desktop
 skip-if(B2G||Mulet) == canvas-outside-document-invalidate-01.html about:blank # bug 773482 # Initial mulet triage: parity with B2G/B2G Desktop
 skip-if(B2G||Mulet) fails-if(azureSkia) fails-if(cocoaWidget) == canvas-outside-document-invalidate-02.html about:blank # See bug 666800 # bug 773482 # Initial mulet triage: parity with B2G/B2G Desktop
 #fails with Skia due to Skia bug http://code.google.com/p/skia/issues/detail?id=568
--- a/layout/style/nsCSSRuleProcessor.cpp
+++ b/layout/style/nsCSSRuleProcessor.cpp
@@ -445,20 +445,20 @@ public:
 protected:
   typedef nsTArray<RuleValue> RuleValueList;
   void AppendRuleToTable(PLDHashTable* aTable, const void* aKey,
                          const RuleSelectorPair& aRuleInfo);
   void AppendUniversalRule(const RuleSelectorPair& aRuleInfo);
 
   int32_t     mRuleCount;
 
-  PLDHashTable2 mIdTable;
-  PLDHashTable2 mClassTable;
-  PLDHashTable2 mTagTable;
-  PLDHashTable2 mNameSpaceTable;
+  PLDHashTable mIdTable;
+  PLDHashTable mClassTable;
+  PLDHashTable mTagTable;
+  PLDHashTable mNameSpaceTable;
   RuleValueList mUniversalRules;
 
   struct EnumData {
     const RuleValue* mCurValue;
     const RuleValue* mEnd;
   };
   EnumData* mEnumList;
   int32_t   mEnumListSize;
@@ -887,24 +887,24 @@ struct RuleCascadeData {
 
   size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const;
 
   RuleHash                 mRuleHash;
   RuleHash*
     mPseudoElementRuleHashes[nsCSSPseudoElements::ePseudo_PseudoElementCount];
   nsTArray<nsCSSRuleProcessor::StateSelector>  mStateSelectors;
   EventStates              mSelectorDocumentStates;
-  PLDHashTable2            mClassSelectors;
-  PLDHashTable2            mIdSelectors;
+  PLDHashTable             mClassSelectors;
+  PLDHashTable             mIdSelectors;
   nsTArray<nsCSSSelector*> mPossiblyNegatedClassSelectors;
   nsTArray<nsCSSSelector*> mPossiblyNegatedIDSelectors;
-  PLDHashTable2            mAttributeSelectors;
-  PLDHashTable2            mAnonBoxRules;
+  PLDHashTable             mAttributeSelectors;
+  PLDHashTable             mAnonBoxRules;
 #ifdef MOZ_XUL
-  PLDHashTable2            mXULTreeRules;
+  PLDHashTable             mXULTreeRules;
 #endif
 
   nsTArray<nsFontFaceRuleContainer> mFontFaceRules;
   nsTArray<nsCSSKeyframesRule*> mKeyframesRules;
   nsTArray<nsCSSFontFeatureValuesRule*> mFontFeatureValuesRules;
   nsTArray<nsCSSPageRule*> mPageRules;
   nsTArray<nsCSSCounterStyleRule*> mCounterStyleRules;
 
@@ -3316,17 +3316,17 @@ struct CascadeEnumData {
   nsTArray<nsCSSKeyframesRule*>& mKeyframesRules;
   nsTArray<nsCSSFontFeatureValuesRule*>& mFontFeatureValuesRules;
   nsTArray<nsCSSPageRule*>& mPageRules;
   nsTArray<nsCSSCounterStyleRule*>& mCounterStyleRules;
   nsMediaQueryResultCacheKey& mCacheKey;
   PLArenaPool mArena;
   // Hooray, a manual PLDHashTable since nsClassHashtable doesn't
   // provide a getter that gives me a *reference* to the value.
-  PLDHashTable2 mRulesByWeight; // of PerWeightDataListItem linked lists
+  PLDHashTable mRulesByWeight; // of PerWeightDataListItem linked lists
   uint8_t mSheetType;
 };
 
 /*
  * This enumerates style rules in a sheet (and recursively into any
  * grouping rules) in order to:
  *  (1) add any style rules, in order, into data->mRulesByWeight (for
  *      the primary CSS cascade), where they are separated by weight
@@ -3516,19 +3516,16 @@ nsCSSRuleProcessor::RefreshRuleCascade(n
     if (newCascade) {
       CascadeEnumData data(aPresContext, newCascade->mFontFaceRules,
                            newCascade->mKeyframesRules,
                            newCascade->mFontFeatureValuesRules,
                            newCascade->mPageRules,
                            newCascade->mCounterStyleRules,
                            newCascade->mCacheKey,
                            mSheetType);
-      if (!data.mRulesByWeight.IsInitialized())
-        return; /* out of memory */
-
       for (uint32_t i = 0; i < mSheets.Length(); ++i) {
         if (!CascadeSheet(mSheets.ElementAt(i), &data))
           return; /* out of memory */
       }
 
       // Sort the hash table of per-weight linked lists by weight.
       uint32_t weightCount = data.mRulesByWeight.EntryCount();
       nsAutoArrayPtr<PerWeightData> weightArray(new PerWeightData[weightCount]);
--- a/layout/style/nsHTMLStyleSheet.h
+++ b/layout/style/nsHTMLStyleSheet.h
@@ -154,13 +154,13 @@ public: // for mLangRuleTable structures
 private:
   nsIDocument*            mDocument;
   nsRefPtr<HTMLColorRule> mLinkRule;
   nsRefPtr<HTMLColorRule> mVisitedRule;
   nsRefPtr<HTMLColorRule> mActiveRule;
   nsRefPtr<TableQuirkColorRule> mTableQuirkColorRule;
   nsRefPtr<TableTHRule>   mTableTHRule;
 
-  PLDHashTable2           mMappedAttrTable;
-  PLDHashTable2           mLangRuleTable;
+  PLDHashTable            mMappedAttrTable;
+  PLDHashTable            mLangRuleTable;
 };
 
 #endif /* !defined(nsHTMLStyleSheet_h_) */
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -1422,17 +1422,17 @@ nsRuleNode::DestroyInternal(nsRuleNode *
   if (aDestroyQueueTail) {
     destroyQueueTail = *aDestroyQueueTail;
   } else {
     destroyQueue = nullptr;
     destroyQueueTail = &destroyQueue;
   }
 
   if (ChildrenAreHashed()) {
-    PLDHashTable2 *children = ChildrenHash();
+    PLDHashTable *children = ChildrenHash();
     PL_DHashTableEnumerate(children, EnqueueRuleNodeChildren,
                            &destroyQueueTail);
     *destroyQueueTail = nullptr; // ensure null-termination
     delete children;
   } else if (HaveChildren()) {
     *destroyQueueTail = ChildrenList();
     do {
       destroyQueueTail = &(*destroyQueueTail)->mNextSibling;
@@ -1609,19 +1609,19 @@ void nsRuleNode::SetUsedDirectly()
   }
 }
 
 void
 nsRuleNode::ConvertChildrenToHash(int32_t aNumKids)
 {
   NS_ASSERTION(!ChildrenAreHashed() && HaveChildren(),
                "must have a non-empty list of children");
-  PLDHashTable2 *hash = new PLDHashTable2(&ChildrenHashOps,
-                                          sizeof(ChildrenHashEntry),
-                                          aNumKids);
+  PLDHashTable *hash = new PLDHashTable(&ChildrenHashOps,
+                                        sizeof(ChildrenHashEntry),
+                                        aNumKids);
   for (nsRuleNode* curr = ChildrenList(); curr; curr = curr->mNextSibling) {
     // This will never fail because of the initial size we gave the table.
     ChildrenHashEntry *entry = static_cast<ChildrenHashEntry*>(
       PL_DHashTableAdd(hash, curr->mRule, fallible));
     NS_ASSERTION(!entry->mRuleNode, "duplicate entries in list");
     entry->mRuleNode = curr;
   }
   SetChildrenHash(hash);
@@ -9369,17 +9369,17 @@ nsRuleNode::SweepChildren(nsTArray<nsRul
 {
   NS_ASSERTION(!(mDependentBits & NS_RULE_NODE_GC_MARK),
                "missing DestroyIfNotMarked() call");
   NS_ASSERTION(HaveChildren(),
                "why call SweepChildren with no children?");
   uint32_t childrenDestroyed = 0;
   nsRuleNode* survivorsWithChildren = nullptr;
   if (ChildrenAreHashed()) {
-    PLDHashTable2* children = ChildrenHash();
+    PLDHashTable* children = ChildrenHash();
     uint32_t oldChildCount = children->EntryCount();
     PL_DHashTableEnumerate(children, SweepHashEntry, &survivorsWithChildren);
     childrenDestroyed = oldChildCount - children->EntryCount();
     if (childrenDestroyed == oldChildCount) {
       delete children;
       mChildren.asVoid = nullptr;
     }
   } else {
--- a/layout/style/nsRuleNode.h
+++ b/layout/style/nsRuleNode.h
@@ -312,17 +312,17 @@ private:
   // The children of this node are stored in either a hashtable or list
   // that maps from rules to our nsRuleNode children.  When matching
   // rules, we use this mapping to transition from node to node
   // (constructing new nodes as needed to flesh out the tree).
 
   union {
     void* asVoid;
     nsRuleNode* asList;
-    PLDHashTable2* asHash;
+    PLDHashTable* asHash;
   } mChildren; // Accessed only through the methods below.
 
   enum {
     kTypeMask = 0x1,
     kListType = 0x0,
     kHashType = 0x1
   };
   enum {
@@ -338,28 +338,28 @@ private:
     return (intptr_t(mChildren.asVoid) & kTypeMask) == kHashType;
   }
   nsRuleNode* ChildrenList() {
     return mChildren.asList;
   }
   nsRuleNode** ChildrenListPtr() {
     return &mChildren.asList;
   }
-  PLDHashTable2* ChildrenHash() {
-    return (PLDHashTable2*) (intptr_t(mChildren.asHash) & ~intptr_t(kTypeMask));
+  PLDHashTable* ChildrenHash() {
+    return (PLDHashTable*) (intptr_t(mChildren.asHash) & ~intptr_t(kTypeMask));
   }
   void SetChildrenList(nsRuleNode *aList) {
     NS_ASSERTION(!(intptr_t(aList) & kTypeMask),
                  "pointer not 2-byte aligned");
     mChildren.asList = aList;
   }
-  void SetChildrenHash(PLDHashTable2 *aHashtable) {
+  void SetChildrenHash(PLDHashTable *aHashtable) {
     NS_ASSERTION(!(intptr_t(aHashtable) & kTypeMask),
                  "pointer not 2-byte aligned");
-    mChildren.asHash = (PLDHashTable2*)(intptr_t(aHashtable) | kHashType);
+    mChildren.asHash = (PLDHashTable*)(intptr_t(aHashtable) | kHashType);
   }
   void ConvertChildrenToHash(int32_t aNumKids);
 
   nsCachedStyleData mStyleData;   // Any data we cached on the rule node.
 
   uint32_t mDependentBits; // Used to cache the fact that we can look up
                            // cached data under a parent rule.
 
--- a/layout/tables/SpanningCellSorter.h
+++ b/layout/tables/SpanningCellSorter.h
@@ -57,17 +57,17 @@ private:
     Item *mArray[ARRAY_SIZE];
     int32_t SpanToIndex(int32_t aSpan) { return aSpan - ARRAY_BASE; }
     int32_t IndexToSpan(int32_t aIndex) { return aIndex + ARRAY_BASE; }
     bool UseArrayForSpan(int32_t aSpan) {
         NS_ASSERTION(SpanToIndex(aSpan) >= 0, "cell without colspan");
         return SpanToIndex(aSpan) < ARRAY_SIZE;
     }
 
-    PLDHashTable2 mHashTable;
+    PLDHashTable mHashTable;
     struct HashTableEntry : public PLDHashEntryHdr {
         int32_t mColSpan;
         Item *mItems;
     };
 
     static const PLDHashTableOps HashTableOps;
 
     static PLDHashNumber
--- a/layout/xul/nsMenuBarFrame.cpp
+++ b/layout/xul/nsMenuBarFrame.cpp
@@ -413,12 +413,13 @@ nsMenuBarFrame::DestroyFrom(nsIFrame* aD
   mTarget->RemoveSystemEventListener(NS_LITERAL_STRING("keypress"), mMenuBarListener, false);
   mTarget->RemoveSystemEventListener(NS_LITERAL_STRING("keydown"), mMenuBarListener, false);
   mTarget->RemoveSystemEventListener(NS_LITERAL_STRING("keyup"), mMenuBarListener, false);
 
   mTarget->RemoveEventListener(NS_LITERAL_STRING("mousedown"), mMenuBarListener, true);
   mTarget->RemoveEventListener(NS_LITERAL_STRING("mousedown"), mMenuBarListener, false);
   mTarget->RemoveEventListener(NS_LITERAL_STRING("blur"), mMenuBarListener, true);
 
+  mMenuBarListener->OnDestroyMenuBarFrame();
   mMenuBarListener = nullptr;
 
   nsBoxFrame::DestroyFrom(aDestructRoot);
 }
--- a/layout/xul/nsMenuBarListener.cpp
+++ b/layout/xul/nsMenuBarListener.cpp
@@ -43,16 +43,22 @@ nsMenuBarListener::nsMenuBarListener(nsM
 }
 
 ////////////////////////////////////////////////////////////////////////
 nsMenuBarListener::~nsMenuBarListener() 
 {
 }
 
 void
+nsMenuBarListener::OnDestroyMenuBarFrame()
+{
+  mMenuBarFrame = nullptr;
+}
+
+void
 nsMenuBarListener::InitializeStatics()
 {
   Preferences::AddBoolVarCache(&mAccessKeyFocuses,
                                "ui.key.menuAccessKeyFocuses");
 }
 
 nsresult
 nsMenuBarListener::GetMenuAccessKey(int32_t* aAccessKey)
@@ -135,25 +141,40 @@ nsMenuBarListener::KeyUp(nsIDOMEvent* aK
     uint32_t theChar;
     keyEvent->GetKeyCode(&theChar);
 
     if (!defaultPrevented && mAccessKeyDown && !mAccessKeyDownCanceled &&
         (int32_t)theChar == mAccessKey)
     {
       // The access key was down and is now up, and no other
       // keys were pressed in between.
+      bool toggleMenuActiveState = true;
       if (!mMenuBarFrame->IsActive()) {
-        mMenuBarFrame->SetActiveByKeyboard();
+        // First, close all existing popups because other popups shouldn't
+        // handle key events when menubar is active and IME should be
+        // disabled.
+        nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
+        if (pm) {
+          pm->Rollup(0, false, nullptr, nullptr);
+        }
+        // If menubar active state is changed or the menubar is destroyed
+        // during closing the popups, we should do nothing anymore.
+        toggleMenuActiveState = !Destroyed() && !mMenuBarFrame->IsActive();
       }
-      ToggleMenuActiveState();
+      if (toggleMenuActiveState) {
+        if (!mMenuBarFrame->IsActive()) {
+          mMenuBarFrame->SetActiveByKeyboard();
+        }
+        ToggleMenuActiveState();
+      }
     }
     mAccessKeyDown = false;
     mAccessKeyDownCanceled = false;
 
-    bool active = mMenuBarFrame->IsActive();
+    bool active = !Destroyed() && mMenuBarFrame->IsActive();
     if (active) {
       aKeyEvent->StopPropagation();
       aKeyEvent->PreventDefault();
       return NS_OK; // I am consuming event
     }
   }
   
   return NS_OK; // means I am NOT consuming event
--- a/layout/xul/nsMenuBarListener.h
+++ b/layout/xul/nsMenuBarListener.h
@@ -37,29 +37,33 @@ public:
   nsresult MouseDown(nsIDOMEvent* aMouseEvent);
 
   static nsresult GetMenuAccessKey(int32_t* aAccessKey);
   
   NS_DECL_ISUPPORTS
 
   static bool IsAccessKeyPressed(nsIDOMKeyEvent* event);
 
+  void OnDestroyMenuBarFrame();
+
 protected:
   /** default destructor
    */
   virtual ~nsMenuBarListener();
 
   static void InitAccessKey();
 
   static mozilla::Modifiers GetModifiersForAccessKey(nsIDOMKeyEvent* event);
 
   // This should only be called by the nsMenuBarListener during event dispatch,
   // thus ensuring that this doesn't get destroyed during the process.
   void ToggleMenuActiveState();
 
+  bool Destroyed() const { return !mMenuBarFrame; }
+
   nsMenuBarFrame* mMenuBarFrame; // The menu bar object.
   // Whether or not the ALT key is currently down.
   bool mAccessKeyDown;
   // Whether or not the ALT key down is canceled by other action.
   bool mAccessKeyDownCanceled;
   static bool mAccessKeyFocuses; // Does the access key by itself focus the menubar?
   static int32_t mAccessKey;     // See nsIDOMKeyEvent.h for sample values
   static mozilla::Modifiers mAccessKeyMask;// Modifier mask for the access key
--- a/layout/xul/test/browser.ini
+++ b/layout/xul/test/browser.ini
@@ -1,5 +1,7 @@
 [DEFAULT]
 
 [browser_bug685470.js]
 [browser_bug703210.js]
 [browser_bug706743.js]
+[browser_bug1163304.js]
+skip-if = os != 'linux' && os != 'win' // Due to testing menubar behavior with keyboard
new file mode 100644
--- /dev/null
+++ b/layout/xul/test/browser_bug1163304.js
@@ -0,0 +1,35 @@
+function test() {
+  waitForExplicitFinish();
+
+  let searchBar = BrowserSearch.searchBar;
+  searchBar.focus();
+
+  let DOMWindowUtils = EventUtils._getDOMWindowUtils();
+  is(DOMWindowUtils.IMEStatus, DOMWindowUtils.IME_STATUS_ENABLED,
+     "IME should be available when searchbar has focus");
+
+  let searchPopup = document.getElementById("PopupSearchAutoComplete");
+  searchPopup.addEventListener("popupshown", function (aEvent) {
+    searchPopup.removeEventListener("popupshown", arguments.callee);
+    setTimeout(function () {
+      is(DOMWindowUtils.IMEStatus, DOMWindowUtils.IME_STATUS_ENABLED,
+         "IME should be available even when the popup of searchbar is open");
+      searchPopup.addEventListener("popuphidden", function (aEvent) {
+        searchPopup.removeEventListener("popuphidden", arguments.callee);
+        setTimeout(function () {
+          is(DOMWindowUtils.IMEStatus, DOMWindowUtils.IME_STATUS_DISABLED,
+             "IME should not be available when menubar is active");
+          // Inactivate the menubar (and restore the focus to the searchbar
+          EventUtils.synthesizeKey("VK_ESCAPE", {});
+          is(DOMWindowUtils.IMEStatus, DOMWindowUtils.IME_STATUS_ENABLED,
+             "IME should be available after focus is back to the searchbar");
+          finish();
+        }, 0);
+      });
+      // Activate the menubar, then, the popup should be closed
+      EventUtils.synthesizeKey("VK_ALT", {});
+    }, 0);
+  });
+  // Open popup of the searchbar
+  EventUtils.synthesizeKey("VK_F4", {});
+}
--- a/memory/mozalloc/mozalloc_abort.cpp
+++ b/memory/mozalloc/mozalloc_abort.cpp
@@ -50,20 +50,29 @@ void fillAbortMessage(char (&msg)[N], ui
         reinterpret_cast<void*>(retAddress - uintptr_t(info.dli_fbase));
     const char* const sym = info.dli_sname ? info.dli_sname : "";
 
     snprintf(msg, sizeof(msg), "abort() called from %s:%p (%s)",
              base_module ? base_module + 1 : module, module_offset, sym);
 }
 #endif
 
-#if defined(XP_UNIX)
+#if defined(XP_UNIX) && !defined(MOZ_ASAN)
 // Define abort() here, so that it is used instead of the system abort(). This
 // lets us control the behavior when aborting, in order to get better results
 // on *NIX platforms. See mozalloc_abort for details.
+//
+// For AddressSanitizer, we must not redefine system abort because the ASan
+// option "abort_on_error=1" calls abort() and therefore causes the following
+// call chain with our redefined abort:
+//
+// ASan -> abort() -> moz_abort() -> MOZ_CRASH() -> Segmentation fault
+//
+// That segmentation fault will be interpreted as another bug by ASan and as a
+// result, ASan will just exit(1) instead of aborting.
 void abort(void)
 {
 #ifdef MOZ_WIDGET_ANDROID
     char msg[64] = {};
     fillAbortMessage(msg, uintptr_t(__builtin_return_address(0)));
 #else
     const char* const msg = "Redirecting call to abort() to mozalloc_abort\n";
 #endif
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -565,18 +565,19 @@ pref("apz.test.logging_enabled", false);
 #ifdef XP_MACOSX
 // Whether to run in native HiDPI mode on machines with "Retina"/HiDPI display;
 //   <= 0 : hidpi mode disabled, display will just use pixel-based upscaling
 //   == 1 : hidpi supported if all screens share the same backingScaleFactor
 //   >= 2 : hidpi supported even with mixed backingScaleFactors (somewhat broken)
 pref("gfx.hidpi.enabled", 2);
 #endif
 
-#if !defined(MOZ_WIDGET_ANDROID)
-// Containerless scrolling for root frames does not yet pass tests on Android.
+#if !defined(MOZ_WIDGET_GONK) && !defined(MOZ_WIDGET_ANDROID)
+// Containerless scrolling for root frames does not yet pass tests on Android
+// or B2G.
 pref("layout.scroll.root-frame-containers", false);
 #endif
 
 // Whether to enable LayerScope tool and default listening port
 pref("gfx.layerscope.enabled", false);
 pref("gfx.layerscope.port", 23456);
 
 // Log severe performance warnings to the error console and profiles.
@@ -1376,16 +1377,22 @@ pref("network.http.tcp_keepalive.short_l
 pref("network.http.tcp_keepalive.short_lived_idle_time", 10);
 
 pref("network.http.tcp_keepalive.long_lived_connections", true);
 pref("network.http.tcp_keepalive.long_lived_idle_time", 600);
 
 pref("network.http.enforce-framing.http1", false); // should be named "strict"
 pref("network.http.enforce-framing.soft", true);
 
+// Whether nsHttpChannel should use the PackagedAppService to load
+// resources from a package when directed to a URL
+// such as http://domain.com/package.pak!//resource.html
+// See http://www.w3.org/TR/web-packaging/#streamable-package-format
+pref("network.http.enable-packaged-apps", false);
+
 // default values for FTP
 // in a DSCP environment this should be 40 (0x28, or AF11), per RFC-4594,
 // Section 4.8 "High-Throughput Data Service Class", and 80 (0x50, or AF22)
 // per Section 4.7 "Low-Latency Data Service Class".
 pref("network.ftp.data.qos", 0);
 pref("network.ftp.control.qos", 0);
 
 // If this pref is false only one xpcom event will be served per poll
--- a/modules/libpref/nsPrefBranch.cpp
+++ b/modules/libpref/nsPrefBranch.cpp
@@ -550,19 +550,16 @@ NS_IMETHODIMP nsPrefBranch::GetChildList
 
   NS_ENSURE_ARG(aStartingAt);
   NS_ENSURE_ARG_POINTER(aCount);
   NS_ENSURE_ARG_POINTER(aChildArray);
 
   *aChildArray = nullptr;
   *aCount = 0;
 
-  if (!gHashTable->IsInitialized())
-    return NS_ERROR_NOT_INITIALIZED;
-
   // this will contain a list of all the pref name strings
   // allocate on the stack for speed
 
   ed.parent = getPrefName(aStartingAt);
   ed.pref_list = &prefArray;
   PL_DHashTableEnumerate(gHashTable, pref_enumChild, &ed);
 
   // now that we've built up the list, run the callback on
--- a/modules/libpref/prefapi.cpp
+++ b/modules/libpref/prefapi.cpp
@@ -63,17 +63,17 @@ matchPrefEntry(PLDHashTable*, const PLDH
     if (prefEntry->key == key) return true;
 
     if (!prefEntry->key || !key) return false;
 
     const char *otherKey = reinterpret_cast<const char*>(key);
     return (strcmp(prefEntry->key, otherKey) == 0);
 }
 
-PLDHashTable2*      gHashTable;
+PLDHashTable*       gHashTable;
 static PLArenaPool  gPrefNameArena;
 bool                gDirty = false;
 
 static struct CallbackNode* gCallbacks = nullptr;
 static bool         gIsAnyPrefLocked = false;
 // These are only used during the call to pref_DoCallback
 static bool         gCallbacksInProgress = false;
 static bool         gShouldCleanupDeadNodes = false;
@@ -145,19 +145,19 @@ enum {
 };
 static nsresult pref_HashPref(const char *key, PrefValue value, PrefType type, uint32_t flags);
 
 #define PREF_HASHTABLE_INITIAL_LENGTH   1024
 
 nsresult PREF_Init()
 {
     if (!gHashTable) {
-        gHashTable = new PLDHashTable2(&pref_HashTableOps,
-                                       sizeof(PrefHashEntry),
-                                       PREF_HASHTABLE_INITIAL_LENGTH);
+        gHashTable = new PLDHashTable(&pref_HashTableOps,
+                                      sizeof(PrefHashEntry),
+                                      PREF_HASHTABLE_INITIAL_LENGTH);
 
         PL_INIT_ARENA_POOL(&gPrefNameArena, "PrefNameArena",
                            PREFNAME_ARENA_SIZE);
     }
     return NS_OK;
 }
 
 /* Frees the callback list. */
--- a/modules/libpref/prefapi_private_data.h
+++ b/modules/libpref/prefapi_private_data.h
@@ -5,17 +5,17 @@
 
 /* Data shared between prefapi.c and nsPref.cpp */
 
 #ifndef prefapi_private_data_h
 #define prefapi_private_data_h
 
 #include "mozilla/MemoryReporting.h"
 
-extern PLDHashTable2* gHashTable;
+extern PLDHashTable* gHashTable;
 extern bool gDirty;
 
 namespace mozilla {
 namespace dom {
 class PrefSetting;
 }
 }
 
--- a/netwerk/base/moz.build
+++ b/netwerk/base/moz.build
@@ -62,16 +62,17 @@ XPIDL_SOURCES += [
     'nsINetworkInterceptController.idl',
     'nsINetworkLinkService.idl',
     'nsINetworkPredictor.idl',
     'nsINetworkPredictorVerifier.idl',
     'nsINetworkProperties.idl',
     'nsINSSErrorsService.idl',
     'nsINullChannel.idl',
     'nsIPACGenerator.idl',
+    'nsIPackagedAppService.idl',
     'nsIParentChannel.idl',
     'nsIParentRedirectingChannel.idl',
     'nsIPermission.idl',
     'nsIPermissionManager.idl',
     'nsIPrivateBrowsingChannel.idl',
     'nsIProgressEventSink.idl',
     'nsIPrompt.idl',
     'nsIProtocolHandler.idl',
new file mode 100644
--- /dev/null
+++ b/netwerk/base/nsIPackagedAppService.idl
@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+interface nsIURI;
+interface nsILoadContextInfo;
+interface nsICacheEntryOpenCallback;
+
+%{C++
+  #define PACKAGED_APP_TOKEN "!//"
+%}
+
+/**
+ * nsIPackagedAppService
+ */
+[scriptable, builtinclass, uuid(77f9a34d-d082-43f1-9f83-e852d0173cd5)]
+interface nsIPackagedAppService : nsISupports
+{
+  /**
+   * @aURI is a URL to a packaged resource
+   *   - format:  package_url + PACKAGED_APP_TOKEN + resource_path
+   *   - example: http://test.com/path/to/package!//resource.html
+   * @aCallback is an object implementing nsICacheEntryOpenCallback
+   *   - this is the target of the async result of the operation
+   *   - aCallback->OnCacheEntryCheck() is called to verify the entry is valid
+   *   - aCallback->OnCacheEntryAvailable() is called with a pointer to the
+   *     the cached entry, if one exists, or an error code otherwise
+   *   - aCallback is kept alive using an nsCOMPtr until OnCacheEntryAvailable
+   *     is called
+   * @aInfo is an object used to determine the cache jar this resource goes in.
+   *   - usually created by calling GetLoadContextInfo(requestingChannel)
+   *
+   * Calling this method will either download the package containing the given
+   * resource URI, store it in the cache and pass the cache entry to aCallback,
+   * or if that resource has already been downloaded it will be served from
+   * the cache.
+   */
+  void requestURI(in nsIURI aURI, in nsILoadContextInfo aInfo, in nsICacheEntryOpenCallback aCallback);
+};
--- a/netwerk/base/nsLoadGroup.h
+++ b/netwerk/base/nsLoadGroup.h
@@ -65,17 +65,17 @@ protected:
     uint32_t                        mLoadFlags;
     uint32_t                        mDefaultLoadFlags;
 
     nsCOMPtr<nsILoadGroup>          mLoadGroup; // load groups can contain load groups
     nsCOMPtr<nsIInterfaceRequestor> mCallbacks;
     nsCOMPtr<nsILoadGroupConnectionInfo> mConnectionInfo;
 
     nsCOMPtr<nsIRequest>            mDefaultLoadRequest;
-    PLDHashTable2                   mRequests;
+    PLDHashTable                    mRequests;
 
     nsWeakPtr                       mObserver;
     nsWeakPtr                       mParentLoadGroup;
     
     nsresult                        mStatus;
     int32_t                         mPriority;
     bool                            mIsCanceling;
 
--- a/netwerk/build/nsNetCID.h
+++ b/netwerk/build/nsNetCID.h
@@ -863,16 +863,26 @@
 #define NS_DASHBOARD_CID                               \
 {   /*c79eb3c6-091a-45a6-8544-5a8d1ab79537 */          \
     0xc79eb3c6,                                        \
     0x091a,                                            \
     0x45a6,                                            \
     { 0x85, 0x44, 0x5a, 0x8d, 0x1a, 0xb7, 0x95, 0x37 } \
 }
 
+#define NS_PACKAGEDAPPSERVICE_CONTRACTID \
+    "@mozilla.org/network/packaged-app-service;1"
+#define NS_PACKAGEDAPPSERVICE_CID                      \
+{   /* adef6762-41b9-4470-a06a-dc29cf8de381 */         \
+    0xadef6762,                                        \
+    0x41b9,                                            \
+    0x4470,                                            \
+  { 0xa0, 0x6a, 0xdc, 0x29, 0xcf, 0x8d, 0xe3, 0x81 }   \
+}
+
 
 /******************************************************************************
  * netwerk/cookie classes
  */
 
 // service implementing nsICookieManager and nsICookieManager2.
 #define NS_COOKIEMANAGER_CONTRACTID \
     "@mozilla.org/cookiemanager;1"
--- a/netwerk/build/nsNetModule.cpp
+++ b/netwerk/build/nsNetModule.cpp
@@ -246,19 +246,21 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsHttpCha
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsHttpActivityDistributor)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsHttpBasicAuth)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsHttpDigestAuth)
 }
 }
 #endif // !NECKO_PROTOCOL_http
 
 #include "mozilla/net/Dashboard.h"
+#include "mozilla/net/PackagedAppService.h"
 namespace mozilla {
 namespace net {
   NS_GENERIC_FACTORY_CONSTRUCTOR(Dashboard)
+  NS_GENERIC_FACTORY_CONSTRUCTOR(PackagedAppService)
 }
 }
 #include "AppProtocolHandler.h"
 
 #ifdef NECKO_PROTOCOL_res
 // resource
 #include "nsResProtocolHandler.h"
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsResProtocolHandler, Init)
@@ -704,16 +706,17 @@ NS_DEFINE_NAMED_CID(NS_NOAUTHURLPARSER_C
 NS_DEFINE_NAMED_CID(NS_AUTHURLPARSER_CID);
 NS_DEFINE_NAMED_CID(NS_STANDARDURL_CID);
 NS_DEFINE_NAMED_CID(NS_ARRAYBUFFERINPUTSTREAM_CID);
 NS_DEFINE_NAMED_CID(NS_BUFFEREDINPUTSTREAM_CID);
 NS_DEFINE_NAMED_CID(NS_BUFFEREDOUTPUTSTREAM_CID);
 NS_DEFINE_NAMED_CID(NS_MIMEINPUTSTREAM_CID);
 NS_DEFINE_NAMED_CID(NS_PROTOCOLPROXYSERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_STREAMCONVERTERSERVICE_CID);
+NS_DEFINE_NAMED_CID(NS_PACKAGEDAPPSERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_DASHBOARD_CID);
 #ifdef NECKO_PROTOCOL_ftp
 NS_DEFINE_NAMED_CID(NS_FTPDIRLISTINGCONVERTER_CID);
 #endif
 NS_DEFINE_NAMED_CID(NS_NSINDEXEDTOHTMLCONVERTER_CID);
 NS_DEFINE_NAMED_CID(NS_DIRINDEXPARSER_CID);
 NS_DEFINE_NAMED_CID(NS_MULTIMIXEDCONVERTER_CID);
 NS_DEFINE_NAMED_CID(NS_UNKNOWNDECODER_CID);
@@ -848,16 +851,17 @@ static const mozilla::Module::CIDEntry k
     { &kNS_AUTHURLPARSER_CID, false, nullptr, nsAuthURLParserConstructor },
     { &kNS_STANDARDURL_CID, false, nullptr, nsStandardURLConstructor },
     { &kNS_ARRAYBUFFERINPUTSTREAM_CID, false, nullptr, ArrayBufferInputStreamConstructor },
     { &kNS_BUFFEREDINPUTSTREAM_CID, false, nullptr, nsBufferedInputStream::Create },
     { &kNS_BUFFEREDOUTPUTSTREAM_CID, false, nullptr, nsBufferedOutputStream::Create },
     { &kNS_MIMEINPUTSTREAM_CID, false, nullptr, nsMIMEInputStreamConstructor },
     { &kNS_PROTOCOLPROXYSERVICE_CID, true, nullptr, nsProtocolProxyServiceConstructor },
     { &kNS_STREAMCONVERTERSERVICE_CID, false, nullptr, CreateNewStreamConvServiceFactory },
+    { &kNS_PACKAGEDAPPSERVICE_CID, false, NULL, mozilla::net::PackagedAppServiceConstructor },
     { &kNS_DASHBOARD_CID, false, nullptr, mozilla::net::DashboardConstructor },
 #ifdef NECKO_PROTOCOL_ftp
     { &kNS_FTPDIRLISTINGCONVERTER_CID, false, nullptr, CreateNewFTPDirListingConv },
 #endif
     { &kNS_NSINDEXEDTOHTMLCONVERTER_CID, false, nullptr, nsIndexedToHTML::Create },
     { &kNS_DIRINDEXPARSER_CID, false, nullptr, nsDirIndexParserConstructor },
     { &kNS_MULTIMIXEDCONVERTER_CID, false, nullptr, CreateNewMultiMixedConvFactory },
     { &kNS_UNKNOWNDECODER_CID, false, nullptr, CreateNewUnknownDecoderFactory },
@@ -994,16 +998,17 @@ static const mozilla::Module::ContractID
     { NS_AUTHURLPARSER_CONTRACTID, &kNS_AUTHURLPARSER_CID },
     { NS_STANDARDURL_CONTRACTID, &kNS_STANDARDURL_CID },
     { NS_ARRAYBUFFERINPUTSTREAM_CONTRACTID, &kNS_ARRAYBUFFERINPUTSTREAM_CID },
     { NS_BUFFEREDINPUTSTREAM_CONTRACTID, &kNS_BUFFEREDINPUTSTREAM_CID },
     { NS_BUFFEREDOUTPUTSTREAM_CONTRACTID, &kNS_BUFFEREDOUTPUTSTREAM_CID },
     { NS_MIMEINPUTSTREAM_CONTRACTID, &kNS_MIMEINPUTSTREAM_CID },
     { NS_PROTOCOLPROXYSERVICE_CONTRACTID, &kNS_PROTOCOLPROXYSERVICE_CID },
     { NS_STREAMCONVERTERSERVICE_CONTRACTID, &kNS_STREAMCONVERTERSERVICE_CID },
+    { NS_PACKAGEDAPPSERVICE_CONTRACTID, &kNS_PACKAGEDAPPSERVICE_CID },
     { NS_DASHBOARD_CONTRACTID, &kNS_DASHBOARD_CID },
 #ifdef NECKO_PROTOCOL_ftp
     { NS_ISTREAMCONVERTER_KEY FTP_TO_INDEX, &kNS_FTPDIRLISTINGCONVERTER_CID },
 #endif
     { NS_ISTREAMCONVERTER_KEY INDEX_TO_HTML, &kNS_NSINDEXEDTOHTMLCONVERTER_CID },
     { NS_DIRINDEXPARSER_CONTRACTID, &kNS_DIRINDEXPARSER_CID },
     { NS_ISTREAMCONVERTER_KEY MULTI_MIXED_X, &kNS_MULTIMIXEDCONVERTER_CID },
     { NS_ISTREAMCONVERTER_KEY MULTI_BYTERANGES, &kNS_MULTIMIXEDCONVERTER_CID },
--- a/netwerk/cache/nsCacheEntry.h
+++ b/netwerk/cache/nsCacheEntry.h
@@ -300,15 +300,15 @@ private:
     static
     PLDHashOperator       VisitEntry(PLDHashTable *         table,
                                      PLDHashEntryHdr *      hdr,
                                      uint32_t               number,
                                      void *                 arg);
 
     // member variables
     static const PLDHashTableOps ops;
-    PLDHashTable2                table;
+    PLDHashTable                 table;
     bool                         initialized;
 
     static const uint32_t kInitialTableLength = 256;
 };
 
 #endif // _nsCacheEntry_h_
--- a/netwerk/cache/nsDiskCacheBinding.h
+++ b/netwerk/cache/nsDiskCacheBinding.h
@@ -109,15 +109,15 @@ public:
 
     size_t                 SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf);
 
 private:
     nsresult                AddBinding(nsDiskCacheBinding * binding);
 
     // member variables
     static const PLDHashTableOps ops;
-    PLDHashTable2          table;
+    PLDHashTable           table;
     bool                   initialized;
 
     static const uint32_t kInitialTableLength = 0;
 };
 
 #endif /* _nsDiskCacheBinding_h_ */
--- a/netwerk/dns/nsHostResolver.h
+++ b/netwerk/dns/nsHostResolver.h
@@ -340,17 +340,17 @@ private:
     uint32_t      mMaxCacheEntries;
     uint32_t      mDefaultCacheLifetime; // granularity seconds
     uint32_t      mDefaultGracePeriod; // granularity seconds
     mutable Mutex mLock;    // mutable so SizeOfIncludingThis can be const
     CondVar       mIdleThreadCV;
     uint32_t      mNumIdleThreads;
     uint32_t      mThreadCount;
     uint32_t      mActiveAnyThreadCount;
-    PLDHashTable2 mDB;
+    PLDHashTable  mDB;
     PRCList       mHighQ;
     PRCList       mMediumQ;
     PRCList       mLowQ;
     PRCList       mEvictionQ;
     uint32_t      mEvictionQSize;
     uint32_t      mPendingCount;
     PRTime        mCreationTime;
     bool          mShutdown;
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/http/PackagedAppService.cpp
@@ -0,0 +1,554 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+#include "PackagedAppService.h"
+#include "nsICacheStorage.h"
+#include "LoadContextInfo.h"
+#include "nsICacheStorageService.h"
+#include "nsIResponseHeadProvider.h"
+#include "nsIMultiPartChannel.h"
+#include "../../cache2/CacheFileUtils.h"
+#include "nsStreamUtils.h"
+
+namespace mozilla {
+namespace net {
+
+static PackagedAppService *gPackagedAppService = nullptr;
+
+NS_IMPL_ISUPPORTS(PackagedAppService, nsIPackagedAppService)
+
+NS_IMPL_ISUPPORTS(PackagedAppService::CacheEntryWriter, nsIStreamListener)
+
+/* static */ nsresult
+PackagedAppService::CacheEntryWriter::Create(nsIURI *aURI,
+                                             nsICacheStorage *aStorage,
+                                             CacheEntryWriter **aResult)
+{
+  nsRefPtr<CacheEntryWriter> writer = new CacheEntryWriter();
+  nsresult rv = aStorage->OpenTruncate(aURI, EmptyCString(),
+                                       getter_AddRefs(writer->mEntry));
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  rv = writer->mEntry->ForceValidFor(PR_UINT32_MAX);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  writer.forget(aResult);
+  return NS_OK;
+}
+
+NS_METHOD
+PackagedAppService::CacheEntryWriter::ConsumeData(nsIInputStream *aStream,
+                                                  void *aClosure,
+                                                  const char *aFromRawSegment,
+                                                  uint32_t aToOffset,
+                                                  uint32_t aCount,
+                                                  uint32_t *aWriteCount)
+{
+  MOZ_ASSERT(aClosure, "The closure must not be null");
+  CacheEntryWriter *self = static_cast<CacheEntryWriter*>(aClosure);
+  MOZ_ASSERT(self->mOutputStream, "The stream should not be null");
+  return self->mOutputStream->Write(aFromRawSegment, aCount, aWriteCount);
+}
+
+NS_IMETHODIMP
+PackagedAppService::CacheEntryWriter::OnStartRequest(nsIRequest *aRequest,
+                                                     nsISupports *aContext)
+{
+  nsCOMPtr<nsIResponseHeadProvider> provider(do_QueryInterface(aRequest));
+  if (!provider) {
+    return NS_ERROR_INVALID_ARG;
+  }
+  nsHttpResponseHead *responseHead = provider->GetResponseHead();
+  if (!responseHead) {
+    return NS_ERROR_FAILURE;
+  }
+
+  mEntry->SetPredictedDataSize(responseHead->TotalEntitySize());
+
+  nsAutoCString head;
+  responseHead->Flatten(head, true);
+  nsresult rv = mEntry->SetMetaDataElement("response-head", head.get());
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = mEntry->SetMetaDataElement("request-method", "GET");
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = mEntry->OpenOutputStream(0, getter_AddRefs(mOutputStream));
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+PackagedAppService::CacheEntryWriter::OnStopRequest(nsIRequest *aRequest,
+                                                    nsISupports *aContext,
+                                                    nsresult aStatusCode)
+{
+  if (mOutputStream) {
+    mOutputStream->Close();
+    mOutputStream = nullptr;
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+PackagedAppService::CacheEntryWriter::OnDataAvailable(nsIRequest *aRequest,
+                                                      nsISupports *aContext,
+                                                      nsIInputStream *aInputStream,
+                                                      uint64_t aOffset,
+                                                      uint32_t aCount)
+{
+  if (!aInputStream) {
+    return NS_ERROR_INVALID_ARG;
+  }
+  // Calls ConsumeData to read the data into the cache entry
+  uint32_t n;
+  return aInputStream->ReadSegments(ConsumeData, this, aCount, &n);
+}
+
+
+NS_IMPL_ISUPPORTS(PackagedAppService::PackagedAppDownloader, nsIStreamListener)
+
+nsresult
+PackagedAppService::PackagedAppDownloader::Init(nsILoadContextInfo* aInfo,
+                                                const nsCString& aKey)
+{
+  nsresult rv;
+  nsCOMPtr<nsICacheStorageService> cacheStorageService =
+    do_GetService("@mozilla.org/netwerk/cache-storage-service;1", &rv);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  rv = cacheStorageService->DiskCacheStorage(aInfo, false,
+                                             getter_AddRefs(mCacheStorage));
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  mPackageKey = aKey;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+PackagedAppService::PackagedAppDownloader::OnStartRequest(nsIRequest *aRequest,
+                                                          nsISupports *aContext)
+{
+  // In case an error occurs in this method mWriter should be null
+  // so we don't accidentally write to the previous resource's cache entry.
+  mWriter = nullptr;
+
+  nsCOMPtr<nsIURI> uri;
+  nsresult rv = GetSubresourceURI(aRequest, getter_AddRefs(uri));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return NS_OK;
+  }
+
+  rv = CacheEntryWriter::Create(uri, mCacheStorage, getter_AddRefs(mWriter));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return NS_OK;
+  }
+
+  MOZ_ASSERT(mWriter);
+  rv = mWriter->OnStartRequest(aRequest, aContext);
+  NS_WARN_IF(NS_FAILED(rv));
+  return NS_OK;
+}
+
+nsresult
+PackagedAppService::PackagedAppDownloader::GetSubresourceURI(nsIRequest * aRequest,
+                                                             nsIURI ** aResult)
+{
+  nsresult rv;
+  nsCOMPtr<nsIResponseHeadProvider> provider(do_QueryInterface(aRequest));
+  nsCOMPtr<nsIChannel> chan(do_QueryInterface(aRequest));
+
+  if (NS_WARN_IF(!provider || !chan)) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  nsHttpResponseHead *responseHead = provider->GetResponseHead();
+  if (NS_WARN_IF(!responseHead)) {
+    return NS_ERROR_FAILURE;
+  }
+  nsAutoCString contentLocation;
+  rv = responseHead->GetHeader(nsHttp::ResolveAtom("Content-Location"), contentLocation);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  nsCOMPtr<nsIURI> uri;
+  rv = chan->GetURI(getter_AddRefs(uri));
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  nsAutoCString path;
+  rv = uri->GetPath(path);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  path += PACKAGED_APP_TOKEN;
+
+  // TODO: make sure the path is normalized
+  if (StringBeginsWith(contentLocation, NS_LITERAL_CSTRING("/"))) {
+    contentLocation = Substring(contentLocation, 1);
+  }
+
+  path += contentLocation;
+
+  nsCOMPtr<nsIURI> partURI;
+  rv = uri->CloneIgnoringRef(getter_AddRefs(partURI));
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  rv = partURI->SetPath(path);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  partURI.forget(aResult);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+PackagedAppService::PackagedAppDownloader::OnStopRequest(nsIRequest *aRequest,
+                                                         nsISupports *aContext,
+                                                         nsresult aStatusCode)
+{
+  nsCOMPtr<nsIMultiPartChannel> multiChannel(do_QueryInterface(aRequest));
+  nsresult rv;
+
+
+  // The request is normally a multiPartChannel. If it isn't, it generally means
+  // an error has occurred in nsMultiMixedConv.
+  // If an error occurred in OnStartRequest, mWriter could be null.
+  if (multiChannel && mWriter) {
+    mWriter->OnStopRequest(aRequest, aContext, aStatusCode);
+
+    nsCOMPtr<nsIURI> uri;
+    rv = GetSubresourceURI(aRequest, getter_AddRefs(uri));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return NS_OK;
+    }
+
+    nsCOMPtr<nsICacheEntry> entry;
+    mWriter->mEntry.swap(entry);
+
+    // We don't need the writer anymore - this will close its stream
+    mWriter = nullptr;
+    CallCallbacks(uri, entry, aStatusCode);
+  }
+
+  bool lastPart = false;
+  if (multiChannel) {
+    rv = multiChannel->GetIsLastPart(&lastPart);
+    if (NS_SUCCEEDED(rv) && !lastPart) {
+      // If this isn't the last part, we don't do the cleanup yet
+      return NS_OK;
+    }
+  }
+
+  // If this is the last part of the package, it means the requested resources
+  // have not been found in the package so we return an appropriate error.
+  if (NS_SUCCEEDED(aStatusCode) && lastPart) {
+    aStatusCode = NS_ERROR_FILE_NOT_FOUND;
+  }
+
+  nsRefPtr<PackagedAppDownloader> kungFuDeathGrip(this);
+  // NotifyPackageDownloaded removes the ref from the array. Keep a temp ref
+  if (gPackagedAppService) {
+    gPackagedAppService->NotifyPackageDownloaded(mPackageKey);
+  }
+  ClearCallbacks(aStatusCode);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+PackagedAppService::PackagedAppDownloader::OnDataAvailable(nsIRequest *aRequest,
+                                                           nsISupports *aContext,
+                                                           nsIInputStream *aInputStream,
+                                                           uint64_t aOffset,
+                                                           uint32_t aCount)
+{
+  if (!mWriter) {
+    uint32_t n;
+    return aInputStream->ReadSegments(NS_DiscardSegment, nullptr, aCount, &n);
+  }
+  return mWriter->OnDataAvailable(aRequest, aContext, aInputStream, aOffset,
+                                  aCount);
+}
+
+nsresult
+PackagedAppService::PackagedAppDownloader::AddCallback(nsIURI *aURI,
+                                                       nsICacheEntryOpenCallback *aCallback)
+{
+  MOZ_RELEASE_ASSERT(NS_IsMainThread(), "mCallbacks hashtable is not thread safe");
+  nsAutoCString spec;
+  aURI->GetAsciiSpec(spec);
+
+  // Check if we already have a resource waiting for this resource
+  nsCOMArray<nsICacheEntryOpenCallback>* array = mCallbacks.Get(spec);
+  if (array) {
+    // Add this resource to the callback array
+    array->AppendObject(aCallback);
+  } else {
+    // This is the first callback for this URI.
+    // Create a new array and add the callback
+    nsCOMArray<nsICacheEntryOpenCallback>* newArray =
+      new nsCOMArray<nsICacheEntryOpenCallback>();
+    newArray->AppendObject(aCallback);
+    mCallbacks.Put(spec, newArray);
+  }
+  return NS_OK;
+}
+
+nsresult
+PackagedAppService::PackagedAppDownloader::CallCallbacks(nsIURI *aURI,
+                                                         nsICacheEntry *aEntry,
+                                                         nsresult aResult)
+{
+  MOZ_RELEASE_ASSERT(NS_IsMainThread(), "mCallbacks hashtable is not thread safe");
+  // Hold on to this entry while calling the callbacks
+  nsCOMPtr<nsICacheEntry> handle(aEntry);
+
+  nsAutoCString spec;
+  aURI->GetSpec(spec);
+
+  nsCOMArray<nsICacheEntryOpenCallback>* array = mCallbacks.Get(spec);
+  if (array) {
+    // Call all the callbacks for this URI
+    for (uint32_t i = 0; i < array->Length(); ++i) {
+      nsCOMPtr<nsICacheEntryOpenCallback> callback(array->ObjectAt(i));
+      // We call to AsyncOpenURI which automatically calls the callback.
+      mCacheStorage->AsyncOpenURI(aURI, EmptyCString(),
+                                  nsICacheStorage::OPEN_READONLY, callback);
+    }
+    // Clear the array and remove it from the hashtable
+    array->Clear();
+    mCallbacks.Remove(spec);
+    aEntry->ForceValidFor(0);
+  }
+  return NS_OK;
+}
+
+PLDHashOperator
+PackagedAppService::PackagedAppDownloader::ClearCallbacksEnumerator(const nsACString& key,
+  nsAutoPtr<nsCOMArray<nsICacheEntryOpenCallback> >& callbackArray,
+  void* arg)
+{
+  MOZ_ASSERT(arg, "The void* parameter should be a pointer to nsresult");
+  nsresult *result = static_cast<nsresult*>(arg);
+  for (uint32_t i = 0; i < callbackArray->Length(); ++i) {
+    nsCOMPtr<nsICacheEntryOpenCallback> callback = callbackArray->ObjectAt(i);
+    callback->OnCacheEntryAvailable(nullptr, false, nullptr, *result);
+  }
+  // Remove entry from hashtable
+  return PL_DHASH_REMOVE;
+}
+
+nsresult
+PackagedAppService::PackagedAppDownloader::ClearCallbacks(nsresult aResult)
+{
+  MOZ_RELEASE_ASSERT(NS_IsMainThread(), "mCallbacks hashtable is not thread safe");
+  mCallbacks.Enumerate(ClearCallbacksEnumerator, &aResult);
+  return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS(PackagedAppService::CacheEntryChecker, nsICacheEntryOpenCallback)
+
+NS_IMETHODIMP
+PackagedAppService::CacheEntryChecker::OnCacheEntryCheck(nsICacheEntry *aEntry,
+                                                         nsIApplicationCache *aApplicationCache,
+                                                         uint32_t *_retval)
+{
+  return mCallback->OnCacheEntryCheck(aEntry, aApplicationCache, _retval);
+}
+
+NS_IMETHODIMP
+PackagedAppService::CacheEntryChecker::OnCacheEntryAvailable(nsICacheEntry *aEntry,
+                                                             bool aNew,
+                                                             nsIApplicationCache *aApplicationCache,
+                                                             nsresult aResult)
+{
+  if (aResult == NS_ERROR_CACHE_KEY_NOT_FOUND) {
+    MOZ_ASSERT(!aEntry, "No entry");
+    // trigger download
+    // download checks if package download is already in progress
+    gPackagedAppService->OpenNewPackageInternal(mURI, mCallback,
+                                                mLoadContextInfo);
+  } else {
+    // TODO: if aResult is another error code, should we pass it off to the
+    // consumer, or should we try to download the package again?
+    mCallback->OnCacheEntryAvailable(aEntry, aNew, aApplicationCache, aResult);
+    // TODO: update last access entry for the entire package
+  }
+  return NS_OK;
+}
+
+PackagedAppService::PackagedAppService()
+{
+  gPackagedAppService = this;
+}
+
+PackagedAppService::~PackagedAppService()
+{
+  gPackagedAppService = nullptr;
+}
+
+NS_IMETHODIMP
+PackagedAppService::RequestURI(nsIURI *aURI,
+                               nsILoadContextInfo *aInfo,
+                               nsICacheEntryOpenCallback *aCallback)
+{
+  // Check arguments are not null
+  if (!aURI || !aCallback || !aInfo) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  nsAutoCString path;
+  aURI->GetPath(path);
+  int32_t pos = path.Find(PACKAGED_APP_TOKEN);
+  if (pos == kNotFound) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  nsresult rv;
+  nsCOMPtr<nsICacheStorageService> cacheStorageService =
+    do_GetService("@mozilla.org/netwerk/cache-storage-service;1", &rv);
+
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsCOMPtr<nsICacheStorage> cacheStorage;
+
+  rv = cacheStorageService->DiskCacheStorage(aInfo, false,
+                                             getter_AddRefs(cacheStorage));
+
+  nsRefPtr<CacheEntryChecker> checker = new CacheEntryChecker(aURI, aCallback, aInfo);
+  return cacheStorage->AsyncOpenURI(aURI, EmptyCString(),
+                                    nsICacheStorage::OPEN_READONLY, checker);
+}
+
+nsresult
+PackagedAppService::NotifyPackageDownloaded(nsCString aKey)
+{
+  MOZ_RELEASE_ASSERT(NS_IsMainThread(), "mDownloadingPackages hashtable is not thread safe");
+  mDownloadingPackages.Remove(aKey);
+  return NS_OK;
+}
+
+nsresult
+PackagedAppService::OpenNewPackageInternal(nsIURI *aURI,
+                                           nsICacheEntryOpenCallback *aCallback,
+                                           nsILoadContextInfo *aInfo)
+{
+  MOZ_RELEASE_ASSERT(NS_IsMainThread(), "mDownloadingPackages hashtable is not thread safe");
+
+  nsAutoCString path;
+  nsresult rv = aURI->GetPath(path);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  int32_t pos = path.Find(PACKAGED_APP_TOKEN);
+  MOZ_ASSERT(pos != kNotFound,
+             "This should never be called if the token is missing");
+
+  nsCOMPtr<nsIURI> packageURI;
+  rv = aURI->CloneIgnoringRef(getter_AddRefs(packageURI));
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  rv = packageURI->SetPath(Substring(path, 0, pos));
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  nsAutoCString key;
+  CacheFileUtils::AppendKeyPrefix(aInfo, key);
+
+  {
+    nsAutoCString spec;
+    packageURI->GetAsciiSpec(spec);
+    key += ":";
+    key += spec;
+  }
+
+  nsRefPtr<PackagedAppDownloader> downloader;
+  if (mDownloadingPackages.Get(key, getter_AddRefs(downloader))) {
+    // We have determined that the file is not in the cache.
+    // If we find that the package that the file belongs to is currently being
+    // downloaded, we will add the callback to the package's queue, and it will
+    // be called once the file is processed and saved in the cache.
+
+    downloader->AddCallback(aURI, aCallback);
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsIChannel> channel;
+  rv = NS_NewChannel(
+    getter_AddRefs(channel), packageURI, nsContentUtils::GetSystemPrincipal(),
+    nsILoadInfo::SEC_NORMAL, nsIContentPolicy::TYPE_OTHER, nullptr, nullptr,
+    nsIRequest::LOAD_NORMAL);
+
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsCOMPtr<nsICachingChannel> cacheChan(do_QueryInterface(channel));
+  if (cacheChan) {
+    // Each resource in the package will be put in its own cache entry
+    // during the first load of the package, so we only want the channel to
+    // cache the response head, not the entire content of the package.
+    cacheChan->SetCacheOnlyMetadata(true);
+  }
+
+  downloader = new PackagedAppDownloader();
+  rv = downloader->Init(aInfo, key);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  downloader->AddCallback(aURI, aCallback);
+
+  nsCOMPtr<nsIStreamConverterService> streamconv =
+    do_GetService("@mozilla.org/streamConverters;1", &rv);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsCOMPtr<nsIStreamListener> mimeConverter;
+  rv = streamconv->AsyncConvertData("multipart/mixed", "*/*", downloader, nullptr,
+                                    getter_AddRefs(mimeConverter));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  // Add the package to the hashtable.
+  mDownloadingPackages.Put(key, downloader);
+
+  return channel->AsyncOpen(mimeConverter, nullptr);
+}
+
+} // namespace net
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/http/PackagedAppService.h
@@ -0,0 +1,191 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_net_PackagedAppService_h
+#define mozilla_net_PackagedAppService_h
+
+#include "nsIPackagedAppService.h"
+#include "nsILoadContextInfo.h"
+#include "nsICacheStorage.h"
+
+namespace mozilla {
+namespace net {
+
+// This service is used to download packages from the web.
+// Individual resources in the package are saved in the browser cache. It also
+// provides an interface to asynchronously request resources from packages,
+// which are either returned from the cache if they exist and are valid,
+// or downloads the package.
+// The package format is defined at:
+//     https://w3ctag.github.io/packaging-on-the-web/#streamable-package-format
+// Downloading the package is triggered by calling requestURI(aURI, aInfo, aCallback)
+//     aURI is the subresource uri - http://domain.com/path/package!//resource.html
+//     aInfo is a nsILoadContextInfo used to pick the cache jar the resource goes into
+//     aCallback is the target of the async call to requestURI
+// When requestURI is called, a CacheEntryChecker is created to verify if the
+// resource is already in the cache. If it is, it passes it to the callback.
+// Otherwise, it starts downloading the package. When the packaged resource has
+// been downloaded, its cache entry gets passed to the callback.
+class PackagedAppService final
+  : public nsIPackagedAppService
+{
+  NS_DECL_THREADSAFE_ISUPPORTS
+  NS_DECL_NSIPACKAGEDAPPSERVICE
+
+  PackagedAppService();
+
+private:
+  ~PackagedAppService();
+
+  // This method is called if an entry wasn't found in the cache.
+  // It checks to see if the package is currently being downloaded.
+  // If so, then it simply adds the callback to that PackageAppDownloader
+  // Else it begins downloading the new package and adds it to mDownloadingPackages
+  // - aURI is the packaged resource's URL
+  // - aCallback is the listener which gets called when the requested
+  //   resource is available.
+  // - aInfo is needed because cache entries are located in separate cache jars
+  //   If a resource isn't found in the package, aCallback->OnCacheEntryAvailable
+  //   will be called with a null entry and an error result as a status.
+  nsresult OpenNewPackageInternal(nsIURI *aURI,
+                                  nsICacheEntryOpenCallback *aCallback,
+                                  nsILoadContextInfo *aInfo);
+
+  // Called by PackageAppDownloader once the download has finished
+  // (or encountered an error) to remove the package from mDownloadingPackages
+  // Should be called on the main thread.
+  nsresult NotifyPackageDownloaded(nsCString aKey);
+
+  // This class is used to write data into the cache entry corresponding to the
+  // packaged resource being downloaded.
+  // The PackagedAppDownloader will hold a ref to a CacheEntryWriter that
+  // corresponds to the entry that is currently being downloaded.
+  class CacheEntryWriter final
+    : public nsIStreamListener
+  {
+  public:
+    NS_DECL_ISUPPORTS
+    NS_DECL_NSISTREAMLISTENER
+    NS_DECL_NSIREQUESTOBSERVER
+
+    // If successful, calling this static method will create a new
+    // CacheEntryWriter and will create the cache entry associated to the
+    // resource and open an output stream which we use for writing the resource's
+    // content into the cache entry.
+    static nsresult Create(nsIURI*, nsICacheStorage*, CacheEntryWriter**);
+
+    nsCOMPtr<nsICacheEntry> mEntry;
+  private:
+    CacheEntryWriter() { }
+    ~CacheEntryWriter() { }
+
+    // Static method used to write data into the cache entry
+    // Called from OnDataAvailable
+    static NS_METHOD ConsumeData(nsIInputStream *in, void *closure,
+                                 const char *fromRawSegment, uint32_t toOffset,
+                                 uint32_t count, uint32_t *writeCount);
+    // We write the data we read from the network into this stream which goes
+    // to the cache entry.
+    nsCOMPtr<nsIOutputStream> mOutputStream;
+  };
+
+  // This class is used to download a packaged app. It acts as a listener
+  // for the nsMultiMixedConv object that parses the package.
+  // There is an OnStartRequest, OnDataAvailable*, OnStopRequest sequence called
+  // for each resource
+  // The PackagedAppService holds a hash-table of the PackagedAppDownloaders
+  // that are in progress to coalesce same loads.
+  // Once the downloading is completed, it should call
+  // NotifyPackageDownloaded(packageURI), so the service releases the ref.
+  class PackagedAppDownloader final
+    : public nsIStreamListener
+  {
+  public:
+    NS_DECL_ISUPPORTS
+    NS_DECL_NSISTREAMLISTENER
+    NS_DECL_NSIREQUESTOBSERVER
+
+    // Initializes mCacheStorage and saves aKey as mPackageKey which is later
+    // used to remove this object from PackagedAppService::mDownloadingPackages
+    // - aKey is a string which uniquely identifies this package within the
+    //   packagedAppService
+    nsresult Init(nsILoadContextInfo* aInfo, const nsCString &aKey);
+    // Registers a callback which gets called when the given nsIURI is downloaded
+    // aURI is the full URI of a subresource, composed of packageURI + !// + subresourcePath
+    nsresult AddCallback(nsIURI *aURI, nsICacheEntryOpenCallback *aCallback);
+
+  private:
+    ~PackagedAppDownloader() { }
+
+    // Calls all the callbacks registered for the given URI.
+    // aURI is the full URI of a subresource, composed of packageURI + !// + subresourcePath
+    // It passes the cache entry and the result when calling OnCacheEntryAvailable
+    nsresult CallCallbacks(nsIURI *aURI, nsICacheEntry *aEntry, nsresult aResult);
+    // Clears all the callbacks for this package
+    // This would get called at the end of downloading the package and would
+    // cause us to call OnCacheEntryAvailable with a null entry. This would be
+    // equivalent to a 404 when loading from the net.
+    nsresult ClearCallbacks(nsresult aResult);
+    static PLDHashOperator ClearCallbacksEnumerator(const nsACString& key,
+      nsAutoPtr<nsCOMArray<nsICacheEntryOpenCallback>>& callbackArray,
+      void* arg);
+    // Returns a URI with the subresource's full URI
+    // The request must be QIable to nsIResponseHeadProvider since it looks
+    // at the Content-Location header to compute the full path.
+    static nsresult GetSubresourceURI(nsIRequest * aRequest, nsIURI **aResult);
+    // Used to write data into the cache entry of the resource currently being
+    // downloaded. It is kept alive until the downloader receives OnStopRequest
+    nsRefPtr<CacheEntryWriter> mWriter;
+    // Cached value of nsICacheStorage
+    nsCOMPtr<nsICacheStorage> mCacheStorage;
+    // A hastable containing all the consumers which requested a resource and need
+    // to be notified once it is inserted into the cache.
+    // The key is a subresource URI - http://example.com/package.pak!//res.html
+    // Should only be used on the main thread.
+    nsClassHashtable<nsCStringHashKey, nsCOMArray<nsICacheEntryOpenCallback>> mCallbacks;
+    // The key with which this package is inserted in
+    // PackagedAppService::mDownloadingPackages
+    nsCString mPackageKey;
+  };
+
+  // This class is used to check if a packaged resource has already been
+  // downloaded and saved into the cache.
+  // It calls aCallback->OnCacheEntryAvailable if the resource exists in the
+  // cache or PackagedAppService::OpenNewPackageInternal if it needs
+  // to be downloaded
+  class CacheEntryChecker final
+    : public nsICacheEntryOpenCallback
+  {
+  public:
+    NS_DECL_ISUPPORTS
+    NS_DECL_NSICACHEENTRYOPENCALLBACK
+
+    CacheEntryChecker(nsIURI *aURI, nsICacheEntryOpenCallback * aCallback,
+                      nsILoadContextInfo *aInfo)
+      : mURI(aURI)
+      , mCallback(aCallback)
+      , mLoadContextInfo(aInfo)
+    {
+    }
+  private:
+    ~CacheEntryChecker() { }
+
+    nsCOMPtr<nsIURI> mURI;
+    nsCOMPtr<nsICacheEntryOpenCallback> mCallback;
+    nsCOMPtr<nsILoadContextInfo> mLoadContextInfo;
+  };
+
+  // A hashtable of packages that are currently being downloaded.
+  // The key is a string formed by concatenating LoadContextInfo and package URI
+  // Should only be used on the main thread.
+  nsRefPtrHashtable<nsCStringHashKey, PackagedAppDownloader> mDownloadingPackages;
+};
+
+
+} // namespace net
+} // namespace mozilla
+
+#endif // mozilla_net_PackagedAppService_h
\ No newline at end of file
--- a/netwerk/protocol/http/moz.build
+++ b/netwerk/protocol/http/moz.build
@@ -29,16 +29,17 @@ EXPORTS += [
 ]
 
 EXPORTS.mozilla.net += [
     'HttpBaseChannel.h',
     'HttpChannelChild.h',
     'HttpChannelParent.h',
     'HttpInfo.h',
     'NullHttpChannel.h',
+    'PackagedAppService.h',
     'PHttpChannelParams.h',
     'PSpdyPush.h',
     'TimingStruct.h',
 ]
 
 # ASpdySession.cpp and nsHttpAuthCache cannot be built in unified mode because
 # they use plarena.h.
 SOURCES += [
@@ -73,16 +74,17 @@ UNIFIED_SOURCES += [
     'nsHttpHeaderArray.cpp',
     'nsHttpNTLMAuth.cpp',
     'nsHttpPipeline.cpp',
     'nsHttpRequestHead.cpp',
     'nsHttpResponseHead.cpp',
     'nsHttpTransaction.cpp',
     'NullHttpChannel.cpp',
     'NullHttpTransaction.cpp',
+    'PackagedAppService.cpp',
     'SpdyPush31.cpp',
     'SpdySession31.cpp',
     'SpdyStream31.cpp',
     'SpdyZlibReporter.cpp',
     'TunnelUtils.cpp',
 ]
 
 # These files cannot be built in unified mode because of OS X headers.
--- a/netwerk/protocol/http/nsHttp.cpp
+++ b/netwerk/protocol/http/nsHttp.cpp
@@ -35,17 +35,17 @@ enum {
 // the atom table is destroyed.  The structure and value string are allocated
 // as one contiguous block.
 
 struct HttpHeapAtom {
     struct HttpHeapAtom *next;
     char                 value[1];
 };
 
-static PLDHashTable2       *sAtomTable;
+static PLDHashTable        *sAtomTable;
 static struct HttpHeapAtom *sHeapAtoms = nullptr;
 static Mutex               *sLock = nullptr;
 
 HttpHeapAtom *
 NewHeapAtom(const char *value) {
     int len = strlen(value);
 
     HttpHeapAtom *a =
@@ -98,18 +98,18 @@ nsHttp::CreateAtomTable()
 
     if (!sLock) {
         sLock = new Mutex("nsHttp.sLock");
     }
 
     // The initial length for this table is a value greater than the number of
     // known atoms (NUM_HTTP_ATOMS) because we expect to encounter a few random
     // headers right off the bat.
-    sAtomTable = new PLDHashTable2(&ops, sizeof(PLDHashEntryStub),
-                                   NUM_HTTP_ATOMS + 10);
+    sAtomTable = new PLDHashTable(&ops, sizeof(PLDHashEntryStub),
+                                  NUM_HTTP_ATOMS + 10);
 
     // fill the table with our known atoms
     const char *const atoms[] = {
 #define HTTP_ATOM(_name, _value) nsHttp::_name._val,
 #include "nsHttpAtomList.h"
 #undef HTTP_ATOM
         nullptr
     };
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -74,16 +74,17 @@
 #include "CacheObserver.h"
 #include "mozilla/Telemetry.h"
 #include "AlternateServices.h"
 #include "InterceptedChannel.h"
 #include "nsIHttpPushListener.h"
 #include "nsIX509Cert.h"
 #include "ScopedNSSTypes.h"
 #include "nsNullPrincipal.h"
+#include "nsIPackagedAppService.h"
 
 namespace mozilla { namespace net {
 
 namespace {
 
 // Monotonically increasing ID for generating unique cache entries per
 // intercepted channel.
 static uint64_t gNumIntercepted = 0;
@@ -244,16 +245,17 @@ nsHttpChannel::nsHttpChannel()
     , mRequestTimeInitialized(false)
     , mCacheEntryIsReadOnly(false)
     , mCacheEntryIsWriteOnly(false)
     , mCacheEntriesToWaitFor(0)
     , mHasQueryString(0)
     , mConcurentCacheAccess(0)
     , mIsPartialRequest(0)
     , mHasAutoRedirectVetoNotifier(0)
+    , mIsPackagedAppResource(0)
     , mPushedStream(nullptr)
     , mLocalBlocklist(false)
     , mWarningReporter(nullptr)
     , mDidReval(false)
 {
     LOG(("Creating nsHttpChannel [this=%p]\n", this));
     mChannelCreationTime = PR_Now();
     mChannelCreationTimestamp = TimeStamp::Now();
@@ -3334,16 +3336,23 @@ nsHttpChannel::OnCacheEntryAvailableInte
     }
 
     if (NS_FAILED(rv) && (mLoadFlags & LOAD_ONLY_FROM_CACHE)) {
         // If we have a fallback URI (and we're not already
         // falling back), process the fallback asynchronously.
         if (!mFallbackChannel && !mFallbackKey.IsEmpty()) {
             return AsyncCall(&nsHttpChannel::HandleAsyncFallback);
         }
+
+        if (mIsPackagedAppResource) {
+            // We need to return FILE_NOT_FOUND in case an error occurs
+            // or we will take the user to the <you're offline> screen.
+            return NS_ERROR_FILE_NOT_FOUND;
+        }
+
         return NS_ERROR_DOCUMENT_NOT_CACHED;
     }
 
     if (NS_FAILED(rv)) {
         return rv;
     }
 
     // We may be waiting for more callbacks...
@@ -4765,16 +4774,23 @@ nsHttpChannel::AsyncOpen(nsIStreamListen
     LOG(("nsHttpChannel::AsyncOpen [this=%p]\n", this));
 
     NS_ENSURE_ARG_POINTER(listener);
     NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
     NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED);
 
     nsresult rv;
 
+    MOZ_ASSERT(NS_IsMainThread());
+    if (gHttpHandler->PackagedAppsEnabled()) {
+        nsAutoCString path;
+        mURI->GetPath(path);
+        mIsPackagedAppResource = path.Find(PACKAGED_APP_TOKEN) != kNotFound;
+    }
+
     rv = NS_CheckPortSafety(mURI);
     if (NS_FAILED(rv)) {
         ReleaseListeners();
         return rv;
     }
 
     if (ShouldIntercept()) {
         mInterceptCache = MAYBE_INTERCEPT;
@@ -4977,16 +4993,32 @@ nsHttpChannel::BeginConnect()
         }
     }
 
     // If mTimingEnabled flag is not set after OnModifyRequest() then
     // clear the already recorded AsyncOpen value for consistency.
     if (!mTimingEnabled)
         mAsyncOpenTime = TimeStamp();
 
+    if (mIsPackagedAppResource) {
+        // If this is a packaged app resource, the content will be fetched
+        // by the packaged app service into the cache, and the cache entry will
+        // be passed to OnCacheEntryAvailable.
+        mLoadFlags |= LOAD_ONLY_FROM_CACHE;
+        mLoadFlags |= LOAD_FROM_CACHE;
+        mLoadFlags &= ~VALIDATE_ALWAYS;
+        nsCOMPtr<nsIPackagedAppService> pas =
+          do_GetService("@mozilla.org/network/packaged-app-service;1", &rv);
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          return rv;
+        }
+
+        return pas->RequestURI(mURI, GetLoadContextInfo(this), this);
+    }
+
     // when proxying only use the pipeline bit if ProxyPipelining() allows it.
     if (!mConnectionInfo->UsingConnect() && mConnectionInfo->UsingHttpProxy()) {
         mCaps &= ~NS_HTTP_ALLOW_PIPELINING;
         if (gHttpHandler->ProxyPipelining())
             mCaps |= NS_HTTP_ALLOW_PIPELINING;
     }
 
     // if this somehow fails we can go on without it
--- a/netwerk/protocol/http/nsHttpChannel.h
+++ b/netwerk/protocol/http/nsHttpChannel.h
@@ -470,16 +470,21 @@ private:
     // when true, after we finish read from cache we must check all data
     // had been loaded from cache. If not, then an error has to be propagated
     // to the consumer.
     uint32_t                          mConcurentCacheAccess : 1;
     // whether the request is setup be byte-range
     uint32_t                          mIsPartialRequest : 1;
     // true iff there is AutoRedirectVetoNotifier on the stack
     uint32_t                          mHasAutoRedirectVetoNotifier : 1;
+    // Whether fetching the content is meant to be handled by the
+    // packaged app service, which behaves like a caching layer.
+    // Upon successfully fetching the package, the resource will be placed in
+    // the cache, and served by calling OnCacheEntryAvailable.
+    uint32_t                          mIsPackagedAppResource : 1;
 
     nsTArray<nsContinueRedirectionFunc> mRedirectFuncStack;
 
     // Needed for accurate DNS timing
     nsRefPtr<nsDNSPrefetch>           mDNSPrefetch;
 
     Http2PushedStream                 *mPushedStream;
     // True if the channel's principal was found on a phishing, malware, or
--- a/netwerk/protocol/http/nsHttpHandler.cpp
+++ b/netwerk/protocol/http/nsHttpHandler.cpp
@@ -279,16 +279,22 @@ nsHttpHandler::Init()
         prefBranch->AddObserver(TELEMETRY_ENABLED, this, true);
         prefBranch->AddObserver(H2MANDATORY_SUITE, this, true);
         prefBranch->AddObserver(HTTP_PREF("tcp_keepalive.short_lived_connections"), this, true);
         prefBranch->AddObserver(HTTP_PREF("tcp_keepalive.long_lived_connections"), this, true);
         prefBranch->AddObserver(SAFE_HINT_HEADER_VALUE, this, true);
         PrefsChanged(prefBranch, nullptr);
     }
 
+    rv = Preferences::AddBoolVarCache(&mPackagedAppsEnabled,
+        "network.http.enable-packaged-apps", false);
+    if (NS_FAILED(rv)) {
+        mPackagedAppsEnabled = false;
+    }
+
     nsHttpChannelAuthProvider::InitializePrefs();
 
     mMisc.AssignLiteral("rv:" MOZILLA_UAVERSION);
 
     mCompatFirefox.AssignLiteral("Firefox/" MOZILLA_UAVERSION);
 
     nsCOMPtr<nsIXULAppInfo> appInfo =
         do_GetService("@mozilla.org/xre/app-info;1");
--- a/netwerk/protocol/http/nsHttpHandler.h
+++ b/netwerk/protocol/http/nsHttpHandler.h
@@ -74,16 +74,17 @@ public:
 
     nsHttpVersion  HttpVersion()             { return mHttpVersion; }
     nsHttpVersion  ProxyHttpVersion()        { return mProxyHttpVersion; }
     uint8_t        ReferrerLevel()           { return mReferrerLevel; }
     bool           SpoofReferrerSource()     { return mSpoofReferrerSource; }
     uint8_t        ReferrerTrimmingPolicy()  { return mReferrerTrimmingPolicy; }
     uint8_t        ReferrerXOriginPolicy()   { return mReferrerXOriginPolicy; }
     bool           SendSecureXSiteReferrer() { return mSendSecureXSiteReferrer; }
+    bool           PackagedAppsEnabled()     { return mPackagedAppsEnabled; }
     uint8_t        RedirectionLimit()        { return mRedirectionLimit; }
     PRIntervalTime IdleTimeout()             { return mIdleTimeout; }
     PRIntervalTime SpdyTimeout()             { return mSpdyTimeout; }
     PRIntervalTime ResponseTimeout() {
       return mResponseTimeoutEnabled ? mResponseTimeout : 0;
     }
     PRIntervalTime ResponseTimeoutEnabled()  { return mResponseTimeoutEnabled; }
     uint32_t       NetworkChangedTimeout()   { return mNetworkChangedTimeout; }
@@ -403,16 +404,19 @@ private:
     uint16_t mMaxOptimisticPipelinedRequests;
     bool     mPipelineAggressive;
     int64_t  mMaxPipelineObjectSize;
     bool     mPipelineRescheduleOnTimeout;
     PRIntervalTime mPipelineRescheduleTimeout;
     PRIntervalTime mPipelineReadTimeout;
     nsCOMPtr<nsITimer> mPipelineTestTimer;
 
+    // Whether a URL containing !// should be interpreted as a packaged app channel
+    bool mPackagedAppsEnabled = false;
+
     uint8_t  mRedirectionLimit;
 
     // we'll warn the user if we load an URL containing a userpass field
     // unless its length is less than this threshold.  this warning is
     // intended to protect the user against spoofing attempts that use
     // the userpass field of the URL to obscure the actual origin server.
     uint8_t  mPhishyUserPassLength;
 
new file mode 100644
--- /dev/null
+++ b/netwerk/test/unit/test_packaged_app_channel.js
@@ -0,0 +1,138 @@
+//
+// This tests checks that a regular http channel can be used to load a
+// packaged app resource. It sets the network.http.enable-packaged-apps pref
+// to enable this feature, then creates a channel and loads the resource.
+// reset_pref resets the pref at the end of the test.
+
+Cu.import("resource://testing-common/httpd.js");
+Cu.import("resource://gre/modules/Services.jsm");
+
+XPCOMUtils.defineLazyGetter(this, "uri", function() {
+  return "http://localhost:" + httpserver.identity.primaryPort;
+});
+
+const nsIBinaryInputStream = Components.Constructor("@mozilla.org/binaryinputstream;1",
+                              "nsIBinaryInputStream",
+                              "setInputStream"
+                              );
+
+
+function make_channel(url) {
+  var ios = Cc["@mozilla.org/network/io-service;1"].
+            getService(Ci.nsIIOService);
+  return ios.newChannel2(url,
+                         "",
+                         null,
+                         null,      // aLoadingNode
+                         Services.scriptSecurityManager.getSystemPrincipal(),
+                         null,      // aTriggeringPrincipal
+                         Ci.nsILoadInfo.SEC_NORMAL,
+                         Ci.nsIContentPolicy.TYPE_OTHER);
+}
+
+function Listener(callback) {
+    this._callback = callback;
+}
+
+Listener.prototype = {
+    gotStartRequest: false,
+    available: -1,
+    gotStopRequest: false,
+    QueryInterface: function(iid) {
+        if (iid.equals(Ci.nsISupports) ||
+            iid.equals(Ci.nsIRequestObserver))
+            return this;
+        throw Cr.NS_ERROR_NO_INTERFACE;
+    },
+    onDataAvailable: function(request, ctx, stream, offset, count) {
+        try {
+            this.available = stream.available();
+            do_check_eq(this.available, count);
+            // Need to consume stream to avoid assertion
+            var str = new nsIBinaryInputStream(stream).readBytes(count);
+            equal(str, "<html>\r\n  <head>\r\n    <script src=\"/scripts/app.js\"></script>\r\n    ...\r\n  </head>\r\n  ...\r\n</html>\r\n", "check proper content");
+        }
+        catch (ex) {
+            do_throw(ex);
+        }
+    },
+    onStartRequest: function(request, ctx) {
+        this.gotStartRequest = true;
+    },
+    onStopRequest: function(request, ctx, status) {
+        this.gotStopRequest = true;
+        do_check_eq(status, 0);
+        if (this._callback) {
+            this._callback.call(null, this);
+        }
+    }
+};
+
+// The package content
+// getData formats it as described at http://www.w3.org/TR/web-packaging/#streamable-package-format
+var testData = {
+  content: [
+   { headers: ["Content-Location: /index.html", "Content-Type: text/html"], data: "<html>\r\n  <head>\r\n    <script src=\"/scripts/app.js\"></script>\r\n    ...\r\n  </head>\r\n  ...\r\n</html>\r\n", type: "text/html" },
+   { headers: ["Content-Location: /scripts/app.js", "Content-Type: text/javascript"], data: "module Math from '/scripts/helpers/math.js';\r\n...\r\n", type: "text/javascript" },
+   { headers: ["Content-Location: /scripts/helpers/math.js", "Content-Type: text/javascript"], data: "export function sum(nums) { ... }\r\n...\r\n", type: "text/javascript" }
+  ],
+  token : "gc0pJq0M:08jU534c0p",
+  getData: function() {
+    var str = "";
+    for (var i in this.content) {
+      str += "--" + this.token + "\r\n";
+      for (var j in this.content[i].headers) {
+        str += this.content[i].headers[j] + "\r\n";
+      }
+      str += "\r\n";
+      str += this.content[i].data + "\r\n";
+    }
+
+    str += "--" + this.token + "--";
+    return str;
+  }
+}
+
+function contentHandler(metadata, response)
+{
+  response.setHeader("Content-Type", 'application/package');
+  var body = testData.getData();
+  response.bodyOutputStream.write(body, body.length);
+}
+
+var httpserver = null;
+var originalPref = false;
+
+function run_test()
+{
+  // setup test
+  httpserver = new HttpServer();
+  httpserver.registerPathHandler("/package", contentHandler);
+  httpserver.start(-1);
+
+  // Enable the feature and save the original pref value
+  originalPref = Services.prefs.getBoolPref("network.http.enable-packaged-apps");
+  Services.prefs.setBoolPref("network.http.enable-packaged-apps", true);
+  do_register_cleanup(reset_pref);
+
+  add_test(test_channel);
+
+  // run tests
+  run_next_test();
+}
+
+function test_channel() {
+  var channel = make_channel(uri+"/package!//index.html");
+  channel.asyncOpen(new Listener(function(l) {
+    // XXX: no content length available for this resource
+    //do_check_true(channel.contentLength > 0);
+    do_check_true(l.gotStartRequest);
+    do_check_true(l.gotStopRequest);
+    run_next_test();
+  }), null);
+}
+
+function reset_pref() {
+  // Set the pref to its original value
+  Services.prefs.setBoolPref("network.http.enable-packaged-apps", originalPref);
+}
new file mode 100644
--- /dev/null
+++ b/netwerk/test/unit/test_packaged_app_service.js
@@ -0,0 +1,255 @@
+//
+// This file tests the packaged app service - nsIPackagedAppService
+// NOTE: The order in which tests are run is important
+//       If you need to add more tests, it's best to define them at the end
+//       of the file and to add them at the end of run_test
+//
+// ----------------------------------------------------------------------------
+//
+// test_bad_args
+//     - checks that calls to nsIPackagedAppService::requestURI do not accept a null argument
+// test_callback_gets_called
+//     - checks the regular use case -> requesting a resource should asynchronously return an entry
+// test_same_content
+//     - makes another request for the same file, and checks that the same content is returned
+// test_request_number
+//     - this test does not make a request, but checks that the package has only
+//       been requested once. The entry returned by the call to requestURI in
+//       test_same_content should be returned from the cache.
+//
+// test_package_does_not_exist
+//     - checks that requesting a file from a <package that does not exist>
+//       calls the listener with an error code
+// test_file_does_not_exist
+//     - checks that requesting a <subresource that doesn't exist> inside a
+//       package calls the listener with an error code
+//
+// test_bad_package
+//    - tests that a package with missing headers for some of the files
+//      will still return files that are correct
+// test_bad_package_404
+//    - tests that a request for a missing subresource doesn't hang if
+//      if the last file in the package is missing some headers
+
+Cu.import('resource://gre/modules/LoadContextInfo.jsm');
+Cu.import("resource://testing-common/httpd.js");
+Cu.import("resource://gre/modules/Services.jsm");
+
+// The number of times this package has been requested
+// This number might be reset by tests that use it
+var packagedAppRequestsMade = 0;
+// The default content handler. It just responds by sending the package data
+// with an application/package content type
+function packagedAppContentHandler(metadata, response)
+{
+  packagedAppRequestsMade++;
+  response.setHeader("Content-Type", 'application/package');
+  var body = testData.getData();
+  response.bodyOutputStream.write(body, body.length);
+}
+
+// The package content
+// getData formats it as described at http://www.w3.org/TR/web-packaging/#streamable-package-format
+var testData = {
+  content: [
+   { headers: ["Content-Location: /index.html", "Content-Type: text/html"], data: "<html>\r\n  <head>\r\n    <script src=\"/scripts/app.js\"></script>\r\n    ...\r\n  </head>\r\n  ...\r\n</html>\r\n", type: "text/html" },
+   { headers: ["Content-Location: /scripts/app.js", "Content-Type: text/javascript"], data: "module Math from '/scripts/helpers/math.js';\r\n...\r\n", type: "text/javascript" },
+   { headers: ["Content-Location: /scripts/helpers/math.js", "Content-Type: text/javascript"], data: "export function sum(nums) { ... }\r\n...\r\n", type: "text/javascript" }
+  ],
+  token : "gc0pJq0M:08jU534c0p",
+  getData: function() {
+    var str = "";
+    for (var i in this.content) {
+      str += "--" + this.token + "\r\n";
+      for (var j in this.content[i].headers) {
+        str += this.content[i].headers[j] + "\r\n";
+      }
+      str += "\r\n";
+      str += this.content[i].data + "\r\n";
+    }
+
+    str += "--" + this.token + "--";
+    return str;
+  }
+}
+
+XPCOMUtils.defineLazyGetter(this, "uri", function() {
+  return "http://localhost:" + httpserver.identity.primaryPort;
+});
+
+// The active http server initialized in run_test
+var httpserver = null;
+// The packaged app service initialized in run_test
+var paservice = null;
+// This variable is set before requestURI is called. The listener uses this variable
+// to check the correct resource path for the returned entry
+var packagePath = null;
+
+function run_test()
+{
+  // setup test
+  httpserver = new HttpServer();
+  httpserver.registerPathHandler("/package", packagedAppContentHandler);
+  httpserver.registerPathHandler("/304Package", packagedAppContentHandler);
+  httpserver.registerPathHandler("/badPackage", packagedAppBadContentHandler);
+  httpserver.start(-1);
+
+  paservice = Cc["@mozilla.org/network/packaged-app-service;1"]
+                     .getService(Ci.nsIPackagedAppService);
+  ok(!!paservice, "test service exists");
+
+  add_test(test_bad_args);
+
+  add_test(test_callback_gets_called);
+  add_test(test_same_content);
+  add_test(test_request_number);
+
+  add_test(test_package_does_not_exist);
+  add_test(test_file_does_not_exist);
+
+  add_test(test_bad_package);
+  add_test(test_bad_package_404);
+
+  // run tests
+  run_next_test();
+}
+
+// This checks the proper metadata is on the entry
+var metadataListener = {
+  onMetaDataElement: function(key, value) {
+    if (key == 'response-head')
+      equal(value, "HTTP/1.1 200 \r\nContent-Location: /index.html\r\nContent-Type: text/html\r\n");
+    else if (key == 'request-method')
+      equal(value, "GET");
+    else
+      ok(false, "unexpected metadata key")
+  }
+}
+
+// A listener we use to check the proper cache entry is returned by the service
+// NOTE: this listener only checks the content of index.html
+//       Don't use it when requesting other packaged resources! :)
+var cacheListener = {
+  onCacheEntryCheck: function() { return Ci.nsICacheEntryOpenCallback.ENTRY_WANTED; },
+  onCacheEntryAvailable: function (entry, isnew, appcache, status) {
+    ok(!!entry, "Needs to have an entry");
+    equal(status, Cr.NS_OK, "status is NS_OK");
+    equal(entry.key, uri + packagePath + "!//index.html", "Check entry has correct name");
+    entry.visitMetaData(metadataListener);
+    var inputStream = entry.openInputStream(0);
+    pumpReadStream(inputStream, function(read) {
+        inputStream.close();
+        equal(read,"<html>\r\n  <head>\r\n    <script src=\"/scripts/app.js\"></script>\r\n    ...\r\n  </head>\r\n  ...\r\n</html>\r\n"); // not using do_check_eq since logger will fail for the 1/4MB string
+    });
+    run_next_test();
+  }
+};
+
+// ----------------------------------------------------------------------------
+
+// These calls should fail, since one of the arguments is invalid or null
+function test_bad_args() {
+  Assert.throws(() => { paservice.requestURI(createURI("http://test.com"), LoadContextInfo.default, cacheListener); }, "url's with no !// aren't allowed");
+  Assert.throws(() => { paservice.requestURI(createURI("http://test.com/package!//test"), LoadContextInfo.default, null); }, "should have a callback");
+  Assert.throws(() => { paservice.requestURI(null, LoadContextInfo.default, cacheListener); }, "should have a URI");
+  Assert.throws(() => { paservice.requestURI(createURI("http://test.com/package!//test"), null, cacheListener); }, "should have a LoadContextInfo");
+  run_next_test();
+}
+
+// ----------------------------------------------------------------------------
+
+// This tests that the callback gets called, and the cacheListener gets the proper content.
+function test_callback_gets_called() {
+  packagePath = "/package";
+  paservice.requestURI(createURI(uri + packagePath + "!//index.html"), LoadContextInfo.default, cacheListener);
+}
+
+// Tests that requesting the same resource returns the same content
+function test_same_content() {
+  packagePath = "/package";
+  paservice.requestURI(createURI(uri + packagePath + "!//index.html"), LoadContextInfo.default, cacheListener);
+}
+
+// Check the package has only been requested once.
+function test_request_number() {
+  equal(packagedAppRequestsMade, 1, "only one request should be made. Second should be loaded from cache");
+  run_next_test();
+}
+
+// ----------------------------------------------------------------------------
+
+// This listener checks that the requested resources are not returned
+// either because the package does not exist, or because the requested resource
+// is not contained in the package.
+var listener404 = {
+  onCacheEntryCheck: function() { return Ci.nsICacheEntryOpenCallback.ENTRY_WANTED; },
+  onCacheEntryAvailable: function (entry, isnew, appcache, status) {
+    // XXX: it returns NS_ERROR_FAILURE for a missing package
+    // and NS_ERROR_FILE_NOT_FOUND for a missing file from the package.
+    // Maybe make them both return NS_ERROR_FILE_NOT_FOUND?
+    notEqual(status, Cr.NS_OK, "NOT FOUND");
+    ok(!entry, "There should be no entry");
+    run_next_test();
+  }
+};
+
+// Tests that an error is returned for a non existing package
+function test_package_does_not_exist() {
+  packagePath = "/package_non_existent";
+  paservice.requestURI(createURI(uri + packagePath + "!//index.html"), LoadContextInfo.default, listener404);
+}
+
+// Tests that an error is returned for a non existing resource in a package
+function test_file_does_not_exist() {
+  packagePath = "/package"; // This package exists
+  paservice.requestURI(createURI(uri + packagePath + "!//file_non_existent.html"), LoadContextInfo.default, listener404);
+}
+
+// ----------------------------------------------------------------------------
+
+// Broken package. The first and last resources do not contain a "Content-Location" header
+// and should be ignored.
+var badTestData = {
+  content: [
+   { headers: ["Content-Type: text/javascript"], data: "module Math from '/scripts/helpers/math.js';\r\n...\r\n", type: "text/javascript" },
+   { headers: ["Content-Location: /index.html", "Content-Type: text/html"], data: "<html>\r\n  <head>\r\n    <script src=\"/scripts/app.js\"></script>\r\n    ...\r\n  </head>\r\n  ...\r\n</html>\r\n", type: "text/html" },
+   { headers: ["Content-Type: text/javascript"], data: "export function sum(nums) { ... }\r\n...\r\n", type: "text/javascript" }
+  ],
+  token : "gc0pJq0M:08jU534c0p",
+  getData: function() {
+    var str = "";
+    for (var i in this.content) {
+      str += "--" + this.token + "\r\n";
+      for (var j in this.content[i].headers) {
+        str += this.content[i].headers[j] + "\r\n";
+      }
+      str += "\r\n";
+      str += this.content[i].data + "\r\n";
+    }
+
+    str += "--" + this.token + "--";
+    return str;
+  }
+}
+
+// Returns the content of the package with "Content-Location" headers missing for the first and last resource
+function packagedAppBadContentHandler(metadata, response)
+{
+  response.setHeader("Content-Type", 'application/package');
+  var body = badTestData.getData();
+  response.bodyOutputStream.write(body, body.length);
+}
+
+// Checks that the resource with the proper headers inside the bad package is still returned
+function test_bad_package() {
+  packagePath = "/badPackage";
+  paservice.requestURI(createURI(uri + packagePath + "!//index.html"), LoadContextInfo.default, cacheListener);
+}
+
+// Checks that the request for a non-existent resource doesn't hang for a bad package
+function test_bad_package_404() {
+  packagePath = "/badPackage";
+  paservice.requestURI(createURI(uri + packagePath + "!//file_non_existent.html"), LoadContextInfo.default, listener404);
+}
+
+// ----------------------------------------------------------------------------
--- a/netwerk/test/unit/xpcshell.ini
+++ b/netwerk/test/unit/xpcshell.ini
@@ -16,16 +16,17 @@ support-files =
   data/test_readline7.txt
   data/test_readline8.txt
   data/signed_win.exe
   socks_client_subprocess.js
   test_link.desktop
   test_link.url
   ../../dns/effective_tld_names.dat
 
+[test_packaged_app_channel.js]
 [test_nsIBufferedOutputStream_writeFrom_block.js]
 [test_cache2-00-service-get.js]
 [test_cache2-01-basic.js]
 [test_cache2-01a-basic-readonly.js]
 [test_cache2-01b-basic-datasize.js]
 [test_cache2-01c-basic-hasmeta-only.js]
 [test_cache2-01d-basic-not-wanted.js]
 [test_cache2-01e-basic-bypass-if-busy.js]
@@ -310,8 +311,9 @@ skip-if = os != "win"
 [test_reply_without_content_type.js]
 [test_websocket_offline.js]
 [test_tls_server.js]
 # The local cert service used by this test is not currently shipped on Android
 skip-if = os == "android"
 [test_1073747.js]
 [test_multipart_streamconv_application_package.js]
 [test_safeoutputstream_append.js]
+[test_packaged_app_service.js]
--- a/parser/htmlparser/nsHTMLEntities.cpp
+++ b/parser/htmlparser/nsHTMLEntities.cpp
@@ -60,38 +60,38 @@ static const PLDHashTableOps EntityToUni
 static const PLDHashTableOps UnicodeToEntityOps = {
   hashUnicodeValue,
   matchNodeUnicode,
   PL_DHashMoveEntryStub,
   PL_DHashClearEntryStub,
   nullptr,
 };
 
-static PLDHashTable2* gEntityToUnicode;
-static PLDHashTable2* gUnicodeToEntity;
+static PLDHashTable* gEntityToUnicode;
+static PLDHashTable* gUnicodeToEntity;
 static nsrefcnt gTableRefCnt = 0;
 
 #define HTML_ENTITY(_name, _value) { #_name, _value },
 static const EntityNode gEntityArray[] = {
 #include "nsHTMLEntityList.h"
 };
 #undef HTML_ENTITY
 
 #define NS_HTML_ENTITY_COUNT ((int32_t)ArrayLength(gEntityArray))
 
 nsresult
 nsHTMLEntities::AddRefTable(void)
 {
   if (!gTableRefCnt) {
-    gEntityToUnicode = new PLDHashTable2(&EntityToUnicodeOps,
-                                         sizeof(EntityNodeEntry),
-                                         NS_HTML_ENTITY_COUNT);
-    gUnicodeToEntity = new PLDHashTable2(&UnicodeToEntityOps,
-                                         sizeof(EntityNodeEntry),
-                                         NS_HTML_ENTITY_COUNT);
+    gEntityToUnicode = new PLDHashTable(&EntityToUnicodeOps,
+                                        sizeof(EntityNodeEntry),
+                                        NS_HTML_ENTITY_COUNT);
+    gUnicodeToEntity = new PLDHashTable(&UnicodeToEntityOps,
+                                        sizeof(EntityNodeEntry),
+                                        NS_HTML_ENTITY_COUNT);
     for (const EntityNode *node = gEntityArray,
                  *node_end = ArrayEnd(gEntityArray);
          node < node_end; ++node) {
 
       // add to Entity->Unicode table
       EntityNodeEntry* entry =
         static_cast<EntityNodeEntry*>
                    (PL_DHashTableAdd(gEntityToUnicode, node->mStr, fallible));
--- a/python/mozbuild/mozbuild/frontend/emitter.py
+++ b/python/mozbuild/mozbuild/frontend/emitter.py
@@ -845,43 +845,46 @@ class TreeMetadataEmitter(LoggingMixin):
     def _process_generated_files(self, context):
         generated_files = context.get('GENERATED_FILES')
         if not generated_files:
             return
 
         for f in generated_files:
             flags = generated_files[f]
             output = f
+            inputs = []
             if flags.script:
                 method = "main"
+                script = SourcePath(context, flags.script).full_path
+
                 # Deal with cases like "C:\\path\\to\\script.py:function".
-                if not flags.script.endswith('.py') and ':' in flags.script:
-                    script, method = flags.script.rsplit(':', 1)
-                else:
-                    script = flags.script
-                script = mozpath.join(context.srcdir, script)
-                inputs = [mozpath.join(context.srcdir, i) for i in flags.inputs]
+                if '.py:' in script:
+                    script, method = script.rsplit('.py:', 1)
+                    script += '.py'
 
                 if not os.path.exists(script):
                     raise SandboxValidationError(
                         'Script for generating %s does not exist: %s'
                         % (f, script), context)
                 if os.path.splitext(script)[1] != '.py':
                     raise SandboxValidationError(
                         'Script for generating %s does not end in .py: %s'
                         % (f, script), context)
-                for i in inputs:
-                    if not os.path.exists(i):
+
+                for i in flags.inputs:
+                    p = Path(context, i)
+                    if (isinstance(p, SourcePath) and
+                            not os.path.exists(p.full_path)):
                         raise SandboxValidationError(
                             'Input for generating %s does not exist: %s'
-                            % (f, i), context)
+                            % (f, p.full_path), context)
+                    inputs.append(p.full_path)
             else:
                 script = None
                 method = None
-                inputs = []
             yield GeneratedFile(context, script, method, output, inputs)
 
     def _process_test_harness_files(self, context):
         test_harness_files = context.get('TEST_HARNESS_FILES')
         if not test_harness_files:
             return
 
         srcdir_files = defaultdict(list)
--- a/python/mozbuild/mozbuild/test/frontend/data/generated-files-absolute-script/moz.build
+++ b/python/mozbuild/mozbuild/test/frontend/data/generated-files-absolute-script/moz.build
@@ -1,9 +1,9 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # Any copyright is dedicated to the Public Domain.
 # http://creativecommons.org/publicdomain/zero/1.0/
 
 GENERATED_FILES += ['bar.c']
 
 bar = GENERATED_FILES['bar.c']
-bar.script = TOPSRCDIR + '/script.py:make_bar'
+bar.script = '/script.py:make_bar'
 bar.inputs = []
--- a/rdf/base/nsInMemoryDataSource.cpp
+++ b/rdf/base/nsInMemoryDataSource.cpp
@@ -120,17 +120,17 @@ public:
     // These are shared between hash/as (see the union below)
     nsIRDFResource*         mSource;
     Assertion*              mNext;
 
     union
     {
         struct hash
         {
-            PLDHashTable2*  mPropertyHash;
+            PLDHashTable*  mPropertyHash;
         } hash;
         struct as
         {
             nsIRDFResource* mProperty;
             nsIRDFNode*     mTarget;
             Assertion*      mInvNext;
             // make sure bool are final elements
             bool            mTruthValue;
@@ -158,17 +158,17 @@ Assertion::Assertion(nsIRDFResource* aSo
       mRefCnt(0),
       mHashEntry(true)
 {
     MOZ_COUNT_CTOR(Assertion);
 
     NS_ADDREF(mSource);
 
     u.hash.mPropertyHash =
-        new PLDHashTable2(PL_DHashGetStubOps(), sizeof(Entry));
+        new PLDHashTable(PL_DHashGetStubOps(), sizeof(Entry));
 }
 
 Assertion::Assertion(nsIRDFResource* aSource,
                      nsIRDFResource* aProperty,
                      nsIRDFNode* aTarget,
                      bool aTruthValue)
     : mSource(aSource),
       mNext(nullptr),
@@ -245,18 +245,18 @@ class InMemoryDataSource : public nsIRDF
                            public rdfIDataSource
 {
 protected:
     // These hash tables are keyed on pointers to nsIRDFResource
     // objects (the nsIRDFService ensures that there is only ever one
     // nsIRDFResource object per unique URI). The value of an entry is
     // an Assertion struct, which is a linked list of (subject
     // predicate object) triples.
-    PLDHashTable2 mForwardArcs;
-    PLDHashTable2 mReverseArcs;
+    PLDHashTable mForwardArcs;
+    PLDHashTable mReverseArcs;
 
     nsCOMArray<nsIRDFObserver> mObservers;  
     uint32_t                   mNumObservers;
 
     // VisitFoo needs to block writes, [Un]Assert only allowed
     // during mReadCount == 0
     uint32_t mReadCount;
 
--- a/rdf/base/nsRDFService.h
+++ b/rdf/base/nsRDFService.h
@@ -33,21 +33,21 @@ class nsIRDFInt;
 class nsIRDFDate;
 class BlobImpl;
 
 class RDFServiceImpl final : public nsIRDFService,
                              public nsSupportsWeakReference
 {
 protected:
     PLHashTable* mNamedDataSources;
-    PLDHashTable2 mResources;
-    PLDHashTable2 mLiterals;
-    PLDHashTable2 mInts;
-    PLDHashTable2 mDates;
-    PLDHashTable2 mBlobs;
+    PLDHashTable mResources;
+    PLDHashTable mLiterals;
+    PLDHashTable mInts;
+    PLDHashTable mDates;
+    PLDHashTable mBlobs;
 
     nsAutoCString mLastURIPrefix;
     nsCOMPtr<nsIFactory> mLastFactory;
     nsCOMPtr<nsIFactory> mDefaultResourceFactory;
 
     RDFServiceImpl();
     nsresult Init();
     virtual ~RDFServiceImpl();
--- a/security/apps/moz.build
+++ b/security/apps/moz.build
@@ -18,17 +18,17 @@ LOCAL_INCLUDES += [
     '/security/manager/ssl',
     '/security/pkix/include',
 ]
 
 DEFINES['NSS_ENABLE_ECC'] = 'True'
 for var in ('DLL_PREFIX', 'DLL_SUFFIX'):
     DEFINES[var] = '"%s"' % CONFIG[var]
 
-test_ssl_path = TOPSRCDIR + '/security/manager/ssl/tests/unit'
+test_ssl_path = '/security/manager/ssl/tests/unit'
 
 headers_arrays_certs = [
     ('marketplace-prod-public.inc', 'marketplaceProdPublicRoot', 'marketplace-prod-public.crt'),
     ('marketplace-prod-reviewers.inc', 'marketplaceProdReviewersRoot', 'marketplace-prod-reviewers.crt'),
     ('marketplace-dev-public.inc', 'marketplaceDevPublicRoot', 'marketplace-dev-public.crt'),
     ('marketplace-dev-reviewers.inc', 'marketplaceDevReviewersRoot', 'marketplace-dev-reviewers.crt'),
     ('marketplace-stage.inc', 'marketplaceStageRoot', 'marketplace-stage.crt'),
     ('manifest-signing-root.inc', 'trustedAppPublicRoot', 'trusted-app-public.der'),
--- a/security/manager/ssl/nsCertTree.h
+++ b/security/manager/ssl/nsCertTree.h
@@ -119,17 +119,17 @@ private:
   static const uint32_t kInitialCacheLength = 64;
 
   nsTArray< mozilla::RefPtr<nsCertTreeDispInfo> > mDispInfo;
   nsCOMPtr<nsITreeBoxObject>  mTree;
   nsCOMPtr<nsITreeSelection>  mSelection;
   treeArrayEl                *mTreeArray;
   int32_t                         mNumOrgs;
   int32_t                         mNumRows;
-  PLDHashTable2 mCompareCache;
+  PLDHashTable mCompareCache;
   nsCOMPtr<nsINSSComponent> mNSSComponent;
   nsCOMPtr<nsICertOverrideService> mOverrideService;
   mozilla::RefPtr<nsCertOverrideService> mOriginalOverrideService;
 
   treeArrayEl *GetThreadDescAtIndex(int32_t _index);
   already_AddRefed<nsIX509Cert> 
     GetCertAtIndex(int32_t _index, int32_t *outAbsoluteCertOffset = nullptr);
   mozilla::TemporaryRef<nsCertTreeDispInfo>
--- a/security/manager/ssl/nsNSSShutDown.h
+++ b/security/manager/ssl/nsNSSShutDown.h
@@ -153,18 +153,18 @@ private:
 
   static PLDHashOperator
   doPK11LogoutHelper(PLDHashTable *table, PLDHashEntryHdr *hdr,
                                                     uint32_t number, void *arg);
 protected:
   mozilla::Mutex mListLock;
   static nsNSSShutDownList *singleton;
   uint32_t mActiveSSLSockets;
-  PLDHashTable2 mObjects;
-  PLDHashTable2 mPK11LogoutCancelObjects;
+  PLDHashTable mObjects;
+  PLDHashTable mPK11LogoutCancelObjects;
   nsNSSActivityState mActivityState;
 };
 
 /*
   A class deriving from nsNSSShutDownObject will have its instances
   automatically tracked in a list. However, it must follow some rules
   to assure correct behaviour.
   
--- a/security/manager/ssl/nsSecureBrowserUIImpl.h
+++ b/security/manager/ssl/nsSecureBrowserUIImpl.h
@@ -95,13 +95,13 @@ protected:
   void UpdateSubrequestMembers(nsISupports* securityInfo, nsIRequest* request);
 
   void ObtainEventSink(nsIChannel *channel, 
                        nsCOMPtr<nsISecurityEventSink> &sink);
 
   nsCOMPtr<nsISSLStatus> mSSLStatus;
   nsCOMPtr<nsISupports> mCurrentToplevelSecurityInfo;
 
-  PLDHashTable2 mTransferringRequests;
+  PLDHashTable mTransferringRequests;
 };
 
 
 #endif /* nsSecureBrowserUIImpl_h_ */
--- a/security/manager/ssl/tests/unit/pycert.py
+++ b/security/manager/ssl/tests/unit/pycert.py
@@ -376,22 +376,18 @@ class Certificate:
 # The build harness will call this function with an output file-like
 # object, a path to a file containing a specification, and the path to
 # the directory containing the buildid file. This will read the
 # specification and output the certificate as PEM. The purpose of the
 # buildid file is to provide a single definition of 'now'. This is
 # particularly important when building on OS X, where we generate
 # everything twice for unified builds. During the unification step, if
 # any pair of input files differ, the build system throws an error.
-# While it would make the most sense to provide the path to the buildid
-# file itself, since it doesn't exist when processing moz.build files
-# (but it does exist when actually running this script), the build
-# system won't let us pass it in directly.
 def main(output, inputPath, buildIDPath):
-    with open('%s/buildid' % buildIDPath) as buildidFile:
+    with open(buildIDPath) as buildidFile:
         buildid = buildidFile.read().strip()
     now = datetime.datetime.strptime(buildid, '%Y%m%d%H%M%S')
     with open(inputPath) as configStream:
         output.write(Certificate(configStream, now=now).toPEM())
 
 # When run as a standalone program, this will read a specification from
 # stdin and output the certificate as PEM to stdout.
 if __name__ == '__main__':
--- a/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/moz.build
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints/moz.build
@@ -30,10 +30,10 @@ test_certificates = (
     'int-valid-ku-server-eku.pem',
 )
 
 for test_certificate in test_certificates:
     input_file = test_certificate + '.certspec'
     GENERATED_FILES += [test_certificate]
     props = GENERATED_FILES[test_certificate]
     props.script = '../pycert.py'
-    props.inputs = [input_file, TOPOBJDIR + '/config']
+    props.inputs = [input_file, '!/config/buildid']
     TEST_HARNESS_FILES.xpcshell.security.manager.ssl.tests.unit.test_intermediate_basic_usage_constraints += ['!%s' % test_certificate]
--- a/testing/mozharness/mozharness.json
+++ b/testing/mozharness/mozharness.json
@@ -1,4 +1,4 @@
 {
     "repo": "https://hg.mozilla.org/build/mozharness",
-    "revision": "3030ddc03dd3"
+    "revision": "476752fc4ea8"
 }
--- a/testing/web-platform/meta/encoding/gbk-encoder.html.ini
+++ b/testing/web-platform/meta/encoding/gbk-encoder.html.ini
@@ -1,8 +1,5 @@
 [gbk-encoder.html]
   type: testharness
-  [gbk encoder: Euro]
-    expected: FAIL
-
   [gbk encoder: poo]
     expected: FAIL
 
--- a/toolkit/components/asyncshutdown/AsyncShutdown.jsm
+++ b/toolkit/components/asyncshutdown/AsyncShutdown.jsm
@@ -266,16 +266,59 @@ function looseTimer(delay) {
     --beats;
   }, DELAY_BEAT, Ci.nsITimer.TYPE_REPEATING_PRECISE_CAN_SKIP);
   // Ensure that the timer is both canceled once we are done with it
   // and not garbage-collected until then.
   deferred.promise.then(() => timer.cancel(), () => timer.cancel());
   return deferred;
 }
 
+/**
+ * Given an nsIStackFrame object, find the caller filename, line number,
+ * and stack if necessary, and return them as an object.
+ *
+ * @param {nsIStackFrame} topFrame Top frame of the call stack.
+ * @param {string} filename Pre-supplied filename or null if unknown.
+ * @param {number} lineNumber Pre-supplied line number or null if unknown.
+ * @param {string} stack Pre-supplied stack or null if unknown.
+ *
+ * @return object
+ */
+function getOrigin(topFrame, filename = null, lineNumber = null, stack = null) {
+  // Determine the filename and line number of the caller.
+  let frame = topFrame;
+
+  for (; frame && frame.filename == topFrame.filename; frame = frame.caller) {
+    // Climb up the stack
+  }
+
+  if (filename == null) {
+    filename = frame ? frame.filename : "?";
+  }
+  if (lineNumber == null) {
+    lineNumber = frame ? frame.lineNumber : 0;
+  }
+  if (stack == null) {
+    // Now build the rest of the stack as a string, using Task.jsm's rewriting
+    // to ensure that we do not lose information at each call to `Task.spawn`.
+    let frames = [];
+    while (frame != null) {
+      frames.push(frame.filename + ":" + frame.name + ":" + frame.lineNumber);
+      frame = frame.caller;
+    }
+    stack = Task.Debugging.generateReadableStack(frames.join("\n")).split("\n");
+  }
+
+  return {
+    filename: filename,
+    lineNumber: lineNumber,
+    stack: stack,
+  };
+}
+
 this.EXPORTED_SYMBOLS = ["AsyncShutdown"];
 
 /**
  * {string} topic -> phase
  */
 let gPhases = new Map();
 
 this.AsyncShutdown = {
@@ -600,46 +643,19 @@ function Barrier(name) {
       }
       if (!this._waitForMe) {
         throw new Error(`Phase "${ this._name } is finished, it is too late to register completion condition "${ name }"`);
       }
 
       // Normalize the details
 
       let fetchState = details.fetchState || null;
-      let filename = details.filename || "?";
-      let lineNumber = details.lineNumber || -1;
-      let stack = details.stack || undefined;
-
-      if (filename == "?" || lineNumber == -1 || stack === undefined) {
-        // Determine the filename and line number of the caller.
-        let leaf = Components.stack;
-        let frame;
-        for (frame = leaf; frame != null && frame.filename == leaf.filename; frame = frame.caller) {
-          // Climb up the stack
-        }
-
-        if (filename == "?") {
-          filename = frame ? frame.filename : "?";
-        }
-        if (lineNumber == -1) {
-          lineNumber = frame ? frame.lineNumber : -1;
-        }
-
-        // Now build the rest of the stack as a string, using Task.jsm's rewriting
-        // to ensure that we do not lose information at each call to `Task.spawn`.
-        let frames = [];
-        while (frame != null) {
-          frames.push(frame.filename + ":" + frame.name + ":" + frame.lineNumber);
-          frame = frame.caller;
-        }
-        if (stack === undefined) {
-          stack = Task.Debugging.generateReadableStack(frames.join("\n")).split("\n");
-        }
-      }
+      let filename = details.filename || null;
+      let lineNumber = details.lineNumber || null;
+      let stack = details.stack || null;
 
       // Split the condition between a trigger function and a promise.
 
       // The function to call to notify the blocker that we have started waiting.
       // This function returns a promise resolved/rejected once the
       // condition is complete, and never throws.
       let trigger;
 
@@ -670,24 +686,27 @@ function Barrier(name) {
           State: ${ safeGetState(fetchState) }`;
         warn(msg, error);
 
         // The error should remain uncaught, to ensure that it
         // still causes tests to fail.
         Promise.reject(error);
       });
 
+      let topFrame = null;
+      if (filename == null || lineNumber == null || stack == null) {
+        topFrame = Components.stack;
+      }
+
       let blocker = {