Merge mozilla-inbound to mozilla-central. a=merge
authorDorel Luca <dluca@mozilla.com>
Tue, 03 Apr 2018 01:00:11 +0300
changeset 411380 445255800255bb13ed096b5b7da36aa835e41dd8
parent 411379 69bfd5a40fee2cc70306e6fc67854bc7000ee358 (current diff)
parent 411378 0702a754328d4e220a67c53161c2be15401de373 (diff)
child 411381 05b85c67123d3bdfb4a6c5186b05083e468e44c7
child 411425 2a199de2ee43248183e4b2ef40cf8d51fe49465d
push id62032
push userdluca@mozilla.com
push dateMon, 02 Apr 2018 22:12:37 +0000
treeherderautoland@05b85c67123d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone61.0a1
first release with
nightly linux32
445255800255 / 61.0a1 / 20180402220122 / files
nightly linux64
445255800255 / 61.0a1 / 20180402220122 / files
nightly mac
445255800255 / 61.0a1 / 20180402220122 / files
nightly win32
445255800255 / 61.0a1 / 20180402220122 / files
nightly win64
445255800255 / 61.0a1 / 20180402220122 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-inbound to mozilla-central. a=merge
browser/themes/linux/social/services-16.png
browser/themes/linux/social/services-64.png
browser/themes/osx/social/services-16.png
browser/themes/osx/social/services-16@2x.png
browser/themes/osx/social/services-64.png
browser/themes/osx/social/services-64@2x.png
browser/themes/windows/social/services-16.png
browser/themes/windows/social/services-64.png
--- a/browser/components/enterprisepolicies/schemas/policies-schema.json
+++ b/browser/components/enterprisepolicies/schemas/policies-schema.json
@@ -444,16 +444,17 @@
       "first_available": "60.0",
 
       "type": "boolean"
     },
 
     "SearchBar": {
       "description": "Sets the default location of the search bar. Only applies on firtrun, but can be changed.",
       "first_available": "60.0",
+      "enterprise_only": true,
 
       "type": "string",
       "enum": ["unified", "separate"]
     },
 
     "SearchEngines": {
       "description": "Modifies the list of search engines built into Firefox",
       "first_available": "60.0",
--- a/browser/config/mozconfigs/win32/clang
+++ b/browser/config/mozconfigs/win32/clang
@@ -5,11 +5,14 @@ MOZ_AUTOMATION_L10N_CHECK=0
 . "$topsrcdir/build/mozconfig.win-common"
 . "$topsrcdir/browser/config/mozconfigs/common"
 
 ac_add_options --enable-optimize
 
 ac_add_options --enable-clang-plugin
 
 . $topsrcdir/build/win32/mozconfig.vs-latest
+# Regardless of what mozconfig.vs-latest sets, clang-plugin builds need to use
+# the Microsoft linker until at least bugs 1414287 and 1427808 are resolved.
+export LINKER=link
 
 . "$topsrcdir/build/mozconfig.common.override"
 . "$topsrcdir/build/mozconfig.clang-cl"
--- a/browser/config/mozconfigs/win32/clang-debug
+++ b/browser/config/mozconfigs/win32/clang-debug
@@ -6,11 +6,14 @@ MOZ_AUTOMATION_L10N_CHECK=0
 . "$topsrcdir/browser/config/mozconfigs/common"
 
 ac_add_options --enable-optimize
 ac_add_options --enable-debug
 
 ac_add_options --enable-clang-plugin
 
 . $topsrcdir/build/win32/mozconfig.vs-latest
+# Regardless of what mozconfig.vs-latest sets, clang-plugin builds need to use
+# the Microsoft linker until at least bugs 1414287 and 1427808 are resolved.
+export LINKER=link
 
 . "$topsrcdir/build/mozconfig.common.override"
 . "$topsrcdir/build/mozconfig.clang-cl"
--- a/browser/config/mozconfigs/win32/debug-static-analysis
+++ b/browser/config/mozconfigs/win32/debug-static-analysis
@@ -6,11 +6,14 @@ MOZ_AUTOMATION_L10N_CHECK=0
 . "$topsrcdir/browser/config/mozconfigs/common"
 
 ac_add_options --enable-debug
 ac_add_options --enable-dmd
 
 ac_add_options --enable-clang-plugin
 
 . $topsrcdir/build/win32/mozconfig.vs-latest
+# Regardless of what mozconfig.vs-latest sets, clang-plugin builds need to use
+# the Microsoft linker until at least bugs 1414287 and 1427808 are resolved.
+export LINKER=link
 
 . "$topsrcdir/build/mozconfig.common.override"
 . "$topsrcdir/build/mozconfig.clang-cl"
--- a/browser/config/mozconfigs/win64/clang
+++ b/browser/config/mozconfigs/win64/clang
@@ -5,11 +5,14 @@ MOZ_AUTOMATION_PACKAGE_TEST=0
 . "$topsrcdir/browser/config/mozconfigs/common"
 . "$topsrcdir/browser/config/mozconfigs/win64/common-win64"
 
 ac_add_options --enable-optimize
 
 ac_add_options --enable-clang-plugin
 
 . $topsrcdir/build/win64/mozconfig.vs-latest
+# Regardless of what mozconfig.vs-latest sets, clang-plugin builds need to use
+# the Microsoft linker until at least bugs 1414287 and 1427808 are resolved.
+export LINKER=link
 
 . "$topsrcdir/build/mozconfig.common.override"
 . "$topsrcdir/build/mozconfig.clang-cl"
--- a/browser/config/mozconfigs/win64/clang-debug
+++ b/browser/config/mozconfigs/win64/clang-debug
@@ -6,11 +6,14 @@ MOZ_AUTOMATION_PACKAGE_TEST=0
 . "$topsrcdir/browser/config/mozconfigs/win64/common-win64"
 
 ac_add_options --enable-optimize
 ac_add_options --enable-debug
 
 ac_add_options --enable-clang-plugin
 
 . $topsrcdir/build/win64/mozconfig.vs-latest
+# Regardless of what mozconfig.vs-latest sets, clang-plugin builds need to use
+# the Microsoft linker until at least bugs 1414287 and 1427808 are resolved.
+export LINKER=link
 
 . "$topsrcdir/build/mozconfig.common.override"
 . "$topsrcdir/build/mozconfig.clang-cl"
--- a/browser/config/mozconfigs/win64/debug-searchfox
+++ b/browser/config/mozconfigs/win64/debug-searchfox
@@ -8,11 +8,14 @@ MOZ_AUTOMATION_L10N_CHECK=0
 
 ac_add_options --enable-optimize
 ac_add_options --enable-debug
 
 ac_add_options --enable-clang-plugin
 ac_add_options --enable-mozsearch-plugin
 
 . $topsrcdir/build/win64/mozconfig.vs-latest
+# Regardless of what mozconfig.vs-latest sets, clang-plugin builds need to use
+# the Microsoft linker until at least bugs 1414287 and 1427808 are resolved.
+export LINKER=link
 
 . "$topsrcdir/build/mozconfig.common.override"
 . "$topsrcdir/build/mozconfig.clang-cl"
--- a/browser/installer/allowed-dupes.mn
+++ b/browser/installer/allowed-dupes.mn
@@ -6,20 +6,20 @@
 #
 
 # updater on osx is bug 1311194
 LaunchServices/org.mozilla.updater
 updater.app/Contents/MacOS/org.mozilla.updater
 updater.app/Contents/PkgInfo
 browser/chrome.manifest
 # browser branding / themes is bug 1313106
-browser/chrome/browser/content/branding/icon128.png
 browser/chrome/browser/content/branding/icon16.png
 browser/chrome/browser/content/branding/icon32.png
 browser/chrome/browser/content/branding/icon48.png
+browser/chrome/browser/content/branding/icon64.png
 browser/chrome/browser/content/browser/defaultthemes/5.header.png
 browser/chrome/browser/content/browser/extension.svg
 browser/chrome/browser/content/browser/places/bookmarkProperties.xul
 browser/chrome/browser/content/browser/places/bookmarkProperties2.xul
 browser/chrome/browser/skin/classic/browser/addons/addon-install-confirm.svg
 browser/chrome/browser/skin/classic/browser/connection-secure.svg
 browser/chrome/browser/skin/classic/browser/controlcenter/warning-gray.svg
 # devtools reduction is bug 1311178
@@ -140,10 +140,10 @@ res/table-remove-column-active.gif
 res/table-remove-column-hover.gif
 res/table-remove-column.gif
 res/table-remove-row-active.gif
 res/table-remove-row-hover.gif
 res/table-remove-row.gif
 res/multilocale.txt
 update.locale
 # Aurora branding
-browser/chrome/browser/content/branding/icon64.png
+browser/chrome/browser/content/branding/icon128.png
 browser/chrome/devtools/content/framework/dev-edition-promo/dev-edition-logo.png
deleted file mode 100644
index ecd40524b58d05685e784ae119ddff5d0ee94a7b..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index e4db1bf938dd1f2526f032c4cdab70ff5de15fed..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index ecd40524b58d05685e784ae119ddff5d0ee94a7b..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 0078b054924b4b50601a49c6d3eb9cec19f5edfe..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index e4db1bf938dd1f2526f032c4cdab70ff5de15fed..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 8d9f0233425f3c4685a55fab1b29c1333b24af76..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index ecd40524b58d05685e784ae119ddff5d0ee94a7b..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index e4db1bf938dd1f2526f032c4cdab70ff5de15fed..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
--- a/build/build-clang/clang-win64.json
+++ b/build/build-clang/clang-win64.json
@@ -1,10 +1,10 @@
 {
-    "llvm_revision": "326909",
+    "llvm_revision": "328769",
     "stages": "3",
     "build_libcxx": false,
     "build_type": "Release",
     "assertions": false,
     "llvm_repo": "https://llvm.org/svn/llvm-project/llvm/trunk",
     "clang_repo": "https://llvm.org/svn/llvm-project/cfe/trunk",
     "lld_repo": "https://llvm.org/svn/llvm-project/lld/trunk",
     "compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/trunk",
index 882a538d5748f4de77f9d7e6cb03bb8c40b97ad5..7f753ff20600f017bdf78c36b628587c4aa0d311
GIT binary patch
literal 14312
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
W*+fcBVnnR~0000<MNUMnLSTZ6nPv+B
--- a/dom/payments/PaymentRequest.cpp
+++ b/dom/payments/PaymentRequest.cpp
@@ -386,40 +386,40 @@ PaymentRequest::IsValidCurrency(const ns
 
 nsresult
 PaymentRequest::IsValidCurrencyAmount(const nsAString& aItem,
                                       const PaymentCurrencyAmount& aAmount,
                                       const bool aIsTotalItem,
                                       nsAString& aErrorMsg)
 {
   nsresult rv;
-  if (aIsTotalItem) {
-    rv = IsNonNegativeNumber(aItem, aAmount.mValue, aErrorMsg);
-    if (NS_FAILED(rv)) {
-      return rv;
-    }
-  } else {
-    rv = IsValidNumber(aItem, aAmount.mValue, aErrorMsg);
-    if (NS_FAILED(rv)) {
-      return rv;
-    }
-  }
   // currencySystem must equal urn:iso:std:iso:4217
   if (!aAmount.mCurrencySystem.EqualsASCII("urn:iso:std:iso:4217")) {
     aErrorMsg.AssignLiteral("The amount.currencySystem of \"");
     aErrorMsg.Append(aItem);
     aErrorMsg.AppendLiteral("\"(");
     aErrorMsg.Append(aAmount.mCurrencySystem);
     aErrorMsg.AppendLiteral(") must equal urn:iso:std:iso:4217.");
     return NS_ERROR_RANGE_ERR;
   }
   rv = IsValidCurrency(aItem, aAmount.mCurrency, aErrorMsg);
   if (NS_FAILED(rv)) {
     return rv;
   }
+  if (aIsTotalItem) {
+    rv = IsNonNegativeNumber(aItem, aAmount.mValue, aErrorMsg);
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
+  } else {
+    rv = IsValidNumber(aItem, aAmount.mValue, aErrorMsg);
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
+  }
   return NS_OK;
 }
 
 nsresult
 PaymentRequest::IsValidDetailsInit(const PaymentDetailsInit& aDetails,
                                    const bool aRequestShipping,
                                    nsAString& aErrorMsg)
 {
@@ -676,17 +676,18 @@ void
 PaymentRequest::RespondCanMakePayment(bool aResult)
 {
   MOZ_ASSERT(mResultPromise);
   mResultPromise->MaybeResolve(aResult);
   mResultPromise = nullptr;
 }
 
 already_AddRefed<Promise>
-PaymentRequest::Show(ErrorResult& aRv)
+PaymentRequest::Show(const Optional<OwningNonNull<Promise>>& aDetailsPromise,
+                     ErrorResult& aRv)
 {
   if (mState != eCreated) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return nullptr;
   }
 
   if (!EventStateManager::IsHandlingUserInput()) {
     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
@@ -703,16 +704,22 @@ PaymentRequest::Show(ErrorResult& aRv)
   }
 
   RefPtr<PaymentRequestManager> manager = PaymentRequestManager::GetSingleton();
   if (NS_WARN_IF(!manager)) {
     mState = eClosed;
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
+
+  if (aDetailsPromise.WasPassed()) {
+    aDetailsPromise.Value().AppendNativeHandler(this);
+    mUpdating = true;
+  }
+
   nsresult rv = manager->ShowPayment(mInternalId);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     if (rv == NS_ERROR_ABORT) {
       promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
     } else {
       promise->MaybeReject(NS_ERROR_DOM_NOT_ALLOWED_ERR);
     }
     mState = eClosed;
@@ -994,16 +1001,53 @@ PaymentRequest::SetShippingType(const Nu
 }
 
 Nullable<PaymentShippingType>
 PaymentRequest::GetShippingType() const
 {
   return mShippingType;
 }
 
+void
+PaymentRequest::ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue)
+{
+  MOZ_ASSERT(aCx);
+  mUpdating = false;
+  if (NS_WARN_IF(!aValue.isObject())) {
+    return;
+  }
+
+  // Converting value to a PaymentDetailsUpdate dictionary
+  PaymentDetailsUpdate details;
+  if (!details.Init(aCx, aValue)) {
+    AbortUpdate(NS_ERROR_DOM_TYPE_ERR);
+    JS_ClearPendingException(aCx);
+    return;
+  }
+
+  nsresult rv = IsValidDetailsUpdate(details, mRequestShipping);
+  if (NS_FAILED(rv)) {
+    AbortUpdate(rv);
+    return;
+  }
+
+  // Update the PaymentRequest with the new details
+  if (NS_FAILED(UpdatePayment(aCx, details))) {
+    AbortUpdate(NS_ERROR_DOM_ABORT_ERR);
+    return;
+  }
+}
+
+void
+PaymentRequest::RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue)
+{
+  mUpdating = false;
+  AbortUpdate(NS_ERROR_DOM_ABORT_ERR);
+}
+
 PaymentRequest::~PaymentRequest()
 {
 }
 
 JSObject*
 PaymentRequest::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return PaymentRequestBinding::Wrap(aCx, this, aGivenProto);
--- a/dom/payments/PaymentRequest.h
+++ b/dom/payments/PaymentRequest.h
@@ -5,28 +5,30 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_PaymentRequest_h
 #define mozilla_dom_PaymentRequest_h
 
 #include "mozilla/DOMEventTargetHelper.h"
 #include "mozilla/dom/PaymentRequestBinding.h"
 #include "mozilla/dom/Promise.h"
+#include "mozilla/dom/PromiseNativeHandler.h"
 #include "mozilla/ErrorResult.h"
 #include "nsWrapperCache.h"
 #include "PaymentRequestUpdateEvent.h"
 
 namespace mozilla {
 namespace dom {
 
 class EventHandlerNonNull;
 class PaymentAddress;
 class PaymentResponse;
 
 class PaymentRequest final : public DOMEventTargetHelper
+                           , public PromiseNativeHandler
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
 
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(PaymentRequest, DOMEventTargetHelper)
 
   virtual JSObject* WrapObject(JSContext* aCx,
                                JS::Handle<JSObject*> aGivenProto) override;
@@ -85,17 +87,18 @@ public:
               const Sequence<PaymentMethodData>& aMethodData,
               const PaymentDetailsInit& aDetails,
               const PaymentOptions& aOptions,
               ErrorResult& aRv);
 
   already_AddRefed<Promise> CanMakePayment(ErrorResult& aRv);
   void RespondCanMakePayment(bool aResult);
 
-  already_AddRefed<Promise> Show(ErrorResult& aRv);
+  already_AddRefed<Promise> Show(const Optional<OwningNonNull<Promise>>& detailsPromise,
+                                 ErrorResult& aRv);
   void RespondShowPayment(const nsAString& aMethodName,
                           const nsAString& aDetails,
                           const nsAString& aPayerName,
                           const nsAString& aPayerEmail,
                           const nsAString& aPayerPhone,
                           nsresult aRv);
   void RejectShowPayment(nsresult aRejectReason);
   void RespondComplete();
@@ -105,16 +108,17 @@ public:
 
   void GetId(nsAString& aRetVal) const;
   void GetInternalId(nsAString& aRetVal);
   void SetId(const nsAString& aId);
 
   bool Equals(const nsAString& aInternalId) const;
 
   bool ReadyForUpdate();
+  bool IsUpdating() const { return mUpdating; }
   void SetUpdating(bool aUpdating);
 
   already_AddRefed<PaymentAddress> GetShippingAddress() const;
   // Update mShippingAddress and fire shippingaddresschange event
   nsresult UpdateShippingAddress(const nsAString& aCountry,
                                  const nsTArray<nsString>& aAddressLine,
                                  const nsAString& aRegion,
                                  const nsAString& aCity,
@@ -137,16 +141,21 @@ public:
   void SetShippingType(const Nullable<PaymentShippingType>& aShippingType);
   Nullable<PaymentShippingType> GetShippingType() const;
 
   inline void ShippingWasRequested()
   {
     mRequestShipping = true;
   }
 
+  void
+  ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
+  void
+  RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
+
   IMPL_EVENT_HANDLER(shippingaddresschange);
   IMPL_EVENT_HANDLER(shippingoptionchange);
 
 protected:
   ~PaymentRequest();
 
   nsresult DispatchUpdateEvent(const nsAString& aType);
 
--- a/dom/payments/PaymentRequestManager.cpp
+++ b/dom/payments/PaymentRequestManager.cpp
@@ -369,17 +369,17 @@ PaymentRequestManager::GetPaymentRequest
       RefPtr<PaymentRequest> paymentRequest = request;
       return paymentRequest.forget();
     }
   }
   return nullptr;
 }
 
 void
-GetSelectedShippingOption(const PaymentDetailsInit& aDetails,
+GetSelectedShippingOption(const PaymentDetailsBase& aDetails,
                           nsAString& aOption)
 {
   SetDOMStringToNull(aOption);
   if (!aDetails.mShippingOptions.WasPassed()) {
     return;
   }
 
   const Sequence<PaymentShippingOption>& shippingOptions =
@@ -497,20 +497,22 @@ PaymentRequestManager::ShowPayment(const
 {
   if (mShowingRequest) {
     return NS_ERROR_ABORT;
   }
   RefPtr<PaymentRequest> request = GetPaymentRequestById(aRequestId);
   if (!request) {
     return NS_ERROR_FAILURE;
   }
-
-  nsAutoString requestId(aRequestId);
-  IPCPaymentShowActionRequest action(requestId);
-  nsresult rv = SendRequestPayment(request, action);
+  nsresult rv = NS_OK;
+  if (!request->IsUpdating()) {
+    nsAutoString requestId(aRequestId);
+    IPCPaymentShowActionRequest action(requestId);
+    rv = SendRequestPayment(request, action);
+  }
   mShowingRequest = request;
   return rv;
 }
 
 nsresult
 PaymentRequestManager::AbortPayment(const nsAString& aRequestId)
 {
   RefPtr<PaymentRequest> request = GetPaymentRequestById(aRequestId);
@@ -552,29 +554,29 @@ PaymentRequestManager::UpdatePayment(JSC
                                      const PaymentDetailsUpdate& aDetails,
                                      bool aRequestShipping)
 {
   NS_ENSURE_ARG_POINTER(aCx);
   RefPtr<PaymentRequest> request = GetPaymentRequestById(aRequestId);
   if (!request) {
     return NS_ERROR_UNEXPECTED;
   }
-
-  // [TODO] Process details.shippingOptions if presented.
-  //        1) Check if there are duplicate IDs in details.shippingOptions,
-  //           if so, reset details.shippingOptions to an empty sequence.
-  //        2) Set request's selectedShippingOption to the ID of last selected
-  //           option.
-
   IPCPaymentDetails details;
   nsresult rv = ConvertDetailsUpdate(aCx, aDetails, details, aRequestShipping);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
+  nsAutoString shippingOption;
+  SetDOMStringToNull(shippingOption);
+  if (aRequestShipping) {
+    GetSelectedShippingOption(aDetails, shippingOption);
+    request->SetShippingOption(shippingOption);
+  }
+
   nsAutoString requestId(aRequestId);
   IPCPaymentUpdateActionRequest action(requestId, details);
   return SendRequestPayment(request, action);
 }
 
 nsresult
 PaymentRequestManager::RespondPayment(const IPCPaymentActionResponse& aResponse)
 {
@@ -638,22 +640,22 @@ PaymentRequestManager::RespondPayment(co
       const IPCPaymentAbortActionResponse& response = aResponse;
       RefPtr<PaymentRequest> request = GetPaymentRequestById(response.requestId());
       if (NS_WARN_IF(!request)) {
         return NS_ERROR_FAILURE;
       }
       request->RespondAbortPayment(response.isSucceeded());
       if (response.isSucceeded()) {
         MOZ_ASSERT(mShowingRequest == request);
-        mShowingRequest = nullptr;
         mRequestQueue.RemoveElement(request);
-        nsresult rv = ReleasePaymentChild(request);
-        if (NS_WARN_IF(NS_FAILED(rv))) {
-          return rv;
-        }
+      }
+      mShowingRequest = nullptr;
+      nsresult rv = ReleasePaymentChild(request);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
       }
       break;
     }
     case IPCPaymentActionResponse::TIPCPaymentCompleteActionResponse: {
       const IPCPaymentCompleteActionResponse& response = aResponse;
       RefPtr<PaymentRequest> request = GetPaymentRequestById(response.requestId());
       if (NS_WARN_IF(!request)) {
         return NS_ERROR_FAILURE;
--- a/dom/payments/PaymentRequestService.cpp
+++ b/dom/payments/PaymentRequestService.cpp
@@ -363,17 +363,23 @@ PaymentRequestService::RequestPayment(ns
       rv = GetPaymentRequestById(requestId, getter_AddRefs(payment));
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
       rv = payment->UpdatePaymentDetails(details);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
-      rv = LaunchUIAction(requestId, type);
+      if (mShowingRequest) {
+        MOZ_ASSERT(mShowingRequest == payment);
+        rv = LaunchUIAction(requestId, type);
+      } else {
+        mShowingRequest = payment;
+        rv = LaunchUIAction(requestId, nsIPaymentActionRequest::SHOW_ACTION);
+      }
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return NS_ERROR_FAILURE;
       }
       break;
     }
     default: {
       return NS_ERROR_FAILURE;
     }
@@ -415,18 +421,18 @@ PaymentRequestService::RespondPayment(ns
   switch (type) {
     case nsIPaymentActionResponse::ABORT_ACTION: {
       nsCOMPtr<nsIPaymentAbortActionResponse> response =
         do_QueryInterface(aResponse);
       MOZ_ASSERT(response);
       bool isSucceeded;
       rv = response->IsSucceeded(&isSucceeded);
       NS_ENSURE_SUCCESS(rv, rv);
+      mShowingRequest = nullptr;
       if (isSucceeded) {
-        mShowingRequest = nullptr;
         mRequestQueue.RemoveElement(request);
       }
       break;
     }
     case nsIPaymentActionResponse::SHOW_ACTION: {
       nsCOMPtr<nsIPaymentShowActionResponse> response =
         do_QueryInterface(aResponse);
       MOZ_ASSERT(response);
--- a/dom/payments/PaymentRequestUpdateEvent.cpp
+++ b/dom/payments/PaymentRequestUpdateEvent.cpp
@@ -59,16 +59,18 @@ PaymentRequestUpdateEvent::ResolvedCallb
 
   if (NS_WARN_IF(!aValue.isObject()) || !mWaitForUpdate) {
     return;
   }
 
   // Converting value to a PaymentDetailsUpdate dictionary
   PaymentDetailsUpdate details;
   if (!details.Init(aCx, aValue)) {
+    mRequest->AbortUpdate(NS_ERROR_TYPE_ERR);
+    JS_ClearPendingException(aCx);
     return;
   }
 
   // Validate and canonicalize the details
   // requestShipping must be true here. PaymentRequestUpdateEvent is only
   // dispatched when shippingAddress/shippingOption is changed, and it also means
   // Options.RequestShipping must be true while creating the corresponding
   // PaymentRequest.
--- a/dom/payments/test/ShowPaymentChromeScript.js
+++ b/dom/payments/test/ShowPaymentChromeScript.js
@@ -1,17 +1,19 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 "use strict";
 
 const { XPCOMUtils } = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 const paymentSrv = Cc["@mozilla.org/dom/payments/payment-request-service;1"].getService(Ci.nsIPaymentRequestService);
-let expectedCompleteStatus;
+let expectedCompleteStatus = null;
+let expectedShowAction = "accept";
+let expectedUpdateAction = "accept";
 
 function emitTestFail(message) {
   sendAsyncMessage("test-fail", message);
 }
 function emitTestPass(message) {
   sendAsyncMessage("test-pass", message);
 }
 
@@ -28,254 +30,172 @@ shippingAddress.init("USA",             
                      "Test locality",    // dependent locality
                      "94066",            // postal code
                      "123456",           // sorting code
                      "en",               // language code
                      "Testing Org",      // organization
                      "Bill A. Pacheco",  // recipient
                      "+1-434-441-3879"); // phone
 
-const DummyUIService = {
-  showPayment: function(requestId) {
-    const showResponseData = Cc["@mozilla.org/dom/payments/general-response-data;1"].
-                                createInstance(Ci.nsIGeneralResponseData);
-    try {
-      showResponseData.initData({ paymentToken: "6880281f-0df3-4b8e-916f-66575e2457c1",});
-    } catch (e) {
-      emitTestFail("Fail to initialize response data with { paymentToken: \"6880281f-0df3-4b8e-916f-66575e2457c1\",}");
-    }
-    let showResponse = Cc["@mozilla.org/dom/payments/payment-show-action-response;1"].
-                   createInstance(Ci.nsIPaymentShowActionResponse);
-    showResponse.init(requestId,
-                      Ci.nsIPaymentActionResponse.PAYMENT_ACCEPTED,
-                      "testing-payment-method",   // payment method
-                      showResponseData,           // payment method data
-                      "Bill A. Pacheco",          // payer name
-                      "",                         // payer email
-                      "");                        // payer phone
-    paymentSrv.respondPayment(showResponse.QueryInterface(Ci.nsIPaymentActionResponse));
-  },
-  abortPayment: function(requestId) {
-  },
-  completePayment: function(requestId) {
-    let payRequest = paymentSrv.getPaymentRequestById(requestId);
+function acceptShow(requestId) {
+  const responseData = Cc["@mozilla.org/dom/payments/general-response-data;1"].
+                          createInstance(Ci.nsIGeneralResponseData);
+  responseData.initData({ paymentToken: "6880281f-0df3-4b8e-916f-66575e2457c1",});
+  let showResponse = Cc["@mozilla.org/dom/payments/payment-show-action-response;1"].
+                        createInstance(Ci.nsIPaymentShowActionResponse);
+  showResponse.init(requestId,
+                    Ci.nsIPaymentActionResponse.PAYMENT_ACCEPTED,
+                    "testing-payment-method",   // payment method
+                    responseData,           // payment method data
+                    "Bill A. Pacheco",          // payer name
+                    "",                         // payer email
+                    "");                        // payer phone
+  paymentSrv.respondPayment(showResponse.QueryInterface(Ci.nsIPaymentActionResponse));
+}
+
+function rejectShow(requestId) {
+  const responseData = Cc["@mozilla.org/dom/payments/general-response-data;1"].
+                          createInstance(Ci.nsIGeneralResponseData);
+  responseData.initData({});
+  const showResponse = Cc["@mozilla.org/dom/payments/payment-show-action-response;1"].
+                          createInstance(Ci.nsIPaymentShowActionResponse);
+  showResponse.init(requestId,
+                    Ci.nsIPaymentActionResponse.PAYMENT_REJECTED,
+                    "",                 // payment method
+                    responseData,       // payment method data
+                    "",                 // payer name
+                    "",                 // payer email
+                    "");                // payer phone
+  paymentSrv.respondPayment(showResponse.QueryInterface(Ci.nsIPaymentActionResponse));
+}
+
+function updateShow(requestId) {
+  if (expectedUpdateAction == "updateaddress") {
+    paymentSrv.changeShippingAddress(requestId, shippingAddress);
+  } else if (expectedUpdateAction == "accept" || expectedUpdateAction == "error"){
+    paymentSrv.changeShippingOption(requestId, "FastShipping");
+  } else {
+    emitTestFail("Unknown expected update action: " + expectedUpdateAction);
+  }
+}
+
+function showRequest(requestId) {
+  if (expectedShowAction == "accept") {
+    acceptShow(requestId);
+  } else if (expectedShowAction == "reject") {
+    rejectShow(requestId);
+  } else if (expectedShowAction == "update") {
+    updateShow(requestId);
+  } else {
+    emitTestFail("Unknown expected show action: " + expectedShowAction);
+  }
+}
+
+function abortRequest(requestId) {
+  let abortResponse = Cc["@mozilla.org/dom/payments/payment-abort-action-response;1"].
+                         createInstance(Ci.nsIPaymentAbortActionResponse);
+  abortResponse.init(requestId, Ci.nsIPaymentActionResponse.ABORT_SUCCEEDED);
+  paymentSrv.respondPayment(abortResponse);
+}
+
+function completeRequest(requestId) {
+  let payRequest = paymentSrv.getPaymentRequestById(requestId);
+  if (expectedCompleteStatus) {
     if (payRequest.completeStatus == expectedCompleteStatus) {
-      emitTestPass("request.completeStatus matches expectation of " + expectedCompleteStatus);
-    } else {
-      emitTestFail("request.completeStatus incorrect. Expected " +
-                   expectedCompleteStatus + ", got " + payRequest.completeStatus);
-    }
-
-    let completeResponse = Cc["@mozilla.org/dom/payments/payment-complete-action-response;1"].
-                           createInstance(Ci.nsIPaymentCompleteActionResponse);
-    completeResponse.init(requestId, Ci.nsIPaymentActionResponse.COMPLETE_SUCCEEDED);
-    paymentSrv.respondPayment(completeResponse.QueryInterface(Ci.nsIPaymentActionResponse));
-  },
-  updatePayment: function(requestId) {
-  },
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPaymentUIService]),
-};
-
-const NormalUIService = {
-  shippingOptionChanged: false,
-  showPayment: function(requestId) {
-    paymentSrv.changeShippingAddress(requestId, shippingAddress);
-  },
-  abortPayment: function(requestId) {
-  },
-  completePayment: function(requestId) {
-    let payRequest = paymentSrv.getPaymentRequestById(requestId);
-    if (payRequest.completeStatus == expectedCompleteStatus) {
-      emitTestPass("request.completeStatus matches expectation of " + expectedCompleteStatus);
+      emitTestPass("request.completeStatus matches expectation of " +
+                   expectedCompleteStatus);
     } else {
       emitTestFail("request.completeStatus incorrect. Expected " +
                    expectedCompleteStatus + ", got " + payRequest.completeStatus);
     }
+  }
+  let completeResponse = Cc["@mozilla.org/dom/payments/payment-complete-action-response;1"].
+                            createInstance(Ci.nsIPaymentCompleteActionResponse);
+  completeResponse.init(requestId, Ci.nsIPaymentActionResponse.COMPLETE_SUCCEEDED);
+  paymentSrv.respondPayment(completeResponse.QueryInterface(Ci.nsIPaymentActionResponse));
+}
 
-    let completeResponse = Cc["@mozilla.org/dom/payments/payment-complete-action-response;1"].
-                           createInstance(Ci.nsIPaymentCompleteActionResponse);
-    completeResponse.init(requestId, Ci.nsIPaymentActionResponse.COMPLETE_SUCCEEDED);
-    paymentSrv.respondPayment(completeResponse.QueryInterface(Ci.nsIPaymentActionResponse));
-  },
-  updatePayment: function(requestId) {
-    let showResponse = null;
-    let payRequest = paymentSrv.getPaymentRequestById(requestId);
-    if (payRequest.paymentDetails.error != "") {
-      emitTestFail("updatedDetails should not have errors(" + payRequest.paymentDetails.error + ").");
+function updateRequest(requestId) {
+  let request = paymentSrv.getPaymentRequestById(requestId);
+  if (expectedUpdateAction == "accept") {
+    if (request.paymentDetails.error != "") {
+      emitTestFail("updatedDetails should not have errors(" + request.paymentDetails.error + ").");
+    }
+    const shippingOptions = request.paymentDetails.shippingOptions;
+    let shippingOption = shippingOptions.queryElementAt(0, Ci.nsIPaymentShippingOption);
+    if (shippingOption.selected) {
+      emitTestFail(shippingOption.label + " should not be selected.");
+    }
+    shippingOption = shippingOptions.queryElementAt(1, Ci.nsIPaymentShippingOption);
+    if (!shippingOption.selected) {
+      emitTestFail(shippingOption.label + " should be selected.");
     }
-    if (!this.shippingOptionChanged) {
-      paymentSrv.changeShippingOption(requestId, "FastShipping");
-      this.shippingOptionChanged = true;
-    } else {
-      const shippingOptions = payRequest.paymentDetails.shippingOptions;
-      let shippingOption = shippingOptions.queryElementAt(0, Ci.nsIPaymentShippingOption);
-      if (shippingOption.selected) {
-        emitTestFail(shippingOption.label + " should not be selected.");
-      }
-      shippingOption = shippingOptions.queryElementAt(1, Ci.nsIPaymentShippingOption);
-      if (!shippingOption.selected) {
-        emitTestFail(shippingOption.label + " should be selected.");
-      }
-
-      const showResponseData = Cc["@mozilla.org/dom/payments/general-response-data;1"].
-                                  createInstance(Ci.nsIGeneralResponseData);
+    acceptShow(requestId);
+  } else if (expectedUpdateAction == "error") {
+    if (request.paymentDetails.error != "Update with Error") {
+      emitTestFail("details.error should be 'Update with Error', but got " + request.paymentDetails.error + ".");
+    }
+    rejectShow(requestId);
+  } else if (expectedUpdateAction == "updateaddress") {
+    if (request.paymentDetails.error != "") {
+      emitTestFail("updatedDetails should not have errors(" + request.paymentDetails.error + ").");
+    }
+    expectedUpdateAction = "accept";
+    paymentSrv.changeShippingOption(requestId, "FastShipping");
+  } else {
+    emitTestFail("Unknown expected update aciton: " + expectedUpdateAction);
+  }
+}
 
-      try {
-        showResponseData.initData({ paymentToken: "6880281f-0df3-4b8e-916f-66575e2457c1",});
-      } catch (e) {
-        emitTestFail("Fail to initialize response data with { paymentToken: \"6880281f-0df3-4b8e-916f-66575e2457c1\",}");
-      }
-
-      showResponse = Cc["@mozilla.org/dom/payments/payment-show-action-response;1"].
-                     createInstance(Ci.nsIPaymentShowActionResponse);
-      showResponse.init(requestId,
-                        Ci.nsIPaymentActionResponse.PAYMENT_ACCEPTED,
-                        "testing-payment-method",   // payment method
-                        showResponseData,           // payment method data
-                        "Bill A. Pacheco",          // payer name
-                        "",                         // payer email
-                        "");                        // payer phone
-      paymentSrv.respondPayment(showResponse.QueryInterface(Ci.nsIPaymentActionResponse));
-    }
-  },
+const DummyUIService = {
+  showPayment: showRequest,
+  abortPayment: abortRequest,
+  completePayment: completeRequest,
+  updatePayment: updateRequest,
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIPaymentUIService]),
 };
 
-const RejectUIService = {
-  showPayment: function(requestId) {
-    const responseData = Cc["@mozilla.org/dom/payments/general-response-data;1"].
-                            createInstance(Ci.nsIGeneralResponseData);
-
-    try {
-      responseData.initData({});
-    } catch (e) {
-      emitTestFail("Fail to initialize response data with empty object.");
-    }
-    const showResponse = Cc["@mozilla.org/dom/payments/payment-show-action-response;1"].
-                            createInstance(Ci.nsIPaymentShowActionResponse);
-    showResponse.init(requestId,
-                      Ci.nsIPaymentActionResponse.PAYMENT_REJECTED,
-                      "",                 // payment method
-                      responseData,       // payment method data
-                      "",                 // payer name
-                      "",                 // payer email
-                      "");                // payer phone
-    paymentSrv.respondPayment(showResponse.QueryInterface(Ci.nsIPaymentActionResponse));
-  },
-  abortPayment: function(requestId) {
-  },
-  completePayment: function(requestId) {
-  },
-  updatePayment: function(requestId) {
-  },
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPaymentUIService]),
-};
+paymentSrv.setTestingUIService(DummyUIService.QueryInterface(Ci.nsIPaymentUIService));
 
-const ErrorUIService = {
-  showPayment: function(requestId) {
-    paymentSrv.changeShippingOption(requestId, "");
-  },
-  abortPayment: function(requestId) {
-  },
-  completePayment: function(requestId) {
-  },
-  updatePayment: function(requestId) {
-    let payRequest = paymentSrv.getPaymentRequestById(requestId);
-    if (!payRequest) {
-      emitTestFail("Fail to get existing payment request.");
-    }
-    if (payRequest.paymentDetails.error != "Update with Error") {
-      emitTestFail("details.error should be 'Update with Error', but got " + payRequest.paymentDetails.error + ".");
-    }
-    const responseData = Cc["@mozilla.org/dom/payments/general-response-data;1"].
-                            createInstance(Ci.nsIGeneralResponseData);
-    try {
-      responseData.initData({});
-    } catch (e) {
-      emitTestFail("Fail to initialize response data with empty object.");
-    }
-    const showResponse = Cc["@mozilla.org/dom/payments/payment-show-action-response;1"].
-                            createInstance(Ci.nsIPaymentShowActionResponse);
-    showResponse.init(requestId,
-                      Ci.nsIPaymentActionResponse.PAYMENT_REJECTED,
-                      "",                 // payment method
-                      responseData,       // payment method data
-                      "",                 // payer name
-                      "",                 // payer email
-                      "");                // payer phone
-    paymentSrv.respondPayment(showResponse.QueryInterface(Ci.nsIPaymentActionResponse));
-  },
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPaymentUIService]),
-
-};
-
-const CompleteUIService = {
-  showPayment: function(requestId) {
-    const showResponseData = Cc["@mozilla.org/dom/payments/general-response-data;1"].
-                                createInstance(Ci.nsIGeneralResponseData);
-
-    try {
-      showResponseData.initData({});
-    } catch (e) {
-      emitTestFail("Fail to initialize response data with empty object.");
-    }
-
-    let showResponse = Cc["@mozilla.org/dom/payments/payment-show-action-response;1"].
-                          createInstance(Ci.nsIPaymentShowActionResponse);
-    showResponse.init(requestId,
-                      Ci.nsIPaymentActionResponse.PAYMENT_ACCEPTED,
-                      "",                  // payment method
-                      showResponseData,    // payment method data
-                      "",                  // payer name
-                      "",                  // payer email
-                      "");                 // payer phone
-    paymentSrv.respondPayment(showResponse.QueryInterface(Ci.nsIPaymentActionResponse));
-  },
-  abortPayment: function(requestId) {
-  },
-  completePayment: function(requestId) {
-  },
-  updatePayment: function(requestId) {
-  },
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPaymentUIService]),
-};
-
-function testInitDataAndResponse() {
+function testShowResponseInit() {
   const showResponseData = Cc["@mozilla.org/dom/payments/general-response-data;1"].
                               createInstance(Ci.nsIGeneralResponseData);
   try {
     showResponseData.initData(null);
     emitTestFail("nsIGeneralResponseData can not be initialized with null object.");
   } catch (e) {
     if (e.name != "NS_ERROR_FAILURE") {
       emitTestFail("Expected 'NS_ERROR_FAILURE' when initializing nsIGeneralResponseData with null object, but got " + e.name + ".");
     }
+    emitTestPass("Get expected result for initializing nsIGeneralResponseData with null object");
   }
   const showResponse = Cc["@mozilla.org/dom/payments/payment-show-action-response;1"].
                           createInstance(Ci.nsIPaymentShowActionResponse);
   try {
     showResponse.init("test request id",
                       Ci.nsIPaymentActionResponse.PAYMENT_ACCEPTED,
                       "testing-payment-method",   // payment method
                       showResponseData,           // payment method data
                       "Bill A. Pacheco",          // payer name
                       "",                         // payer email
                       "");                        // payer phone
+    emitTestPass("Get expected result for initializing response with accepted and empty data.");
   } catch (e) {
     emitTestFail("Unexpected error " + e.name + " when initializing response with accepted and empty data.");
   }
 
   try {
     showResponse.init("test request id",
                       Ci.nsIPaymentActionResponse.PAYMENT_REJECTED,
                       "testing-payment-method",
                       null,
                       "Bill A. Pacheco",
                       "",
                       "");
+    emitTestPass("Get expected result for initializing response with rejected and null data.");
   } catch (e) {
     emitTestFail("Unexpected error " + e.name + " when initializing response with rejected and null data.");
   }
 
   try {
     showResponse.init("test request id",
                       Ci.nsIPaymentActionResponse.PAYMENT_ACCEPTED,
                       "testing-payment-method",
@@ -283,41 +203,52 @@ function testInitDataAndResponse() {
                       "Bill A. Pacheco",
                       "",
                       "");
     emitTestFail("nsIPaymentShowActionResponse can not be initialized with accpeted and null data.");
   } catch (e) {
     if (e.name != "NS_ERROR_ILLEGAL_VALUE") {
       emitTestFail("Expected 'NS_ERROR_ILLEGAL_VALUE', but got " + e.name + ".");
     }
+    emitTestPass("Get expected result for initializing response with accepted and null data.")
   }
-  sendAsyncMessage("test-init-data-and-response-complete");
+  sendAsyncMessage("test-show-response-init-complete");
 }
 
-addMessageListener("set-dummy-ui-service", function() {
-  paymentSrv.setTestingUIService(DummyUIService.QueryInterface(Ci.nsIPaymentUIService));
+addMessageListener("set-simple-ui-service", function() {
+  expectedCompleteStatus = null;
+  expectedShowAction = "accept";
+  expectedUpdateAction = "accept";
 });
 
 addMessageListener("set-normal-ui-service", function() {
-  paymentSrv.setTestingUIService(NormalUIService.QueryInterface(Ci.nsIPaymentUIService));
+  expectedCompleteStatus = null;
+  expectedShowAction = "update";
+  expectedUpdateAction = "updateaddress";
 });
 
 addMessageListener("set-reject-ui-service", function() {
-  paymentSrv.setTestingUIService(RejectUIService.QueryInterface(Ci.nsIPaymentUIService));
+  expectedCompleteStatus = null;
+  expectedShowAction = "reject";
+  expectedUpdateAction = "accept";
+});
+
+addMessageListener("set-update-with-ui-service", function() {
+  expectedCompleteStatus = null;
+  expectedShowAction = "update";
+  expectedUpdateAction = "accept";
 });
 
 addMessageListener("set-update-with-error-ui-service", function() {
-  paymentSrv.setTestingUIService(ErrorUIService.QueryInterface(Ci.nsIPaymentUIService));
+  expectedCompleteStatus = null;
+  expectedShowAction = "update";
+  expectedUpdateAction = "error";
 });
 
-addMessageListener("set-complete-ui-service", function() {
-  paymentSrv.setTestingUIService(CompleteUIService.QueryInterface(Ci.nsIPaymentUIService));
-});
-
-addMessageListener("test-init-data-and-response", testInitDataAndResponse);
+addMessageListener("test-show-response-init", testShowResponseInit);
 
 addMessageListener("set-complete-status-success", function() {
   expectedCompleteStatus = "success";
 });
 
 addMessageListener("set-complete-status-fail", function() {
   expectedCompleteStatus = "fail";
 });
--- a/dom/payments/test/test_showPayment.html
+++ b/dom/payments/test/test_showPayment.html
@@ -20,36 +20,41 @@ https://bugzilla.mozilla.org/show_bug.cg
     ok(false, message);
   }
   function testPassHandler(message) {
     ok(true, message);
   }
   gScript.addMessageListener("test-fail", testFailHandler);
   gScript.addMessageListener("test-pass", testPassHandler);
 
+  // testing data declaration
+  // default parameters for PaymentRequest construction
   const defaultMethods = [{
     supportedMethods: "basic-card",
     data: {
       supportedNetworks: ['unionpay', 'visa', 'mastercard', 'amex', 'discover',
                           'diners', 'jcb', 'mir',
       ],
       supportedTypes: ['prepaid', 'debit', 'credit'],
     },
   }, {
     supportedMethods: "testing-payment-method",
   }];
+
+  const defaultTotal = {
+    label: "Total",
+    amount: {
+      currency: "USD",
+      value: "1.00",
+    },
+  }
+
   const defaultDetails = {
     id: "test payment",
-    total: {
-      label: "Total",
-      amount: {
-        currency: "USD",
-        value: "1.00"
-      }
-    },
+    total: defaultTotal,
     shippingOptions: [
       {
         id: "NormalShipping",
         label: "NormalShipping",
         amount: {
           currency: "USD",
           value: "10.00"
         },
@@ -70,24 +75,19 @@ https://bugzilla.mozilla.org/show_bug.cg
   const defaultOptions = {
     requestPayerName: true,
     requestPayerEmail: false,
     reqeustPayerPhone: false,
     requestShipping: true,
     shippingType: "shipping"
   };
 
-  const updatedOptionDetails = {
-    total: {
-      label: "Total",
-      amount: {
-        currency: "USD",
-        value: "1.00"
-      }
-    },
+  // testing data for PaymentRequestUpdateEvent.updateWith()
+  const updatedShippingOptionsDetails = {
+    total: defaultTotal,
     shippingOptions: [
       {
         id: "NormalShipping",
         label: "NormalShipping",
         amount: {
           currency: "USD",
           value: "10.00"
         },
@@ -101,311 +101,365 @@ https://bugzilla.mozilla.org/show_bug.cg
           value: "30.00"
         },
         selected: true,
       },
     ],
   };
 
   const updatedErrorDetails = {
-    total: {
-      label: "Total",
-      amount: {
-        currency: "USD",
-        value: "1.00"
-      }
-    },
-    shippingOptions: [
-      {
-        id: "NormalShipping",
-        label: "NormalShipping",
-        amount: {
-          currency: "USD",
-          value: "10.00"
-        },
-        selected: false,
-      },
-      {
-        id: "FastShipping",
-        label: "FastShipping",
-        amount: {
-          currency: "USD",
-          value: "30.00"
-        },
-        selected: false,
-      },
-    ],
+    total: defaultTotal,
     error: "Update with Error",
   };
 
+  // Promise function for PaymentRequestUpdateEvent.updateWith()
+  function updateWithPromise(detailsUpdate) {
+    return new Promise((resolve, reject) => {
+      if (detailsUpdate) {
+        resolve(detailsUpdate);
+      } else {
+        reject();
+      }
+    });
+  }
+
+  // testing data for PaymentRequest.show() with Non-supported methods
   const nonSupportedMethods = [{
     supportedMethods: "nonsupported-method",
   }];
 
 
-  function updateWithShippingAddress() {
-    return new Promise((resolve, reject) => {
-      resolve(defaultDetails);
-    });
+  // checking functions
+  function checkAddress(address, fromEvent) {
+    is(address.country, "USA", "address.country should be 'USA'.");
+    is(address.region, "CA", "address.region should be 'CA'.");
+    is(address.city, "San Bruno", "address.city should be 'San Bruno'.");
+    is(address.dependentLocality, "Test locality",
+       "address.dependentLocality should be 'Test locality'.");
+    is(address.postalCode, "94066", "address.postalCode should be '94066'.");
+    is(address.sortingCode, "123456", "address.sortingCode should be '123456'.");
+    if (fromEvent) {
+      is(address.addressLine.length, 0, "address.addressLine.length should be 0 from event.");
+      is(address.organization, "", "address.organization should be empty from event.");
+      is(address.recipient, "", "address.recipient should be empty from event.");
+      is(address.phone, "", "address.phone should be empty from event.");
+    } else {
+      is(address.addressLine.length, 1, "address.addressLine.length should be 1 from promise.");
+      is(address.addressLine[0], "Easton Ave", "address.addressLine[0] should be 'Easton Ave' from promise.");
+      is(address.organization, "Testing Org", "address.organization should be 'Testing Org' from promise.");
+      is(address.recipient, "Bill A. Pacheco", "address.recipient should be 'Bill A. Pacheco' from promise.");
+      is(address.phone, "+1-434-441-3879", "address.phone should be '+1-434-441-3879' from promise.");
+    }
   }
 
-  function updateWithShippingOption() {
-    return new Promise((resolve, reject) => {
-      resolve(updatedOptionDetails);
-    });
+  function checkResponse(response) {
+    is(response.requestId, "test payment", "response.requestId should be 'test payment'.");
+    is(response.methodName, "testing-payment-method", "response.methodName should be 'testing-payment-method'.");
+    is(response.details.paymentToken, "6880281f-0df3-4b8e-916f-66575e2457c1", "response.details.paymentToken should be '6880281f-0df3-4b8e-916f-66575e2457c1'.");
+    checkAddress(response.shippingAddress, false/*fromEvent*/);
+    is(response.shippingOption, "FastShipping", "response.shippingOption should be 'FastShipping'.");
+    is(response.payerName, "Bill A. Pacheco", "response.payerName should be 'Bill A. Pacheco'.");
+    ok(!response.payerEmail, "response.payerEmail should be empty");
+    ok(!response.payerPhone, "response.payerPhone should be empty");
   }
 
-  function updateWithError() {
-    return new Promise((resolve, reject) => {
-      resolve(updatedErrorDetails);
-    });
-  }
-
-  function testShowWithSuccess() {
-    info("starting testShowWithSuccess");
+  // testing functions
+  function testShowNormalFlow() {
     gScript.sendAsyncMessage("set-normal-ui-service");
-    gScript.sendAsyncMessage("set-complete-status-success");
     return new Promise((resolve, reject) => {
       const payRequest = new PaymentRequest(defaultMethods, defaultDetails, defaultOptions);
       payRequest.addEventListener("shippingaddresschange", event => {
-        is(payRequest.shippingAddress.country, "USA", "payRequest.shippingAddress.country should be 'USA' from event.");
-        is(payRequest.shippingAddress.addressLine.length, 0, "payRequest.shippingAddress.addressLine.length should be 0 from event.");
-        is(payRequest.shippingAddress.region, "CA", "payRequest.shippingAddress.region should be 'CA' from event.");
-        is(payRequest.shippingAddress.city, "San Bruno", "payRequest.shippingAddress.city should be 'San Bruno' from event.");
-        is(payRequest.shippingAddress.dependentLocality, "Test locality", "payRequest.shippingAddress.dependentLocality should be 'Test locality' from event.");
-        is(payRequest.shippingAddress.postalCode, "94066", "payRequest.shippingAddress.postalCode should be '94066' from event.");
-        is(payRequest.shippingAddress.sortingCode, "123456", "payRequest.shippingAddress.sortingCode should be '123456' from event.");
-        is(payRequest.shippingAddress.organization, "", "payRequest.shippingAddress.organization should be empty from event.");
-        is(payRequest.shippingAddress.recipient, "", "payRequest.shippingAddress.recipient should be empty from event.");
-        is(payRequest.shippingAddress.phone, "", "payRequest.shippingAddress.phone should be empty from event.");
-        event.updateWith(updateWithShippingAddress());
+        checkAddress(payRequest.shippingAddress, true/*fromEvent*/);
+        event.updateWith(updateWithPromise(defaultDetails));
       });
       payRequest.addEventListener("shippingoptionchange", event => {
-        event.updateWith(updateWithShippingOption());
+        event.updateWith(updateWithPromise(updatedShippingOptionsDetails));
       });
-
       const handler = SpecialPowers.getDOMWindowUtils(window).setHandlingUserInput(true);
       payRequest.show().then(response => {
-        is(response.requestId, "test payment", "response.requestId should be 'test payment'.");
-        is(response.methodName, "testing-payment-method", "response.methodName should be 'testing-payment-method'.");
-        is(response.details.paymentToken, "6880281f-0df3-4b8e-916f-66575e2457c1", "response.details.paymentToken should be '6880281f-0df3-4b8e-916f-66575e2457c1'.");
-        is(response.shippingAddress.country, "USA", "response.shippingAddress.country should be 'USA'.");
-        is(response.shippingAddress.addressLine.length, 1, "response.shippingAddress.addressLine.length should be 1.");
-        is(response.shippingAddress.addressLine[0], "Easton Ave", "response.shippingAddress.addressLine[0] should be 'Easton Ave'.");
-        is(response.shippingAddress.region, "CA", "response.shippingAddress.region should be 'CA'.");
-        is(response.shippingAddress.city, "San Bruno", "response.shippingAddress.city should be 'San Bruno'.");
-        is(response.shippingAddress.dependentLocality, "Test locality", "response.shippingAddress.dependentLocality should be 'Test locality'.");
-        is(response.shippingAddress.postalCode, "94066", "response.shippingAddress.postalCode should be '94066'.");
-        is(response.shippingAddress.sortingCode, "123456", "response.shippingAddress.sortingCode should be '123456'.");
-        is(response.shippingAddress.organization, "Testing Org", "response.shippingAddress.organization should be 'Testing Org'.");
-        is(response.shippingAddress.recipient, "Bill A. Pacheco", "response.shippingAddress.recipient should be 'Bill A. Pacheco'.");
-        is(response.shippingAddress.phone, "+1-434-441-3879", "response.shippingAddress.phone should be '+1-434-441-3879'.");
-        is(response.shippingOption, "FastShipping", "response.shippingOption should be 'FastShipping'.");
-        is(response.payerName, "Bill A. Pacheco", "response.payerName should be 'Bill A. Pacheco'.");
-        ok(!response.payerEmail, "response.payerEmail should be empty");
-        ok(!response.payerPhone, "response.payerPhone should be empty");
-        is(payRequest.shippingAddress.country, "USA", "payRequest.shippingAddress.country should be 'USA' from promise.");
-        is(payRequest.shippingAddress.addressLine.length, 1, "payRequest.shippingAddress.addressLine.length should be 1 from promise.");
-        is(payRequest.shippingAddress.addressLine[0], "Easton Ave", "payRequest.shippingAddress.addressLine[0] should be 'Easton Ave' from promise.");
-        is(payRequest.shippingAddress.region, "CA", "payRequest.shippingAddress.region should be 'CA' from promise.");
-        is(payRequest.shippingAddress.city, "San Bruno", "payRequest.shippingAddress.city should be 'San Bruno' from promise.");
-        is(payRequest.shippingAddress.dependentLocality, "Test locality", "payRequest.shippingAddress.dependentLocality should be 'Test locality' from promise.");
-        is(payRequest.shippingAddress.postalCode, "94066", "payRequest.shippingAddress.postalCode should be '94066' from promise.");
-        is(payRequest.shippingAddress.sortingCode, "123456", "payRequest.shippingAddress.sortingCode should be '123456' from promise.");
-        is(payRequest.shippingAddress.organization, "Testing Org", "payRequest.shippingAddress.organization should be 'Testing Org' from promise.");
-        is(payRequest.shippingAddress.recipient, "Bill A. Pacheco", "payRequest.shippingAddress.recipient should be 'Bill A. Pacheco' from promise.");
-        is(payRequest.shippingAddress.phone, "+1-434-441-3879", "payRequest.shippingAddress.phone should be '+1-434-441-3879' from promise.");
-        response.complete("success").then(() =>{
+        checkResponse(response, false);
+	checkAddress(payRequest.shippingAddress, false);
+        response.complete().then(() =>{
           resolve();
         }).catch(e => {
           ok(false, "Unexpected error: " + e.name);
           resolve();
         });
       }).catch( e => {
         ok(false, "Unexpected error: " + e.name);
         resolve();
       }).finally(handler.destruct);
     });
   }
 
-  function testShowWithFail() {
-    info("starting testShowWithFail");
-    gScript.sendAsyncMessage("set-dummy-ui-service");
-    gScript.sendAsyncMessage("set-complete-status-fail");
-    return new Promise((resolve, reject) => {
-      const payRequest = new PaymentRequest(defaultMethods, defaultDetails, defaultOptions);
-      const handler = SpecialPowers.getDOMWindowUtils(window).setHandlingUserInput(true);
-
-      payRequest.show().then(response => {
-        response.complete("fail").then(() => {
-          resolve();
-        }).catch(e => {
-          ok(false, "Unexpected error: " + e.name);
-          resolve();
-        });
-      }).catch( e => {
-        ok(false, "Unexpected error: " + e.name);
-        resolve();
-      }).finally(handler.destruct);
-    });
-  }
-
-  function testShowWithUnknown() {
-    info("starting testShowWithUnknown");
-    gScript.sendAsyncMessage("set-dummy-ui-service");
-    gScript.sendAsyncMessage("set-complete-status-unknown");
-    return new Promise((resolve, reject) => {
-      const payRequest = new PaymentRequest(defaultMethods, defaultDetails, defaultOptions);
-      const handler = SpecialPowers.getDOMWindowUtils(window).setHandlingUserInput(true);
-
-      payRequest.show().then(response => {
-        response.complete("unknown").then(() => {
-          resolve();
-        }).catch(e => {
-          ok(false, "Unexpected error: " + e.name);
-          resolve();
-        });
-      }).catch( e => {
-        ok(false, "Unexpected error: " + e.name);
-        resolve();
-      }).finally(handler.destruct);
-    });
-  }
-
-  function testShowWithEmpty() {
-    info("starting testShowWithEmpty");
-    gScript.sendAsyncMessage("set-dummy-ui-service");
-    gScript.sendAsyncMessage("set-complete-status-unknown");
-    return new Promise((resolve, reject) => {
-      const payRequest = new PaymentRequest(defaultMethods, defaultDetails, defaultOptions);
-      const handler = SpecialPowers.getDOMWindowUtils(window).setHandlingUserInput(true);
-
-      payRequest.show().then(response => {
-        response.complete().then(() => {
-          resolve();
-        }).catch(e => {
-          ok(false, "Unexpected error: " + e.name);
-          resolve();
-        });
-      }).catch( e => {
-        ok(false, "Unexpected error: " + e.name);
-        resolve();
-      }).finally(handler.destruct);
-    });
-  }
-
+  // testing show with nonsupported methods
   function testCannotMakePaymentShow() {
-    const handler = SpecialPowers.getDOMWindowUtils(window).setHandlingUserInput(true);
-
+    gScript.sendAsyncMessage("set-simple-ui-service");
     return new Promise((resolve, reject) => {
       const payRequest = new PaymentRequest(nonSupportedMethods, defaultDetails);
+      const handler = SpecialPowers.getDOMWindowUtils(window).setHandlingUserInput(true);
       payRequest.canMakePayment().then(result => {
         ok(!result, "canMakePayment() should return false, but got " + result + ".");
         payRequest.show().then( () => {
           ok(false, "Should be rejected with 'NotSupportedError', but got resolved");
           resolve();
         }).catch( e => {
           is(e.name, "NotSupportedError", "Should be rejected with 'NotSupportedError', but got " + e.name + ".");
           resolve();
         });
       }).finally(handler.destruct);
     });
   }
 
+  // testing show rejected by user
   function testRejectShow() {
-    const handler = SpecialPowers.getDOMWindowUtils(window).setHandlingUserInput(true);
-
     gScript.sendAsyncMessage("set-reject-ui-service");
     return new Promise((resolve, reject) => {
       const payRequest = new PaymentRequest(defaultMethods, defaultDetails, defaultOptions);
-      payRequest.show().then((result) => {
-        ok(false, "Should be rejected with 'AbortError', but got resolved");
-        resolve();
-      }, (result) => {
-        is(result.name, "AbortError", "Should be rejected with 'AbortError', but got " + result.name + ".");
-        resolve();
-      }).catch(e => {
-        ok(false, "Unexpected error: " + e.name);
-        resolve();
-      }).finally(handler.destruct);
-    });
-  }
-
-  function testUpdateWithError() {
-    const handler = SpecialPowers.getDOMWindowUtils(window).setHandlingUserInput(true);
-
-    gScript.sendAsyncMessage("set-update-with-error-ui-service");
-    return new Promise((resolve, reject) => {
-      const payRequest = new PaymentRequest(defaultMethods, defaultDetails, defaultOptions);
-      payRequest.addEventListener("shippingaddresschange", event => {
-        event.updateWith(updateWithError());
-      });
-      payRequest.addEventListener("shippingoptionchange", event => {
-        event.updateWith(updateWithError());
-      });
+      const handler = SpecialPowers.getDOMWindowUtils(window).setHandlingUserInput(true);
       payRequest.show().then((result) => {
         ok(false, "Should be rejected with 'AbortError', but got resolved");
         resolve();
       }, (result) => {
         is(result.name, "AbortError", "Should be rejected with 'AbortError', but got " + result.name + ".");
         resolve();
       }).catch(e => {
         ok(false, "Unexpected error: " + e.name);
         resolve();
       }).finally(handler.destruct);
     });
   }
 
-  function testNullDetailsResponse() {
+  // testing PaymentResponse.complete() with specified result
+  function testCompleteStatus(result) {
+    gScript.sendAsyncMessage("set-simple-ui-service");
+    if (result) {
+      gScript.sendAsyncMessage("set-complete-status-"+result);
+    } else {
+      gScript.sendAsyncMessage("set-complete-status-unknown");
+    }
     return new Promise((resolve, reject) => {
-      gScript.addMessageListener("test-init-data-and-response-complete",
-                                 function nullDetailsResponseCompleteHandler() {
-        gScript.removeMessageListener("test-init-data-and-response-complete",
-                                      nullDetailsResponseCompleteHandler);
+      const payRequest = new PaymentRequest(defaultMethods, defaultDetails, defaultOptions);
+      const handler = SpecialPowers.getDOMWindowUtils(window).setHandlingUserInput(true);
+      payRequest.show().then(response => {
+        response.complete(result).then(() => {
+          resolve();
+        }).catch(e => {
+          ok(false, "Unexpected error: " + e.name);
+          resolve();
+        });
+      }).catch( e => {
+        ok(false, "Unexpected error: " + e.name);
         resolve();
+      }).finally(handler.destruct);
+    });
+  }
+
+  function testCompleteFail() {
+    return testCompleteStatus("fail");
+  }
+
+  function testCompleteSuccess() {
+    return testCompleteStatus("success");
+  }
+
+  function testCompleteUnknown() {
+    return testCompleteStatus("unknown");
+  }
+
+  function testCompleteEmpty() {
+    return testCompleteStatus();
+  }
+
+  // testing PaymentRequestUpdateEvent.updateWith with specified details and error
+  function testUpdateWith(detailsUpdate, expectedError) {
+    if (expectedError) {
+      gScript.sendAsyncMessage("set-update-with-error-ui-service");
+    } else {
+      gScript.sendAsyncMessage("set-update-with-ui-service");
+    }
+    return new Promise((resolve, reject) => {
+      const payRequest = new PaymentRequest(defaultMethods, defaultDetails, defaultOptions);
+      payRequest.addEventListener("shippingaddresschange", event => {
+        event.updateWith(updateWithPromise(detailsUpdate));
       });
-      gScript.sendAsyncMessage("test-init-data-and-response");
+      payRequest.addEventListener("shippingoptionchange", event => {
+        event.updateWith(updateWithPromise(detailsUpdate));
+      });
+      const handler = SpecialPowers.getDOMWindowUtils(window).setHandlingUserInput(true);
+      payRequest.show().then(response => {
+        if (expectedError) {
+          ok(false, "Should be rejected with " + expectedError + ", but got resolved");
+	  resolve();
+	} else {
+	  response.complete("success").then(() => {
+	    resolve();
+	  })
+	}
+      }, response => {
+        if (expectedError) {
+          is(response.name, expectedError,
+	     "Should be rejected with " + expectedError + ", but got " + response.name);
+	} else {
+	  ok(false, "Unexpected error: " + response.name);
+	}
+	resolve();
+      }).catch(e => {
+        ok(false, "Unexpected error: " + e.name);
+	resolve();
+      }).finally(handler.destruct);
     });
   }
 
-  function testShowUnsafe() {
-    gScript.sendAsyncMessage("set-complete-ui-service");
+  function testUpdateWithReject() {
+    return testUpdateWith(null, "AbortError");
+  }
+
+  function testUpdateWithValidDetails() {
+    return testUpdateWith(updatedShippingOptionsDetails, null);
+  }
+
+  function testUpdateWithInvalidDetails() {
+    return testUpdateWith({total: "invalid details"}, "TypeError");
+  }
+
+  function testUpdateWithError() {
+    return testUpdateWith(updatedErrorDetails, "AbortError");
+  }
+
+  // testing show with detailsUpdate promise
+  function testShowWithDetailsPromise(detailsUpdate, expectedError) {
+    if (expectedError) {
+      gScript.sendAsyncMessage("set-reject-ui-service");
+    } else {
+      gScript.sendAsyncMessage("set-simple-ui-service");
+    }
+    return new Promise((resolve, reject) => {
+      const payRequest = new PaymentRequest(defaultMethods, defaultDetails, defaultOptions);
+      ok(!payRequest.shippingOption, "payRequest.shippingOption should be null.");
+      const handler = SpecialPowers.getDOMWindowUtils(window).setHandlingUserInput(true);
+      payRequest.show(updateWithPromise(detailsUpdate)).then(response => {
+        if (expectedError) {
+          ok(false, "Should be rejected with " + expectedError + ", but got resolved");
+	  resolve();
+	} else {
+          ok(response.shippingOption, "response.shippingOption should not be null.");
+	  response.complete().then(() => {
+	    resolve();
+	  })
+	}
+      }, response => {
+        if (expectedError) {
+          is(response.name, expectedError,
+	     "Should be rejected with " + expectedError + ", but got " + response.name);
+	} else {
+	  ok(false, "Unexpected error: " + response.name);
+	}
+	resolve();
+      }).catch(e => {
+        ok(false, "Unexpected error: " + e.name);
+	resolve();
+      }).finally(handler.destruct);
+    });
+  }
+  function testShowWithValidPromise() {
+    return testShowWithDetailsPromise(updatedShippingOptionsDetails, null);
+  }
+
+  function testShowWithRejectedPromise() {
+    return testShowWithDetailsPromise(null, "AbortError");
+  }
+
+  function testShowWithInvalidPromise() {
+    return testShowWithDetailsPromise({total: "invalid details"}, "TypeError");
+  }
+
+  function testShowWithErrorPromise() {
+    return testShowWithDetailsPromise(updatedErrorDetails, "AbortError");
+  }
+
+  function testShowWithPromiseResolvedByRejectedPromise() {
+    gScript.sendAsyncMessage("set-reject-ui-service");
+    return new Promise((resolve, reject)=> {
+      const request = new PaymentRequest(defaultMethods, defaultDetails, defaultOptions);
+      const handler = SpecialPowers.getDOMWindowUtils(window).setHandlingUserInput(true);
+      let rejectPromise = Promise.reject(new TypeError());
+      let detailsUpdatePromise = Promise.resolve(rejectPromise);
+      request.show(detailsUpdatePromise).then(response => {
+        ok(false, "should be rejected with 'AbortError', but got resolved.");
+	resolve();
+      }, response => {
+        is(response.name, "AbortError", "Exepcted 'AbortError', but got " + response.name + ".");
+	resolve();
+      }).catch(error => {
+        ok(false, "Unexpected error: " + error.name + ".");
+      }).finally(handler.destruct);
+    });
+  }
+
+  // testing show response initialization in chrome process
+  function testShowResponseInit() {
+    return new Promise((resolve, reject) => {
+      gScript.addMessageListener("test-show-response-init-complete",
+                                 function showResponseInitCompleteHandler() {
+        gScript.removeMessageListener("test-show-response-init-complete",
+                                      showResponseInitCompleteHandler);
+        resolve();
+      });
+      gScript.sendAsyncMessage("test-show-response-init");
+    });
+  }
+
+  // testing show that is not triggered by user.
+  function testShowNotTriggeredByUser() {
+    gScript.sendAsyncMessage("set-simple-ui-service");
     return new Promise((resolve, reject) => {
       const payRequest = new PaymentRequest(defaultMethods, defaultDetails);
       payRequest.show().then(() => {
         ok(false, "Expected 'SecurityError', but got resolved")
         resolve();
       }).catch((err) => {
         is(err.name, "SecurityError", `Should be rejected with 'SecurityError', but got ${err.name}`);
         resolve();
       });
     });
   }
 
+  // teardown function
   function teardown() {
     gScript.addMessageListener("teardown-complete", function teardownCompleteHandler() {
       gScript.removeMessageListener("teardown-complete", teardownCompleteHandler);
       gScript.removeMessageListener("test-fail", testFailHandler);
       gScript.removeMessageListener("test-pass", testPassHandler);
       gScript.destroy();
       SimpleTest.finish();
     });
     gScript.sendAsyncMessage("teardown");
   }
 
+  // test main body
   function runTests() {
     testCannotMakePaymentShow()
     .then(testRejectShow)
-    .then(testShowWithSuccess)
-    .then(testShowWithFail)
-    .then(testShowWithUnknown)
-    .then(testShowWithEmpty)
+    .then(testShowNormalFlow)
+    .then(testCompleteSuccess)
+    .then(testCompleteFail)
+    .then(testCompleteUnknown)
+    .then(testCompleteEmpty)
+    .then(testUpdateWithReject)
+    .then(testUpdateWithValidDetails)
+    .then(testUpdateWithInvalidDetails)
     .then(testUpdateWithError)
-    .then(testNullDetailsResponse)
-    .then(testShowUnsafe)
+    .then(testShowWithValidPromise)
+    .then(testShowWithInvalidPromise)
+    .then(testShowWithRejectedPromise)
+    .then(testShowWithErrorPromise)
+    .then(testShowWithPromiseResolvedByRejectedPromise)
+    .then(testShowResponseInit)
+    .then(testShowNotTriggeredByUser)
     .then(teardown)
     .catch( e => {
       ok(false, "Unexpected error: " + e.name);
       SimpleTest.finish();
     });
   }
 
   window.addEventListener('load', function() {
--- a/dom/serviceworkers/ServiceWorkerManager.cpp
+++ b/dom/serviceworkers/ServiceWorkerManager.cpp
@@ -2078,18 +2078,18 @@ ServiceWorkerManager::RemoveScopeAndRegi
     entry.Remove();
   }
 
   // Verify there are no controlled clients for the purged registration.
   for (auto iter = swm->mControlledClients.Iter(); !iter.Done(); iter.Next()) {
     auto& reg = iter.UserData()->mRegistrationInfo;
     if (reg->Scope().Equals(aRegistration->Scope()) &&
         reg->Principal()->Equals(aRegistration->Principal())) {
-      MOZ_DIAGNOSTIC_ASSERT(false,
-                            "controlled client when removing registration");
+      MOZ_DIAGNOSTIC_ASSERT(aRegistration->IsCorrupt(),
+                            "controlled client when removing non-corrupt registration");
       iter.Remove();
       break;
     }
   }
 
   RefPtr<ServiceWorkerRegistrationInfo> info;
   data->mInfos.Remove(aRegistration->Scope(), getter_AddRefs(info));
   data->mOrderedScopes.RemoveElement(aRegistration->Scope());
--- a/dom/serviceworkers/ServiceWorkerPrivate.cpp
+++ b/dom/serviceworkers/ServiceWorkerPrivate.cpp
@@ -193,17 +193,17 @@ private:
   }
 };
 
 } // anonymous namespace
 
 nsresult
 ServiceWorkerPrivate::CheckScriptEvaluation(LifeCycleEventCallback* aScriptEvaluationCallback)
 {
-  nsresult rv = SpawnWorkerIfNeeded(LifeCycleEvent, nullptr);
+  nsresult rv = SpawnWorkerIfNeeded(LifeCycleEvent);
   NS_ENSURE_SUCCESS(rv, rv);
 
   RefPtr<KeepAliveToken> token = CreateEventKeepAliveToken();
   RefPtr<WorkerRunnable> r = new CheckScriptEvaluationWithCallback(mWorkerPrivate,
                                                                    this, token,
                                                                    aScriptEvaluationCallback);
   if (NS_WARN_IF(!r->Dispatch())) {
     return NS_ERROR_FAILURE;
@@ -566,17 +566,17 @@ public:
 nsresult
 ServiceWorkerPrivate::SendMessageEvent(JSContext* aCx,
                                        JS::Handle<JS::Value> aMessage,
                                        const Sequence<JSObject*>& aTransferable,
                                        const ClientInfoAndState& aClientInfoAndState)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
-  ErrorResult rv(SpawnWorkerIfNeeded(MessageEvent, nullptr));
+  ErrorResult rv(SpawnWorkerIfNeeded(MessageEvent));
   if (NS_WARN_IF(rv.Failed())) {
     return rv.StealNSResult();
   }
 
   JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedHandleValue);
 
   rv = nsContentUtils::CreateJSValueFromSequenceOfObject(aCx, aTransferable,
                                                          &transferable);
@@ -829,20 +829,19 @@ LifecycleEventWorkerRunnable::DispatchLi
 
   return true;
 }
 
 } // anonymous namespace
 
 nsresult
 ServiceWorkerPrivate::SendLifeCycleEvent(const nsAString& aEventType,
-                                         LifeCycleEventCallback* aCallback,
-                                         nsIRunnable* aLoadFailure)
+                                         LifeCycleEventCallback* aCallback)
 {
-  nsresult rv = SpawnWorkerIfNeeded(LifeCycleEvent, aLoadFailure);
+  nsresult rv = SpawnWorkerIfNeeded(LifeCycleEvent);
   NS_ENSURE_SUCCESS(rv, rv);
 
   RefPtr<KeepAliveToken> token = CreateEventKeepAliveToken();
   RefPtr<WorkerRunnable> r = new LifecycleEventWorkerRunnable(mWorkerPrivate,
                                                               token,
                                                               aEventType,
                                                               aCallback);
   if (NS_WARN_IF(!r->Dispatch())) {
@@ -1018,17 +1017,17 @@ public:
 
 } // anonymous namespace
 
 nsresult
 ServiceWorkerPrivate::SendPushEvent(const nsAString& aMessageId,
                                     const Maybe<nsTArray<uint8_t>>& aData,
                                     ServiceWorkerRegistrationInfo* aRegistration)
 {
-  nsresult rv = SpawnWorkerIfNeeded(PushEvent, nullptr);
+  nsresult rv = SpawnWorkerIfNeeded(PushEvent);
   NS_ENSURE_SUCCESS(rv, rv);
 
   RefPtr<KeepAliveToken> token = CreateEventKeepAliveToken();
 
   nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo> regInfo(
     new nsMainThreadPtrHolder<ServiceWorkerRegistrationInfo>(
       "ServiceWorkerRegistrationInfoProxy", aRegistration, false));
 
@@ -1050,17 +1049,17 @@ ServiceWorkerPrivate::SendPushEvent(cons
   }
 
   return NS_OK;
 }
 
 nsresult
 ServiceWorkerPrivate::SendPushSubscriptionChangeEvent()
 {
-  nsresult rv = SpawnWorkerIfNeeded(PushSubscriptionChangeEvent, nullptr);
+  nsresult rv = SpawnWorkerIfNeeded(PushSubscriptionChangeEvent);
   NS_ENSURE_SUCCESS(rv, rv);
 
   RefPtr<KeepAliveToken> token = CreateEventKeepAliveToken();
   RefPtr<WorkerRunnable> r =
     new SendPushSubscriptionChangeEventRunnable(mWorkerPrivate, token);
   if (NS_WARN_IF(!r->Dispatch())) {
     return NS_ERROR_FAILURE;
   }
@@ -1309,17 +1308,17 @@ ServiceWorkerPrivate::SendNotificationEv
       Preferences::GetInt("dom.serviceWorkers.disable_open_click_delay");
   } else if (aEventName.EqualsLiteral(NOTIFICATION_CLOSE_EVENT_NAME)) {
     why = NotificationCloseEvent;
   } else {
     MOZ_ASSERT_UNREACHABLE("Invalid notification event name");
     return NS_ERROR_FAILURE;
   }
 
-  nsresult rv = SpawnWorkerIfNeeded(why, nullptr);
+  nsresult rv = SpawnWorkerIfNeeded(why);
   NS_ENSURE_SUCCESS(rv, rv);
 
   RefPtr<KeepAliveToken> token = CreateEventKeepAliveToken();
 
   RefPtr<WorkerRunnable> r =
     new SendNotificationEventRunnable(mWorkerPrivate, token,
                                       aEventName, aID, aTitle, aDir, aLang,
                                       aBody, aTag, aIcon, aData, aBehavior,
@@ -1746,29 +1745,21 @@ ServiceWorkerPrivate::SendFetchEvent(nsI
     }
 
     // Trigger soft updates if necessary.
     registration->MaybeScheduleTimeCheckAndUpdate();
 
     return NS_OK;
   }
 
-  // if the ServiceWorker script fails to load for some reason, just resume
-  // the original channel.
-  nsCOMPtr<nsIRunnable> failRunnable =
-    NewRunnableMethod("nsIInterceptedChannel::ResetInterception",
-                      aChannel,
-                      &nsIInterceptedChannel::ResetInterception);
-
   aChannel->SetLaunchServiceWorkerStart(TimeStamp::Now());
   aChannel->SetDispatchFetchEventStart(TimeStamp::Now());
 
   bool newWorkerCreated = false;
   nsresult rv = SpawnWorkerIfNeeded(FetchEvent,
-                                    failRunnable,
                                     &newWorkerCreated,
                                     aLoadGroup);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (!newWorkerCreated) {
     aChannel->SetLaunchServiceWorkerEnd(TimeStamp::Now());
   }
 
@@ -1803,17 +1794,16 @@ ServiceWorkerPrivate::SendFetchEvent(nsI
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
 nsresult
 ServiceWorkerPrivate::SpawnWorkerIfNeeded(WakeUpReason aWhy,
-                                          nsIRunnable* aLoadFailedRunnable,
                                           bool* aNewWorkerCreated,
                                           nsILoadGroup* aLoadGroup)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   // Defaults to no new worker created, but if there is one, we'll set the value
   // to true at the end of this function.
   if (aNewWorkerCreated) {
@@ -1867,17 +1857,16 @@ ServiceWorkerPrivate::SpawnWorkerIfNeede
   info.mResolvedScriptURI = info.mBaseURI;
   MOZ_ASSERT(!mInfo->CacheName().IsEmpty());
   info.mServiceWorkerCacheName = mInfo->CacheName();
 
   info.mServiceWorkerDescriptor.emplace(mInfo->Descriptor());
   info.mServiceWorkerRegistrationDescriptor.emplace(reg->Descriptor());
 
   info.mLoadGroup = aLoadGroup;
-  info.mLoadFailedAsyncRunnable = aLoadFailedRunnable;
 
   // If we are loading a script for a ServiceWorker then we must not
   // try to intercept it.  If the interception matches the current
   // ServiceWorker's scope then we could deadlock the load.
   info.mLoadFlags = mInfo->GetImportsLoadFlags() |
                     nsIChannel::LOAD_BYPASS_SERVICE_WORKER;
 
   rv = info.mBaseURI->GetHost(info.mDomain);
@@ -2090,17 +2079,17 @@ nsresult
 ServiceWorkerPrivate::AttachDebugger()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   // When the first debugger attaches to a worker, we spawn a worker if needed,
   // and cancel the idle timeout. The idle timeout should not be reset until
   // the last debugger detached from the worker.
   if (!mDebuggerCount) {
-    nsresult rv = SpawnWorkerIfNeeded(AttachEvent, nullptr);
+    nsresult rv = SpawnWorkerIfNeeded(AttachEvent);
     NS_ENSURE_SUCCESS(rv, rv);
 
     mIdleWorkerTimer->Cancel();
   }
 
   ++mDebuggerCount;
 
   return NS_OK;
--- a/dom/serviceworkers/ServiceWorkerPrivate.h
+++ b/dom/serviceworkers/ServiceWorkerPrivate.h
@@ -88,18 +88,17 @@ public:
 
   // This is used to validate the worker script and continue the installation
   // process.
   nsresult
   CheckScriptEvaluation(LifeCycleEventCallback* aCallback);
 
   nsresult
   SendLifeCycleEvent(const nsAString& aEventType,
-                     LifeCycleEventCallback* aCallback,
-                     nsIRunnable* aLoadFailure);
+                     LifeCycleEventCallback* aCallback);
 
   nsresult
   SendPushEvent(const nsAString& aMessageId,
                 const Maybe<nsTArray<uint8_t>>& aData,
                 ServiceWorkerRegistrationInfo* aRegistration);
 
   nsresult
   SendPushSubscriptionChangeEvent();
@@ -185,21 +184,18 @@ private:
   ResetIdleTimeout();
 
   void
   AddToken();
 
   void
   ReleaseToken();
 
-  // |aLoadFailedRunnable| is a runnable dispatched to the main thread
-  // if the script loader failed for some reason, but can be null.
   nsresult
   SpawnWorkerIfNeeded(WakeUpReason aWhy,
-                      nsIRunnable* aLoadFailedRunnable,
                       bool* aNewWorkerCreated = nullptr,
                       nsILoadGroup* aLoadGroup = nullptr);
 
   ~ServiceWorkerPrivate();
 
   already_AddRefed<KeepAliveToken>
   CreateEventKeepAliveToken();
 
--- a/dom/serviceworkers/ServiceWorkerRegistrationInfo.cpp
+++ b/dom/serviceworkers/ServiceWorkerRegistrationInfo.cpp
@@ -75,29 +75,43 @@ ServiceWorkerRegistrationInfo::Clear()
     active->UpdateState(ServiceWorkerState::Redundant);
     active->UpdateRedundantTime();
     active->WorkerPrivate()->NoteDeadServiceWorkerInfo();
   }
 
   NotifyChromeRegistrationListeners();
 }
 
+void
+ServiceWorkerRegistrationInfo::ClearAsCorrupt()
+{
+  mCorrupt = true;
+  Clear();
+}
+
+bool
+ServiceWorkerRegistrationInfo::IsCorrupt() const
+{
+  return mCorrupt;
+}
+
 ServiceWorkerRegistrationInfo::ServiceWorkerRegistrationInfo(
     const nsACString& aScope,
     nsIPrincipal* aPrincipal,
     ServiceWorkerUpdateViaCache aUpdateViaCache)
   : mPrincipal(aPrincipal)
   , mDescriptor(GetNextId(), aPrincipal, aScope, aUpdateViaCache)
   , mControlledClientsCounter(0)
   , mDelayMultiplier(0)
   , mUpdateState(NoUpdate)
   , mCreationTime(PR_Now())
   , mCreationTimeStamp(TimeStamp::Now())
   , mLastUpdateTime(0)
   , mPendingUninstall(false)
+  , mCorrupt(false)
 {}
 
 ServiceWorkerRegistrationInfo::~ServiceWorkerRegistrationInfo()
 {
   MOZ_DIAGNOSTIC_ASSERT(!IsControllingClients());
 }
 
 const nsCString&
@@ -303,33 +317,32 @@ ServiceWorkerRegistrationInfo::Activate(
   TransitionWaitingToActive();
 
   // FIXME(nsm): Unlink appcache if there is one.
 
   // "Queue a task to fire a simple event named controllerchange..."
   MOZ_DIAGNOSTIC_ASSERT(mActiveWorker);
   swm->UpdateClientControllers(this);
 
-  nsCOMPtr<nsIRunnable> failRunnable = NewRunnableMethod<bool>(
-    "dom::ServiceWorkerRegistrationInfo::FinishActivate",
-    this,
-    &ServiceWorkerRegistrationInfo::FinishActivate,
-    false /* success */);
-
   nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo> handle(
     new nsMainThreadPtrHolder<ServiceWorkerRegistrationInfo>(
       "ServiceWorkerRegistrationInfoProxy", this));
   RefPtr<LifeCycleEventCallback> callback = new ContinueActivateRunnable(handle);
 
   ServiceWorkerPrivate* workerPrivate = mActiveWorker->WorkerPrivate();
   MOZ_ASSERT(workerPrivate);
   nsresult rv = workerPrivate->SendLifeCycleEvent(NS_LITERAL_STRING("activate"),
-                                                  callback, failRunnable);
+                                                  callback);
   if (NS_WARN_IF(NS_FAILED(rv))) {
-    MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(failRunnable));
+    nsCOMPtr<nsIRunnable> failRunnable = NewRunnableMethod<bool>(
+      "dom::ServiceWorkerRegistrationInfo::FinishActivate",
+      this,
+      &ServiceWorkerRegistrationInfo::FinishActivate,
+      false /* success */);
+    MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(failRunnable.forget()));
     return;
   }
 }
 
 void
 ServiceWorkerRegistrationInfo::FinishActivate(bool aSuccess)
 {
   if (mPendingUninstall || !mActiveWorker ||
--- a/dom/serviceworkers/ServiceWorkerRegistrationInfo.h
+++ b/dom/serviceworkers/ServiceWorkerRegistrationInfo.h
@@ -45,16 +45,18 @@ class ServiceWorkerRegistrationInfo fina
 
   virtual ~ServiceWorkerRegistrationInfo();
 
   // When unregister() is called on a registration, it is not immediately
   // removed since documents may be controlled. It is marked as
   // pendingUninstall and when all controlling documents go away, removed.
   bool mPendingUninstall;
 
+  bool mCorrupt;
+
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSISERVICEWORKERREGISTRATIONINFO
 
   ServiceWorkerRegistrationInfo(const nsACString& aScope,
                                 nsIPrincipal* aPrincipal,
                                 ServiceWorkerUpdateViaCache aUpdateViaCache);
 
@@ -110,16 +112,22 @@ public:
   {
     return mActiveWorker && mControlledClientsCounter;
   }
 
   void
   Clear();
 
   void
+  ClearAsCorrupt();
+
+  bool
+  IsCorrupt() const;
+
+  void
   TryToActivateAsync();
 
   void
   TryToActivate();
 
   void
   Activate();
 
--- a/dom/serviceworkers/ServiceWorkerScriptCache.cpp
+++ b/dom/serviceworkers/ServiceWorkerScriptCache.cpp
@@ -295,16 +295,17 @@ public:
 
   explicit CompareManager(ServiceWorkerRegistrationInfo* aRegistration,
                           CompareCallback* aCallback)
     : mRegistration(aRegistration)
     , mCallback(aCallback)
     , mLoadFlags(nsIChannel::LOAD_BYPASS_SERVICE_WORKER)
     , mState(WaitingForInitialization)
     , mPendingCount(0)
+    , mOnFailure(OnFailure::DoNothing)
     , mAreScriptsEqual(true)
   {
     MOZ_ASSERT(NS_IsMainThread());
     MOZ_ASSERT(aRegistration);
   }
 
   nsresult
   Initialize(nsIPrincipal* aPrincipal, const nsAString& aURL,
@@ -355,16 +356,17 @@ public:
     if (--mPendingCount) {
       return;
     }
 
     if (mAreScriptsEqual) {
       MOZ_ASSERT(mCallback);
       mCallback->ComparisonResult(aStatus,
                                   true /* aSameScripts */,
+                                  mOnFailure,
                                   EmptyString(),
                                   mMaxScope,
                                   mLoadFlags);
       Cleanup();
       return;
     }
 
     // Write to Cache so ScriptLoader reads succeed.
@@ -471,48 +473,68 @@ private:
       return;
     }
 
     // Fetch and compare the source scripts.
     MOZ_ASSERT(mPendingCount == 0);
 
     mState = WaitingForScriptOrComparisonResult;
 
-    // Always make sure to fetch the main script.  If the old cache has
-    // no entries or the main script entry is missing, then the loop below
-    // may not trigger it.  This should not really happen, but we handle it
-    // gracefully if it does occur.  Its possible the bad cache state is due
-    // to a crash or shutdown during an update, etc.
-    rv = FetchScript(mURL, true /* aIsMainScript */, mOldCache);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return;
-    }
+    bool hasMainScript = false;
+    AutoTArray<nsString, 8> urlList;
 
+    // Extract the list of URLs in the old cache.
     for (uint32_t i = 0; i < len; ++i) {
       JS::Rooted<JS::Value> val(aCx);
       if (NS_WARN_IF(!JS_GetElement(aCx, obj, i, &val)) ||
           NS_WARN_IF(!val.isObject())) {
         return;
       }
 
       Request* request;
       JS::Rooted<JSObject*> requestObj(aCx, &val.toObject());
       if (NS_WARN_IF(NS_FAILED(UNWRAP_OBJECT(Request, &requestObj, request)))) {
         return;
       };
 
-      nsString URL;
-      request->GetUrl(URL);
+      nsString url;
+      request->GetUrl(url);
+
+      if (!hasMainScript && url == mURL) {
+        hasMainScript = true;
+      }
+
+      urlList.AppendElement(url);
+    }
 
+    // If the main script is missing, then something has gone wrong.  We
+    // will try to continue with the update process to trigger a new
+    // installation.  If that fails, however, then uninstall the registration
+    // because it is broken in a way that cannot be fixed.
+    if (!hasMainScript) {
+      mOnFailure = OnFailure::Uninstall;
+    }
+
+    // Always make sure to fetch the main script.  If the old cache has
+    // no entries or the main script entry is missing, then the loop below
+    // may not trigger it.  This should not really happen, but we handle it
+    // gracefully if it does occur.  Its possible the bad cache state is due
+    // to a crash or shutdown during an update, etc.
+    rv = FetchScript(mURL, true /* aIsMainScript */, mOldCache);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return;
+    }
+
+    for (const auto& url : urlList) {
       // We explicitly start the fetch for the main script above.
-      if (mURL == URL) {
+      if (mURL == url) {
         continue;
       }
 
-      rv = FetchScript(URL, false /* aIsMainScript */, mOldCache);
+      rv = FetchScript(url, false /* aIsMainScript */, mOldCache);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return;
       }
     }
 
     guard.release();
   }
 
@@ -669,16 +691,17 @@ private:
     WaitingForExistingKeys,
     WaitingForScriptOrComparisonResult,
     WaitingForOpen,
     WaitingForPut,
     Finished
   } mState;
 
   uint32_t mPendingCount;
+  OnFailure mOnFailure;
   bool mAreScriptsEqual;
 };
 
 NS_IMPL_ISUPPORTS0(CompareManager)
 
 nsresult
 CompareNetwork::Initialize(nsIPrincipal* aPrincipal,
                            const nsAString& aURL,
@@ -1321,16 +1344,17 @@ CompareManager::ResolvedCallback(JSConte
     case WaitingForOpen:
       ManageNewCache(aCx, aValue);
       return;
     case WaitingForPut:
       MOZ_DIAGNOSTIC_ASSERT(mPendingCount > 0);
       if (--mPendingCount == 0) {
         mCallback->ComparisonResult(NS_OK,
                                     false /* aIsEqual */,
+                                    mOnFailure,
                                     mNewCacheName,
                                     mMaxScope,
                                     mLoadFlags);
         Cleanup();
       }
       return;
     default:
       MOZ_DIAGNOSTIC_ASSERT(false);
@@ -1362,17 +1386,17 @@ CompareManager::RejectedCallback(JSConte
 
   Fail(NS_ERROR_FAILURE);
 }
 
 void
 CompareManager::Fail(nsresult aStatus)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  mCallback->ComparisonResult(aStatus, false /* aIsEqual */,
+  mCallback->ComparisonResult(aStatus, false /* aIsEqual */, mOnFailure,
                               EmptyString(), EmptyCString(), mLoadFlags);
   Cleanup();
 }
 
 void
 CompareManager::Cleanup()
 {
   MOZ_ASSERT(NS_IsMainThread());
--- a/dom/serviceworkers/ServiceWorkerScriptCache.h
+++ b/dom/serviceworkers/ServiceWorkerScriptCache.h
@@ -20,28 +20,35 @@ class ServiceWorkerRegistrationInfo;
 namespace serviceWorkerScriptCache {
 
 nsresult
 PurgeCache(nsIPrincipal* aPrincipal, const nsAString& aCacheName);
 
 nsresult
 GenerateCacheName(nsAString& aName);
 
+enum class OnFailure : uint8_t {
+    DoNothing,
+    Uninstall
+};
+
 class CompareCallback
 {
 public:
+
   /*
    * If there is an error, ignore aInCacheAndEqual and aNewCacheName.
    * On success, if the cached result and network result matched,
    * aInCacheAndEqual will be true and no new cache name is passed, otherwise
    * use the new cache name to load the ServiceWorker.
    */
   virtual void
   ComparisonResult(nsresult aStatus,
                    bool aInCacheAndEqual,
+                   OnFailure aOnFailure,
                    const nsAString& aNewCacheName,
                    const nsACString& aMaxScope,
                    nsLoadFlags aLoadFlags) = 0;
 
   NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
 };
 
 nsresult
--- a/dom/serviceworkers/ServiceWorkerUpdateJob.cpp
+++ b/dom/serviceworkers/ServiceWorkerUpdateJob.cpp
@@ -9,16 +9,18 @@
 #include "nsIScriptError.h"
 #include "nsIURL.h"
 #include "ServiceWorkerScriptCache.h"
 #include "mozilla/dom/WorkerCommon.h"
 
 namespace mozilla {
 namespace dom {
 
+using serviceWorkerScriptCache::OnFailure;
+
 namespace {
 
 /**
  * The spec mandates slightly different behaviors for computing the scope
  * prefix string in case a Service-Worker-Allowed header is specified versus
  * when it's not available.
  *
  * With the header:
@@ -91,22 +93,24 @@ public:
     : mJob(aJob)
   {
     MOZ_ASSERT(mJob);
   }
 
   virtual void
   ComparisonResult(nsresult aStatus,
                    bool aInCacheAndEqual,
+                   OnFailure aOnFailure,
                    const nsAString& aNewCacheName,
                    const nsACString& aMaxScope,
                    nsLoadFlags aLoadFlags) override
   {
     mJob->ComparisonResult(aStatus,
                            aInCacheAndEqual,
+                           aOnFailure,
                            aNewCacheName,
                            aMaxScope,
                            aLoadFlags);
   }
 
   NS_INLINE_DECL_REFCOUNTING(ServiceWorkerUpdateJob::CompareCallback, override)
 };
 
@@ -172,16 +176,17 @@ ServiceWorkerUpdateJob::ServiceWorkerUpd
     nsIPrincipal* aPrincipal,
     const nsACString& aScope,
     const nsACString& aScriptSpec,
     nsILoadGroup* aLoadGroup,
     ServiceWorkerUpdateViaCache aUpdateViaCache)
   : ServiceWorkerJob(Type::Update, aPrincipal, aScope, aScriptSpec)
   , mLoadGroup(aLoadGroup)
   , mUpdateViaCache(aUpdateViaCache)
+  , mOnFailure(OnFailure::DoNothing)
 {
 }
 
 already_AddRefed<ServiceWorkerRegistrationInfo>
 ServiceWorkerUpdateJob::GetRegistration() const
 {
   MOZ_ASSERT(NS_IsMainThread());
   RefPtr<ServiceWorkerRegistrationInfo> ref = mRegistration;
@@ -207,30 +212,47 @@ ServiceWorkerUpdateJob::~ServiceWorkerUp
 
 void
 ServiceWorkerUpdateJob::FailUpdateJob(ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aRv.Failed());
 
   // Cleanup after a failed installation.  This essentially implements
-  // step 12 of the Install algorithm.
+  // step 13 of the Install algorithm.
   //
-  //  https://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html#installation-algorithm
+  //  https://w3c.github.io/ServiceWorker/#installation-algorithm
   //
   // The spec currently only runs this after an install event fails,
   // but we must handle many more internal errors.  So we check for
   // cleanup on every non-successful exit.
   if (mRegistration) {
-    mRegistration->ClearEvaluating();
-    mRegistration->ClearInstalling();
+    // Some kinds of failures indicate there is something broken in the currently
+    // installed registration.  In these cases we want to fully unregister.
+    if (mOnFailure == OnFailure::Uninstall) {
+      mRegistration->ClearAsCorrupt();
+    }
+
+    // Otherwise just clear the workers we may have created as part of the
+    // update process.
+    else {
+      mRegistration->ClearEvaluating();
+      mRegistration->ClearInstalling();
+    }
 
     RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
     if (swm) {
       swm->MaybeRemoveRegistration(mRegistration);
+
+      // Also clear the registration on disk if we are forcing uninstall
+      // due to a particularly bad failure.
+      if (mOnFailure == OnFailure::Uninstall) {
+        swm->MaybeSendUnregister(mRegistration->Principal(),
+                                 mRegistration->Scope());
+      }
     }
   }
 
   mRegistration = nullptr;
 
   Finish(aRv);
 }
 
@@ -332,22 +354,25 @@ ServiceWorkerUpdateViaCache
 ServiceWorkerUpdateJob::GetUpdateViaCache() const
 {
   return mUpdateViaCache;
 }
 
 void
 ServiceWorkerUpdateJob::ComparisonResult(nsresult aStatus,
                                          bool aInCacheAndEqual,
+                                         OnFailure aOnFailure,
                                          const nsAString& aNewCacheName,
                                          const nsACString& aMaxScope,
                                          nsLoadFlags aLoadFlags)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
+  mOnFailure = aOnFailure;
+
   RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
   if (NS_WARN_IF(Canceled() || !swm)) {
     FailUpdateJob(NS_ERROR_DOM_ABORT_ERR);
     return;
   }
 
   // Handle failure of the download or comparison.  This is part of Update
   // step 5 as "If the algorithm asynchronously completes with null, then:".
@@ -435,16 +460,24 @@ ServiceWorkerUpdateJob::ComparisonResult
 
   RefPtr<ServiceWorkerInfo> sw =
     new ServiceWorkerInfo(mRegistration->Principal(),
                           mRegistration->Scope(),
                           mScriptSpec,
                           aNewCacheName,
                           flags);
 
+  // If the registration is corrupt enough to force an uninstall if the
+  // upgrade fails, then we want to make sure the upgrade takes effect
+  // if it succeeds.  Therefore force the skip-waiting flag on to replace
+  // the broken worker after install.
+  if (aOnFailure == OnFailure::Uninstall) {
+    sw->SetSkipWaitingFlag();
+  }
+
   mRegistration->SetEvaluating(sw);
 
   nsMainThreadPtrHandle<ServiceWorkerUpdateJob> handle(
       new nsMainThreadPtrHolder<ServiceWorkerUpdateJob>(
         "ServiceWorkerUpdateJob", this));
   RefPtr<LifeCycleEventCallback> callback = new ContinueUpdateRunnable(handle);
 
   ServiceWorkerPrivate* workerPrivate = sw->WorkerPrivate();
@@ -510,34 +543,26 @@ ServiceWorkerUpdateJob::Install(ServiceW
     NewRunnableMethod<RefPtr<ServiceWorkerRegistrationInfo>>(
       "dom::ServiceWorkerManager::"
       "FireUpdateFoundOnServiceWorkerRegistrations",
       aSWM,
       &ServiceWorkerManager::FireUpdateFoundOnServiceWorkerRegistrations,
       mRegistration);
   NS_DispatchToMainThread(upr);
 
-  // Call ContinueAfterInstallEvent(false) on main thread if the SW
-  // script fails to load.
-  nsCOMPtr<nsIRunnable> failRunnable = NewRunnableMethod<bool>(
-    "dom::ServiceWorkerUpdateJob::ContinueAfterInstallEvent",
-    this,
-    &ServiceWorkerUpdateJob::ContinueAfterInstallEvent,
-    false);
-
   nsMainThreadPtrHandle<ServiceWorkerUpdateJob> handle(
     new nsMainThreadPtrHolder<ServiceWorkerUpdateJob>(
       "ServiceWorkerUpdateJob", this));
   RefPtr<LifeCycleEventCallback> callback = new ContinueInstallRunnable(handle);
 
   // Send the install event to the worker thread
   ServiceWorkerPrivate* workerPrivate =
     mRegistration->GetInstalling()->WorkerPrivate();
   nsresult rv = workerPrivate->SendLifeCycleEvent(NS_LITERAL_STRING("install"),
-                                                  callback, failRunnable);
+                                                  callback);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     ContinueAfterInstallEvent(false /* aSuccess */);
   }
 }
 
 void
 ServiceWorkerUpdateJob::ContinueAfterInstallEvent(bool aInstallEventSuccess)
 {
--- a/dom/serviceworkers/ServiceWorkerUpdateJob.h
+++ b/dom/serviceworkers/ServiceWorkerUpdateJob.h
@@ -8,16 +8,20 @@
 #define mozilla_dom_serviceworkerupdatejob_h
 
 #include "ServiceWorkerJob.h"
 #include "ServiceWorkerRegistration.h"
 
 namespace mozilla {
 namespace dom {
 
+namespace serviceWorkerScriptCache {
+enum class OnFailure : uint8_t;
+} // namespace serviceWorkerScriptCache
+
 class ServiceWorkerManager;
 
 // A job class that performs the Update and Install algorithms from the
 // service worker spec.  This class is designed to be inherited and customized
 // as a different job type.  This is necessary because the register job
 // performs largely the same operations as the update job, but has a few
 // different starting steps.
 class ServiceWorkerUpdateJob : public ServiceWorkerJob
@@ -78,34 +82,35 @@ private:
   class ContinueUpdateRunnable;
   class ContinueInstallRunnable;
 
   // Utility method called after a script is loaded and compared to
   // our current cached script.
   void
   ComparisonResult(nsresult aStatus,
                    bool aInCacheAndEqual,
+                   serviceWorkerScriptCache::OnFailure aOnFailure,
                    const nsAString& aNewCacheName,
                    const nsACString& aMaxScope,
                    nsLoadFlags aLoadFlags);
 
   // Utility method called after evaluating the worker script.
   void
   ContinueUpdateAfterScriptEval(bool aScriptEvaluationResult);
 
   // Utility method corresponding to the spec Install algorithm.
   void
   Install(ServiceWorkerManager* aSWM);
 
   // Utility method called after the install event is handled.
   void
   ContinueAfterInstallEvent(bool aInstallEventSuccess);
 
+  RefPtr<ServiceWorkerRegistrationInfo> mRegistration;
   nsCOMPtr<nsILoadGroup> mLoadGroup;
   ServiceWorkerUpdateViaCache mUpdateViaCache;
-
-  RefPtr<ServiceWorkerRegistrationInfo> mRegistration;
+  serviceWorkerScriptCache::OnFailure mOnFailure;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_serviceworkerupdatejob_h
--- a/dom/serviceworkers/test/browser.ini
+++ b/dom/serviceworkers/test/browser.ini
@@ -7,18 +7,20 @@ support-files =
   download_canceled/page_download_canceled.html
   download_canceled/server-stream-download.sjs
   download_canceled/sw_download_canceled.js
   fetch.js
   file_userContextId_openWindow.js
   force_refresh_browser_worker.js
   empty.html
   empty.js
+  storage_recovery_worker.sjs
   utils.js
 
 [browser_devtools_serviceworker_interception.js]
 [browser_force_refresh.js]
 [browser_download.js]
 [browser_download_canceled.js]
 [browser_storage_permission.js]
+[browser_storage_recovery.js]
 [browser_unregister_with_containers.js]
 [browser_userContextId_openWindow.js]
 skip-if = !e10s
new file mode 100644
--- /dev/null
+++ b/dom/serviceworkers/test/browser_storage_recovery.js
@@ -0,0 +1,146 @@
+"use strict";
+
+
+// This test registers a SW for a scope that will never control a document
+// and therefore never trigger a "fetch" functional event that would
+// automatically attempt to update the registration.  The overlap of the
+// PAGE_URI and SCOPE is incidental.  checkForUpdate is the only thing that
+// will trigger an update of the registration and so there is no need to
+// worry about Schedule Job races to coalesce an update job.
+
+const BASE_URI = "http://mochi.test:8888/browser/dom/serviceworkers/test/";
+const PAGE_URI = BASE_URI + "empty.html";
+const SCOPE = PAGE_URI + "?storage_recovery";
+const SW_SCRIPT = BASE_URI + "storage_recovery_worker.sjs";
+
+async function checkForUpdate(browser) {
+  return ContentTask.spawn(browser, SCOPE, async function(uri) {
+    let reg = await content.navigator.serviceWorker.getRegistration(uri);
+    await reg.update();
+    return !!reg.installing;
+  });
+}
+
+// Delete all of our chrome-namespace Caches for this origin, leaving any
+// content-owned caches in place.  This is exclusively for simulating loss
+// of the origin's storage without loss of the registration and without
+// having to worry that future enhancements to QuotaClients/ServiceWorkerRegistrar
+// will break this test.  If you want to wipe storage for an origin, use
+// QuotaManager APIs
+async function wipeStorage(u) {
+  let uri = Services.io.newURI(u);
+  let principal =
+    Services.scriptSecurityManager.createCodebasePrincipal(uri, {});
+  let caches = new CacheStorage("chrome", principal);
+  let list = await caches.keys();
+  return Promise.all(list.map(c => caches.delete(c)));
+}
+
+add_task(async function setup() {
+  await SpecialPowers.pushPrefEnv({"set": [
+    // Until the e10s refactor is complete, use a single process to avoid
+    // service worker propagation race.
+    ["dom.ipc.processCount", 1],
+    ["dom.serviceWorkers.enabled", true],
+    ["dom.serviceWorkers.testing.enabled", true],
+    ["dom.serviceWorkers.idle_timeout", 0],
+  ]});
+
+  // Configure the server script to not redirect.
+  await fetch(SW_SCRIPT + "?clear-redirect");
+
+  let tab = BrowserTestUtils.addTab(gBrowser, PAGE_URI);
+  let browser = gBrowser.getBrowserForTab(tab);
+  await BrowserTestUtils.browserLoaded(browser);
+
+  await ContentTask.spawn(browser, { script: SW_SCRIPT, scope: SCOPE },
+    async function(opts) {
+      let reg = await content.navigator.serviceWorker.register(opts.script,
+                                                               { scope: opts.scope });
+      let worker = reg.installing || reg.waiting || reg.active;
+      await new Promise(resolve => {
+        if (worker.state === "activated") {
+          resolve();
+          return;
+        }
+        worker.addEventListener("statechange", function onStateChange() {
+          if (worker.state === "activated") {
+            worker.removeEventListener("statechange", onStateChange);
+            resolve();
+          }
+        });
+      });
+    }
+  );
+
+  BrowserTestUtils.removeTab(tab);
+});
+
+// Verify that our service worker doesn't update normally.
+add_task(async function normal_update_check() {
+  let tab = BrowserTestUtils.addTab(gBrowser, PAGE_URI);
+  let browser = gBrowser.getBrowserForTab(tab);
+  await BrowserTestUtils.browserLoaded(browser);
+
+  let updated = await checkForUpdate(browser);
+  ok(!updated, "normal update check should not trigger an update");
+
+  BrowserTestUtils.removeTab(tab);
+});
+
+// Test what happens when we wipe the service worker scripts
+// out from under the site before triggering the update.  This
+// should cause an update to occur.
+add_task(async function wiped_update_check() {
+  // Wipe the backing cache storage, but leave the SW registered.
+  await wipeStorage(PAGE_URI);
+
+  let tab = BrowserTestUtils.addTab(gBrowser, PAGE_URI);
+  let browser = gBrowser.getBrowserForTab(tab);
+  await BrowserTestUtils.browserLoaded(browser);
+
+  let updated = await checkForUpdate(browser);
+  ok(updated, "wiping the service worker scripts should trigger an update");
+
+  BrowserTestUtils.removeTab(tab);
+});
+
+// Test what happens when we wipe the service worker scripts
+// out from under the site before triggering the update.  This
+// should cause an update to occur.
+add_task(async function wiped_and_failed_update_check() {
+  // Wipe the backing cache storage, but leave the SW registered.
+  await wipeStorage(PAGE_URI);
+
+  // Configure the service worker script to redirect.  This will
+  // prevent the update from completing successfully.
+  await fetch(SW_SCRIPT + "?set-redirect");
+
+  let tab = BrowserTestUtils.addTab(gBrowser, PAGE_URI);
+  let browser = gBrowser.getBrowserForTab(tab);
+  await BrowserTestUtils.browserLoaded(browser);
+
+  // Attempt to update the service worker.  This should throw
+  // an error because the script is now redirecting.
+  let updateFailed = false;
+  try {
+    await checkForUpdate(browser);
+  } catch (e) {
+    updateFailed = true;
+  }
+  ok(updateFailed, "redirecting service worker script should fail to update");
+
+  // Also, since the existing service worker's scripts are broken
+  // we should also remove the registration completely when the
+  // update fails.
+  let exists = await ContentTask.spawn(browser, SCOPE, async function(uri) {
+    let reg = await content.navigator.serviceWorker.getRegistration(uri);
+    return !!reg;
+  });
+  ok(!exists, "registration should be removed after scripts are wiped and update fails");
+
+  // Note, we don't have to clean up the service worker registration
+  // since its effectively been force-removed here.
+
+  BrowserTestUtils.removeTab(tab);
+});
new file mode 100644
--- /dev/null
+++ b/dom/serviceworkers/test/storage_recovery_worker.sjs
@@ -0,0 +1,23 @@
+const BASE_URI = "http://mochi.test:8888/browser/dom/serviceworkers/test/";
+
+function handleRequest(request, response) {
+  let redirect = getState("redirect");
+  setState("redirect", "false");
+
+  if (request.queryString.includes("set-redirect")) {
+    setState("redirect", "true");
+  } else if (request.queryString.includes("clear-redirect")) {
+    setState("redirect", "false");
+  }
+
+  response.setHeader("Cache-Control", "no-store");
+
+  if (redirect === "true") {
+    response.setStatusLine(request.httpVersion, 307, "Moved Temporarily");
+    response.setHeader("Location", BASE_URI + "empty.js");
+    return;
+  }
+
+  response.setHeader("Content-Type", "application/javascript");
+  response.write("");
+}
--- a/dom/webidl/PaymentRequest.webidl
+++ b/dom/webidl/PaymentRequest.webidl
@@ -49,16 +49,21 @@ dictionary PaymentDetailsBase {
   sequence<PaymentDetailsModifier> modifiers;
 };
 
 dictionary PaymentDetailsInit : PaymentDetailsBase {
            DOMString   id;
   required PaymentItem total;
 };
 
+dictionary PaymentDetailsUpdate : PaymentDetailsBase {
+  DOMString   error;
+  PaymentItem total;
+};
+
 enum PaymentShippingType {
   "shipping",
   "delivery",
   "pickup"
 };
 
 dictionary PaymentOptions {
   boolean             requestPayerName = false;
@@ -69,17 +74,17 @@ dictionary PaymentOptions {
 };
 
 [Constructor(sequence<PaymentMethodData> methodData, PaymentDetailsInit details,
              optional PaymentOptions options),
  SecureContext,
  Func="mozilla::dom::PaymentRequest::PrefEnabled"]
 interface PaymentRequest : EventTarget {
   [NewObject]
-  Promise<PaymentResponse> show();
+  Promise<PaymentResponse> show(optional Promise<PaymentDetailsUpdate> detailsPromise);
   [NewObject]
   Promise<void>            abort();
   [NewObject]
   Promise<boolean>         canMakePayment();
 
   readonly attribute DOMString            id;
   readonly attribute PaymentAddress?      shippingAddress;
   readonly attribute DOMString?           shippingOption;
--- a/dom/webidl/PaymentRequestUpdateEvent.webidl
+++ b/dom/webidl/PaymentRequestUpdateEvent.webidl
@@ -2,21 +2,16 @@
 /* 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/.
  *
  * The origin of this WebIDL file is
  *   https://www.w3.org/TR/payment-request/#paymentrequestupdateevent-interface
  */
 
-dictionary PaymentDetailsUpdate : PaymentDetailsBase {
-  DOMString   error;
-  PaymentItem total;
-};
-
 [Constructor(DOMString type,
              optional PaymentRequestUpdateEventInit eventInitDict),
  SecureContext,
  Func="mozilla::dom::PaymentRequest::PrefEnabled"]
 interface PaymentRequestUpdateEvent : Event {
   [Throws]
   void updateWith(Promise<PaymentDetailsUpdate> detailsPromise);
 };
--- a/dom/workers/ScriptLoader.cpp
+++ b/dom/workers/ScriptLoader.cpp
@@ -59,16 +59,17 @@
 #include "mozilla/dom/nsCSPUtils.h"
 #include "mozilla/dom/PerformanceStorage.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/PromiseNativeHandler.h"
 #include "mozilla/dom/Response.h"
 #include "mozilla/dom/ScriptLoader.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/SRILogHelper.h"
+#include "mozilla/dom/ServiceWorkerBinding.h"
 #include "mozilla/UniquePtr.h"
 #include "Principal.h"
 #include "WorkerHolder.h"
 #include "WorkerPrivate.h"
 #include "WorkerRunnable.h"
 #include "WorkerScope.h"
 
 #define MAX_CONCURRENT_SCRIPTS 1000
@@ -175,16 +176,27 @@ ChannelFromScriptURL(nsIPrincipal* princ
   if (aIsMainScript && NS_SUCCEEDED(uri->SchemeIs("data", &isData)) && isData) {
     secFlags = nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL;
   }
 
   nsContentPolicyType contentPolicyType =
     aIsMainScript ? aMainScriptContentPolicyType
                   : nsIContentPolicy::TYPE_INTERNAL_WORKER_IMPORT_SCRIPTS;
 
+  // The main service worker script should never be loaded over the network
+  // in this path.  It should always be offlined by ServiceWorkerScriptCache.
+  // We assert here since this error should also be caught by the runtime
+  // check in CacheScriptLoader.
+  //
+  // Note, if we ever allow service worker scripts to be loaded from network
+  // here we need to configure the channel properly.  For example, it must
+  // not allow redirects.
+  MOZ_DIAGNOSTIC_ASSERT(contentPolicyType !=
+                        nsIContentPolicy::TYPE_INTERNAL_SERVICE_WORKER);
+
   nsCOMPtr<nsIChannel> channel;
   // If we have the document, use it. Unfortunately, for dedicated workers
   // 'parentDoc' ends up being the parent document, which is not the document
   // that we want to use. So make sure to avoid using 'parentDoc' in that
   // situation.
   if (parentDoc && parentDoc->NodePrincipal() == principal) {
     rv = NS_NewChannel(getter_AddRefs(channel),
                        uri,
@@ -459,16 +471,17 @@ public:
   CacheScriptLoader(WorkerPrivate* aWorkerPrivate, ScriptLoadInfo& aLoadInfo,
                     uint32_t aIndex, bool aIsWorkerScript,
                     ScriptLoaderRunnable* aRunnable)
     : mLoadInfo(aLoadInfo)
     , mIndex(aIndex)
     , mRunnable(aRunnable)
     , mIsWorkerScript(aIsWorkerScript)
     , mFailed(false)
+    , mState(aWorkerPrivate->GetServiceWorkerDescriptor().State())
   {
     MOZ_ASSERT(aWorkerPrivate);
     MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
     mMainThreadEventTarget = aWorkerPrivate->MainThreadEventTarget();
     MOZ_ASSERT(mMainThreadEventTarget);
     mBaseURI = GetBaseURI(mIsWorkerScript, aWorkerPrivate);
     AssertIsOnMainThread();
   }
@@ -491,16 +504,17 @@ private:
     AssertIsOnMainThread();
   }
 
   ScriptLoadInfo& mLoadInfo;
   uint32_t mIndex;
   RefPtr<ScriptLoaderRunnable> mRunnable;
   bool mIsWorkerScript;
   bool mFailed;
+  const ServiceWorkerState mState;
   nsCOMPtr<nsIInputStreamPump> mPump;
   nsCOMPtr<nsIURI> mBaseURI;
   mozilla::dom::ChannelInfo mChannelInfo;
   UniquePtr<PrincipalInfo> mPrincipalInfo;
   nsCString mCSPHeaderValue;
   nsCString mCSPReportOnlyHeaderValue;
   nsCString mReferrerPolicyHeaderValue;
   nsCOMPtr<nsIEventTarget> mMainThreadEventTarget;
@@ -1752,17 +1766,30 @@ CacheScriptLoader::ResolvedCallback(JSCo
   if (mFailed) {
     return;
   }
 
   MOZ_ASSERT(mLoadInfo.mCacheStatus == ScriptLoadInfo::Uncached);
 
   nsresult rv;
 
+  // The ServiceWorkerScriptCache will store data for any scripts it
+  // it knows about.  This is always at least the top level script.
+  // Depending on if a previous version of the service worker has
+  // been installed or not it may also know about importScripts().  We
+  // must handle loading and offlining new importScripts() here, however.
   if (aValue.isUndefined()) {
+    // If this is the main script or we're not loading a new service worker
+    // then this is an error.  The storage was probably wiped without
+    // removing the service worker registration.
+    if (NS_WARN_IF(mIsWorkerScript || mState != ServiceWorkerState::Parsed)) {
+      Fail(NS_ERROR_DOM_INVALID_STATE_ERR);
+      return;
+    }
+
     mLoadInfo.mCacheStatus = ScriptLoadInfo::ToBeCached;
     rv = mRunnable->LoadScript(mIndex);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       Fail(rv);
     }
     return;
   }
 
@@ -2042,20 +2069,16 @@ ScriptExecutorRunnable::WorkerRun(JSCont
     NS_ASSERTION(loadInfo.mExecutionScheduled, "Should be scheduled!");
     NS_ASSERTION(!loadInfo.mExecutionResult, "Should not have executed yet!");
 
     MOZ_ASSERT(!mScriptLoader.mRv.Failed(), "Who failed it and why?");
     mScriptLoader.mRv.MightThrowJSException();
     if (NS_FAILED(loadInfo.mLoadResult)) {
       workerinternals::ReportLoadError(mScriptLoader.mRv,
                                        loadInfo.mLoadResult, loadInfo.mURL);
-      // Top level scripts only!
-      if (mIsWorkerScript) {
-        aWorkerPrivate->MaybeDispatchLoadFailedRunnable();
-      }
       return true;
     }
 
     // If this is a top level script that succeeded, then mark the
     // Client execution ready and possible controlled by a service worker.
     if (mIsWorkerScript) {
       if (mScriptLoader.mController.isSome()) {
         aWorkerPrivate->Control(mScriptLoader.mController.ref());
--- a/dom/workers/WorkerLoadInfo.cpp
+++ b/dom/workers/WorkerLoadInfo.cpp
@@ -128,19 +128,16 @@ WorkerLoadInfo::StealFrom(WorkerLoadInfo
   aOther.mCSP.swap(mCSP);
 
   MOZ_ASSERT(!mChannel);
   aOther.mChannel.swap(mChannel);
 
   MOZ_ASSERT(!mLoadGroup);
   aOther.mLoadGroup.swap(mLoadGroup);
 
-  MOZ_ASSERT(!mLoadFailedAsyncRunnable);
-  aOther.mLoadFailedAsyncRunnable.swap(mLoadFailedAsyncRunnable);
-
   MOZ_ASSERT(!mInterfaceRequestor);
   aOther.mInterfaceRequestor.swap(mInterfaceRequestor);
 
   MOZ_ASSERT(!mPrincipalInfo);
   mPrincipalInfo = aOther.mPrincipalInfo.forget();
 
   mDomain = aOther.mDomain;
   mOrigin = aOther.mOrigin;
@@ -397,29 +394,28 @@ WorkerLoadInfo::ProxyReleaseMainThreadOb
   return ProxyReleaseMainThreadObjects(aWorkerPrivate, nullLoadGroup);
 }
 
 bool
 WorkerLoadInfo::ProxyReleaseMainThreadObjects(WorkerPrivate* aWorkerPrivate,
                                               nsCOMPtr<nsILoadGroup>& aLoadGroupToCancel)
 {
 
-  static const uint32_t kDoomedCount = 11;
+  static const uint32_t kDoomedCount = 10;
   nsTArray<nsCOMPtr<nsISupports>> doomed(kDoomedCount);
 
   SwapToISupportsArray(mWindow, doomed);
   SwapToISupportsArray(mScriptContext, doomed);
   SwapToISupportsArray(mBaseURI, doomed);
   SwapToISupportsArray(mResolvedScriptURI, doomed);
   SwapToISupportsArray(mPrincipal, doomed);
   SwapToISupportsArray(mLoadingPrincipal, doomed);
   SwapToISupportsArray(mChannel, doomed);
   SwapToISupportsArray(mCSP, doomed);
   SwapToISupportsArray(mLoadGroup, doomed);
-  SwapToISupportsArray(mLoadFailedAsyncRunnable, doomed);
   SwapToISupportsArray(mInterfaceRequestor, doomed);
   // Before adding anything here update kDoomedCount above!
 
   MOZ_ASSERT(doomed.Length() == kDoomedCount);
 
   RefPtr<MainThreadReleaseRunnable> runnable =
     new MainThreadReleaseRunnable(doomed, aLoadGroupToCancel);
   return NS_SUCCEEDED(aWorkerPrivate->DispatchToMainThread(runnable.forget()));
--- a/dom/workers/WorkerLoadInfo.h
+++ b/dom/workers/WorkerLoadInfo.h
@@ -51,22 +51,16 @@ struct WorkerLoadInfo
   nsCOMPtr<nsIPrincipal> mPrincipal;
 
   nsCOMPtr<nsIScriptContext> mScriptContext;
   nsCOMPtr<nsPIDOMWindowInner> mWindow;
   nsCOMPtr<nsIContentSecurityPolicy> mCSP;
   nsCOMPtr<nsIChannel> mChannel;
   nsCOMPtr<nsILoadGroup> mLoadGroup;
 
-  // mLoadFailedAsyncRunnable will execute on main thread if script loading
-  // fails during script loading.  If script loading is never started due to
-  // a synchronous error, then the runnable is never executed.  The runnable
-  // is guaranteed to be released on the main thread.
-  nsCOMPtr<nsIRunnable> mLoadFailedAsyncRunnable;
-
   class InterfaceRequestor final : public nsIInterfaceRequestor
   {
     NS_DECL_ISUPPORTS
 
   public:
     InterfaceRequestor(nsIPrincipal* aPrincipal, nsILoadGroup* aLoadGroup);
     void MaybeAddTabChild(nsILoadGroup* aLoadGroup);
     NS_IMETHOD GetInterface(const nsIID& aIID, void** aSink) override;
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -468,16 +468,25 @@ private:
     JSAutoCompartment ac(aCx, globalScope->GetGlobalJSObject());
     if (rv.MaybeSetPendingException(aCx)) {
       return false;
     }
 
     aWorkerPrivate->SetWorkerScriptExecutedSuccessfully();
     return true;
   }
+
+  void
+  PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate, bool aRunResult) override
+  {
+    if (!aRunResult) {
+      aWorkerPrivate->CloseInternal();
+    }
+    WorkerRunnable::PostRun(aCx, aWorkerPrivate, aRunResult);
+  }
 };
 
 class NotifyRunnable final : public WorkerControlRunnable
 {
   WorkerStatus mStatus;
 
 public:
   NotifyRunnable(WorkerPrivate* aWorkerPrivate, WorkerStatus aStatus)
@@ -3292,29 +3301,16 @@ WorkerPrivate::OnProcessNextEvent()
 
 void
 WorkerPrivate::AfterProcessNextEvent()
 {
   AssertIsOnWorkerThread();
   MOZ_ASSERT(CycleCollectedJSContext::Get()->RecursionDepth());
 }
 
-void
-WorkerPrivate::MaybeDispatchLoadFailedRunnable()
-{
-  AssertIsOnWorkerThread();
-
-  nsCOMPtr<nsIRunnable> runnable = StealLoadFailedAsyncRunnable();
-  if (!runnable) {
-    return;
-  }
-
-  MOZ_ALWAYS_SUCCEEDS(DispatchToMainThread(runnable.forget()));
-}
-
 nsIEventTarget*
 WorkerPrivate::MainThreadEventTarget()
 {
   return mMainThreadEventTarget;
 }
 
 nsresult
 WorkerPrivate::DispatchToMainThread(nsIRunnable* aRunnable, uint32_t aFlags)
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -522,19 +522,16 @@ public:
   // Only valid after CompileScriptRunnable has finished running!
   bool
   WorkerScriptExecutedSuccessfully() const
   {
     AssertIsOnWorkerThread();
     return mWorkerScriptExecutedSuccessfully;
   }
 
-  void
-  MaybeDispatchLoadFailedRunnable();
-
   // Get the event target to use when dispatching to the main thread
   // from this Worker thread.  This may be the main thread itself or
   // a ThrottledEventQueue to the main thread.
   nsIEventTarget*
   MainThreadEventTarget();
 
   nsresult
   DispatchToMainThread(nsIRunnable* aRunnable,
@@ -1051,22 +1048,16 @@ public:
 
   // Determine if the SW testing per-window flag is set by devtools
   bool
   ServiceWorkersTestingInWindow() const
   {
     return mLoadInfo.mServiceWorkersTestingInWindow;
   }
 
-  already_AddRefed<nsIRunnable>
-  StealLoadFailedAsyncRunnable()
-  {
-    return mLoadInfo.mLoadFailedAsyncRunnable.forget();
-  }
-
   // This is used to handle importScripts(). When the worker is first loaded
   // and executed, it happens in a sync loop. At this point it sets
   // mLoadingWorkerScript to true. importScripts() calls that occur during the
   // execution run in nested sync loops and so this continues to return true,
   // leading to these scripts being cached offline.
   // mLoadingWorkerScript is set to false when the top level loop ends.
   // importScripts() in function calls or event handlers are always fetched
   // from the network.
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -593,17 +593,17 @@ DoGetElemFallback(JSContext* cx, Baselin
                                CacheKind::GetElem, stub->state().mode(),
                                &isTemporarilyUnoptimizable, lhs, rhs, lhs,
                                GetPropertyResultFlags::All);
         if (gen.tryAttachStub()) {
             ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
                                                         BaselineCacheIRStubKind::Monitored,
                                                         engine, script, stub, &attached);
             if (newStub) {
-                JitSpew(JitSpew_BaselineIC, "  Attached CacheIR stub");
+                JitSpew(JitSpew_BaselineIC, "  Attached GetElem CacheIR stub");
                 if (gen.shouldNotePreliminaryObjectStub())
                     newStub->toCacheIR_Monitored()->notePreliminaryObject();
                 else if (gen.shouldUnlinkPreliminaryObjectStubs())
                     StripPreliminaryObjectStubs(cx, stub);
             }
         }
         if (!attached && !isTemporarilyUnoptimizable)
             stub->state().trackNotAttached();
@@ -666,17 +666,17 @@ DoGetElemSuperFallback(JSContext* cx, Ba
         GetPropIRGenerator gen(cx, script, pc, CacheKind::GetElemSuper, stub->state().mode(),
                                &isTemporarilyUnoptimizable, lhs, rhs, receiver,
                                GetPropertyResultFlags::All);
         if (gen.tryAttachStub()) {
             ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
                                                         BaselineCacheIRStubKind::Monitored,
                                                         engine, script, stub, &attached);
             if (newStub) {
-                JitSpew(JitSpew_BaselineIC, "  Attached CacheIR stub");
+                JitSpew(JitSpew_BaselineIC, "  Attached GetElemSuper CacheIR stub");
                 if (gen.shouldNotePreliminaryObjectStub())
                     newStub->toCacheIR_Monitored()->notePreliminaryObject();
                 else if (gen.shouldUnlinkPreliminaryObjectStubs())
                     StripPreliminaryObjectStubs(cx, stub);
             }
         }
         if (!attached && !isTemporarilyUnoptimizable)
             stub->state().trackNotAttached();
@@ -836,17 +836,17 @@ DoSetElemFallback(JSContext* cx, Baselin
         SetPropIRGenerator gen(cx, script, pc, CacheKind::SetElem, stub->state().mode(),
                                &isTemporarilyUnoptimizable, objv, index, rhs);
         if (gen.tryAttachStub()) {
             ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
                                                         BaselineCacheIRStubKind::Updated,
                                                         ICStubEngine::Baseline, frame->script(),
                                                         stub, &attached);
             if (newStub) {
-                JitSpew(JitSpew_BaselineIC, "  Attached CacheIR stub");
+                JitSpew(JitSpew_BaselineIC, "  Attached SetElem CacheIR stub");
 
                 SetUpdateStubData(newStub->toCacheIR_Updated(), gen.typeCheckInfo());
 
                 if (gen.shouldNotePreliminaryObjectStub())
                     newStub->toCacheIR_Updated()->notePreliminaryObject();
                 else if (gen.shouldUnlinkPreliminaryObjectStubs())
                     StripPreliminaryObjectStubs(cx, stub);
 
@@ -905,17 +905,17 @@ DoSetElemFallback(JSContext* cx, Baselin
                                                         ICStubEngine::Baseline, frame->script(),
                                                         stub, &attached);
             if (newStub) {
                 if (gen.shouldNotePreliminaryObjectStub())
                     newStub->toCacheIR_Updated()->notePreliminaryObject();
                 else if (gen.shouldUnlinkPreliminaryObjectStubs())
                     StripPreliminaryObjectStubs(cx, stub);
 
-                JitSpew(JitSpew_BaselineIC, "  Attached CacheIR stub");
+                JitSpew(JitSpew_BaselineIC, "  Attached SetElem CacheIR stub");
                 SetUpdateStubData(newStub->toCacheIR_Updated(), gen.typeCheckInfo());
                 return true;
             }
         } else {
             gen.trackAttached(IRGenerator::NotAttached);
         }
         if (!attached && !isTemporarilyUnoptimizable)
             stub->state().trackNotAttached();
@@ -1097,17 +1097,17 @@ DoInFallback(JSContext* cx, BaselineFram
         ICStubEngine engine = ICStubEngine::Baseline;
         HasPropIRGenerator gen(cx, script, pc, CacheKind::In, stub->state().mode(), key, objValue);
         bool attached = false;
         if (gen.tryAttachStub()) {
             ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
                                                         BaselineCacheIRStubKind::Regular,
                                                         engine, script, stub, &attached);
             if (newStub)
-                JitSpew(JitSpew_BaselineIC, "  Attached CacheIR stub");
+                JitSpew(JitSpew_BaselineIC, "  Attached In CacheIR stub");
         }
         if (!attached)
             stub->state().trackNotAttached();
     }
 
     RootedObject obj(cx, &objValue.toObject());
     bool cond = false;
     if (!OperatorIn(cx, key, obj, &cond))
@@ -1166,17 +1166,17 @@ DoHasOwnFallback(JSContext* cx, Baseline
         HasPropIRGenerator gen(cx, script, pc, CacheKind::HasOwn,
                                stub->state().mode(), keyValue, objValue);
         bool attached = false;
         if (gen.tryAttachStub()) {
             ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
                                                         BaselineCacheIRStubKind::Regular,
                                                         engine, script, stub, &attached);
             if (newStub)
-                JitSpew(JitSpew_BaselineIC, "  Attached CacheIR stub");
+                JitSpew(JitSpew_BaselineIC, "  Attached HasOwn CacheIR stub");
         }
         if (!attached)
             stub->state().trackNotAttached();
     }
 
     bool found;
     if (!HasOwnProperty(cx, objValue, keyValue, &found))
         return false;
@@ -1238,17 +1238,17 @@ DoGetNameFallback(JSContext* cx, Baselin
     if (stub->state().canAttachStub()) {
         ICStubEngine engine = ICStubEngine::Baseline;
         GetNameIRGenerator gen(cx, script, pc, stub->state().mode(), envChain, name);
         if (gen.tryAttachStub()) {
             ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
                                                         BaselineCacheIRStubKind::Monitored,
                                                         engine, script, stub, &attached);
             if (newStub)
-                JitSpew(JitSpew_BaselineIC, "  Attached CacheIR stub");
+                JitSpew(JitSpew_BaselineIC, "  Attached GetName CacheIR stub");
         }
         if (!attached)
             stub->state().trackNotAttached();
     }
 
     static_assert(JSOP_GETGNAME_LENGTH == JSOP_GETNAME_LENGTH,
                   "Otherwise our check for JSOP_TYPEOF isn't ok");
     if (JSOp(pc[JSOP_GETGNAME_LENGTH]) == JSOP_TYPEOF) {
@@ -1319,17 +1319,17 @@ DoBindNameFallback(JSContext* cx, Baseli
         RootedScript script(cx, frame->script());
         BindNameIRGenerator gen(cx, script, pc, stub->state().mode(), envChain, name);
         if (gen.tryAttachStub()) {
             ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
                                                         BaselineCacheIRStubKind::Regular,
                                                         ICStubEngine::Baseline, script, stub,
                                                         &attached);
             if (newStub)
-                JitSpew(JitSpew_BaselineIC, "  Attached CacheIR stub");
+                JitSpew(JitSpew_BaselineIC, "  Attached BindName CacheIR stub");
         }
         if (!attached)
             stub->state().trackNotAttached();
     }
 
     RootedObject scope(cx);
     if (!LookupNameUnqualified(cx, name, envChain, &scope))
         return false;
@@ -1397,17 +1397,17 @@ DoGetIntrinsicFallback(JSContext* cx, Ba
         RootedScript script(cx, frame->script());
         GetIntrinsicIRGenerator gen(cx, script, pc, stub->state().mode(), res);
         if (gen.tryAttachStub()) {
             ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
                                                         BaselineCacheIRStubKind::Regular,
                                                         ICStubEngine::Baseline, script, stub,
                                                         &attached);
             if (newStub)
-                JitSpew(JitSpew_BaselineIC, "  Attached CacheIR stub");
+                JitSpew(JitSpew_BaselineIC, "  Attached GetIntrinsic CacheIR stub");
         }
         if (!attached)
             stub->state().trackNotAttached();
     }
 
     return true;
 }
 
@@ -1495,17 +1495,17 @@ DoSetPropFallback(JSContext* cx, Baselin
         SetPropIRGenerator gen(cx, script, pc, CacheKind::SetProp, stub->state().mode(),
                                &isTemporarilyUnoptimizable, lhs, idVal, rhs);
         if (gen.tryAttachStub()) {
             ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
                                                         BaselineCacheIRStubKind::Updated,
                                                         ICStubEngine::Baseline, frame->script(),
                                                         stub, &attached);
             if (newStub) {
-                JitSpew(JitSpew_BaselineIC, "  Attached CacheIR stub");
+                JitSpew(JitSpew_BaselineIC, "  Attached SetProp CacheIR stub");
 
                 SetUpdateStubData(newStub->toCacheIR_Updated(), gen.typeCheckInfo());
 
                 if (gen.shouldNotePreliminaryObjectStub())
                     newStub->toCacheIR_Updated()->notePreliminaryObject();
                 else if (gen.shouldUnlinkPreliminaryObjectStubs())
                     StripPreliminaryObjectStubs(cx, stub);
             }
@@ -1572,17 +1572,17 @@ DoSetPropFallback(JSContext* cx, Baselin
                                                         ICStubEngine::Baseline, frame->script(),
                                                         stub, &attached);
             if (newStub) {
                 if (gen.shouldNotePreliminaryObjectStub())
                     newStub->toCacheIR_Updated()->notePreliminaryObject();
                 else if (gen.shouldUnlinkPreliminaryObjectStubs())
                     StripPreliminaryObjectStubs(cx, stub);
 
-                JitSpew(JitSpew_BaselineIC, "  Attached CacheIR stub");
+                JitSpew(JitSpew_BaselineIC, "  Attached SetProp CacheIR stub");
                 SetUpdateStubData(newStub->toCacheIR_Updated(), gen.typeCheckInfo());
             }
         } else {
             gen.trackAttached(IRGenerator::NotAttached);
         }
         if (!attached && !isTemporarilyUnoptimizable)
             stub->state().trackNotAttached();
     }
@@ -2329,17 +2329,17 @@ DoCallFallback(JSContext* cx, BaselineFr
                             HandleValueArray::fromMarkedLocation(argc, vp+2));
         if (gen.tryAttachStub()) {
             ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
                                                         gen.cacheIRStubKind(),
                                                         ICStubEngine::Baseline,
                                                         script, stub, &handled);
 
             if (newStub) {
-                JitSpew(JitSpew_BaselineIC, "  Attached CacheIR stub");
+                JitSpew(JitSpew_BaselineIC, "  Attached Call CacheIR stub");
 
                 // If it's an updated stub, initialize it.
                 if (gen.cacheIRStubKind() == BaselineCacheIRStubKind::Updated)
                     SetUpdateStubData(newStub->toCacheIR_Updated(), gen.typeCheckInfo());
             }
         }
 
         // Try attaching a regular call stub, but only if the CacheIR attempt didn't add
@@ -3931,17 +3931,17 @@ DoGetIteratorFallback(JSContext* cx, Bas
         ICStubEngine engine = ICStubEngine::Baseline;
         GetIteratorIRGenerator gen(cx, script, pc, stub->state().mode(), value);
         bool attached = false;
         if (gen.tryAttachStub()) {
             ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
                                                         BaselineCacheIRStubKind::Regular,
                                                         engine, script, stub, &attached);
             if (newStub)
-                JitSpew(JitSpew_BaselineIC, "  Attached CacheIR stub");
+                JitSpew(JitSpew_BaselineIC, "  Attached GetIterator CacheIR stub");
         }
         if (!attached)
             stub->state().trackNotAttached();
     }
 
     JSObject* iterobj = ValueToIterator(cx, value);
     if (!iterobj)
         return false;
@@ -4232,17 +4232,17 @@ DoTypeOfFallback(JSContext* cx, Baseline
         ICStubEngine engine = ICStubEngine::Baseline;
         TypeOfIRGenerator gen(cx, script, pc, stub->state().mode(), val);
         bool attached = false;
         if (gen.tryAttachStub()) {
             ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
                                                         BaselineCacheIRStubKind::Regular,
                                                         engine, script, stub, &attached);
             if (newStub)
-                JitSpew(JitSpew_BaselineIC, "  Attached CacheIR stub");
+                JitSpew(JitSpew_BaselineIC, "  Attached TypeOf CacheIR stub");
         }
         if (!attached)
             stub->state().trackNotAttached();
     }
 
     JSType type = js::TypeOfValue(val);
     RootedString string(cx, TypeName(type, cx->names()));
     res.setString(string);
@@ -4585,17 +4585,17 @@ DoUnaryArithFallback(JSContext* cx, Base
         UnaryArithIRGenerator gen(cx, script, pc, stub->state().mode(),
                                     op, val, res);
         if (gen.tryAttachStub()) {
             bool attached = false;
             ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
                                                         BaselineCacheIRStubKind::Regular,
                                                         ICStubEngine::Baseline, script, stub, &attached);
             if (newStub) {
-                JitSpew(JitSpew_BaselineIC, "  Attached (shared) CacheIR stub for %s", CodeName[op]);
+                JitSpew(JitSpew_BaselineIC, "  Attached UnaryArith CacheIR stub for %s", CodeName[op]);
             }
         }
     }
 
     return true;
 }
 
 typedef bool (*DoUnaryArithFallbackFn)(JSContext*, BaselineFrame*, ICUnaryArith_Fallback*,
--- a/js/src/wasm/WasmFrameIter.cpp
+++ b/js/src/wasm/WasmFrameIter.cpp
@@ -838,17 +838,24 @@ js::wasm::StartUnwinding(const RegisterS
     switch (codeRange->kind()) {
       case CodeRange::Function:
       case CodeRange::FarJumpIsland:
       case CodeRange::ImportJitExit:
       case CodeRange::ImportInterpExit:
       case CodeRange::BuiltinThunk:
       case CodeRange::DebugTrap:
 #if defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
-        if (offsetFromEntry < PushedFP || codeRange->isThunk()) {
+        if (codeRange->isThunk()) {
+            // The FarJumpIsland sequence temporary scrambles ra.
+            // Don't unwind to caller.
+            fixedPC = pc;
+            fixedFP = fp;
+            *unwoundCaller = false;
+            AssertMatchesCallSite(fp->returnAddress, fp->callerFP);
+        } else if (offsetFromEntry < PushedFP) {
             // On MIPS we rely on register state instead of state saved on
             // stack until the wasm::Frame is completely built.
             // On entry the return address is in ra (registers.lr) and
             // fp holds the caller's fp.
             fixedPC = (uint8_t*) registers.lr;
             fixedFP = fp;
             AssertMatchesCallSite(fixedPC, fixedFP);
         } else
--- a/layout/style/MediaQueryList.cpp
+++ b/layout/style/MediaQueryList.cpp
@@ -18,17 +18,18 @@
 #define ONCHANGE_STRING NS_LITERAL_STRING("change")
 
 namespace mozilla {
 namespace dom {
 
 MediaQueryList::MediaQueryList(nsIDocument* aDocument,
                                const nsAString& aMediaQueryList,
                                CallerType aCallerType)
-  : mDocument(aDocument)
+  : DOMEventTargetHelper(aDocument->GetInnerWindow())
+  , mDocument(aDocument)
   , mMatches(false)
   , mMatchesValid(false)
 {
   mMediaList = MediaList::Create(aMediaQueryList, aCallerType);
 
   KeepAliveIfHasListenersFor(ONCHANGE_STRING);
 }
 
--- a/netwerk/protocol/websocket/WebSocketChannel.cpp
+++ b/netwerk/protocol/websocket/WebSocketChannel.cpp
@@ -1047,16 +1047,19 @@ public:
   nsresult ConvertStreamToString()
   {
     MOZ_ASSERT(mMsgType == kMsgTypeStream, "Not a stream!");
 
     nsAutoPtr<nsCString> temp(new nsCString());
     nsresult rv = NS_ReadInputStreamToString(mMsg.pStream, *temp, mLength);
 
     NS_ENSURE_SUCCESS(rv, rv);
+    if (temp->Length() != mLength) {
+      return NS_ERROR_UNEXPECTED;
+    }
 
     mMsg.pStream->Close();
     mMsg.pStream->Release();
     mMsg.pString.mValue = temp.forget();
     mMsg.pString.mOrigValue = nullptr;
     mMsgType = kMsgTypeBinaryString;
 
     return NS_OK;
--- a/testing/mozharness/mozharness/mozilla/testing/verify_tools.py
+++ b/testing/mozharness/mozharness/mozilla/testing/verify_tools.py
@@ -40,18 +40,26 @@ class VerifyToolsMixin(object):
             (os.path.join(dirs['abs_mochitest_dir'], 'browser', 'browser-chrome.ini'), 'browser-chrome'),
             (os.path.join(dirs['abs_mochitest_dir'], 'a11y', 'a11y.ini'), 'a11y'),
             (os.path.join(dirs['abs_xpcshell_dir'], 'tests', 'xpcshell.ini'), 'xpcshell'),
         ]
         tests_by_path = {}
         for (path, suite) in manifests:
             if os.path.exists(path):
                 man = TestManifest([path], strict=False)
-                active = man.active_tests(exists=False, disabled=False, filters=[], **mozinfo.info)
-                tests_by_path.update({t['relpath']:(suite,t.get('subsuite')) for t in active})
+                active = man.active_tests(exists=False, disabled=True, filters=[], **mozinfo.info)
+                # Remove disabled tests. Also, remove tests with the same path as
+                # disabled tests, even if they are not disabled, since test-verify
+                # specifies tests by path (it cannot distinguish between two or more
+                # tests with the same path specified in multiple manifests).
+                disabled = [t['relpath'] for t in active if 'disabled' in t]
+                new_by_path = {t['relpath']:(suite,t.get('subsuite')) \
+                               for t in active if 'disabled' not in t and \
+                               t['relpath'] not in disabled}
+                tests_by_path.update(new_by_path)
                 self.info("Verification updated with manifest %s" % path)
 
         ref_manifests = [
             (os.path.join(dirs['abs_reftest_dir'], 'tests', 'layout', 'reftests', 'reftest.list'), 'reftest'),
             (os.path.join(dirs['abs_reftest_dir'], 'tests', 'testing', 'crashtest', 'crashtests.list'), 'crashtest'),
         ]
         sys.path.append(dirs['abs_reftest_dir'])
         import manifest