Merge mozilla-central to mozilla-inbound
authorarthur.iakab <aiakab@mozilla.com>
Thu, 11 Oct 2018 13:10:33 +0300
changeset 489014 8940f3ccdbd00b246dc41608545855580f675a90
parent 488997 777ef3d920f01a85b677dea8914fc38df9135814 (current diff)
parent 489013 ddcd7cc2f3cdd88f5f399f4c59d530aceda0d722 (diff)
child 489015 d0c617e2d35101856d1f26431520a65187f29c78
push id246
push userfmarier@mozilla.com
push dateSat, 13 Oct 2018 00:15:40 +0000
milestone64.0a1
Merge mozilla-central to mozilla-inbound
toolkit/locales/en-US/chrome/global/aboutAbout.dtd
--- a/browser/components/customizableui/content/panelUI.js
+++ b/browser/components/customizableui/content/panelUI.js
@@ -695,17 +695,22 @@ const PanelUI = {
     }
 
     let doorhangers =
       notifications.filter(n => !n.dismissed && !n.options.badgeOnly);
 
     if (this.panel.state == "showing" || this.panel.state == "open") {
       // If the menu is already showing, then we need to dismiss all notifications
       // since we don't want their doorhangers competing for attention
-      doorhangers.forEach(n => { n.dismissed = true; });
+      doorhangers.forEach(n => {
+        n.dismissed = true;
+        if (n.options.onDismissed) {
+          n.options.onDismissed();
+        }
+      });
       this._hidePopup();
       this._clearBadge();
       if (!notifications[0].options.badgeOnly) {
         this._showBannerItem(notifications[0]);
       }
     } else if (doorhangers.length > 0) {
       // Only show the doorhanger if the window is focused and not fullscreen
       if ((window.fullScreen && this.autoHideToolbarInFullScreen) || Services.focus.activeWindow !== window) {
--- a/browser/modules/ExtensionsUI.jsm
+++ b/browser/modules/ExtensionsUI.jsm
@@ -418,17 +418,20 @@ var ExtensionsUI = {
 
       let icon = addon.isWebExtension ?
                  AddonManager.getPreferredIconURL(addon, 32, window) || DEFAULT_EXTENSION_ICON :
                  "chrome://browser/skin/addons/addon-install-installed.svg";
       let options = {
         name: addon.name,
         message,
         popupIconURL: icon,
-        onDismissed: resolve,
+        onDismissed: () => {
+          AppMenuNotifications.removeNotification("addon-installed");
+          resolve();
+        },
       };
 
       AppMenuNotifications.showNotification("addon-installed", action, null, options);
     });
   },
 };
 
 EventEmitter.decorate(ExtensionsUI);
--- a/dom/locales/en-US/chrome/dom/dom.properties
+++ b/dom/locales/en-US/chrome/dom/dom.properties
@@ -319,20 +319,16 @@ LargeAllocationNonGetRequest=A Large-All
 LargeAllocationNotOnlyToplevelInTabGroup=A Large-Allocation header was ignored due to the presence of windows which have a reference to this browsing context through the frame hierarchy or window.opener.
 # LOCALIZATION NOTE: Do not translate "Large-Allocation", as it is a literal header name
 LargeAllocationNonE10S=A Large-Allocation header was ignored due to the document not being loaded out of process.
 GeolocationInsecureRequestIsForbidden=A Geolocation request can only be fulfilled in a secure context.
 # LOCALIZATION NOTE: Do not translate "Large-Allocation", as it is a literal header name.
 LargeAllocationNonWin32=This page would be loaded in a new process due to a Large-Allocation header, however Large-Allocation process creation is disabled on non-Win32 platforms.
 # LOCALIZATION NOTE: Do not translate URL.createObjectURL(MediaStream).
 URLCreateObjectURL_MediaStreamWarning=URL.createObjectURL(MediaStream) is deprecated and will be removed soon.
-# LOCALIZATION NOTE: Do not translate MozAutoGainControl or autoGainControl.
-MozAutoGainControlWarning=mozAutoGainControl is deprecated. Use autoGainControl instead.
-# LOCALIZATION NOTE: Do not translate mozNoiseSuppression or noiseSuppression.
-MozNoiseSuppressionWarning=mozNoiseSuppression is deprecated. Use noiseSuppression instead.
 # LOCALIZATION NOTE: Do not translate xml:base.
 XMLBaseAttributeWarning=Use of xml:base attribute is deprecated and will be removed soon. Please remove any use of it.
 # LOCALIZATION NOTE: Do not translate "content", "Window", and "window.top"
 WindowContentUntrustedWarning=The ‘content’ attribute of Window objects is deprecated.  Please use ‘window.top’ instead.
 # LOCALIZATION NOTE: The first %S is the tag name of the element that starts the loop, the second %S is the element's ID.
 SVGRefLoopWarning=The SVG <%S> with ID “%S” has a reference loop.
 # LOCALIZATION NOTE: The first %S is the tag name of the element in the chain where the chain was broken, the second %S is the element's ID.
 SVGRefChainLengthExceededWarning=An SVG <%S> reference chain which is too long was abandoned at the element with ID “%S”.
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -2790,24 +2790,16 @@ MediaManager::GetUserMedia(nsPIDOMWindow
   } else if (IsOn(c.mVideo)) {
     videoType = MediaSourceEnum::Camera;
     Telemetry::Accumulate(Telemetry::WEBRTC_GET_USER_MEDIA_TYPE,
                           (uint32_t) videoType);
   }
 
   if (c.mAudio.IsMediaTrackConstraints()) {
     auto& ac = c.mAudio.GetAsMediaTrackConstraints();
-    MediaConstraintsHelper::ConvertOldWithWarning(ac.mMozAutoGainControl,
-                                                  ac.mAutoGainControl,
-                                                  "MozAutoGainControlWarning",
-                                                  aWindow);
-    MediaConstraintsHelper::ConvertOldWithWarning(ac.mMozNoiseSuppression,
-                                                  ac.mNoiseSuppression,
-                                                  "MozNoiseSuppressionWarning",
-                                                  aWindow);
     audioType = StringToEnum(dom::MediaSourceEnumValues::strings,
                              ac.mMediaSource,
                              MediaSourceEnum::Other);
     // Work around WebIDL default since spec uses same dictionary w/audio & video.
     if (audioType == MediaSourceEnum::Camera) {
       audioType = MediaSourceEnum::Microphone;
       ac.mMediaSource.AssignASCII(EnumToASCII(dom::MediaSourceEnumValues::strings,
                                               audioType));
@@ -4766,59 +4758,49 @@ SourceListener::CapturingSource(MediaSou
 
   return CaptureState::Disabled;
 }
 
 RefPtr<SourceListener::ApplyConstraintsPromise>
 SourceListener::ApplyConstraintsToTrack(
     nsPIDOMWindowInner* aWindow,
     TrackID aTrackID,
-    const MediaTrackConstraints& aConstraintsPassedIn,
+    const MediaTrackConstraints& aConstraints,
     dom::CallerType aCallerType)
 {
   MOZ_ASSERT(NS_IsMainThread());
   DeviceState& state = GetDeviceStateFor(aTrackID);
 
   if (mStopped || state.mStopped) {
     LOG(("gUM %s track %d applyConstraints, but source is stopped",
          aTrackID == kAudioTrack ? "audio" : "video", aTrackID));
     return ApplyConstraintsPromise::CreateAndResolve(false, __func__);
   }
 
-  MediaTrackConstraints c(aConstraintsPassedIn); // use a modifiable copy
-  MediaConstraintsHelper::ConvertOldWithWarning(c.mMozAutoGainControl,
-                                                c.mAutoGainControl,
-                                                "MozAutoGainControlWarning",
-                                                aWindow);
-  MediaConstraintsHelper::ConvertOldWithWarning(c.mMozNoiseSuppression,
-                                                c.mNoiseSuppression,
-                                                "MozNoiseSuppressionWarning",
-                                                aWindow);
-
   MediaManager* mgr = MediaManager::GetIfExists();
   if (!mgr) {
     return ApplyConstraintsPromise::CreateAndResolve(false, __func__);
   }
 
   return MediaManager::PostTask<ApplyConstraintsPromise>(__func__,
-      [device = state.mDevice, c,
+      [device = state.mDevice, aConstraints,
        isChrome = aCallerType == dom::CallerType::System]
       (MozPromiseHolder<ApplyConstraintsPromise>& aHolder) mutable {
     MOZ_ASSERT(MediaManager::IsInMediaThread());
     MediaManager* mgr = MediaManager::GetIfExists();
     MOZ_RELEASE_ASSERT(mgr); // Must exist while media thread is alive
     const char* badConstraint = nullptr;
-    nsresult rv = device->Reconfigure(c, mgr->mPrefs, &badConstraint);
+    nsresult rv = device->Reconfigure(aConstraints, mgr->mPrefs, &badConstraint);
     if (rv == NS_ERROR_INVALID_ARG) {
       // Reconfigure failed due to constraints
       if (!badConstraint) {
         nsTArray<RefPtr<MediaDevice>> devices;
         devices.AppendElement(device);
         badConstraint = MediaConstraintsHelper::SelectSettings(
-            NormalizedConstraints(c), devices, isChrome);
+            NormalizedConstraints(aConstraints), devices, isChrome);
       }
 
       aHolder.Reject(Some(NS_ConvertASCIItoUTF16(badConstraint)), __func__);
       return;
     }
 
     if (NS_FAILED(rv)) {
       // Reconfigure failed unexpectedly
--- a/dom/media/webrtc/MediaEngineDefault.cpp
+++ b/dom/media/webrtc/MediaEngineDefault.cpp
@@ -419,18 +419,19 @@ MediaEngineDefaultAudioSource::Allocate(
                                         AllocationHandle** aOutHandle,
                                         const char** aOutBadConstraint)
 {
   AssertIsOnOwningThread();
 
   MOZ_ASSERT(mState == kReleased);
 
   // Mock failure for automated tests.
-  if (aConstraints.mDeviceId.IsString() &&
-      aConstraints.mDeviceId.GetAsString().EqualsASCII("bad device")) {
+  if (aConstraints.mDeviceId.WasPassed() &&
+      aConstraints.mDeviceId.Value().IsString() &&
+      aConstraints.mDeviceId.Value().GetAsString().EqualsASCII("bad device")) {
     return NS_ERROR_FAILURE;
   }
 
   mFreq = aPrefs.mFreq ? aPrefs.mFreq : 1000;
   *aOutHandle = nullptr;
 
   MutexAutoLock lock(mMutex);
   mState = kAllocated;
--- a/dom/media/webrtc/MediaEngineRemoteVideoSource.cpp
+++ b/dom/media/webrtc/MediaEngineRemoteVideoSource.cpp
@@ -986,19 +986,19 @@ MediaEngineRemoteVideoSource::ChooseCapa
 
   TrimLessFitCandidates(candidateSet);
 
   // Any remaining multiples all have the same distance. A common case of this
   // occurs when no ideal is specified. Lean toward defaults.
   uint32_t sameDistance = candidateSet[0].mDistance;
   {
     MediaTrackConstraintSet prefs;
-    prefs.mWidth.SetAsLong() = aPrefs.GetWidth();
-    prefs.mHeight.SetAsLong() = aPrefs.GetHeight();
-    prefs.mFrameRate.SetAsDouble() = aPrefs.mFPS;
+    prefs.mWidth.Construct().SetAsLong() = aPrefs.GetWidth();
+    prefs.mHeight.Construct().SetAsLong() = aPrefs.GetHeight();
+    prefs.mFrameRate.Construct().SetAsDouble() = aPrefs.mFPS;
     NormalizedConstraintSet normPrefs(prefs, false);
 
     for (auto& candidate : candidateSet) {
       candidate.mDistance =
         GetDistance(candidate.mCapability, normPrefs, aDeviceId, aCalculate);
     }
     TrimLessFitCandidates(candidateSet);
   }
--- a/dom/media/webrtc/MediaTrackConstraints.cpp
+++ b/dom/media/webrtc/MediaTrackConstraints.cpp
@@ -16,17 +16,16 @@
 
 mozilla::LogModule* GetMediaManagerLog();
 #undef LOG
 #define LOG(msg, ...) MOZ_LOG(GetMediaManagerLog(), mozilla::LogLevel::Debug, (msg, ##__VA_ARGS__))
 
 namespace mozilla {
 
 using dom::ConstrainBooleanParameters;
-using dom::OwningLongOrConstrainLongRange;
 
 template<class ValueType>
 template<class ConstrainRange>
 void
 NormalizedConstraintSet::Range<ValueType>::SetFrom(const ConstrainRange& aOther)
 {
   if (aOther.mIdeal.WasPassed()) {
     mIdeal.emplace(aOther.mIdeal.Value());
@@ -86,31 +85,35 @@ NormalizedConstraintSet::Range<bool>::Fi
     *mIdeal = !!(counter / denominator);
     mMergeDenominator = 0;
   }
 }
 
 NormalizedConstraintSet::LongRange::LongRange(
     LongPtrType aMemberPtr,
     const char* aName,
-    const dom::OwningLongOrConstrainLongRange& aOther,
+    const dom::Optional<dom::OwningLongOrConstrainLongRange>& aOther,
     bool advanced,
     nsTArray<MemberPtrType>* aList)
 : Range<int32_t>((MemberPtrType)aMemberPtr, aName,
                  1 + INT32_MIN, INT32_MAX, // +1 avoids Windows compiler bug
                  aList)
 {
-  if (aOther.IsLong()) {
+  if (!aOther.WasPassed()) {
+    return;
+  }
+  auto& other = aOther.Value();
+  if (other.IsLong()) {
     if (advanced) {
-      mMin = mMax = aOther.GetAsLong();
+      mMin = mMax = other.GetAsLong();
     } else {
-      mIdeal.emplace(aOther.GetAsLong());
+      mIdeal.emplace(other.GetAsLong());
     }
   } else {
-    SetFrom(aOther.GetAsConstrainLongRange());
+    SetFrom(other.GetAsConstrainLongRange());
   }
 }
 
 NormalizedConstraintSet::LongLongRange::LongLongRange(
     LongLongPtrType aMemberPtr,
     const char* aName,
     const long long& aOther,
     nsTArray<MemberPtrType>* aList)
@@ -119,87 +122,100 @@ NormalizedConstraintSet::LongLongRange::
                  aList)
 {
   mIdeal.emplace(aOther);
 }
 
 NormalizedConstraintSet::DoubleRange::DoubleRange(
     DoublePtrType aMemberPtr,
     const char* aName,
-    const dom::OwningDoubleOrConstrainDoubleRange& aOther, bool advanced,
+    const dom::Optional<dom::OwningDoubleOrConstrainDoubleRange>& aOther,
+    bool advanced,
     nsTArray<MemberPtrType>* aList)
 : Range<double>((MemberPtrType)aMemberPtr, aName,
                 -std::numeric_limits<double>::infinity(),
                 std::numeric_limits<double>::infinity(), aList)
 {
-  if (aOther.IsDouble()) {
+  if (!aOther.WasPassed()) {
+    return;
+  }
+  auto& other = aOther.Value();
+  if (other.IsDouble()) {
     if (advanced) {
-      mMin = mMax = aOther.GetAsDouble();
+      mMin = mMax = other.GetAsDouble();
     } else {
-      mIdeal.emplace(aOther.GetAsDouble());
+      mIdeal.emplace(other.GetAsDouble());
     }
   } else {
-    SetFrom(aOther.GetAsConstrainDoubleRange());
+    SetFrom(other.GetAsConstrainDoubleRange());
   }
 }
 
 NormalizedConstraintSet::BooleanRange::BooleanRange(
     BooleanPtrType aMemberPtr,
     const char* aName,
-    const dom::OwningBooleanOrConstrainBooleanParameters& aOther,
+    const dom::Optional<dom::OwningBooleanOrConstrainBooleanParameters>& aOther,
     bool advanced,
     nsTArray<MemberPtrType>* aList)
 : Range<bool>((MemberPtrType)aMemberPtr, aName, false, true, aList)
 {
-  if (aOther.IsBoolean()) {
+  if (!aOther.WasPassed()) {
+    return;
+  }
+  auto& other = aOther.Value();
+  if (other.IsBoolean()) {
     if (advanced) {
-      mMin = mMax = aOther.GetAsBoolean();
+      mMin = mMax = other.GetAsBoolean();
     } else {
-      mIdeal.emplace(aOther.GetAsBoolean());
+      mIdeal.emplace(other.GetAsBoolean());
     }
   } else {
-    const dom::ConstrainBooleanParameters& r = aOther.GetAsConstrainBooleanParameters();
+    auto& r = other.GetAsConstrainBooleanParameters();
     if (r.mIdeal.WasPassed()) {
       mIdeal.emplace(r.mIdeal.Value());
     }
     if (r.mExact.WasPassed()) {
       mMin = r.mExact.Value();
       mMax = r.mExact.Value();
     }
   }
 }
 
 NormalizedConstraintSet::StringRange::StringRange(
     StringPtrType aMemberPtr,
     const char* aName,
-    const dom::OwningStringOrStringSequenceOrConstrainDOMStringParameters& aOther,
+    const dom::Optional<dom::OwningStringOrStringSequenceOrConstrainDOMStringParameters>& aOther,
     bool advanced,
     nsTArray<MemberPtrType>* aList)
   : BaseRange((MemberPtrType)aMemberPtr, aName, aList)
 {
-  if (aOther.IsString()) {
+  if (!aOther.WasPassed()) {
+    return;
+  }
+  auto& other = aOther.Value();
+  if (other.IsString()) {
     if (advanced) {
-      mExact.insert(aOther.GetAsString());
+      mExact.insert(other.GetAsString());
     } else {
-      mIdeal.insert(aOther.GetAsString());
+      mIdeal.insert(other.GetAsString());
     }
-  } else if (aOther.IsStringSequence()) {
+  } else if (other.IsStringSequence()) {
     if (advanced) {
       mExact.clear();
-      for (auto& str : aOther.GetAsStringSequence()) {
+      for (auto& str : other.GetAsStringSequence()) {
         mExact.insert(str);
       }
     } else {
       mIdeal.clear();
-      for (auto& str : aOther.GetAsStringSequence()) {
+      for (auto& str : other.GetAsStringSequence()) {
         mIdeal.insert(str);
       }
     }
   } else {
-    SetFrom(aOther.GetAsConstrainDOMStringParameters());
+    SetFrom(other.GetAsConstrainDOMStringParameters());
   }
 }
 
 void
 NormalizedConstraintSet::StringRange::SetFrom(
     const dom::ConstrainDOMStringParameters& aOther)
 {
   if (aOther.mIdeal.WasPassed()) {
@@ -603,43 +619,16 @@ MediaConstraintsHelper::FindBadConstrain
 {
   AutoTArray<RefPtr<MediaDevice>, 1> devices;
   devices.AppendElement(MakeRefPtr<MediaDevice>(aMediaEngineSource,
                                                 aMediaEngineSource->GetName(),
                                                 aDeviceId));
   return FindBadConstraint(aConstraints, devices);
 }
 
-/* static */ void
-MediaConstraintsHelper::ConvertOldWithWarning(
-    const dom::OwningBooleanOrConstrainBooleanParameters& old,
-    dom::OwningBooleanOrConstrainBooleanParameters& to,
-    const char* aMessageName,
-    nsPIDOMWindowInner* aWindow) {
-  if ((old.IsBoolean() ||
-       old.GetAsConstrainBooleanParameters().mExact.WasPassed() ||
-       old.GetAsConstrainBooleanParameters().mIdeal.WasPassed()) &&
-      !(to.IsBoolean() ||
-        to.GetAsConstrainBooleanParameters().mExact.WasPassed() ||
-        to.GetAsConstrainBooleanParameters().mIdeal.WasPassed())) {
-    nsCOMPtr<nsIDocument> doc = aWindow->GetDoc();
-    if (doc) {
-      nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
-                                      NS_LITERAL_CSTRING("DOM"), doc,
-                                      nsContentUtils::eDOM_PROPERTIES,
-                                      aMessageName);
-    }
-    if (old.IsBoolean()) {
-      to.SetAsBoolean() = old.GetAsBoolean();
-    } else {
-      to.SetAsConstrainBooleanParameters() = old.GetAsConstrainBooleanParameters();
-    }
-  }
-}
-
 static void
 LogConstraintStringRange(const NormalizedConstraintSet::StringRange& aRange)
 {
   if (aRange.mExact.size() <= 1 && aRange.mIdeal.size() <= 1) {
     LOG("  %s: { exact: [%s], ideal: [%s] }",
         aRange.mName,
         (aRange.mExact.size()? NS_ConvertUTF16toUTF8(*aRange.mExact.begin()).get() : ""),
         (aRange.mIdeal.size()? NS_ConvertUTF16toUTF8(*aRange.mIdeal.begin()).get() : ""));
--- a/dom/media/webrtc/MediaTrackConstraints.h
+++ b/dom/media/webrtc/MediaTrackConstraints.h
@@ -145,17 +145,18 @@ public:
     uint32_t mMergeDenominator;
   };
 
   struct LongRange : public Range<int32_t>
   {
     typedef LongRange NormalizedConstraintSet::* LongPtrType;
 
     LongRange(LongPtrType aMemberPtr, const char* aName,
-              const dom::OwningLongOrConstrainLongRange& aOther, bool advanced,
+              const dom::Optional<dom::OwningLongOrConstrainLongRange>& aOther,
+              bool advanced,
               nsTArray<MemberPtrType>* aList);
   };
 
   struct LongLongRange : public Range<int64_t>
   {
     typedef LongLongRange NormalizedConstraintSet::* LongLongPtrType;
 
     LongLongRange(LongLongPtrType aMemberPtr, const char* aName,
@@ -164,27 +165,27 @@ public:
   };
 
   struct DoubleRange : public Range<double>
   {
     typedef DoubleRange NormalizedConstraintSet::* DoublePtrType;
 
     DoubleRange(DoublePtrType aMemberPtr,
                 const char* aName,
-                const dom::OwningDoubleOrConstrainDoubleRange& aOther,
+                const dom::Optional<dom::OwningDoubleOrConstrainDoubleRange>& aOther,
                 bool advanced,
                 nsTArray<MemberPtrType>* aList);
   };
 
   struct BooleanRange : public Range<bool>
   {
     typedef BooleanRange NormalizedConstraintSet::* BooleanPtrType;
 
     BooleanRange(BooleanPtrType aMemberPtr, const char* aName,
-                 const dom::OwningBooleanOrConstrainBooleanParameters& aOther,
+                 const dom::Optional<dom::OwningBooleanOrConstrainBooleanParameters>& aOther,
                  bool advanced,
                  nsTArray<MemberPtrType>* aList);
 
     BooleanRange(BooleanPtrType aMemberPtr, const char* aName, const bool& aOther,
                  nsTArray<MemberPtrType>* aList)
       : Range<bool>((MemberPtrType)aMemberPtr, aName, false, true, aList) {
       mIdeal.emplace(aOther);
     }
@@ -193,17 +194,17 @@ public:
   struct StringRange : public BaseRange
   {
     typedef std::set<nsString> ValueType;
     ValueType mExact, mIdeal;
 
     typedef StringRange NormalizedConstraintSet::* StringPtrType;
 
     StringRange(StringPtrType aMemberPtr,  const char* aName,
-        const dom::OwningStringOrStringSequenceOrConstrainDOMStringParameters& aOther,
+        const dom::Optional<dom::OwningStringOrStringSequenceOrConstrainDOMStringParameters>& aOther,
         bool advanced,
         nsTArray<MemberPtrType>* aList);
 
     StringRange(StringPtrType aMemberPtr, const char* aName,
                 const nsString& aOther, nsTArray<MemberPtrType>* aList)
       : BaseRange((MemberPtrType)aMemberPtr, aName, aList) {
       mIdeal.insert(aOther);
     }
@@ -332,22 +333,14 @@ public:
   FindBadConstraint(const NormalizedConstraints& aConstraints,
                     const nsTArray<RefPtr<MediaDevice>>& aDevices);
 
   static const char*
   FindBadConstraint(const NormalizedConstraints& aConstraints,
                     const RefPtr<MediaEngineSource>& aMediaEngineSource,
                     const nsString& aDeviceId);
 
-  // Warn on and convert use of deprecated constraints to new ones
-  static void
-  ConvertOldWithWarning(
-      const dom::OwningBooleanOrConstrainBooleanParameters& old,
-      dom::OwningBooleanOrConstrainBooleanParameters& to,
-      const char* aMessageName,
-      nsPIDOMWindowInner* aWindow);
-
   static void LogConstraints(const NormalizedConstraintSet& aConstraints);
 };
 
 } // namespace mozilla
 
 #endif /* MEDIATRACKCONSTRAINTS_H_ */
--- a/dom/webidl/MediaStreamTrack.webidl
+++ b/dom/webidl/MediaStreamTrack.webidl
@@ -37,51 +37,32 @@ typedef (long or ConstrainLongRange) Con
 typedef (double or ConstrainDoubleRange) ConstrainDouble;
 typedef (boolean or ConstrainBooleanParameters) ConstrainBoolean;
 typedef (DOMString or sequence<DOMString> or ConstrainDOMStringParameters) ConstrainDOMString;
 
 // Note: When adding new constraints, remember to update the SelectSettings()
 // function in MediaManager.cpp to make OverconstrainedError's constraint work!
 
 dictionary MediaTrackConstraintSet {
-    // FIXME: bug 1493860 or bug 1493798: should this "= null" be here?
-    ConstrainLong width = null;
-    // FIXME: bug 1493860 or bug 1493798: should this "= null" be here?
-    ConstrainLong height = null;
-    // FIXME: bug 1493860 or bug 1493798: should this "= null" be here?
-    ConstrainDouble frameRate = null;
-    // FIXME: bug 1493860 or bug 1493798: should this "= null" be here?
-    ConstrainDOMString facingMode = null;
+    ConstrainLong width;
+    ConstrainLong height;
+    ConstrainDouble frameRate;
+    ConstrainDOMString facingMode;
     DOMString mediaSource = "camera";
     long long browserWindow;
     boolean scrollWithPage;
-    // FIXME: bug 1493860 or bug 1493798: should this "= null" be here?
-    ConstrainDOMString deviceId = null;
-    // FIXME: bug 1493860 or bug 1493798: should this "= null" be here?
-    ConstrainLong viewportOffsetX = null;
-    // FIXME: bug 1493860 or bug 1493798: should this "= null" be here?
-    ConstrainLong viewportOffsetY = null;
-    // FIXME: bug 1493860 or bug 1493798: should this "= null" be here?
-    ConstrainLong viewportWidth = null;
-    // FIXME: bug 1493860 or bug 1493798: should this "= null" be here?
-    ConstrainLong viewportHeight = null;
-    // FIXME: bug 1493860 or bug 1493798: should this "= null" be here?
-    ConstrainBoolean echoCancellation = null;
-    // FIXME: bug 1493860 or bug 1493798: should this "= null" be here?
-    ConstrainBoolean noiseSuppression = null;
-    // FIXME: bug 1493860 or bug 1493798: should this "= null" be here?
-    ConstrainBoolean autoGainControl = null;
-    // FIXME: bug 1493860 or bug 1493798: should this "= null" be here?
-    ConstrainLong channelCount = null;
-
-    // Deprecated with warnings:
-    // FIXME: bug 1493860 or bug 1493798: should this "= null" be here?
-    ConstrainBoolean mozNoiseSuppression = null;
-    // FIXME: bug 1493860 or bug 1493798: should this "= null" be here?
-    ConstrainBoolean mozAutoGainControl = null;
+    ConstrainDOMString deviceId;
+    ConstrainLong viewportOffsetX;
+    ConstrainLong viewportOffsetY;
+    ConstrainLong viewportWidth;
+    ConstrainLong viewportHeight;
+    ConstrainBoolean echoCancellation;
+    ConstrainBoolean noiseSuppression;
+    ConstrainBoolean autoGainControl;
+    ConstrainLong channelCount;
 };
 
 dictionary MediaTrackConstraints : MediaTrackConstraintSet {
     sequence<MediaTrackConstraintSet> advanced;
 };
 
 enum MediaStreamTrackState {
     "live",
--- a/js/src/jit-test/tests/ion/bug913749.js
+++ b/js/src/jit-test/tests/ion/bug913749.js
@@ -11,11 +11,11 @@ this.toSource();
 y = undefined;
 
 for (var i = 0; i < 3; i++) {
     try {
 	x.toString();
 	assertEq(0, 1);
     } catch (e) {
 	assertEq(e.message === `y is undefined; can't access its "length" property` ||
-		 e.message === "undefined has no properties", true);
+		 e.message === `can't access property "length" of undefined`, true);
     }
 }
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -331,16 +331,43 @@ ICStub::trace(JSTracer* trc)
         TraceCacheIRStub(trc, this, stub->stubInfo());
         break;
       }
       default:
         break;
     }
 }
 
+// This helper handles ICState updates/transitions while attaching CacheIR stubs.
+template<typename IRGenerator, typename... Args>
+static void
+TryAttachStub(const char *name, JSContext* cx, BaselineFrame* frame, ICFallbackStub* stub, BaselineCacheIRStubKind kind, Args&&... args)
+{
+    if (stub->state().maybeTransition()) {
+        stub->discardStubs(cx);
+    }
+
+    if (stub->state().canAttachStub()) {
+        RootedScript script(cx, frame->script());
+        jsbytecode* pc = stub->icEntry()->pc(script);
+
+        bool attached = false;
+        IRGenerator gen(cx, script, pc, stub->state().mode(), std::forward<Args>(args)...);
+        if (gen.tryAttachStub()) {
+            ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
+                                                        kind, script, stub, &attached);
+            if (newStub) {
+                JitSpew(JitSpew_BaselineIC, "  %s %s CacheIR stub", attached ? "Attached" : "Failed to attach", name);
+            }
+        }
+        if (!attached) {
+            stub->state().trackNotAttached();
+        }
+    }
+}
 
 
 //
 // WarmUpCounter_Fallback
 //
 
 
 //
@@ -1579,40 +1606,17 @@ ICTypeUpdate_AnyValue::Compiler::generat
 static bool
 DoToBoolFallback(JSContext* cx, BaselineFrame* frame, ICToBool_Fallback* stub, HandleValue arg,
                  MutableHandleValue ret)
 {
     FallbackICSpew(cx, stub, "ToBool");
 
     MOZ_ASSERT(!arg.isBoolean());
 
-    if (stub->state().maybeTransition()) {
-        stub->discardStubs(cx);
-    }
-
-    if (stub->state().canAttachStub()) {
-
-        RootedScript script(cx, frame->script());
-        jsbytecode* pc = stub->icEntry()->pc(script);
-
-        ToBoolIRGenerator gen(cx, script, pc, stub->state().mode(),
-                              arg);
-        bool attached = false;
-        if (gen.tryAttachStub()) {
-            ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
-                                                        BaselineCacheIRStubKind::Regular,
-                                                        script, stub, &attached);
-            if (newStub) {
-                JitSpew(JitSpew_BaselineIC, "  Attached ToBool CacheIR stub, attached is now %d", attached);
-            }
-        }
-        if (!attached) {
-            stub->state().trackNotAttached();
-        }
-    }
+    TryAttachStub<ToBoolIRGenerator>("ToBool", cx, frame, stub, BaselineCacheIRStubKind::Regular, arg);
 
     bool cond = ToBoolean(arg);
     ret.setBoolean(cond);
 
     return true;
 }
 
 typedef bool (*pf)(JSContext*, BaselineFrame*, ICToBool_Fallback*, HandleValue,
@@ -1781,20 +1785,16 @@ DoGetElemFallback(JSContext* cx, Baselin
 
     // GetElem operations which could access negative indexes generally can't
     // be optimized without the potential for bailouts, as we can't statically
     // determine that an object has no properties on such indexes.
     if (rhs.isNumber() && rhs.toNumber() < 0) {
         stub->noteNegativeIndex();
     }
 
-    if (!attached && !isTemporarilyUnoptimizable) {
-        stub->noteUnoptimizableAccess();
-    }
-
     return true;
 }
 
 static bool
 DoGetElemSuperFallback(JSContext* cx, BaselineFrame* frame, ICGetElem_Fallback* stub_,
                        HandleValue lhs, HandleValue rhs, HandleValue receiver,
                        MutableHandleValue res)
 {
@@ -1862,20 +1862,16 @@ DoGetElemSuperFallback(JSContext* cx, Ba
 
     // GetElem operations which could access negative indexes generally can't
     // be optimized without the potential for bailouts, as we can't statically
     // determine that an object has no properties on such indexes.
     if (rhs.isNumber() && rhs.toNumber() < 0) {
         stub->noteNegativeIndex();
     }
 
-    if (!attached && !isTemporarilyUnoptimizable) {
-        stub->noteUnoptimizableAccess();
-    }
-
     return true;
 }
 
 typedef bool (*DoGetElemFallbackFn)(JSContext*, BaselineFrame*, ICGetElem_Fallback*,
                                     HandleValue, HandleValue, MutableHandleValue);
 static const VMFunction DoGetElemFallbackInfo =
     FunctionInfo<DoGetElemFallbackFn>(DoGetElemFallback, "DoGetElemFallback", TailCall,
                                       PopValues(2));
@@ -2058,24 +2054,25 @@ DoSetElemFallback(JSContext* cx, Baselin
     if (stub->state().canAttachStub()) {
         SetPropIRGenerator gen(cx, script, pc, CacheKind::SetElem, stub->state().mode(),
                                &isTemporarilyUnoptimizable, objv, index, rhs);
         if (gen.tryAttachAddSlotStub(oldGroup, oldShape)) {
             ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
                                                         BaselineCacheIRStubKind::Updated,
                                                         frame->script(), stub, &attached);
             if (newStub) {
+                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);
                 }
-
-                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();
         }
@@ -2243,38 +2240,17 @@ DoInFallback(JSContext* cx, BaselineFram
 
     FallbackICSpew(cx, stub, "In");
 
     if (!objValue.isObject()) {
         ReportInNotObjectError(cx, key, -2, objValue, -1);
         return false;
     }
 
-    if (stub->state().maybeTransition()) {
-        stub->discardStubs(cx);
-    }
-
-    if (stub->state().canAttachStub()) {
-        RootedScript script(cx, frame->script());
-        jsbytecode* pc = stub->icEntry()->pc(script);
-
-        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,
-                                                        script, stub, &attached);
-            if (newStub) {
-                JitSpew(JitSpew_BaselineIC, "  Attached In CacheIR stub");
-            }
-        }
-        if (!attached) {
-            stub->state().trackNotAttached();
-        }
-    }
+    TryAttachStub<HasPropIRGenerator>("In", cx, frame, stub, BaselineCacheIRStubKind::Regular, CacheKind::In, key, objValue);
 
     RootedObject obj(cx, &objValue.toObject());
     bool cond = false;
     if (!OperatorIn(cx, key, obj, &cond)) {
         return false;
     }
     res.setBoolean(cond);
 
@@ -2312,39 +2288,19 @@ static bool
 DoHasOwnFallback(JSContext* cx, BaselineFrame* frame, ICHasOwn_Fallback* stub_,
                  HandleValue keyValue, HandleValue objValue, MutableHandleValue res)
 {
     // This fallback stub may trigger debug mode toggling.
     DebugModeOSRVolatileStub<ICIn_Fallback*> stub(frame, stub_);
 
     FallbackICSpew(cx, stub, "HasOwn");
 
-    if (stub->state().maybeTransition()) {
-        stub->discardStubs(cx);
-    }
-
-    if (stub->state().canAttachStub()) {
-        RootedScript script(cx, frame->script());
-        jsbytecode* pc = stub->icEntry()->pc(script);
-
-        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,
-                                                        script, stub, &attached);
-            if (newStub) {
-                JitSpew(JitSpew_BaselineIC, "  Attached HasOwn CacheIR stub");
-            }
-        }
-        if (!attached) {
-            stub->state().trackNotAttached();
-        }
-    }
+    TryAttachStub<HasPropIRGenerator>("HasOwn", cx, frame, stub,
+        BaselineCacheIRStubKind::Regular, CacheKind::HasOwn,
+        keyValue, objValue);
 
     bool found;
     if (!HasOwnProperty(cx, objValue, keyValue, &found)) {
         return false;
     }
 
     res.setBoolean(found);
     return true;
@@ -2388,36 +2344,18 @@ DoGetNameFallback(JSContext* cx, Baselin
     RootedScript script(cx, frame->script());
     jsbytecode* pc = stub->icEntry()->pc(script);
     mozilla::DebugOnly<JSOp> op = JSOp(*pc);
     FallbackICSpew(cx, stub, "GetName(%s)", CodeName[JSOp(*pc)]);
 
     MOZ_ASSERT(op == JSOP_GETNAME || op == JSOP_GETGNAME);
 
     RootedPropertyName name(cx, script->getName(pc));
-    bool attached = false;
-
-    if (stub->state().maybeTransition()) {
-        stub->discardStubs(cx);
-    }
-
-    if (stub->state().canAttachStub()) {
-        GetNameIRGenerator gen(cx, script, pc, stub->state().mode(), envChain, name);
-        if (gen.tryAttachStub()) {
-            ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
-                                                        BaselineCacheIRStubKind::Monitored,
-                                                        script, stub, &attached);
-            if (newStub) {
-                JitSpew(JitSpew_BaselineIC, "  Attached GetName CacheIR stub");
-            }
-        }
-        if (!attached) {
-            stub->state().trackNotAttached();
-        }
-    }
+
+    TryAttachStub<GetNameIRGenerator>("GetName", cx, frame, stub, BaselineCacheIRStubKind::Monitored, envChain, name);
 
     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) {
         if (!GetEnvironmentName<GetNameMode::TypeOf>(cx, envChain, name, res)) {
             return false;
         }
     } else {
@@ -2434,19 +2372,16 @@ DoGetNameFallback(JSContext* cx, Baselin
         return true;
     }
 
     // Add a type monitor stub for the resulting value.
     if (!stub->addMonitorStubForValue(cx, frame, types, res)) {
         return false;
     }
 
-    if (!attached) {
-        stub->noteUnoptimizableAccess();
-    }
     return true;
 }
 
 typedef bool (*DoGetNameFallbackFn)(JSContext*, BaselineFrame*, ICGetName_Fallback*,
                                     HandleObject, MutableHandleValue);
 static const VMFunction DoGetNameFallbackInfo =
     FunctionInfo<DoGetNameFallbackFn>(DoGetNameFallback, "DoGetNameFallback", TailCall);
 
@@ -2475,36 +2410,17 @@ DoBindNameFallback(JSContext* cx, Baseli
     jsbytecode* pc = stub->icEntry()->pc(frame->script());
     mozilla::DebugOnly<JSOp> op = JSOp(*pc);
     FallbackICSpew(cx, stub, "BindName(%s)", CodeName[JSOp(*pc)]);
 
     MOZ_ASSERT(op == JSOP_BINDNAME || op == JSOP_BINDGNAME);
 
     RootedPropertyName name(cx, frame->script()->getName(pc));
 
-    if (stub->state().maybeTransition()) {
-        stub->discardStubs(cx);
-    }
-
-    if (stub->state().canAttachStub()) {
-        bool attached = false;
-        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,
-                                                        script, stub, &attached);
-            if (newStub) {
-                JitSpew(JitSpew_BaselineIC, "  Attached BindName CacheIR stub");
-            }
-        }
-        if (!attached) {
-            stub->state().trackNotAttached();
-        }
-    }
+    TryAttachStub<BindNameIRGenerator>("BindName", cx, frame, stub, BaselineCacheIRStubKind::Regular, envChain, name);
 
     RootedObject scope(cx);
     if (!LookupNameUnqualified(cx, name, envChain, &scope)) {
         return false;
     }
 
     res.setObject(*scope);
     return true;
@@ -2557,36 +2473,17 @@ DoGetIntrinsicFallback(JSContext* cx, Ba
 
     TypeScript::Monitor(cx, script, pc, res);
 
     // Check if debug mode toggling made the stub invalid.
     if (stub.invalid()) {
         return true;
     }
 
-    if (stub->state().maybeTransition()) {
-        stub->discardStubs(cx);
-    }
-
-    if (stub->state().canAttachStub()) {
-        bool attached = false;
-        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,
-                                                        script, stub, &attached);
-            if (newStub) {
-                JitSpew(JitSpew_BaselineIC, "  Attached GetIntrinsic CacheIR stub");
-            }
-        }
-        if (!attached) {
-            stub->state().trackNotAttached();
-        }
-    }
+    TryAttachStub<GetIntrinsicIRGenerator>("GetIntrinsic", cx, frame, stub, BaselineCacheIRStubKind::Regular, res);
 
     return true;
 }
 
 typedef bool (*DoGetIntrinsicFallbackFn)(JSContext*, BaselineFrame*, ICGetIntrinsic_Fallback*,
                                          MutableHandleValue);
 static const VMFunction DoGetIntrinsicFallbackInfo =
     FunctionInfo<DoGetIntrinsicFallbackFn>(DoGetIntrinsicFallback, "DoGetIntrinsicFallback",
@@ -2674,17 +2571,17 @@ DoGetPropFallback(JSContext* cx, Baselin
         GetPropIRGenerator gen(cx, script, pc, CacheKind::GetProp, stub->state().mode(),
                                &isTemporarilyUnoptimizable, val, idVal, val,
                                GetPropertyResultFlags::All);
         if (gen.tryAttachStub()) {
             ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
                                                         BaselineCacheIRStubKind::Monitored,
                                                         script, stub, &attached);
             if (newStub) {
-                JitSpew(JitSpew_BaselineIC, "  Attached CacheIR stub");
+                JitSpew(JitSpew_BaselineIC, "  Attached GetProp CacheIR stub");
                 if (gen.shouldNotePreliminaryObjectStub()) {
                     newStub->toCacheIR_Monitored()->notePreliminaryObject();
                 } else if (gen.shouldUnlinkPreliminaryObjectStubs()) {
                     StripPreliminaryObjectStubs(cx, stub);
                 }
             }
         }
         if (!attached && !isTemporarilyUnoptimizable) {
@@ -2703,26 +2600,16 @@ DoGetPropFallback(JSContext* cx, Baselin
     if (stub.invalid()) {
         return true;
     }
 
     // Add a type monitor stub for the resulting value.
     if (!stub->addMonitorStubForValue(cx, frame, types, res)) {
         return false;
     }
-
-    if (attached) {
-        return true;
-    }
-
-    MOZ_ASSERT(!attached);
-    if (!isTemporarilyUnoptimizable) {
-        stub->noteUnoptimizableAccess();
-    }
-
     return true;
 }
 
 static bool
 DoGetPropSuperFallback(JSContext* cx, BaselineFrame* frame, ICGetProp_Fallback* stub_,
                        HandleValue receiver, MutableHandleValue val, MutableHandleValue res)
 {
     // This fallback stub may trigger debug mode toggling.
@@ -2752,17 +2639,17 @@ DoGetPropSuperFallback(JSContext* cx, Ba
         GetPropIRGenerator gen(cx, script, pc, CacheKind::GetPropSuper, stub->state().mode(),
                                &isTemporarilyUnoptimizable, val, idVal, receiver,
                                GetPropertyResultFlags::All);
         if (gen.tryAttachStub()) {
             ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
                                                         BaselineCacheIRStubKind::Monitored,
                                                         script, stub, &attached);
             if (newStub) {
-                JitSpew(JitSpew_BaselineIC, "  Attached CacheIR stub");
+                JitSpew(JitSpew_BaselineIC, "  Attached GetPropSuper CacheIR stub");
                 if (gen.shouldNotePreliminaryObjectStub()) {
                     newStub->toCacheIR_Monitored()->notePreliminaryObject();
                 } else if (gen.shouldUnlinkPreliminaryObjectStubs()) {
                     StripPreliminaryObjectStubs(cx, stub);
                 }
             }
         }
         if (!attached && !isTemporarilyUnoptimizable) {
@@ -2784,25 +2671,16 @@ DoGetPropSuperFallback(JSContext* cx, Ba
         return true;
     }
 
     // Add a type monitor stub for the resulting value.
     if (!stub->addMonitorStubForValue(cx, frame, types, res)) {
         return false;
     }
 
-    if (attached) {
-        return true;
-    }
-
-    MOZ_ASSERT(!attached);
-    if (!isTemporarilyUnoptimizable) {
-        stub->noteUnoptimizableAccess();
-    }
-
     return true;
 }
 
 typedef bool (*DoGetPropFallbackFn)(JSContext*, BaselineFrame*, ICGetProp_Fallback*,
                                     MutableHandleValue, MutableHandleValue);
 static const VMFunction DoGetPropFallbackInfo =
     FunctionInfo<DoGetPropFallbackFn>(DoGetPropFallback, "DoGetPropFallback", TailCall,
                                       PopValues(1));
@@ -3020,37 +2898,34 @@ DoSetPropFallback(JSContext* cx, Baselin
         RootedValue idVal(cx, StringValue(name));
         SetPropIRGenerator gen(cx, script, pc, CacheKind::SetProp, stub->state().mode(),
                                &isTemporarilyUnoptimizable, lhs, idVal, rhs);
         if (gen.tryAttachAddSlotStub(oldGroup, oldShape)) {
             ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
                                                         BaselineCacheIRStubKind::Updated,
                                                         frame->script(), stub, &attached);
             if (newStub) {
+                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);
                 }
-
-                JitSpew(JitSpew_BaselineIC, "  Attached SetProp CacheIR stub");
-                SetUpdateStubData(newStub->toCacheIR_Updated(), gen.typeCheckInfo());
             }
         } else {
             gen.trackAttached(IRGenerator::NotAttached);
         }
         if (!attached && !isTemporarilyUnoptimizable) {
             stub->state().trackNotAttached();
         }
     }
 
-    if (!attached && !isTemporarilyUnoptimizable) {
-        stub->noteUnoptimizableAccess();
-    }
-
     return true;
 }
 
 typedef bool (*DoSetPropFallbackFn)(JSContext*, BaselineFrame*, ICSetProp_Fallback*, Value*,
                                     HandleValue, HandleValue);
 static const VMFunction DoSetPropFallbackInfo =
     FunctionInfo<DoSetPropFallbackFn>(DoSetPropFallback, "DoSetPropFallback", TailCall,
                                       PopValues(1));
@@ -3838,17 +3713,16 @@ DoCallFallback(JSContext* cx, BaselineFr
         // optimized ConstStringSplit stub. Note that vp[0] now holds the return value
         // instead of the callee, so we pass the callee as well.
         if (!TryAttachConstStringSplit(cx, stub, script, argc, callee, vp, pc, res, &handled)) {
             return false;
         }
     }
 
     if (!handled) {
-        stub->noteUnoptimizableCall();
         if (canAttachStub) {
             stub->state().trackNotAttached();
         }
     }
     return true;
 }
 
 static bool
@@ -3891,19 +3765,16 @@ DoSpreadCallFallback(JSContext* cx, Base
     }
 
     // Add a type monitor stub for the resulting value.
     StackTypeSet* types = TypeScript::BytecodeTypes(script, pc);
     if (!stub->addMonitorStubForValue(cx, frame, types, res)) {
         return false;
     }
 
-    if (!handled) {
-        stub->noteUnoptimizableCall();
-    }
     return true;
 }
 
 void
 ICCallStubCompiler::pushCallArguments(MacroAssembler& masm, AllocatableGeneralRegisterSet regs,
                                       Register argcReg, bool isJitCall, bool isConstructing)
 {
     MOZ_ASSERT(!regs.has(argcReg));
@@ -5390,38 +5261,17 @@ ICTableSwitch::fixupJumpTable(JSScript* 
 //
 
 static bool
 DoGetIteratorFallback(JSContext* cx, BaselineFrame* frame, ICGetIterator_Fallback* stub,
                       HandleValue value, MutableHandleValue res)
 {
     FallbackICSpew(cx, stub, "GetIterator");
 
-    if (stub->state().maybeTransition()) {
-        stub->discardStubs(cx);
-    }
-
-    if (stub->state().canAttachStub()) {
-        RootedScript script(cx, frame->script());
-        jsbytecode* pc = stub->icEntry()->pc(script);
-
-        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,
-                                                        script, stub, &attached);
-            if (newStub) {
-                JitSpew(JitSpew_BaselineIC, "  Attached GetIterator CacheIR stub");
-            }
-        }
-        if (!attached) {
-            stub->state().trackNotAttached();
-        }
-    }
+    TryAttachStub<GetIteratorIRGenerator>("GetIterator", cx, frame, stub, BaselineCacheIRStubKind::Regular, value);
 
     JSObject* iterobj = ValueToIterator(cx, value);
     if (!iterobj) {
         return false;
     }
 
     res.setObject(*iterobj);
     return true;
@@ -5581,51 +5431,16 @@ ICIteratorClose_Fallback::Compiler::gene
     return tailCallVM(DoIteratorCloseFallbackInfo, masm);
 }
 
 //
 // InstanceOf_Fallback
 //
 
 static bool
-TryAttachInstanceOfStub(JSContext* cx, BaselineFrame* frame, ICInstanceOf_Fallback* stub,
-                        HandleValue lhs, HandleObject rhs, bool* attached)
-{
-    MOZ_ASSERT(!*attached);
-    FallbackICSpew(cx, stub, "InstanceOf");
-
-    if (stub->state().maybeTransition()) {
-        stub->discardStubs(cx);
-    }
-
-    if (stub->state().canAttachStub()) {
-        RootedScript script(cx, frame->script());
-        jsbytecode* pc = stub->icEntry()->pc(script);
-
-        InstanceOfIRGenerator gen(cx, script, pc, stub->state().mode(),
-                                  lhs,
-                                  rhs);
-
-        if (gen.tryAttachStub()) {
-            ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
-                                                        BaselineCacheIRStubKind::Regular,
-                                                        script, stub, attached);
-            if (newStub) {
-                JitSpew(JitSpew_BaselineIC, "  Attached InstanceOf CacheIR stub, attached is now %d", *attached);
-            }
-        }
-        if (!attached) {
-            stub->state().trackNotAttached();
-        }
-    }
-
-    return true;
-}
-
-static bool
 DoInstanceOfFallback(JSContext* cx, BaselineFrame* frame, ICInstanceOf_Fallback* stub_,
                      HandleValue lhs, HandleValue rhs, MutableHandleValue res)
 {
     // This fallback stub may trigger debug mode toggling.
     DebugModeOSRVolatileStub<ICInstanceOf_Fallback*> stub(frame, stub_);
 
     FallbackICSpew(cx, stub, "InstanceOf");
 
@@ -5643,31 +5458,28 @@ DoInstanceOfFallback(JSContext* cx, Base
     res.setBoolean(cond);
 
     // Check if debug mode toggling made the stub invalid.
     if (stub.invalid()) {
         return true;
     }
 
     if (!obj->is<JSFunction>()) {
-        stub->noteUnoptimizableAccess();
+        // ensure we've recorded at least one failure, so we can detect there was a non-optimizable case
+        if (!stub->state().hasFailures()) {
+            stub->state().trackNotAttached();
+        }
         return true;
     }
 
     // For functions, keep track of the |prototype| property in type information,
     // for use during Ion compilation.
     EnsureTrackPropertyTypes(cx, obj, NameToId(cx->names().prototype));
 
-    bool attached = false;
-    if (!TryAttachInstanceOfStub(cx, frame, stub, lhs, obj, &attached)) {
-        return false;
-    }
-    if (!attached) {
-        stub->noteUnoptimizableAccess();
-    }
+    TryAttachStub<InstanceOfIRGenerator>("InstanceOf", cx, frame, stub, BaselineCacheIRStubKind::Regular, lhs, obj);
     return true;
 }
 
 typedef bool (*DoInstanceOfFallbackFn)(JSContext*, BaselineFrame*, ICInstanceOf_Fallback*,
                                        HandleValue, HandleValue, MutableHandleValue);
 static const VMFunction DoInstanceOfFallbackInfo =
     FunctionInfo<DoInstanceOfFallbackFn>(DoInstanceOfFallback, "DoInstanceOfFallback", TailCall,
                                          PopValues(2));
@@ -5694,38 +5506,17 @@ ICInstanceOf_Fallback::Compiler::generat
 //
 
 static bool
 DoTypeOfFallback(JSContext* cx, BaselineFrame* frame, ICTypeOf_Fallback* stub, HandleValue val,
                  MutableHandleValue res)
 {
     FallbackICSpew(cx, stub, "TypeOf");
 
-    if (stub->state().maybeTransition()) {
-        stub->discardStubs(cx);
-    }
-
-    if (stub->state().canAttachStub()) {
-        RootedScript script(cx, frame->script());
-        jsbytecode* pc = stub->icEntry()->pc(script);
-
-        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,
-                                                        script, stub, &attached);
-            if (newStub) {
-                JitSpew(JitSpew_BaselineIC, "  Attached TypeOf CacheIR stub");
-            }
-        }
-        if (!attached) {
-            stub->state().trackNotAttached();
-        }
-    }
+    TryAttachStub<TypeOfIRGenerator>("TypeOf", cx, frame, stub, BaselineCacheIRStubKind::Regular, val);
 
     JSType type = js::TypeOfValue(val);
     RootedString string(cx, TypeName(type, cx->names()));
     res.setString(string);
     return true;
 }
 
 typedef bool (*DoTypeOfFallbackFn)(JSContext*, BaselineFrame* frame, ICTypeOf_Fallback*,
@@ -6056,34 +5847,17 @@ DoUnaryArithFallback(JSContext* cx, Base
     if (debug_stub.invalid()) {
         return true;
     }
 
     if (res.isDouble()) {
         stub->setSawDoubleResult();
     }
 
-    if (stub->state().maybeTransition()) {
-        stub->discardStubs(cx);
-    }
-
-    if (stub->state().canAttachStub()) {
-        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,
-                                                        script, stub, &attached);
-            if (newStub) {
-                JitSpew(JitSpew_BaselineIC, "  Attached UnaryArith CacheIR stub for %s", CodeName[op]);
-            }
-        }
-    }
-
+    TryAttachStub<UnaryArithIRGenerator>("UniaryArith", cx, frame, stub, BaselineCacheIRStubKind::Regular, op, val, res);
     return true;
 }
 
 typedef bool (*DoUnaryArithFallbackFn)(JSContext*, BaselineFrame*, ICUnaryArith_Fallback*,
                                        HandleValue, MutableHandleValue);
 static const VMFunction DoUnaryArithFallbackInfo =
     FunctionInfo<DoUnaryArithFallbackFn>(DoUnaryArithFallback, "DoUnaryArithFallback", TailCall,
                                          PopValues(1));
@@ -6207,45 +5981,17 @@ DoBinaryArithFallback(JSContext* cx, Bas
     if (stub.invalid()) {
         return true;
     }
 
     if (ret.isDouble()) {
         stub->setSawDoubleResult();
     }
 
-    // Check if debug mode toggling made the stub invalid.
-    if (stub.invalid()) {
-        return true;
-    }
-
-    if (ret.isDouble()) {
-        stub->setSawDoubleResult();
-    }
-
-    if (stub->state().maybeTransition()) {
-        stub->discardStubs(cx);
-    }
-
-    if (stub->state().canAttachStub()) {
-        BinaryArithIRGenerator gen(cx, script, pc, stub->state().mode(),
-                                   op, lhs, rhs, ret);
-        if (gen.tryAttachStub()) {
-            bool attached = false;
-            ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
-                                                        BaselineCacheIRStubKind::Regular,
-                                                        script, stub, &attached);
-            if (newStub) {
-                JitSpew(JitSpew_BaselineIC, "  Attached BinaryArith CacheIR stub for %s", CodeName[op]);
-            }
-
-        } else {
-            stub->noteUnoptimizableOperands();
-        }
-    }
+    TryAttachStub<BinaryArithIRGenerator>("BinaryArith", cx, frame, stub, BaselineCacheIRStubKind::Regular, op, lhs, rhs, ret);
     return true;
 }
 
 typedef bool (*DoBinaryArithFallbackFn)(JSContext*, BaselineFrame*, ICBinaryArith_Fallback*,
                                         HandleValue, HandleValue, MutableHandleValue);
 static const VMFunction DoBinaryArithFallbackInfo =
     FunctionInfo<DoBinaryArithFallbackFn>(DoBinaryArithFallback, "DoBinaryArithFallback",
                                           TailCall, PopValues(2));
@@ -6347,38 +6093,17 @@ DoCompareFallback(JSContext* cx, Baselin
 
     ret.setBoolean(out);
 
     // Check if debug mode toggling made the stub invalid.
     if (stub.invalid()) {
         return true;
     }
 
-    // Check to see if a new stub should be generated.
-    if (stub->numOptimizedStubs() >= ICCompare_Fallback::MAX_OPTIMIZED_STUBS) {
-        // TODO: Discard all stubs in this IC and replace with inert megamorphic stub.
-        // But for now we just bail.
-        return true;
-    }
-
-    if (stub->state().canAttachStub()) {
-        CompareIRGenerator gen(cx, script, pc, stub->state().mode(), op, lhs, rhs);
-        bool attached = false;
-        if (gen.tryAttachStub()) {
-            ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
-                                                        BaselineCacheIRStubKind::Regular,
-                                                        script, stub, &attached);
-            if (newStub) {
-                    JitSpew(JitSpew_BaselineIC, "  Attached CacheIR stub");
-            }
-            return true;
-        }
-    }
-
-    stub->noteUnoptimizableAccess();
+    TryAttachStub<CompareIRGenerator>("Compare", cx, frame, stub, BaselineCacheIRStubKind::Regular, op, lhs, rhs);
     return true;
 }
 
 typedef bool (*DoCompareFallbackFn)(JSContext*, BaselineFrame*, ICCompare_Fallback*,
                                     HandleValue, HandleValue, MutableHandleValue);
 static const VMFunction DoCompareFallbackInfo =
     FunctionInfo<DoCompareFallbackFn>(DoCompareFallback, "DoCompareFallback", TailCall,
                                       PopValues(2));
@@ -6481,28 +6206,18 @@ DoNewObject(JSContext* cx, BaselineFrame
         if (obj && !obj->isSingleton() &&
             !obj->group()->maybePreliminaryObjectsDontCheckGeneration())
         {
             templateObject = NewObjectOperation(cx, script, pc, TenuredObject);
             if (!templateObject) {
                 return false;
             }
 
-            if (!JitOptions.disableCacheIR) {
-                bool attached = false;
-                NewObjectIRGenerator gen(cx, script, pc, stub->state().mode(), JSOp(*pc), templateObject);
-                if (gen.tryAttachStub()) {
-                    ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
-                                                                BaselineCacheIRStubKind::Regular,
-                                                                script, stub, &attached);
-                    if (newStub) {
-                        JitSpew(JitSpew_BaselineIC, "  NewObject Attached CacheIR stub");
-                    }
-                }
-            }
+            TryAttachStub<NewObjectIRGenerator>("NewObject", cx, frame, stub, BaselineCacheIRStubKind::Regular, JSOp(*pc), templateObject);
+
             stub->setTemplateObject(templateObject);
         }
     }
 
     if (!obj) {
         return false;
     }
 
--- a/js/src/jit/BaselineIC.h
+++ b/js/src/jit/BaselineIC.h
@@ -1792,31 +1792,24 @@ class ICGetElem_Fallback : public ICMoni
 {
     friend class ICStubSpace;
 
     explicit ICGetElem_Fallback(JitCode* stubCode)
       : ICMonitoredFallbackStub(ICStub::GetElem_Fallback, stubCode)
     { }
 
     static const uint16_t EXTRA_NEGATIVE_INDEX = 0x1;
-    static const uint16_t EXTRA_UNOPTIMIZABLE_ACCESS = 0x2;
 
   public:
     void noteNegativeIndex() {
         extra_ |= EXTRA_NEGATIVE_INDEX;
     }
     bool hasNegativeIndex() const {
         return extra_ & EXTRA_NEGATIVE_INDEX;
     }
-    void noteUnoptimizableAccess() {
-        extra_ |= EXTRA_UNOPTIMIZABLE_ACCESS;
-    }
-    bool hadUnoptimizableAccess() const {
-        return extra_ & EXTRA_UNOPTIMIZABLE_ACCESS;
-    }
 
     // Compiler for this stub kind.
     class Compiler : public ICStubCompiler {
       protected:
         bool hasReceiver_;
         MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override;
 
         virtual int32_t getKey() const override {
@@ -1933,25 +1926,16 @@ class ICGetName_Fallback : public ICMoni
 {
     friend class ICStubSpace;
 
     explicit ICGetName_Fallback(JitCode* stubCode)
       : ICMonitoredFallbackStub(ICStub::GetName_Fallback, stubCode)
     { }
 
   public:
-    static const size_t UNOPTIMIZABLE_ACCESS_BIT = 0;
-
-    void noteUnoptimizableAccess() {
-        extra_ |= (1u << UNOPTIMIZABLE_ACCESS_BIT);
-    }
-    bool hadUnoptimizableAccess() const {
-        return extra_ & (1u << UNOPTIMIZABLE_ACCESS_BIT);
-    }
-
     class Compiler : public ICStubCompiler {
       protected:
         MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override;
 
       public:
         explicit Compiler(JSContext* cx)
           : ICStubCompiler(cx, ICStub::GetName_Fallback)
         { }
@@ -2022,26 +2006,18 @@ class ICGetProp_Fallback : public ICMoni
 {
     friend class ICStubSpace;
 
     explicit ICGetProp_Fallback(JitCode* stubCode)
       : ICMonitoredFallbackStub(ICStub::GetProp_Fallback, stubCode)
     { }
 
   public:
-    static const size_t UNOPTIMIZABLE_ACCESS_BIT = 0;
     static const size_t ACCESSED_GETTER_BIT = 1;
 
-    void noteUnoptimizableAccess() {
-        extra_ |= (1u << UNOPTIMIZABLE_ACCESS_BIT);
-    }
-    bool hadUnoptimizableAccess() const {
-        return extra_ & (1u << UNOPTIMIZABLE_ACCESS_BIT);
-    }
-
     void noteAccessedGetter() {
         extra_ |= (1u << ACCESSED_GETTER_BIT);
     }
     bool hasAccessedGetter() const {
         return extra_ & (1u << ACCESSED_GETTER_BIT);
     }
 
     class Compiler : public ICStubCompiler {
@@ -2079,24 +2055,16 @@ class ICSetProp_Fallback : public ICFall
 {
     friend class ICStubSpace;
 
     explicit ICSetProp_Fallback(JitCode* stubCode)
       : ICFallbackStub(ICStub::SetProp_Fallback, stubCode)
     { }
 
   public:
-    static const size_t UNOPTIMIZABLE_ACCESS_BIT = 0;
-    void noteUnoptimizableAccess() {
-        extra_ |= (1u << UNOPTIMIZABLE_ACCESS_BIT);
-    }
-    bool hadUnoptimizableAccess() const {
-        return extra_ & (1u << UNOPTIMIZABLE_ACCESS_BIT);
-    }
-
     class Compiler : public ICStubCompiler {
       protected:
         CodeOffset bailoutReturnOffset_;
         MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override;
         void postGenerateStubCode(MacroAssembler& masm, Handle<JitCode*> code) override;
 
       public:
         explicit Compiler(JSContext* cx)
@@ -2144,33 +2112,24 @@ class ICCallStubCompiler : public ICStub
     void pushArrayArguments(MacroAssembler& masm, Address arrayVal,
                             AllocatableGeneralRegisterSet regs);
 };
 
 class ICCall_Fallback : public ICMonitoredFallbackStub
 {
     friend class ICStubSpace;
   public:
-    static const unsigned UNOPTIMIZABLE_CALL_FLAG = 0x1;
-
     static const uint32_t MAX_OPTIMIZED_STUBS = 16;
 
   private:
     explicit ICCall_Fallback(JitCode* stubCode)
       : ICMonitoredFallbackStub(ICStub::Call_Fallback, stubCode)
     {}
 
   public:
-    void noteUnoptimizableCall() {
-        extra_ |= UNOPTIMIZABLE_CALL_FLAG;
-    }
-    bool hadUnoptimizableCall() const {
-        return extra_ & UNOPTIMIZABLE_CALL_FLAG;
-    }
-
     bool scriptedStubsAreGeneralized() const {
         return hasStub(Call_AnyScripted);
     }
     bool nativeStubsAreGeneralized() const {
         // Return hasStub(Call_AnyNative) after Call_AnyNative stub is added.
         return false;
     }
 
@@ -2851,27 +2810,17 @@ class ICIteratorClose_Fallback : public 
 class ICInstanceOf_Fallback : public ICFallbackStub
 {
     friend class ICStubSpace;
 
     explicit ICInstanceOf_Fallback(JitCode* stubCode)
       : ICFallbackStub(ICStub::InstanceOf_Fallback, stubCode)
     { }
 
-    static const uint16_t UNOPTIMIZABLE_ACCESS_BIT = 0x1;
-
   public:
-
-    void noteUnoptimizableAccess() {
-        extra_ |= UNOPTIMIZABLE_ACCESS_BIT;
-    }
-    bool hadUnoptimizableAccess() const {
-        return extra_ & UNOPTIMIZABLE_ACCESS_BIT;
-    }
-
     class Compiler : public ICStubCompiler {
       protected:
         MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override;
 
       public:
         explicit Compiler(JSContext* cx)
           : ICStubCompiler(cx, ICStub::InstanceOf_Fallback)
         { }
@@ -3066,26 +3015,16 @@ class ICUnaryArith_Fallback : public ICF
 class ICCompare_Fallback : public ICFallbackStub
 {
     friend class ICStubSpace;
 
     explicit ICCompare_Fallback(JitCode* stubCode)
       : ICFallbackStub(ICStub::Compare_Fallback, stubCode) {}
 
   public:
-    static const uint32_t MAX_OPTIMIZED_STUBS = 8;
-
-    static const size_t UNOPTIMIZABLE_ACCESS_BIT = 0;
-    void noteUnoptimizableAccess() {
-        extra_ |= (1u << UNOPTIMIZABLE_ACCESS_BIT);
-    }
-    bool hadUnoptimizableAccess() const {
-        return extra_ & (1u << UNOPTIMIZABLE_ACCESS_BIT);
-    }
-
     // Compiler for this stub kind.
     class Compiler : public ICStubCompiler {
       protected:
         MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override;
 
       public:
         explicit Compiler(JSContext* cx)
           : ICStubCompiler(cx, ICStub::Compare_Fallback) {}
@@ -3108,33 +3047,26 @@ class ICBinaryArith_Fallback : public IC
 
     explicit ICBinaryArith_Fallback(JitCode* stubCode)
       : ICFallbackStub(BinaryArith_Fallback, stubCode)
     {
         extra_ = 0;
     }
 
     static const uint16_t SAW_DOUBLE_RESULT_BIT = 0x1;
-    static const uint16_t UNOPTIMIZABLE_OPERANDS_BIT = 0x2;
 
   public:
     static const uint32_t MAX_OPTIMIZED_STUBS = 8;
 
     bool sawDoubleResult() const {
         return extra_ & SAW_DOUBLE_RESULT_BIT;
     }
     void setSawDoubleResult() {
         extra_ |= SAW_DOUBLE_RESULT_BIT;
     }
-    bool hadUnoptimizableOperands() const {
-        return extra_ & UNOPTIMIZABLE_OPERANDS_BIT;
-    }
-    void noteUnoptimizableOperands() {
-        extra_ |= UNOPTIMIZABLE_OPERANDS_BIT;
-    }
 
     // Compiler for this stub kind.
     class Compiler : public ICStubCompiler {
       protected:
         MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override;
 
       public:
         explicit Compiler(JSContext* cx)
--- a/js/src/jit/BaselineInspector.cpp
+++ b/js/src/jit/BaselineInspector.cpp
@@ -279,24 +279,18 @@ BaselineInspector::maybeInfoForPropertyO
 
         if (!AddReceiver(receiver, receivers, convertUnboxedGroups)) {
             return false;
         }
 
         stub = stub->next();
     }
 
-    if (stub->isGetProp_Fallback()) {
-        if (stub->toGetProp_Fallback()->hadUnoptimizableAccess()) {
-            receivers.clear();
-        }
-    } else {
-        if (stub->toSetProp_Fallback()->hadUnoptimizableAccess()) {
-            receivers.clear();
-        }
+    if (stub->toFallbackStub()->state().hasFailures()) {
+        receivers.clear();
     }
 
     // Don't inline if there are more than 5 receivers.
     if (receivers.length() > 5) {
         receivers.clear();
     }
 
     return true;
@@ -614,17 +608,17 @@ BaselineInspector::expectedCompareType(j
     ICStub* first = monomorphicStub(pc);
     ICStub* second = nullptr;
     if (!first && !dimorphicStub(pc, &first, &second)) {
         return MCompare::Compare_Unknown;
     }
 
     if (ICStub* fallback = second ? second->next() : first->next()) {
         MOZ_ASSERT(fallback->isFallback());
-        if (fallback->toCompare_Fallback()->hadUnoptimizableAccess()) {
+        if (fallback->toFallbackStub()->state().hasFailures()) {
             return MCompare::Compare_Unknown;
         }
     }
 
     MCompare::CompareType first_type = ParseCacheIRStubForCompareType(first->toCacheIR_Regular());
     if (!second) {
         return first_type;
     }
@@ -692,19 +686,18 @@ BaselineInspector::expectedBinaryArithSp
     if (!hasBaselineScript()) {
         return MIRType::None;
     }
 
     MIRType result;
     ICStub* stubs[2];
 
     const ICEntry& entry = icEntryFromPC(pc);
-    ICStub* stub = entry.fallbackStub();
-    if (stub->isBinaryArith_Fallback() &&
-        stub->toBinaryArith_Fallback()->hadUnoptimizableOperands())
+    ICFallbackStub* stub = entry.fallbackStub();
+    if (stub->state().hasFailures())
     {
         return MIRType::None;
     }
 
     stubs[0] = monomorphicStub(pc);
     if (stubs[0]) {
         if (TryToSpecializeBinaryArithOp(stubs, 1, &result)) {
             return result;
@@ -842,17 +835,17 @@ BaselineInspector::getSingleCallee(jsbyt
 
     if (!hasBaselineScript()) {
         return nullptr;
     }
 
     const ICEntry& entry = icEntryFromPC(pc);
     ICStub* stub = entry.firstStub();
 
-    if (entry.fallbackStub()->toCall_Fallback()->hadUnoptimizableCall()) {
+    if (entry.fallbackStub()->state().hasFailures()) {
         return nullptr;
     }
 
     if (!stub->isCall_Scripted() || stub->next() != entry.fallbackStub()) {
         return nullptr;
     }
 
     return stub->toCall_Scripted()->callee();
@@ -1244,23 +1237,19 @@ BaselineInspector::commonGetPropFunction
         if (stub->isCacheIR_Monitored()) {
             if (!AddCacheIRGetPropFunction(stub->toCacheIR_Monitored(), innerized,
                                            holder, holderShape,
                                            commonGetter, globalShape, isOwnProperty, receivers,
                                            convertUnboxedGroups, script))
             {
                 return false;
             }
-        } else if (stub->isGetProp_Fallback()) {
+        } else if (stub->isFallback()) {
             // If we have an unoptimizable access, don't try to optimize.
-            if (stub->toGetProp_Fallback()->hadUnoptimizableAccess()) {
-                return false;
-            }
-        } else if (stub->isGetName_Fallback()) {
-            if (stub->toGetName_Fallback()->hadUnoptimizableAccess()) {
+            if (stub->toFallbackStub()->state().hasFailures()) {
                 return false;
             }
         } else {
             return false;
         }
     }
 
     if (!*commonGetter) {
@@ -1329,30 +1318,21 @@ BaselineInspector::megamorphicGetterSett
                                                    stub->toCacheIR_Updated()->stubInfo(),
                                                    isGetter);
             if (!setter || (*getterOrSetter && *getterOrSetter != setter)) {
                 return false;
             }
             *getterOrSetter = setter;
             continue;
         }
-        if (stub->isGetProp_Fallback()) {
-            if (stub->toGetProp_Fallback()->hadUnoptimizableAccess()) {
-                return false;
-            }
-            if (stub->toGetProp_Fallback()->state().mode() != ICState::Mode::Megamorphic) {
+        if (stub->isFallback()) {
+            if (stub->toFallbackStub()->state().hasFailures()) {
                 return false;
             }
-            continue;
-        }
-        if (stub->isSetProp_Fallback()) {
-            if (stub->toSetProp_Fallback()->hadUnoptimizableAccess()) {
-                return false;
-            }
-            if (stub->toSetProp_Fallback()->state().mode() != ICState::Mode::Megamorphic) {
+            if (stub->toFallbackStub()->state().mode() != ICState::Mode::Megamorphic) {
                 return false;
             }
             continue;
         }
 
         return false;
     }
 
@@ -1482,18 +1462,18 @@ BaselineInspector::commonSetPropFunction
         if (stub->isCacheIR_Updated()) {
             if (!AddCacheIRSetPropFunction(stub->toCacheIR_Updated(),
                                            holder, holderShape,
                                            commonSetter, isOwnProperty, receivers,
                                            convertUnboxedGroups))
             {
                 return false;
             }
-        } else if (!stub->isSetProp_Fallback() ||
-                   stub->toSetProp_Fallback()->hadUnoptimizableAccess())
+        } else if (!stub->isFallback() ||
+                   stub->toFallbackStub()->state().hasFailures())
         {
             // We have an unoptimizable access, so don't try to optimize.
             return false;
         }
     }
 
     if (!*commonSetter) {
         return false;
@@ -1592,17 +1572,17 @@ BaselineInspector::maybeInfoForProtoRead
 
         if (!AddReceiver(receiver, receivers, convertUnboxedGroups)) {
             return false;
         }
 
         stub = stub->next();
     }
 
-    if (stub->toGetProp_Fallback()->hadUnoptimizableAccess()) {
+    if (stub->toFallbackStub()->state().hasFailures()) {
         receivers.clear();
     }
 
     // Don't inline if there are more than 5 receivers.
     if (receivers.length() > 5) {
         receivers.clear();
     }
 
@@ -1636,38 +1616,28 @@ BaselineInspector::expectedPropertyAcces
     if (!hasBaselineScript()) {
         return MIRType::Value;
     }
 
     const ICEntry& entry = icEntryFromPC(pc);
     MIRType type = MIRType::None;
 
     for (ICStub* stub = entry.firstStub(); stub; stub = stub->next()) {
-        MIRType stubType;
-        switch (stub->kind()) {
-          case ICStub::GetProp_Fallback:
-            if (stub->toGetProp_Fallback()->hadUnoptimizableAccess()) {
-                return MIRType::Value;
-            }
-            continue;
-
-          case ICStub::GetElem_Fallback:
-            if (stub->toGetElem_Fallback()->hadUnoptimizableAccess()) {
-                return MIRType::Value;
-            }
-            continue;
-
-          case ICStub::CacheIR_Monitored:
+        MIRType stubType = MIRType::None;
+        if (stub->isCacheIR_Monitored()) {
             stubType = GetCacheIRExpectedInputType(stub->toCacheIR_Monitored());
             if (stubType == MIRType::Value) {
                 return MIRType::Value;
             }
-            break;
-
-          default:
+        } else if (stub->isGetElem_Fallback() || stub->isGetProp_Fallback()) {
+            // If we have an unoptimizable access, don't try to optimize.
+            if (stub->toFallbackStub()->state().hasFailures()) {
+                return MIRType::Value;
+            }
+        } else {
             MOZ_CRASH("Unexpected stub");
         }
 
         if (type != MIRType::None) {
             if (type != stubType) {
                 return MIRType::Value;
             }
         } else {
@@ -1689,17 +1659,17 @@ BaselineInspector::instanceOfData(jsbyte
 
     const ICEntry& entry = icEntryFromPC(pc);
     ICStub* firstStub = entry.firstStub();
 
     // Ensure singleton instanceof stub
     if (!firstStub->next() ||
         !firstStub->isCacheIR_Regular() ||
         !firstStub->next()->isInstanceOf_Fallback() ||
-         firstStub->next()->toInstanceOf_Fallback()->hadUnoptimizableAccess())
+         firstStub->next()->toInstanceOf_Fallback()->state().hasFailures())
          {
              return false;
          }
 
     ICCacheIR_Regular* stub = entry.firstStub()->toCacheIR_Regular();
     CacheIRReader reader(stub->stubInfo());
 
     ObjOperandId rhsId = ObjOperandId(1);
--- a/js/src/jit/CacheIR.cpp
+++ b/js/src/jit/CacheIR.cpp
@@ -2877,17 +2877,17 @@ BindNameIRGenerator::trackAttached(const
     if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
         sp.valueProperty("base", ObjectValue(*env_));
         sp.valueProperty("property", StringValue(name_));
     }
 #endif
 }
 
 HasPropIRGenerator::HasPropIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc,
-                                       CacheKind cacheKind, ICState::Mode mode,
+                                       ICState::Mode mode, CacheKind cacheKind,
                                        HandleValue idVal, HandleValue val)
   : IRGenerator(cx, script, pc, cacheKind, mode),
     val_(val),
     idVal_(idVal)
 { }
 
 bool
 HasPropIRGenerator::tryAttachDense(HandleObject obj, ObjOperandId objId,
--- a/js/src/jit/CacheIR.h
+++ b/js/src/jit/CacheIR.h
@@ -1819,18 +1819,18 @@ class MOZ_RAII HasPropIRGenerator : publ
                                HandleId key, ValOperandId keyId);
     bool tryAttachProxyElement(HandleObject obj, ObjOperandId objId,
                                ValOperandId keyId);
 
     void trackAttached(const char* name);
 
   public:
     // NOTE: Argument order is PROPERTY, OBJECT
-    HasPropIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc, CacheKind cacheKind,
-                       ICState::Mode mode, HandleValue idVal, HandleValue val);
+    HasPropIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc, ICState::Mode mode,
+                       CacheKind cacheKind, HandleValue idVal, HandleValue val);
 
     bool tryAttachStub();
 };
 
 class MOZ_RAII InstanceOfIRGenerator : public IRGenerator
 {
     HandleValue lhsVal_;
     HandleObject rhsObj_;
--- a/js/src/jit/ICState.h
+++ b/js/src/jit/ICState.h
@@ -59,16 +59,17 @@ class ICState
     ICState()
       : invalid_(false)
     {
         reset();
     }
 
     Mode mode() const { return mode_; }
     size_t numOptimizedStubs() const { return numOptimizedStubs_; }
+    bool hasFailures() const { return (numFailures_ != 0); }
 
     MOZ_ALWAYS_INLINE bool canAttachStub() const {
         // Note: we cannot assert that numOptimizedStubs_ <= MaxOptimizedStubs
         // because old-style baseline ICs may attach more stubs than
         // MaxOptimizedStubs allows.
         if (mode_ == Mode::Generic || JitOptions.disableCacheIR) {
             return false;
         }
@@ -105,17 +106,20 @@ class ICState
     }
     void trackAttached() {
         // We'd like to assert numOptimizedStubs_ < MaxOptimizedStubs, but
         // since this code is also used for non-CacheIR Baseline stubs, assert
         // < 16 for now. Note that we do have the stronger assert in other
         // methods, because they are only used by CacheIR ICs.
         MOZ_ASSERT(numOptimizedStubs_ < 16);
         numOptimizedStubs_++;
-        numFailures_ = 0;
+        // As a heuristic, reduce the failure count after each successful attach
+        // to delay hitting Generic mode. Reset to 1 instead of 0 so that
+        // BaselineInspector can distinguish no-failures from rare-failures.
+        numFailures_ = std::min(numFailures_, static_cast<uint8_t>(1));
     }
     void trackNotAttached() {
         // Note: we can't assert numFailures_ < maxFailures() because
         // maxFailures() depends on numOptimizedStubs_ and it's possible a
         // GC discarded stubs before we got here.
         numFailures_++;
         MOZ_ASSERT(numFailures_ > 0, "numFailures_ should not overflow");
     }
--- a/js/src/jit/IonIC.cpp
+++ b/js/src/jit/IonIC.cpp
@@ -475,17 +475,17 @@ IonHasOwnIC::update(JSContext* cx, Handl
         ic->discardStubs(cx->zone());
     }
 
     jsbytecode* pc = ic->pc();
 
     if (ic->state().canAttachStub()) {
         bool attached = false;
         RootedScript script(cx, ic->script());
-        HasPropIRGenerator gen(cx, script, pc, CacheKind::HasOwn, ic->state().mode(), idVal, val);
+        HasPropIRGenerator gen(cx, script, pc, ic->state().mode(), CacheKind::HasOwn, idVal, val);
         if (gen.tryAttachStub()) {
             ic->attachCacheIRStub(cx, gen.writerRef(), gen.cacheKind(), ionScript, &attached);
         }
 
         if (!attached) {
             ic->state().trackNotAttached();
         }
     }
@@ -509,17 +509,17 @@ IonInIC::update(JSContext* cx, HandleScr
         ic->discardStubs(cx->zone());
     }
 
     if (ic->state().canAttachStub()) {
         bool attached = false;
         RootedScript script(cx, ic->script());
         RootedValue objV(cx, ObjectValue(*obj));
         jsbytecode* pc = ic->pc();
-        HasPropIRGenerator gen(cx, script, pc, CacheKind::In, ic->state().mode(), key, objV);
+        HasPropIRGenerator gen(cx, script, pc, ic->state().mode(), CacheKind::In, key, objV);
         if (gen.tryAttachStub()) {
             ic->attachCacheIRStub(cx, gen.writerRef(), gen.cacheKind(), ionScript, &attached);
         }
 
         if (!attached) {
             ic->state().trackNotAttached();
         }
     }
--- a/js/src/jsexn.h
+++ b/js/src/jsexn.h
@@ -112,30 +112,16 @@ class AutoClearPendingException
       : cx(cxArg)
     { }
 
     ~AutoClearPendingException() {
         JS_ClearPendingException(cx);
     }
 };
 
-class AutoAssertNoPendingException
-{
-    mozilla::DebugOnly<JSContext*> cx;
-
-  public:
-    explicit AutoAssertNoPendingException(JSContext* cxArg)
-      : cx(cxArg)
-    { }
-
-    ~AutoAssertNoPendingException() {
-        MOZ_ASSERT(!JS_IsExceptionPending(cx));
-    }
-};
-
 extern const char*
 ValueToSourceForError(JSContext* cx, HandleValue val, JS::UniqueChars& bytes);
 
 bool
 GetInternalError(JSContext* cx, unsigned errorNumber, MutableHandleValue error);
 bool
 GetTypeError(JSContext* cx, unsigned errorNumber, MutableHandleValue error);
 
--- a/js/src/jsmath.cpp
+++ b/js/src/jsmath.cpp
@@ -230,17 +230,17 @@ bool
 js::math_atan(JSContext* cx, unsigned argc, Value* vp)
 {
     return math_function<math_atan_impl>(cx, argc, vp);
 }
 
 double
 js::ecmaAtan2(double y, double x)
 {
-    AutoUnsafeCallWithABI unsafe;
+    AutoUnsafeCallWithABI unsafe(UnsafeABIStrictness::AllowPendingExceptions);
     return fdlibm::atan2(y, x);
 }
 
 bool
 js::math_atan2_handle(JSContext* cx, HandleValue y, HandleValue x, MutableHandleValue res)
 {
     double dy;
     if (!ToNumber(cx, y, &dy)) {
@@ -263,17 +263,17 @@ js::math_atan2(JSContext* cx, unsigned a
     CallArgs args = CallArgsFromVp(argc, vp);
 
     return math_atan2_handle(cx, args.get(0), args.get(1), args.rval());
 }
 
 double
 js::math_ceil_impl(double x)
 {
-    AutoUnsafeCallWithABI unsafe;
+    AutoUnsafeCallWithABI unsafe(UnsafeABIStrictness::AllowPendingExceptions);
     return fdlibm::ceil(x);
 }
 
 bool
 js::math_ceil_handle(JSContext* cx, HandleValue v, MutableHandleValue res)
 {
     double d;
     if(!ToNumber(cx, v, &d))
@@ -345,17 +345,17 @@ bool
 js::math_exp(JSContext* cx, unsigned argc, Value* vp)
 {
     return math_function<math_exp_impl>(cx, argc, vp);
 }
 
 double
 js::math_floor_impl(double x)
 {
-    AutoUnsafeCallWithABI unsafe;
+    AutoUnsafeCallWithABI unsafe(UnsafeABIStrictness::AllowPendingExceptions);
     return fdlibm::floor(x);
 }
 
 bool
 js::math_floor_handle(JSContext* cx, HandleValue v, MutableHandleValue r)
 {
     double d;
     if (!ToNumber(cx, v, &d)) {
@@ -438,17 +438,17 @@ js::math_fround(JSContext* cx, unsigned 
 
     return RoundFloat32(cx, args[0], args.rval());
 }
 
 
 double
 js::math_log_impl(double x)
 {
-    AutoUnsafeCallWithABI unsafe;
+    AutoUnsafeCallWithABI unsafe(UnsafeABIStrictness::AllowPendingExceptions);
     return fdlibm::log(x);
 }
 
 bool
 js::math_log_handle(JSContext* cx, HandleValue val, MutableHandleValue res)
 {
     return math_function<math_log_impl>(cx, val, res);
 }
@@ -725,17 +725,17 @@ js::GetBiggestNumberLessThan(T x)
 }
 
 template double js::GetBiggestNumberLessThan<>(double x);
 template float js::GetBiggestNumberLessThan<>(float x);
 
 double
 js::math_round_impl(double x)
 {
-    AutoUnsafeCallWithABI unsafe;
+    AutoUnsafeCallWithABI unsafe(UnsafeABIStrictness::AllowPendingExceptions);
 
     int32_t ignored;
     if (NumberEqualsInt32(x, &ignored)) {
         return x;
     }
 
     /* Some numbers are so big that adding 0.5 would give the wrong number. */
     if (ExponentComponent(x) >= int_fast16_t(FloatingPoint<double>::kExponentShift)) {
@@ -776,17 +776,17 @@ js::math_round(JSContext* cx, unsigned a
     }
 
     return math_round_handle(cx, args[0], args.rval());
 }
 
 double
 js::math_sin_impl(double x)
 {
-    AutoUnsafeCallWithABI unsafe;
+    AutoUnsafeCallWithABI unsafe(UnsafeABIStrictness::AllowPendingExceptions);
     return sin(x);
 }
 
 bool
 js::math_sin_handle(JSContext* cx, HandleValue val, MutableHandleValue res)
 {
     return math_function<math_sin_impl>(cx, val, res);
 }
@@ -809,17 +809,17 @@ js::math_sincos_impl(double x, double *s
     *sin = js::math_sin_impl(x);
     *cos = js::math_cos_impl(x);
 #endif
 }
 
 double
 js::math_sqrt_impl(double x)
 {
-    AutoUnsafeCallWithABI unsafe;
+    AutoUnsafeCallWithABI unsafe(UnsafeABIStrictness::AllowPendingExceptions);
     return sqrt(x);
 }
 
 bool
 js::math_sqrt_handle(JSContext* cx, HandleValue number, MutableHandleValue result)
 {
     return math_function<math_sqrt_impl>(cx, number, result);
 }
@@ -1080,17 +1080,17 @@ js::math_hypot_handle(JSContext* cx, Han
                     scale * sqrt(sumsq);
     res.setDouble(result);
     return true;
 }
 
 double
 js::math_trunc_impl(double x)
 {
-    AutoUnsafeCallWithABI unsafe;
+    AutoUnsafeCallWithABI unsafe(UnsafeABIStrictness::AllowPendingExceptions);
     return fdlibm::trunc(x);
 }
 
 float
 js::math_truncf_impl(float x)
 {
     AutoUnsafeCallWithABI unsafe;
     return fdlibm::truncf(x);
--- a/js/src/vm/JSContext.cpp
+++ b/js/src/vm/JSContext.cpp
@@ -1672,25 +1672,39 @@ AutoEnterOOMUnsafeRegion::crash(size_t s
         if (annotateOOMSizeCallback) {
             annotateOOMSizeCallback(size);
         }
     }
     crash(reason);
 }
 
 #ifdef DEBUG
-AutoUnsafeCallWithABI::AutoUnsafeCallWithABI()
+AutoUnsafeCallWithABI::AutoUnsafeCallWithABI(UnsafeABIStrictness strictness)
   : cx_(TlsContext.get()),
     nested_(cx_->hasAutoUnsafeCallWithABI),
     nogc(cx_)
 {
+    switch(strictness) {
+      case UnsafeABIStrictness::NoExceptions:
+        MOZ_ASSERT(!JS_IsExceptionPending(cx_));
+        checkForPendingException_ = true;
+        break;
+      case UnsafeABIStrictness::AllowPendingExceptions:
+        checkForPendingException_ = !JS_IsExceptionPending(cx_);
+        break;
+      case UnsafeABIStrictness::AllowThrownExceptions:
+        checkForPendingException_ = false;
+        break;
+    }
+
     cx_->hasAutoUnsafeCallWithABI = true;
 }
 
 AutoUnsafeCallWithABI::~AutoUnsafeCallWithABI()
 {
     MOZ_ASSERT(cx_->hasAutoUnsafeCallWithABI);
     if (!nested_) {
         cx_->hasAutoUnsafeCallWithABI = false;
         cx_->inUnsafeCallWithABI = false;
     }
+    MOZ_ASSERT_IF(checkForPendingException_, !JS_IsExceptionPending(cx_));
 }
 #endif
--- a/js/src/vm/JSContext.h
+++ b/js/src/vm/JSContext.h
@@ -1145,36 +1145,37 @@ class MOZ_RAII AutoArrayRooter : private
     friend void JS::AutoGCRooter::trace(JSTracer* trc);
 
   private:
     Value* array_;
     size_t length_;
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
-class AutoAssertNoException
+
+class AutoAssertNoPendingException
 {
 #ifdef DEBUG
-    JSContext* cx;
-    bool hadException;
-#endif
+    JSContext* cx_;
 
   public:
-    explicit AutoAssertNoException(JSContext* cx)
-#ifdef DEBUG
-      : cx(cx),
-        hadException(cx->isExceptionPending())
-#endif
+    explicit AutoAssertNoPendingException(JSContext* cxArg)
+      : cx_(cxArg)
     {
+        MOZ_ASSERT(!JS_IsExceptionPending(cx_));
     }
 
-    ~AutoAssertNoException()
-    {
-        MOZ_ASSERT_IF(!hadException, !cx->isExceptionPending());
+    ~AutoAssertNoPendingException() {
+        MOZ_ASSERT(!JS_IsExceptionPending(cx_));
     }
+#else
+  public:
+    explicit AutoAssertNoPendingException(JSContext* cxArg)
+    {}
+#endif
 };
 
 class MOZ_RAII AutoLockScriptData
 {
     JSRuntime* runtime;
 
   public:
     explicit AutoLockScriptData(JSRuntime* rt MOZ_GUARD_OBJECT_NOTIFIER_PARAM) {
@@ -1256,30 +1257,52 @@ class MOZ_RAII AutoEnterIonCompilation
         cx->ionCompiling = false;
         cx->ionCompilingSafeForMinorGC = false;
 #endif
     }
 
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
+enum UnsafeABIStrictness {
+    NoExceptions,
+    AllowPendingExceptions,
+    AllowThrownExceptions
+};
+
 // Should be used in functions called directly from JIT code (with
 // masm.callWithABI) to assert invariants in debug builds.
+// In debug mode, masm.callWithABI inserts code to verify that the
+// callee function uses AutoUnsafeCallWithABI.
+// While this object is live:
+// 1. cx->hasAutoUnsafeCallWithABI must be true.
+// 2. We can't GC.
+// 3. Exceptions should not be pending/thrown.
+//
+// Note that #3 is a precaution, not a requirement. By default, we
+// assert that the function is not called with a pending exception,
+// and that it does not throw an exception itself.
 class MOZ_RAII AutoUnsafeCallWithABI
 {
 #ifdef DEBUG
     JSContext* cx_;
     bool nested_;
+    bool checkForPendingException_;
 #endif
     JS::AutoCheckCannotGC nogc;
 
   public:
 #ifdef DEBUG
-    AutoUnsafeCallWithABI();
+    explicit AutoUnsafeCallWithABI(UnsafeABIStrictness strictness =
+                                   UnsafeABIStrictness::NoExceptions);
     ~AutoUnsafeCallWithABI();
+#else
+    explicit AutoUnsafeCallWithABI(UnsafeABIStrictness unused_ =
+                                   UnsafeABIStrictness::NoExceptions)
+    {}
 #endif
 };
 
 namespace gc {
 
 // In debug builds, set/unset the performing GC flag for the current thread.
 struct MOZ_RAII AutoSetThreadIsPerformingGC
 {
--- a/js/src/vm/JSObject.cpp
+++ b/js/src/vm/JSObject.cpp
@@ -2462,17 +2462,17 @@ js::LookupName(JSContext* cx, HandleProp
     propp.setNotFound();
     return true;
 }
 
 bool
 js::LookupNameNoGC(JSContext* cx, PropertyName* name, JSObject* envChain,
                    JSObject** objp, JSObject** pobjp, PropertyResult* propp)
 {
-    AutoAssertNoException nogc(cx);
+    AutoAssertNoPendingException nogc(cx);
 
     MOZ_ASSERT(!*objp && !*pobjp && !*propp);
 
     for (JSObject* env = envChain; env; env = env->enclosingEnvironment()) {
         if (env->getOpsLookupProperty()) {
             return false;
         }
         if (!LookupPropertyInline<NoGC>(cx, &env->as<NativeObject>(), NameToId(name), pobjp, propp)) {
--- a/js/src/vm/NativeObject.cpp
+++ b/js/src/vm/NativeObject.cpp
@@ -2560,17 +2560,17 @@ js::NativeGetProperty(JSContext* cx, Han
                       MutableHandleValue vp)
 {
     return NativeGetPropertyInline<CanGC>(cx, obj, receiver, id, NotNameLookup, vp);
 }
 
 bool
 js::NativeGetPropertyNoGC(JSContext* cx, NativeObject* obj, const Value& receiver, jsid id, Value* vp)
 {
-    AutoAssertNoException noexc(cx);
+    AutoAssertNoPendingException noexc(cx);
     return NativeGetPropertyInline<NoGC>(cx, obj, receiver, id, NotNameLookup, vp);
 }
 
 bool
 js::NativeGetElement(JSContext* cx, HandleNativeObject obj, HandleValue receiver,
                      int32_t index, MutableHandleValue vp)
 {
     RootedId id(cx);
--- a/mobile/android/chrome/jar.mn
+++ b/mobile/android/chrome/jar.mn
@@ -56,17 +56,16 @@ chrome.jar:
 
 % content branding %content/branding/
 
 % override chrome://global/content/config.xul chrome://browser/content/config.xhtml
 % override chrome://global/content/netError.xhtml chrome://browser/content/netError.xhtml
 % override chrome://mozapps/content/extensions/extensions.xul chrome://browser/content/aboutAddons.xhtml
 
 # L10n resource overrides.
-% override chrome://global/locale/aboutAbout.dtd chrome://browser/locale/overrides/aboutAbout.dtd
 % override chrome://global/locale/aboutReader.properties chrome://browser/locale/overrides/aboutReader.properties
 % override chrome://global/locale/aboutRights.dtd chrome://browser/locale/overrides/aboutRights.dtd
 % override chrome://global/locale/charsetMenu.properties chrome://browser/locale/overrides/charsetMenu.properties
 % override chrome://global/locale/commonDialogs.properties chrome://browser/locale/overrides/commonDialogs.properties
 % override chrome://global/locale/intl.properties chrome://browser/locale/overrides/intl.properties
 % override chrome://global/locale/intl.css chrome://browser/locale/overrides/intl.css
 % override chrome://global/locale/search/search.properties chrome://browser/locale/overrides/search/search.properties
 % override chrome://pluginproblem/locale/pluginproblem.dtd chrome://browser/locale/overrides/plugins/pluginproblem.dtd
--- a/mobile/android/locales/filter.py
+++ b/mobile/android/locales/filter.py
@@ -13,17 +13,16 @@ def test(mod, path, entity=None):
     # ignore anything but mobile, which is our local repo checkout name
     if mod not in ("dom", "toolkit", "mobile",
                    "mobile/android/base",  "mobile/android"):
         return "ignore"
 
     if mod == "toolkit":
         # keep this file list in sync with jar.mn
         if path in (
-            "chrome/global/aboutAbout.dtd",
             "chrome/global/aboutReader.properties",
             "chrome/global/aboutRights.dtd",
             "chrome/global/charsetMenu.properties",
             "chrome/global/commonDialogs.properties",
             "chrome/global/intl.properties",
             "chrome/global/intl.css",
             "chrome/search/search.properties",
             "chrome/pluginproblem/pluginproblem.dtd",
@@ -33,16 +32,19 @@ def test(mod, path, entity=None):
             "chrome/global/aboutTelemetry.dtd",
             "chrome/global/aboutTelemetry.properties",
             "chrome/global/aboutWebrtc.properties",
         ):
             return "error"
         if re.match(r"crashreporter/[^/]*.ftl", path):
             # error on crashreporter/*.ftl
             return "error"
+        if re.match(r"toolkit/about/[^/]*About.ftl", path):
+            # error on toolkit/about/*About.ftl
+            return "error"
         return "ignore"
 
     if mod == "dom":
         # keep this file list in sync with jar.mn
         if path in (
             "chrome/global.dtd",
             "chrome/accessibility/AccessFu.properties",
             "chrome/dom/dom.properties",
--- a/mobile/android/locales/jar.mn
+++ b/mobile/android/locales/jar.mn
@@ -32,17 +32,16 @@
   locale/@AB_CD@/browser/passwordmgr.properties   (%chrome/passwordmgr.properties)
 #if MOZ_UPDATE_CHANNEL != release && MOZ_UPDATE_CHANNEL != esr
   locale/@AB_CD@/browser/webcompatReporter.properties (%chrome/webcompatReporter.properties)
 #endif
 
 # overrides for toolkit l10n, also for en-US
 # keep this file list in sync with l10n.toml and filter.py
 relativesrcdir toolkit/locales:
-  locale/@AB_CD@/browser/overrides/aboutAbout.dtd                  (%chrome/global/aboutAbout.dtd)
   locale/@AB_CD@/browser/overrides/aboutReader.properties          (%chrome/global/aboutReader.properties)
   locale/@AB_CD@/browser/overrides/aboutRights.dtd                 (%chrome/global/aboutRights.dtd)
   locale/@AB_CD@/browser/overrides/charsetMenu.properties          (%chrome/global/charsetMenu.properties)
   locale/@AB_CD@/browser/overrides/commonDialogs.properties        (%chrome/global/commonDialogs.properties)
   locale/@AB_CD@/browser/overrides/intl.properties                 (%chrome/global/intl.properties)
   locale/@AB_CD@/browser/overrides/intl.css                        (%chrome/global/intl.css)
   locale/@AB_CD@/browser/overrides/search/search.properties        (%chrome/search/search.properties)
 # plugins
@@ -74,8 +73,10 @@ relativesrcdir dom/locales:
 
 #define EN_US en-US
 #if AB_CD != EN_US
 [localization] @AB_CD@.jar:
 relativesrcdir toolkit/locales:
 #about:crashes
   crashreporter                                    (%crashreporter/**/*.ftl)
 #endif
+#about:about
+  toolkit                                          (%toolkit/about/*About.ftl)
--- a/mobile/android/locales/l10n.toml
+++ b/mobile/android/locales/l10n.toml
@@ -153,16 +153,20 @@ exclude-multi-locale = [
     reference = "dom/locales/en-US/chrome/dom/dom.properties"
     l10n = "{l}dom/chrome/dom/dom.properties"
 
 [[paths]]
     reference = "dom/locales/en-US/chrome/plugins.properties"
     l10n = "{l}dom/chrome/plugins.properties"
 
 [[paths]]
+    reference = "toolkit/locales/en-US/toolkit/about/*About.ftl"
+    l10n = "{l}toolkit/toolkit/about/*About.ftl"
+
+[[paths]]
     reference = "toolkit/locales/en-US/chrome/global/aboutAbout.dtd"
     l10n = "{l}toolkit/chrome/global/aboutAbout.dtd"
 
 [[paths]]
     reference = "toolkit/locales/en-US/chrome/global/aboutReader.properties"
     l10n = "{l}toolkit/chrome/global/aboutReader.properties"
 
 [[paths]]
--- a/mobile/locales/filter.py
+++ b/mobile/locales/filter.py
@@ -13,17 +13,16 @@ def test(mod, path, entity=None):
     # ignore anything but mobile, which is our local repo checkout name
     if mod not in ("dom", "toolkit", "mobile",
                    "mobile/android/base",  "mobile/android"):
         return "ignore"
 
     if mod == "toolkit":
         # keep this file list in sync with jar.mn
         if path in (
-            "chrome/global/aboutAbout.dtd",
             "chrome/global/aboutReader.properties",
             "chrome/global/aboutRights.dtd",
             "chrome/global/charsetMenu.properties",
             "chrome/global/commonDialogs.properties",
             "chrome/global/intl.properties",
             "chrome/global/intl.css",
             "chrome/search/search.properties",
             "chrome/pluginproblem/pluginproblem.dtd",
@@ -33,16 +32,20 @@ def test(mod, path, entity=None):
             "chrome/global/aboutTelemetry.dtd",
             "chrome/global/aboutTelemetry.properties",
             "chrome/global/aboutWebrtc.properties",
         ):
             return "error"
         if re.match(r"crashreporter/[^/]*.ftl", path):
             # error on crashreporter/*.ftl
             return "error"
+
+        if re.match(r"toolkit/about/[^/]*About.ftl", path):
+            # error on toolkit/about/*About.ftl
+            return "error"
         return "ignore"
 
     if mod == "dom":
         # keep this file list in sync with jar.mn
         if path in (
             "chrome/global.dtd",
             "chrome/accessibility/AccessFu.properties",
             "chrome/dom/dom.properties",
--- a/netwerk/dns/nsHostResolver.cpp
+++ b/netwerk/dns/nsHostResolver.cpp
@@ -233,16 +233,20 @@ nsHostRecord::CheckExpiration(const mozi
 
     return nsHostRecord::EXP_EXPIRED;
 }
 
 void
 nsHostRecord::SetExpiration(const mozilla::TimeStamp& now, unsigned int valid, unsigned int grace)
 {
     mValidStart = now;
+    if ((valid + grace) < 60) {
+        grace = 60 - valid;
+        LOG(("SetExpiration: artificially bumped grace to %d\n", grace));
+    }
     mGraceStart = now + TimeDuration::FromSeconds(valid);
     mValidEnd = now + TimeDuration::FromSeconds(valid + grace);
 }
 
 void
 nsHostRecord::CopyExpirationTimesAndFlagsFrom(const nsHostRecord *aFromHostRecord)
 {
     // This is used to copy information from a cache entry to a record. All
new file mode 100644
--- /dev/null
+++ b/python/l10n/fluent_migrations/bug_1486934_aboutAbout.py
@@ -0,0 +1,21 @@
+# coding=utf8
+
+# Any copyright is dedicated to the Public Domain.
+# http://creativecommons.org/publicdomain/zero/1.0/
+
+from __future__ import absolute_import
+import fluent.syntax.ast as FTL
+from fluent.migrate.helpers import transforms_from
+
+def migrate(ctx):
+    """Bug 1486934 - Modify about:about to use fluent for localization, part {index}."""
+
+    ctx.add_transforms(
+        "toolkit/toolkit/about/aboutAbout.ftl",
+        "toolkit/toolkit/about/aboutAbout.ftl",
+        transforms_from(
+"""
+about-about-title = { COPY("toolkit/chrome/global/aboutAbout.dtd", "aboutAbout.title") }
+about-about-note = { COPY("toolkit/chrome/global/aboutAbout.dtd", "aboutAbout.note", trim:"True") }
+""")
+)
--- a/taskcluster/ci/beetmover-l10n/kind.yml
+++ b/taskcluster/ci/beetmover-l10n/kind.yml
@@ -1,29 +1,32 @@
 # 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/.
 
 loader: taskgraph.loader.single_dep:loader
 
 transforms:
-   - taskgraph.transforms.beetmover_l10n:transforms
-   - taskgraph.transforms.name_sanity:transforms
-   - taskgraph.transforms.beetmover:transforms
-   - taskgraph.transforms.task:transforms
+    - taskgraph.transforms.beetmover_l10n:transforms
+    - taskgraph.transforms.name_sanity:transforms
+    - taskgraph.transforms.beetmover:transforms
+    - taskgraph.transforms.task:transforms
 
 kind-dependencies:
-   - nightly-l10n-signing
+    - nightly-l10n-signing
 
 only-for-attributes:
-   - nightly
+    - nightly
 
 not-for-build-platforms:
-   - linux-nightly/opt
-   - linux64-nightly/opt
-   - macosx64-nightly/opt
-   - win32-nightly/opt
-   - win64-nightly/opt
-   - linux-devedition-nightly/opt
-   - linux64-devedition-nightly/opt
-   - macosx64-devedition-nightly/opt
-   - win32-devedition-nightly/opt
-   - win64-devedition-nightly/opt
+    - linux-nightly/opt
+    - linux64-nightly/opt
+    - macosx64-nightly/opt
+    - win32-nightly/opt
+    - win64-nightly/opt
+    - linux-devedition-nightly/opt
+    - linux64-devedition-nightly/opt
+    - macosx64-devedition-nightly/opt
+    - win32-devedition-nightly/opt
+    - win64-devedition-nightly/opt
+
+job-template:
+    shipping-phase: promote
--- a/taskcluster/ci/beetmover/kind.yml
+++ b/taskcluster/ci/beetmover/kind.yml
@@ -1,30 +1,33 @@
 # 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/.
 
 loader: taskgraph.loader.single_dep:loader
 
 transforms:
-   - taskgraph.transforms.name_sanity:transforms
-   - taskgraph.transforms.beetmover:transforms
-   - taskgraph.transforms.task:transforms
+    - taskgraph.transforms.name_sanity:transforms
+    - taskgraph.transforms.beetmover:transforms
+    - taskgraph.transforms.task:transforms
 
 kind-dependencies:
-   - build-signing
+    - build-signing
 
 only-for-attributes:
-   - nightly
+    - nightly
 
 not-for-build-platforms:
-   - linux-nightly/opt
-   - linux64-nightly/opt
-   - macosx64-nightly/opt
-   - win32-nightly/opt
-   - win64-nightly/opt
-   - linux-devedition-nightly/opt
-   - linux64-devedition-nightly/opt
-   - macosx64-devedition-nightly/opt
-   - win32-devedition-nightly/opt
-   - win64-devedition-nightly/opt
-   - linux64-asan-reporter-nightly/opt
-   - win64-asan-reporter-nightly/opt
+    - linux-nightly/opt
+    - linux64-nightly/opt
+    - macosx64-nightly/opt
+    - win32-nightly/opt
+    - win64-nightly/opt
+    - linux-devedition-nightly/opt
+    - linux64-devedition-nightly/opt
+    - macosx64-devedition-nightly/opt
+    - win32-devedition-nightly/opt
+    - win64-devedition-nightly/opt
+    - linux64-asan-reporter-nightly/opt
+    - win64-asan-reporter-nightly/opt
+
+job-template:
+    shipping-phase: promote
--- a/taskcluster/ci/build/android.yml
+++ b/taskcluster/ci/build/android.yml
@@ -224,17 +224,17 @@ android-x86-fuzzing/debug:
         - linux64-sccache
         - linux64-node
 
 android-x86-nightly/opt:
     description: "Android 4.2 x86 Nightly"
     attributes:
         nightly: true
         enable-full-crashsymbols: true
-    shipping-phase: promote
+    shipping-phase: build
     shipping-product: fennec
     index:
         product: mobile
         job-name: android-x86-opt
         type: nightly
     treeherder:
         platform: android-4-2-x86/opt
         symbol: N
@@ -397,17 +397,17 @@ android-api-16-without-google-play-servi
         - linux64-sccache
         - linux64-node
 
 android-api-16-nightly/opt:
     description: "Android 4.0 api-16+ Nightly"
     attributes:
         nightly: true
         enable-full-crashsymbols: true
-    shipping-phase: promote
+    shipping-phase: build
     shipping-product: fennec
     index:
         product: mobile
         job-name: android-api-16-opt
         type: nightly-with-multi-l10n
     treeherder:
         platform: android-4-0-armv7-api16/opt
         symbol: N
@@ -516,17 +516,17 @@ android-aarch64/opt:
         - linux64-sccache
         - linux64-node
 
 android-aarch64-nightly/opt:
     description: "Android 5.0 AArch64 Nightly"
     attributes:
         nightly: true
         enable-full-crashsymbols: true
-    shipping-phase: promote
+    shipping-phase: build
     shipping-product: fennec
     index:
         product: mobile
         job-name: android-aarch64-opt
         type: nightly
     treeherder:
         platform: android-5-0-aarch64/opt
         symbol: N
@@ -635,17 +635,17 @@ android-x86_64/opt:
         - linux64-sccache
         - linux64-node
 
 android-x86_64-nightly/opt:
     description: "Android 5.0 x86-64 Nightly"
     attributes:
         nightly: true
         enable-full-crashsymbols: true
-    shipping-phase: promote
+    shipping-phase: build
     shipping-product: fennec
     index:
         product: mobile
         job-name: android-x86_64-opt
         type: nightly
     treeherder:
         platform: android-5-0-x86_64/opt
         symbol: N
--- a/taskcluster/ci/release-final-verify/kind.yml
+++ b/taskcluster/ci/release-final-verify/kind.yml
@@ -20,21 +20,17 @@ job-defaults:
    worker:
       implementation: docker-worker
       os: linux
       docker-image:
          in-tree: "update-verify"
       max-run-time: 7200
       retry-exit-status: [1]
       env:
-         BUILD_TOOLS_REPO:
-            by-project:
-               jamun: https://hg.mozilla.org/users/stage-ffxbld/tools
-               maple: https://hg.mozilla.org/users/asasaki_mozilla.com/tools
-               default: https://hg.mozilla.org/build/tools
+         BUILD_TOOLS_REPO: https://hg.mozilla.org/build/tools
 
 jobs:
    firefox:
       description: final verify
       shipping-phase: push
       shipping-product: firefox
       treeherder:
          platform: firefox-release/opt
--- a/taskcluster/ci/release-secondary-final-verify/kind.yml
+++ b/taskcluster/ci/release-secondary-final-verify/kind.yml
@@ -22,21 +22,17 @@ job-defaults:
    worker:
       implementation: docker-worker
       os: linux
       docker-image:
          in-tree: "update-verify"
       max-run-time: 7200
       retry-exit-status: [1]
       env:
-         BUILD_TOOLS_REPO:
-            by-project:
-               jamun: https://hg.mozilla.org/users/stage-ffxbld/tools
-               maple: https://hg.mozilla.org/users/asasaki_mozilla.com/tools
-               default: https://hg.mozilla.org/build/tools
+         BUILD_TOOLS_REPO: https://hg.mozilla.org/build/tools
 
 jobs:
    firefox:
       description: secondary final verify
       shipping-phase: promote
       shipping-product: firefox
       treeherder:
          platform: linux64/opt
--- a/taskcluster/ci/release-secondary-update-verify/kind.yml
+++ b/taskcluster/ci/release-secondary-update-verify/kind.yml
@@ -29,22 +29,17 @@ job-defaults:
       os: linux
       docker-image:
          in-tree: "update-verify"
       max-run-time: 7200
       retry-exit-status:
          - 255
       env:
          NO_BBCONFIG: "1"
-         BUILD_TOOLS_REPO:
-            by-project:
-               birch: https://hg.mozilla.org/users/bhearsum_mozilla.com/tools
-               jamun: https://hg.mozilla.org/users/stage-ffxbld/tools
-               maple: https://hg.mozilla.org/users/asasaki_mozilla.com/tools
-               default: https://hg.mozilla.org/build/tools
+         BUILD_TOOLS_REPO: https://hg.mozilla.org/build/tools
          CHANNEL: "beta-localtest"
    extra:
       chunks: 12
 
 jobs:
    firefox-secondary-linux64:
       description: linux64 secondary channel update verify
       shipping-product: firefox
--- a/taskcluster/ci/release-update-verify/kind.yml
+++ b/taskcluster/ci/release-update-verify/kind.yml
@@ -29,22 +29,17 @@ job-defaults:
       os: linux
       docker-image:
          in-tree: "update-verify"
       max-run-time: 3600
       retry-exit-status:
          - 255
       env:
          NO_BBCONFIG: "1"
-         BUILD_TOOLS_REPO:
-            by-project:
-               birch: https://hg.mozilla.org/users/bhearsum_mozilla.com/tools
-               jamun: https://hg.mozilla.org/users/stage-ffxbld/tools
-               maple: https://hg.mozilla.org/users/asasaki_mozilla.com/tools
-               default: https://hg.mozilla.org/build/tools
+         BUILD_TOOLS_REPO: https://hg.mozilla.org/build/tools
    extra:
       chunks: 12
 
 jobs:
    firefox-linux64:
       description: linux64 update verify
       shipping-product: firefox
       worker:
--- a/taskcluster/taskgraph/target_tasks.py
+++ b/taskcluster/taskgraph/target_tasks.py
@@ -57,18 +57,16 @@ def filter_beta_release_tasks(task, para
             # On beta, Nightly builds are already PGOs
             'linux-pgo', 'linux64-pgo',
             'win32-pgo', 'win64-pgo',
             # ASAN is central-only
             'linux64-asan-reporter-nightly',
             'win64-asan-reporter-nightly',
             ):
         return False
-    if str(platform).startswith('android') and 'nightly' in str(platform):
-        return False
 
     if platform in (
             'linux', 'linux64',
             'macosx64',
             'win32', 'win64',
             ):
         if task.attributes['kind'] == 'l10n':
             # This is on-change l10n
@@ -305,33 +303,20 @@ def target_tasks_mozilla_esr60(full_task
 
 
 @_target_task('promote_desktop')
 def target_tasks_promote_desktop(full_task_graph, parameters, graph_config):
     """Select the superset of tasks required to promote a beta or release build
     of a desktop product. This should include all non-android
     mozilla_{beta,release} tasks, plus l10n, beetmover, balrog, etc."""
 
-    beta_release_tasks = [l for l, t in full_task_graph.tasks.iteritems() if
-                          filter_beta_release_tasks(t, parameters,
-                                                    ignore_kinds=[],
-                                                    allow_l10n=True)]
-
     def filter(task):
         if task.attributes.get('shipping_product') != parameters['release_product']:
             return False
 
-        # Allow for {beta,release}_tasks; these will get optimized out to point
-        # to the previous graph using ``previous_graph_ids`` and
-        # ``previous_graph_kinds``.
-        # At some point this should filter by shipping_phase == 'build' and
-        # shipping_product matches.
-        if task.label in beta_release_tasks:
-            return True
-
         # 'secondary' balrog/update verify/final verify tasks only run for RCs
         if parameters.get('release_type') != 'release-rc':
             if 'secondary' in task.kind:
                 return False
 
         if task.attributes.get('shipping_phase') == 'promote':
             return True
 
@@ -390,27 +375,22 @@ def target_tasks_ship_desktop(full_task_
     return [l for l, t in full_task_graph.tasks.iteritems() if filter(t)]
 
 
 @_target_task('promote_fennec')
 def target_tasks_promote_fennec(full_task_graph, parameters, graph_config):
     """Select the set of tasks required for a candidates build of fennec. The
     nightly build process involves a pipeline of builds, signing,
     and, eventually, uploading the tasks to balrog."""
-    filtered_for_project = target_tasks_nightly_fennec(full_task_graph, parameters, graph_config)
 
     def filter(task):
         attr = task.attributes.get
         # Don't ship single locale fennec anymore - Bug 1408083
         if attr("locale") or attr("chunk_locales"):
             return False
-        if task.label in filtered_for_project:
-            if task.kind not in ('balrog', 'push-apk'):
-                if task.attributes.get('nightly'):
-                    return True
         if task.attributes.get('shipping_product') == 'fennec' and \
                 task.attributes.get('shipping_phase') == 'promote':
             return True
 
     return [l for l, t in full_task_graph.tasks.iteritems() if filter(full_task_graph[l])]
 
 
 @_target_task('ship_fennec')
--- a/taskcluster/taskgraph/transforms/beetmover.py
+++ b/taskcluster/taskgraph/transforms/beetmover.py
@@ -7,17 +7,16 @@ Transform the beetmover task into an act
 
 from __future__ import absolute_import, print_function, unicode_literals
 
 from taskgraph.transforms.base import TransformSequence
 from taskgraph.util.attributes import copy_attributes_from_dependent_job
 from taskgraph.util.schema import validate_schema, Schema
 from taskgraph.util.scriptworker import (get_beetmover_bucket_scope,
                                          get_beetmover_action_scope,
-                                         get_phase,
                                          get_worker_type_for_scope)
 from taskgraph.util.taskcluster import get_artifact_prefix
 from taskgraph.transforms.task import task_description_schema
 from voluptuous import Any, Required, Optional
 
 
 # Until bug 1331141 is fixed, if you are adding any new artifacts here that
 # need to be transfered to S3, please be aware you also need to follow-up
@@ -135,17 +134,17 @@ beetmover_description_schema = Schema({
     # treeherder is allowed here to override any defaults we use for beetmover.  See
     # taskcluster/taskgraph/transforms/task.py for the schema details, and the
     # below transforms for defaults of various values.
     Optional('treeherder'): task_description_schema['treeherder'],
 
     # locale is passed only for l10n beetmoving
     Optional('locale'): basestring,
 
-    Optional('shipping-phase'): task_description_schema['shipping-phase'],
+    Required('shipping-phase'): task_description_schema['shipping-phase'],
     Optional('shipping-product'): task_description_schema['shipping-product'],
 })
 
 
 @transforms.add
 def validate(config, jobs):
     for job in jobs:
         label = job.get('dependent-task', object).__dict__.get('label', '?no-label?')
@@ -193,28 +192,27 @@ def make_task_description(config, jobs):
 
         attributes = copy_attributes_from_dependent_job(dep_job)
 
         if job.get('locale'):
             attributes['locale'] = job['locale']
 
         bucket_scope = get_beetmover_bucket_scope(config)
         action_scope = get_beetmover_action_scope(config)
-        phase = get_phase(config)
 
         task = {
             'label': label,
             'description': description,
             'worker-type': get_worker_type_for_scope(config, bucket_scope),
             'scopes': [bucket_scope, action_scope],
             'dependencies': dependencies,
             'attributes': attributes,
             'run-on-projects': dep_job.attributes.get('run_on_projects'),
             'treeherder': treeherder,
-            'shipping-phase': phase,
+            'shipping-phase': job['shipping-phase'],
         }
 
         yield task
 
 
 def generate_upstream_artifacts(job, signing_task_ref, build_task_ref, platform,
                                 locale=None):
     build_mapping = UPSTREAM_ARTIFACT_UNSIGNED_PATHS
--- a/taskcluster/taskgraph/transforms/beetmover_geckoview.py
+++ b/taskcluster/taskgraph/transforms/beetmover_geckoview.py
@@ -7,18 +7,17 @@ Transform the beetmover task into an act
 
 from __future__ import absolute_import, print_function, unicode_literals
 
 from taskgraph.transforms.base import TransformSequence
 from taskgraph.transforms.beetmover import \
     craft_release_properties as beetmover_craft_release_properties
 from taskgraph.util.attributes import copy_attributes_from_dependent_job
 from taskgraph.util.schema import validate_schema, Schema, resolve_keyed_by, optionally_keyed_by
-from taskgraph.util.scriptworker import (get_phase,
-                                         get_worker_type_for_scope)
+from taskgraph.util.scriptworker import get_worker_type_for_scope
 from taskgraph.transforms.task import task_description_schema
 from voluptuous import Required, Optional
 
 
 _ARTIFACT_ID_PER_PLATFORM = {
     'android-aarch64': 'geckoview{update_channel}-arm64-v8a',
     'android-api-16': 'geckoview{update_channel}-armeabi-v7a',
     'android-x86': 'geckoview{update_channel}-x86',
@@ -99,17 +98,17 @@ def make_task_description(config, jobs):
             'label': label,
             'description': description,
             'worker-type': get_worker_type_for_scope(config, job['bucket-scope']),
             'scopes': [job['bucket-scope'], 'project:releng:beetmover:action:push-to-maven'],
             'dependencies': dependencies,
             'attributes': attributes,
             'run-on-projects': ['mozilla-central'],
             'treeherder': treeherder,
-            'shipping-phase': job.get('shipping-phase', get_phase(config)),
+            'shipping-phase': job['shipping-phase'],
         }
 
         yield task
 
 
 def generate_upstream_artifacts(build_task_ref):
     return [{
         'taskId': {'task-reference': build_task_ref},
--- a/taskcluster/taskgraph/transforms/beetmover_l10n.py
+++ b/taskcluster/taskgraph/transforms/beetmover_l10n.py
@@ -27,10 +27,11 @@ def make_beetmover_description(config, j
             treeherder = {
                 'symbol': join_symbol(group, symbol),
             }
 
             beet_description = {
                 'dependent-task': dep_job,
                 'treeherder': treeherder,
                 'locale': locale,
+                'shipping-phase': job['shipping-phase'],
             }
             yield beet_description
--- a/taskcluster/taskgraph/transforms/beetmover_push_to_release.py
+++ b/taskcluster/taskgraph/transforms/beetmover_push_to_release.py
@@ -7,17 +7,17 @@ Transform the beetmover-push-to-release 
 
 from __future__ import absolute_import, print_function, unicode_literals
 
 from taskgraph.transforms.base import TransformSequence
 from taskgraph.util.schema import (
      validate_schema, Schema,
 )
 from taskgraph.util.scriptworker import (
-    get_beetmover_bucket_scope, get_beetmover_action_scope,
+    get_beetmover_bucket_scope, add_scope_prefix,
     get_worker_type_for_scope,
 )
 from taskgraph.transforms.job import job_description_schema
 from taskgraph.transforms.task import task_description_schema
 from voluptuous import Any, Required, Optional
 
 # Voluptuous uses marker objects as dictionary *keys*, but they are not
 # comparable, so we cast all of the keys back to regular strings
@@ -69,17 +69,17 @@ def make_beetmover_push_to_release_descr
         label = job['name']
         description = (
             "Beetmover push to release for '{product}'".format(
                 product=job['product']
             )
         )
 
         bucket_scope = get_beetmover_bucket_scope(config)
-        action_scope = get_beetmover_action_scope(config)
+        action_scope = add_scope_prefix(config, 'beetmover:action:push-to-releases')
 
         task = {
             'label': label,
             'description': description,
             'worker-type': get_worker_type_for_scope(config, bucket_scope),
             'scopes': [bucket_scope, action_scope],
             'product': job['product'],
             'dependencies': job['dependencies'],
--- a/taskcluster/taskgraph/transforms/beetmover_repackage.py
+++ b/taskcluster/taskgraph/transforms/beetmover_repackage.py
@@ -11,17 +11,16 @@ from taskgraph.transforms.base import Tr
 from taskgraph.transforms.beetmover import craft_release_properties
 from taskgraph.util.attributes import copy_attributes_from_dependent_job
 from taskgraph.util.partials import (get_balrog_platform_name,
                                      get_partials_artifacts,
                                      get_partials_artifact_map)
 from taskgraph.util.schema import validate_schema, Schema
 from taskgraph.util.scriptworker import (get_beetmover_bucket_scope,
                                          get_beetmover_action_scope,
-                                         get_phase,
                                          get_worker_type_for_scope)
 from taskgraph.util.taskcluster import get_artifact_prefix
 from taskgraph.transforms.task import task_description_schema
 from voluptuous import Any, Required, Optional
 
 import logging
 import re
 
@@ -166,17 +165,17 @@ beetmover_description_schema = Schema({
 
     # treeherder is allowed here to override any defaults we use for beetmover.  See
     # taskcluster/taskgraph/transforms/task.py for the schema details, and the
     # below transforms for defaults of various values.
     Optional('treeherder'): task_description_schema['treeherder'],
 
     # locale is passed only for l10n beetmoving
     Optional('locale'): basestring,
-    Optional('shipping-phase'): task_description_schema['shipping-phase'],
+    Required('shipping-phase'): task_description_schema['shipping-phase'],
     Optional('shipping-product'): task_description_schema['shipping-product'],
 })
 
 
 @transforms.add
 def validate(config, jobs):
     for job in jobs:
         label = job.get('dependent-task', object).__dict__.get('label', '?no-label?')
@@ -236,28 +235,27 @@ def make_task_description(config, jobs):
             dependencies["repackage-signing"] = job['grandparent-tasks'][repackage_signing_name]
 
         attributes = copy_attributes_from_dependent_job(dep_job)
         if job.get('locale'):
             attributes['locale'] = job['locale']
 
         bucket_scope = get_beetmover_bucket_scope(config)
         action_scope = get_beetmover_action_scope(config)
-        phase = get_phase(config)
 
         task = {
             'label': label,
             'description': description,
             'worker-type': get_worker_type_for_scope(config, bucket_scope),
             'scopes': [bucket_scope, action_scope],
             'dependencies': dependencies,
             'attributes': attributes,
             'run-on-projects': dep_job.attributes.get('run_on_projects'),
             'treeherder': treeherder,
-            'shipping-phase': job.get('shipping-phase', phase),
+            'shipping-phase': job['shipping-phase'],
             'shipping-product': job.get('shipping-product'),
         }
 
         yield task
 
 
 def generate_upstream_artifacts(job, dependencies, platform, locale=None, project=None):
 
--- a/taskcluster/taskgraph/transforms/release_generate_checksums_beetmover.py
+++ b/taskcluster/taskgraph/transforms/release_generate_checksums_beetmover.py
@@ -6,17 +6,16 @@ Transform the `release-generate-checksum
 """
 from __future__ import absolute_import, print_function, unicode_literals
 
 from taskgraph.transforms.base import TransformSequence
 from taskgraph.util.attributes import copy_attributes_from_dependent_job
 from taskgraph.util.schema import validate_schema, Schema
 from taskgraph.util.scriptworker import (get_beetmover_bucket_scope,
                                          get_beetmover_action_scope,
-                                         get_phase,
                                          get_worker_type_for_scope,
                                          )
 from taskgraph.util.taskcluster import get_artifact_prefix
 from taskgraph.transforms.beetmover import craft_release_properties
 from taskgraph.transforms.task import task_description_schema
 from voluptuous import Required, Optional
 
 transforms = TransformSequence()
@@ -99,28 +98,27 @@ def make_task_description(config, jobs):
         if len(dep_job.dependencies) > 1:
             raise NotImplementedError(
                 "Can't beetmove a signing task with multiple dependencies")
         # update the dependencies with the dependencies of the signing task
         dependencies.update(dep_job.dependencies)
 
         bucket_scope = get_beetmover_bucket_scope(config)
         action_scope = get_beetmover_action_scope(config)
-        phase = get_phase(config)
 
         task = {
             'label': label,
             'description': description,
             'worker-type': get_worker_type_for_scope(config, bucket_scope),
             'scopes': [bucket_scope, action_scope],
             'dependencies': dependencies,
             'attributes': attributes,
             'run-on-projects': dep_job.attributes.get('run_on_projects'),
             'treeherder': treeherder,
-            'shipping-phase': phase,
+            'shipping-phase': 'promote',
         }
 
         yield task
 
 
 def generate_upstream_artifacts(job, signing_task_ref, build_task_ref):
     build_mapping = CHECKSUMS_BUILD_ARTIFACTS
     signing_mapping = CHECKSUMS_SIGNING_ARTIFACTS
--- a/taskcluster/taskgraph/util/scriptworker.py
+++ b/taskcluster/taskgraph/util/scriptworker.py
@@ -71,101 +71,44 @@ DEVEDITION_SIGNING_CERT_SCOPES = {
     'default': 'signing:cert:dep-signing',
 }
 
 """Map beetmover scope aliases to sets of projects.
 """
 BEETMOVER_SCOPE_ALIAS_TO_PROJECT = [[
     'all-nightly-branches', set([
         'mozilla-central',
-        'mozilla-beta',
-        'mozilla-release',
-        'mozilla-esr60',
         'comm-central',
     ])
 ], [
     'all-release-branches', set([
         'mozilla-beta',
         'mozilla-release',
         'mozilla-esr60',
         'comm-beta',
         'comm-esr60',
     ])
 ]]
 
-"""The set of all beetmover release target tasks.
-
-Used for both `BEETMOVER_SCOPE_ALIAS_TO_TARGET_TASK` and `get_release_build_number`
-"""
-BEETMOVER_CANDIDATES_TARGET_TASKS = set([
-    'promote_fennec',
-    'promote_desktop',
-])
-BEETMOVER_PUSH_TARGET_TASKS = set([
-    'push_fennec',
-    'ship_fennec',
-    'push_desktop',
-    'ship_desktop',
-])
-BEETMOVER_RELEASE_TARGET_TASKS = BEETMOVER_CANDIDATES_TARGET_TASKS | BEETMOVER_PUSH_TARGET_TASKS
-
-"""Map beetmover tasks aliases to sets of target task methods.
-
-This is a list of list-pairs, for ordering.
-"""
-BEETMOVER_SCOPE_ALIAS_TO_TARGET_TASK = [[
-    'all-nightly-tasks', set([
-        'nightly_fennec',
-        'nightly_linux',
-        'nightly_macosx',
-        'nightly_win32',
-        'nightly_win64',
-        'nightly_desktop',
-        'mozilla_beta_tasks',
-        'mozilla_release_tasks',
-    ])
-], [
-    'all-candidates-tasks', BEETMOVER_CANDIDATES_TARGET_TASKS
-], [
-    'all-push-tasks', BEETMOVER_PUSH_TARGET_TASKS
-]]
-
 """Map the beetmover scope aliases to the actual scopes.
 """
 BEETMOVER_BUCKET_SCOPES = {
-    'all-candidates-tasks': {
-        'all-release-branches': 'beetmover:bucket:release',
-    },
-    'all-push-tasks': {
-        'all-release-branches': 'beetmover:bucket:release',
-    },
-    'all-nightly-tasks': {
-        'all-nightly-branches': 'beetmover:bucket:nightly',
-    },
+    'all-release-branches': 'beetmover:bucket:release',
+    'all-nightly-branches': 'beetmover:bucket:nightly',
     'default': 'beetmover:bucket:dep',
 }
 
 """Map the beetmover tasks aliases to the actual action scopes.
 """
 BEETMOVER_ACTION_SCOPES = {
-    'all-candidates-tasks': 'beetmover:action:push-to-candidates',
-    'all-push-tasks': 'beetmover:action:push-to-releases',
-    'all-nightly-tasks': 'beetmover:action:push-to-nightly',
-    'default': 'beetmover:action:push-to-nightly',
+    'nightly': 'beetmover:action:push-to-nightly',
+    'default': 'beetmover:action:push-to-candidates',
 }
 
 
-"""Map the beetmover tasks aliases to phases.
-"""
-PHASES = {
-    'all-candidates-tasks': 'promote',
-    'all-push-tasks': 'push',
-    'default': None,
-}
-
 """Known balrog actions."""
 BALROG_ACTIONS = ('submit-locale', 'submit-toplevel', 'schedule')
 
 """Map balrog scope aliases to sets of projects.
 
 This is a list of list-pairs, for ordering.
 """
 BALROG_SCOPE_ALIAS_TO_PROJECT = [[
@@ -291,63 +234,30 @@ def get_scope_from_project(config, alias
     """
     for alias, projects in alias_to_project_map:
         if config.params['project'] in projects and alias in alias_to_scope_map:
             return alias_to_scope_map[alias]
     return alias_to_scope_map['default']
 
 
 @with_scope_prefix
-def get_scope_from_target_method(config, alias_to_tasks_map, alias_to_scope_map):
+def get_scope_from_release_type(config, release_type_to_scope_map):
     """Determine the restricted scope from `config.params['target_tasks_method']`.
 
     Args:
         config (TransformConfig): The configuration for the kind being transformed.
-        alias_to_tasks_map (list of lists): each list pair contains the
-            alias and the set of target methods that match. This is ordered.
-        alias_to_scope_map (dict): the alias alias to scope
+        release_type_to_scope_map (dict): the maps release types to scopes
 
     Returns:
         string: the scope to use.
     """
-    for alias, tasks in alias_to_tasks_map:
-        if config.params['target_tasks_method'] in tasks and alias in alias_to_scope_map:
-            return alias_to_scope_map[alias]
-    return alias_to_scope_map['default']
-
-
-@with_scope_prefix
-def get_scope_from_target_method_and_project(config, alias_to_tasks_map,
-                                             alias_to_project_map, aliases_to_scope_map):
-    """Determine the restricted scope from both `target_tasks_method` and `project`.
-
-    On certain branches, we'll need differing restricted scopes based on
-    `target_tasks_method`.  However, we can't key solely on that, since that
-    `target_tasks_method` might be run on an unprivileged branch.  This method
-    checks both.
-
-    Args:
-        config (TransformConfig): The configuration for the kind being transformed.
-        alias_to_tasks_map (list of lists): each list pair contains the
-            alias and the set of target methods that match. This is ordered.
-        alias_to_project_map (list of lists): each list pair contains the
-            alias and the set of projects that match.  This is ordered.
-        aliases_to_scope_map (dict of dicts): the task alias to project alias to scope
-
-    Returns:
-        string: the scope to use.
-    """
-    project = config.params['project']
-    target = config.params['target_tasks_method']
-    for alias1, tasks in alias_to_tasks_map:
-        for alias2, projects in alias_to_project_map:
-            if target in tasks and project in projects and \
-                    aliases_to_scope_map.get(alias1, {}).get(alias2):
-                return aliases_to_scope_map[alias1][alias2]
-    return aliases_to_scope_map['default']
+    return release_type_to_scope_map.get(
+        config.params['release_type'],
+        release_type_to_scope_map['default']
+    )
 
 
 def get_phase_from_target_method(config, alias_to_tasks_map, alias_to_phase_map):
     """Determine the phase from `config.params['target_tasks_method']`.
 
     Args:
         config (TransformConfig): The configuration for the kind being transformed.
         alias_to_tasks_map (list of lists): each list pair contains the
@@ -377,32 +287,24 @@ get_signing_cert_scope = functools.parti
 
 get_devedition_signing_cert_scope = functools.partial(
     get_scope_from_project,
     alias_to_project_map=DEVEDITION_SIGNING_SCOPE_ALIAS_TO_PROJECT,
     alias_to_scope_map=DEVEDITION_SIGNING_CERT_SCOPES,
 )
 
 get_beetmover_bucket_scope = functools.partial(
-    get_scope_from_target_method_and_project,
-    alias_to_tasks_map=BEETMOVER_SCOPE_ALIAS_TO_TARGET_TASK,
+    get_scope_from_project,
     alias_to_project_map=BEETMOVER_SCOPE_ALIAS_TO_PROJECT,
-    aliases_to_scope_map=BEETMOVER_BUCKET_SCOPES,
+    alias_to_scope_map=BEETMOVER_BUCKET_SCOPES,
 )
 
 get_beetmover_action_scope = functools.partial(
-    get_scope_from_target_method,
-    alias_to_tasks_map=BEETMOVER_SCOPE_ALIAS_TO_TARGET_TASK,
-    alias_to_scope_map=BEETMOVER_ACTION_SCOPES,
-)
-
-get_phase = functools.partial(
-    get_phase_from_target_method,
-    alias_to_tasks_map=BEETMOVER_SCOPE_ALIAS_TO_TARGET_TASK,
-    alias_to_phase_map=PHASES,
+    get_scope_from_release_type,
+    release_type_to_scope_map=BEETMOVER_ACTION_SCOPES,
 )
 
 get_balrog_server_scope = functools.partial(
     get_scope_from_project,
     alias_to_project_map=BALROG_SCOPE_ALIAS_TO_PROJECT,
     alias_to_scope_map=BALROG_SERVER_SCOPES,
 )
 
--- a/toolkit/content/aboutAbout.xhtml
+++ b/toolkit/content/aboutAbout.xhtml
@@ -1,27 +1,24 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd" [
-<!ENTITY % aboutAboutDTD SYSTEM "chrome://global/locale/aboutAbout.dtd" >
-%aboutAboutDTD;
-<!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd">
-%globalDTD;
-]>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
 
 <!-- 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/. -->
 
 <html xmlns="http://www.w3.org/1999/xhtml">
 <head>
-  <title>&aboutAbout.title;</title>
+  <title data-l10n-id="about-about-title"></title>
   <link rel="stylesheet" href="chrome://global/skin/in-content/info-pages.css" type="text/css"/>
+  <link rel="localization" href="toolkit/about/aboutAbout.ftl"/>
   <script type="application/javascript" src="chrome://global/content/aboutAbout.js"></script>
+
 </head>
 
-<body dir="&locale.dir;">
+<body>
   <div class="container">
-    <h1>&aboutAbout.title;</h1>
-    <p><em>&aboutAbout.note;</em></p>
+    <h1 data-l10n-id="about-about-title"></h1>
+    <p><em data-l10n-id="about-about-note"></em></p>
     <ul id="abouts" class="columns"></ul>
   </div>
 </body>
 </html>
deleted file mode 100644
--- a/toolkit/locales/en-US/chrome/global/aboutAbout.dtd
+++ /dev/null
@@ -1,8 +0,0 @@
-<!-- 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/. -->
-
-<!ENTITY aboutAbout.title  "About About">
-<!ENTITY aboutAbout.note   "This is a list of “about” pages for your convenience.<br/>
-                            Some of them might be confusing. Some are for diagnostic purposes only.<br/>
-                            And some are omitted because they require query strings.">
new file mode 100644
--- /dev/null
+++ b/toolkit/locales/en-US/toolkit/about/aboutAbout.ftl
@@ -0,0 +1,9 @@
+# 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/.
+
+about-about-title = About About
+about-about-note =
+    This is a list of “about” pages for your convenience.<br/>
+    Some of them might be confusing. Some are for diagnostic purposes only.<br/>
+    And some are omitted because they require query strings.
--- a/toolkit/locales/jar.mn
+++ b/toolkit/locales/jar.mn
@@ -4,17 +4,16 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 [localization] @AB_CD@.jar:
   crashreporter                                    (%crashreporter/**/*.ftl)
   toolkit                                          (%toolkit/**/*.ftl)
 
 @AB_CD@.jar:
 % locale global @AB_CD@ %locale/@AB_CD@/global/
-  locale/@AB_CD@/global/aboutAbout.dtd                  (%chrome/global/aboutAbout.dtd)
   locale/@AB_CD@/global/aboutReader.properties          (%chrome/global/aboutReader.properties)
   locale/@AB_CD@/global/aboutRights.dtd                 (%chrome/global/aboutRights.dtd)
   locale/@AB_CD@/global/aboutNetworking.dtd             (%chrome/global/aboutNetworking.dtd)
   locale/@AB_CD@/global/aboutStudies.properties         (%chrome/global/aboutStudies.properties)
   locale/@AB_CD@/global/aboutServiceWorkers.dtd         (%chrome/global/aboutServiceWorkers.dtd)
   locale/@AB_CD@/global/aboutServiceWorkers.properties  (%chrome/global/aboutServiceWorkers.properties)
   locale/@AB_CD@/global/aboutSupport.dtd                (%chrome/global/aboutSupport.dtd)
   locale/@AB_CD@/global/aboutSupport.properties         (%chrome/global/aboutSupport.properties)
--- a/toolkit/mozapps/extensions/content/extensions.js
+++ b/toolkit/mozapps/extensions/content/extensions.js
@@ -1130,17 +1130,20 @@ var gViewController = {
             let subject = {
               wrappedJSObject: {
                 target: getBrowserElement(),
                 info: {
                   type: "sideload",
                   addon: aAddon,
                   icon: aAddon.iconURL,
                   permissions: perms,
-                  resolve() { aAddon.enable(); },
+                  resolve() {
+                    aAddon.markAsSeen();
+                    aAddon.enable();
+                  },
                   reject() {},
                 },
               },
             };
             Services.obs.notifyObservers(subject, "webextension-permission-prompt");
             return;
           }
         }
--- a/toolkit/mozapps/extensions/test/browser/browser.ini
+++ b/toolkit/mozapps/extensions/test/browser/browser.ini
@@ -52,16 +52,17 @@ skip-if = os == "linux" && !debug # Bug 
 [browser_bug618502.js]
 [browser_bug679604.js]
 [browser_bug590347.js]
 [browser_checkAddonCompatibility.js]
 [browser_details.js]
 [browser_discovery.js]
 [browser_dragdrop.js]
 [browser_dragdrop_incompat.js]
+[browser_extension_sideloading_permission.js]
 [browser_file_xpi_no_process_switch.js]
 skip-if = true # Bug 1449071 - Frequent failures
 [browser_getmorethemes.js]
 [browser_globalwarnings.js]
 [browser_gmpProvider.js]
 skip-if = os == 'linux' && !debug # Bug 1398766
 [browser_inlinesettings_browser.js]
 skip-if = os == 'mac' || os == 'linux' # Bug 1483347
--- a/toolkit/mozapps/extensions/test/browser/browser_bug567127.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_bug567127.js
@@ -4,42 +4,16 @@
 
 // Tests bug 567127 - Add install button to the add-ons manager
 
 var MockFilePicker = SpecialPowers.MockFilePicker;
 MockFilePicker.init(window);
 
 var gManagerWindow;
 
-/**
- * Wait for the given PopupNotification to display
- *
- * @param {string} name
- *        The name of the notification to wait for.
- *
- * @returns {Promise}
- *          Resolves with the notification window.
- */
-function promisePopupNotificationShown(name) {
-  return new Promise(resolve => {
-    function popupshown() {
-      let notification = PopupNotifications.getNotification(name);
-      if (!notification) { return; }
-
-      ok(notification, `${name} notification shown`);
-      ok(PopupNotifications.isPanelOpen, "notification panel open");
-
-      PopupNotifications.panel.removeEventListener("popupshown", popupshown);
-      resolve(PopupNotifications.panel.firstChild);
-    }
-
-    PopupNotifications.panel.addEventListener("popupshown", popupshown);
-  });
-}
-
 async function checkInstallConfirmation(...names) {
   let notificationCount = 0;
   let observer = {
     observe(aSubject, aTopic, aData) {
       var installInfo = aSubject.wrappedJSObject;
       isnot(installInfo.browser, null, "Notification should have non-null browser");
       Assert.deepEqual(installInfo.installs[0].installTelemetryInfo, {
         source: "about:addons",
--- a/toolkit/mozapps/extensions/test/browser/browser_dragdrop.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_dragdrop.js
@@ -9,42 +9,16 @@
 // this automatically.
 
 // Instead of loading EventUtils.js into the test scope in browser-test.js for all tests,
 // we only need EventUtils.js for a few files which is why we are using loadSubScript.
 var gManagerWindow;
 var EventUtils = {};
 Services.scriptloader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/EventUtils.js", EventUtils);
 
-/**
- * Wait for the given PopupNotification to display
- *
- * @param {string} name
- *        The name of the notification to wait for.
- *
- * @returns {Promise}
- *          Resolves with the notification window.
- */
-function promisePopupNotificationShown(name) {
-  return new Promise(resolve => {
-    function popupshown() {
-      let notification = PopupNotifications.getNotification(name);
-      if (!notification) { return; }
-
-      ok(notification, `${name} notification shown`);
-      ok(PopupNotifications.isPanelOpen, "notification panel open");
-
-      PopupNotifications.panel.removeEventListener("popupshown", popupshown);
-      resolve(PopupNotifications.panel.firstElementChild);
-    }
-
-    PopupNotifications.panel.addEventListener("popupshown", popupshown);
-  });
-}
-
 async function checkInstallConfirmation(...names) {
   let notificationCount = 0;
   let observer = {
     observe(aSubject, aTopic, aData) {
       var installInfo = aSubject.wrappedJSObject;
       isnot(installInfo.browser, null, "Notification should have non-null browser");
 
       is(installInfo.installs.length, 1, "Got one AddonInstall instance as expected");
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/browser/browser_extension_sideloading_permission.js
@@ -0,0 +1,94 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/*
+* Test Permission Popup for Sideloaded Extensions.
+*/
+const {AddonTestUtils} = ChromeUtils.import("resource://testing-common/AddonTestUtils.jsm", {});
+const ADDON_ID = "addon1@test.mozilla.org";
+
+AddonTestUtils.initMochitest(this);
+
+// Loading extension by sideloading method
+add_task(async function test() {
+
+  await SpecialPowers.pushPrefEnv({
+    set: [
+      ["xpinstall.signatures.required", false],
+      ["extensions.autoDisableScopes", 15],
+      ["extensions.ui.ignoreUnsigned", true],
+    ],
+  });
+
+  let options = {
+    manifest: {
+      applications: {gecko: {id: ADDON_ID}},
+      name: "Test 1",
+      permissions: ["history", "https://*/*"],
+      icons: {"64": "foo-icon.png"},
+    },
+  };
+
+  let xpi = AddonTestUtils.createTempWebExtensionFile(options);
+  await AddonTestUtils.manuallyInstall(xpi);
+
+  let changePromise = new Promise(resolve => ExtensionsUI.once("change", resolve));
+  ExtensionsUI._checkForSideloaded();
+  await changePromise;
+
+  // Test click event on permission cancel option.
+  let manager = await open_manager("addons://list/extension");
+  let addon = get_addon_element(manager, ADDON_ID);
+
+  Assert.notEqual(addon, null, "Found sideloaded addon in about:addons");
+  let el = addon.ownerDocument.getAnonymousElementByAttribute(addon, "anonid", "disable-btn");
+  is_element_hidden(el, "Disable button not visible.");
+  el = addon.ownerDocument.getAnonymousElementByAttribute(addon, "anonid", "enable-btn");
+  is_element_visible(el, "Enable button visible");
+
+  let popupPromise = promisePopupNotificationShown("addon-webext-permissions");
+  EventUtils.synthesizeMouseAtCenter(
+    el,
+    { clickCount: 1 },
+    manager
+  );
+
+  let panel = await popupPromise;
+  ok(PopupNotifications.isPanelOpen, "Permission popup should be visible");
+  panel.secondaryButton.click();
+  ok(!PopupNotifications.isPanelOpen, "Permission popup should be closed / closing");
+
+  addon = await AddonManager.getAddonByID(ADDON_ID);
+  ok(!addon.seen, "Seen flag should remain false after permissions are refused");
+
+  // Test click event on permission accept option.
+  addon = get_addon_element(manager, ADDON_ID);
+  Assert.notEqual(addon, null, "Found sideloaded addon in about:addons");
+
+  el = addon.ownerDocument.getAnonymousElementByAttribute(addon, "anonid", "disable-btn");
+  is_element_hidden(el, "Disable button not visible.");
+  el = addon.ownerDocument.getAnonymousElementByAttribute(addon, "anonid", "enable-btn");
+  is_element_visible(el, "Enable button visible");
+
+  popupPromise = promisePopupNotificationShown("addon-webext-permissions");
+  EventUtils.synthesizeMouseAtCenter(
+    manager.document.getAnonymousElementByAttribute(addon, "anonid", "enable-btn"),
+    { clickCount: 1 },
+    manager
+  );
+
+  panel = await popupPromise;
+  ok(PopupNotifications.isPanelOpen, "Permission popup should be visible");
+  panel.button.click();
+  ok(!PopupNotifications.isPanelOpen, "Permission popup should be closed / closing");
+
+  addon = await AddonManager.getAddonByID(ADDON_ID);
+  ok(addon.seen, "Seen flag should be true after permissions are accepted");
+
+  ok(!PopupNotifications.isPanelOpen, "Permission popup should not be visible");
+
+  await addon.uninstall();
+
+  await close_manager(manager);
+});
--- a/toolkit/mozapps/extensions/test/browser/head.js
+++ b/toolkit/mozapps/extensions/test/browser/head.js
@@ -1383,16 +1383,41 @@ function promiseNotification(id = "addon
         PopupNotifications.panel.firstElementChild.button.click();
         resolve();
       }
     }
     PopupNotifications.panel.addEventListener("popupshown", popupshown);
   });
 }
 
+/**
+ * Wait for the given PopupNotification to display
+ *
+ * @param {string} name
+ *        The name of the notification to wait for.
+ *
+ * @returns {Promise}
+ *          Resolves with the notification window.
+ */
+function promisePopupNotificationShown(name = "addon-webext-permissions") {
+  return new Promise(resolve => {
+    function popupshown() {
+      let notification = PopupNotifications.getNotification(name);
+      if (!notification) { return; }
+
+      ok(notification, `${name} notification shown`);
+      ok(PopupNotifications.isPanelOpen, "notification panel open");
+
+      PopupNotifications.panel.removeEventListener("popupshown", popupshown);
+      resolve(PopupNotifications.panel.firstChild);
+    }
+    PopupNotifications.panel.addEventListener("popupshown", popupshown);
+  });
+}
+
 function acceptAppMenuNotificationWhenShown(id) {
   ChromeUtils.import("resource://gre/modules/AppMenuNotifications.jsm");
   return new Promise(resolve => {
     function popupshown() {
       let notification = AppMenuNotifications.activeNotification;
       if (!notification) { return; }
 
       is(notification.id, id, `${id} notification shown`);
--- a/toolkit/mozapps/extensions/test/xpinstall/browser_doorhanger_installs.js
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_doorhanger_installs.js
@@ -70,32 +70,52 @@ async function waitForProgressNotificati
     ok(notification, `Should have seen the right notification`);
     is(notification.button.hasAttribute("disabled"), wantDisabled,
        "The install button should be disabled?");
   }
 
   return PopupNotifications.panel;
 }
 
-function acceptAppMenuNotificationWhenShown(id) {
+function acceptAppMenuNotificationWhenShown(id, dismiss = false) {
   ChromeUtils.import("resource://gre/modules/AppMenuNotifications.jsm");
   return new Promise(resolve => {
+    function appMenuPopupHidden() {
+      PanelUI.panel.removeEventListener("popuphidden", appMenuPopupHidden);
+      is(PanelUI.menuButton.getAttribute("badge-status"), false, "badge is not set after addon-installed");
+      resolve();
+    }
+    function appMenuPopupShown() {
+      PanelUI.panel.removeEventListener("popupshown", appMenuPopupShown);
+      PanelUI.menuButton.click();
+    }
     function popupshown() {
       let notification = AppMenuNotifications.activeNotification;
-      if (!notification) { return; }
+      if (!notification) {
+        return;
+      }
 
       is(notification.id, id, `${id} notification shown`);
       ok(PanelUI.isNotificationPanelOpen, "notification panel open");
 
       PanelUI.notificationPanel.removeEventListener("popupshown", popupshown);
 
+      if (dismiss) {
+        // Dismiss the panel by clicking on the appMenu button.
+        PanelUI.panel.addEventListener("popupshown", appMenuPopupShown);
+        PanelUI.panel.addEventListener("popuphidden", appMenuPopupHidden);
+        PanelUI.menuButton.click();
+        return;
+      }
+
+      // Dismiss the panel by clicking the primary button.
       let popupnotificationID = PanelUI._getPopupId(notification);
       let popupnotification = document.getElementById(popupnotificationID);
+
       popupnotification.button.click();
-
       resolve();
     }
     PanelUI.notificationPanel.addEventListener("popupshown", popupshown);
   });
 }
 
 async function waitForNotification(aId, aExpectedCount = 1) {
   info("Waiting for " + aId + " notification");
@@ -283,17 +303,17 @@ async function test_whitelistedInstall()
                                                 + triggers).then(newTab => tab = newTab);
   await progressPromise;
   let installDialog = await dialogPromise;
   await BrowserTestUtils.waitForCondition(() => !!tab, "tab should be present");
 
   is(gBrowser.selectedTab, tab,
      "tab selected in response to the addon-install-confirmation notification");
 
-  let notificationPromise = acceptAppMenuNotificationWhenShown("addon-installed");
+  let notificationPromise = acceptAppMenuNotificationWhenShown("addon-installed", true);
   acceptInstallDialog(installDialog);
   await notificationPromise;
 
   let installs = await AddonManager.getAllInstalls();
   is(installs.length, 0, "Should be no pending installs");
 
   let addon = await AddonManager.getAddonByID("amosigned-xpi@tests.mozilla.org");
   addon.uninstall();