Merge inbound to mozilla-central. a=merge
authorNarcis Beleuzu <nbeleuzu@mozilla.com>
Wed, 15 Aug 2018 12:48:57 +0300
changeset 486711 4494389e577e
parent 486683 27bd852b023e (current diff)
parent 486710 c7d7fe608132 (diff)
child 486718 582f8a75dd62
child 486782 4609d0b65d7c
push id9719
push userffxbld-merge
push dateFri, 24 Aug 2018 17:49:46 +0000
treeherdermozilla-beta@719ec98fba77 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone63.0a1
first release with
nightly linux32
4494389e577e / 63.0a1 / 20180815100249 / files
nightly linux64
4494389e577e / 63.0a1 / 20180815100249 / files
nightly mac
4494389e577e / 63.0a1 / 20180815100249 / files
nightly win32
4494389e577e / 63.0a1 / 20180815100249 / files
nightly win64
4494389e577e / 63.0a1 / 20180815100249 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to mozilla-central. a=merge
xpcom/ds/nsGkAtomList.h
xpcom/ds/nsStaticAtom.h
xpcom/io/nsDirectoryServiceAtomList.h
--- a/browser/modules/AsyncTabSwitcher.jsm
+++ b/browser/modules/AsyncTabSwitcher.jsm
@@ -420,16 +420,29 @@ class AsyncTabSwitcher {
 
       let tabpanels = this.tabbrowser.tabpanels;
       let showPanel = this.tabbrowser.tabContainer.getRelatedElement(showTab);
       let index = Array.indexOf(tabpanels.children, showPanel);
       if (index != -1) {
         this.log(`Switch to tab ${index} - ${this.tinfo(showTab)}`);
         tabpanels.setAttribute("selectedIndex", index);
         if (showTab === this.requestedTab) {
+          if (requestedTabState == this.STATE_LOADED) {
+            // The new tab will be made visible in the next paint, record the expected
+            // transaction id for that, and we'll mark when we get notified of its
+            // completion.
+            this.switchPaintId = this.window.windowUtils.lastTransactionId + 1;
+          } else {
+            // We're making the tab visible even though we haven't yet got layers for it.
+            // It's hard to know which composite the layers will first be available in (and
+            // the parent process might not even get MozAfterPaint delivered for it), so just
+            // give up measuring this for now. :(
+            TelemetryStopwatch.cancel("FX_TAB_SWITCH_COMPOSITE_E10S_MS", this.window);
+          }
+
           if (this._requestingTab) {
             /*
              * If _requestingTab is set, that means that we're switching the
              * visibility of the tab synchronously, and we need to wait for
              * the "select" event before shifting focus so that
              * _adjustFocusAfterTabSwitch runs with the right information for
              * the tab switch.
              */
--- a/build/valgrind/x86_64-pc-linux-gnu.sup
+++ b/build/valgrind/x86_64-pc-linux-gnu.sup
@@ -505,16 +505,33 @@
    fun:*style*parse_value*
    fun:*cssparser*Iterator*next*
 }
 
 ## END suppressions for Stylo as compiled by rustc 1.25.0
 ##############################################################################
 
 
+# Another suppression for Stylo, August 2018.  See bug 1479055.
+#
+# Conditional jump or move depends on uninitialised value(s)
+#    at style::[..]::clip_path::cascade_property (gecko_properties.rs:17152)
+#    by style::properties::cascade_rules (properties.rs:83642)
+#    by [..]::cascade_style_and_visited (properties.rs:83129)
+#    by [..]::cascade_primary_style (style/style_resolver.rs:216)
+{
+   Bug 1479055: style::properties::longhands::clip_path::cascade_property
+   Memcheck:Cond
+   fun:_ZN5style10properties9longhands9clip_path16cascade_property*
+   fun:_ZN5style10properties13cascade_rules*
+   fun:_ZN109_$LT$style*style_resolver*cascade_style_and_visited*
+   fun:_ZN109_$LT$style*cascade_primary_style*
+}
+
+
 ###################################################
 #  For valgrind-mochitest ("tc-M-V [tier 2]") runs on taskcluster.
 #  See bug 1248365.
 #  These are specific to Ubuntu 12.04.5, 64-bit.
 ###################################################
 
 
 # Not sure what this is.  Is it the well-known
--- a/devtools/client/inspector/animation/actions/animations.js
+++ b/devtools/client/inspector/animation/actions/animations.js
@@ -4,16 +4,17 @@
 
 "use strict";
 
 const {
   UPDATE_ANIMATIONS,
   UPDATE_DETAIL_VISIBILITY,
   UPDATE_ELEMENT_PICKER_ENABLED,
   UPDATE_HIGHLIGHTED_NODE,
+  UPDATE_PLAYBACK_RATES,
   UPDATE_SELECTED_ANIMATION,
   UPDATE_SIDEBAR_SIZE
 } = require("./index");
 
 module.exports = {
   /**
    * Update the list of animation in the animation inspector.
    */
@@ -50,16 +51,25 @@ module.exports = {
   updateHighlightedNode(nodeFront) {
     return {
       type: UPDATE_HIGHLIGHTED_NODE,
       highlightedNode: nodeFront ? nodeFront.actorID : null,
     };
   },
 
   /**
+   * Update the playback rates.
+   */
+  updatePlaybackRates() {
+    return {
+      type: UPDATE_PLAYBACK_RATES,
+    };
+  },
+
+  /**
    * Update selected animation.
    */
   updateSelectedAnimation(selectedAnimation) {
     return {
       type: UPDATE_SELECTED_ANIMATION,
       selectedAnimation,
     };
   },
--- a/devtools/client/inspector/animation/actions/index.js
+++ b/devtools/client/inspector/animation/actions/index.js
@@ -15,15 +15,18 @@ createEnum([
   "UPDATE_DETAIL_VISIBILITY",
 
   // Update state of the picker enabled.
   "UPDATE_ELEMENT_PICKER_ENABLED",
 
   // Update highlighted node.
   "UPDATE_HIGHLIGHTED_NODE",
 
+  // Update playback rate.
+  "UPDATE_PLAYBACK_RATES",
+
   // Update selected animation.
   "UPDATE_SELECTED_ANIMATION",
 
   // Update sidebar size.
   "UPDATE_SIDEBAR_SIZE",
 
 ], module.exports);
--- a/devtools/client/inspector/animation/animation.js
+++ b/devtools/client/inspector/animation/animation.js
@@ -13,16 +13,17 @@ const EventEmitter = require("devtools/s
 const App = createFactory(require("./components/App"));
 const CurrentTimeTimer = require("./current-time-timer");
 
 const {
   updateAnimations,
   updateDetailVisibility,
   updateElementPickerEnabled,
   updateHighlightedNode,
+  updatePlaybackRates,
   updateSelectedAnimation,
   updateSidebarSize
 } = require("./actions/animations");
 const {
   hasAnimationIterationCountInfinite,
   hasRunningAnimation,
 } = require("./utils/utils");
 
@@ -53,16 +54,17 @@ class AnimationInspector {
     this.toggleElementPicker = this.toggleElementPicker.bind(this);
     this.update = this.update.bind(this);
     this.onAnimationStateChanged = this.onAnimationStateChanged.bind(this);
     this.onAnimationsCurrentTimeUpdated = this.onAnimationsCurrentTimeUpdated.bind(this);
     this.onAnimationsMutation = this.onAnimationsMutation.bind(this);
     this.onCurrentTimeTimerUpdated = this.onCurrentTimeTimerUpdated.bind(this);
     this.onElementPickerStarted = this.onElementPickerStarted.bind(this);
     this.onElementPickerStopped = this.onElementPickerStopped.bind(this);
+    this.onNavigate = this.onNavigate.bind(this);
     this.onSidebarResized = this.onSidebarResized.bind(this);
     this.onSidebarSelectionChanged = this.onSidebarSelectionChanged.bind(this);
 
     EventEmitter.decorate(this);
     this.emit = this.emit.bind(this);
 
     this.init();
   }
@@ -144,16 +146,17 @@ class AnimationInspector {
     this.inspector.sidebar.on("select", this.onSidebarSelectionChanged);
     this.inspector.toolbox.on("picker-started", this.onElementPickerStarted);
     this.inspector.toolbox.on("picker-stopped", this.onElementPickerStopped);
     this.inspector.toolbox.on("select", this.onSidebarSelectionChanged);
   }
 
   destroy() {
     this.setAnimationStateChangedListenerEnabled(false);
+    this.inspector.off("new-root", this.onNavigate);
     this.inspector.selection.off("new-node-front", this.update);
     this.inspector.sidebar.off("select", this.onSidebarSelectionChanged);
     this.inspector.toolbox.off("inspector-sidebar-resized", this.onSidebarResized);
     this.inspector.toolbox.off("picker-started", this.onElementPickerStarted);
     this.inspector.toolbox.off("picker-stopped", this.onElementPickerStopped);
     this.inspector.toolbox.off("select", this.onSidebarSelectionChanged);
 
     this.animationsFront.off("mutations", this.onAnimationsMutation);
@@ -336,36 +339,42 @@ class AnimationInspector {
   onElementPickerStarted() {
     this.inspector.store.dispatch(updateElementPickerEnabled(true));
   }
 
   onElementPickerStopped() {
     this.inspector.store.dispatch(updateElementPickerEnabled(false));
   }
 
+  onNavigate() {
+    this.inspector.store.dispatch(updatePlaybackRates());
+  }
+
   async onSidebarSelectionChanged() {
     const isPanelVisibled = this.isPanelVisible();
 
     if (this.wasPanelVisibled === isPanelVisibled) {
       // onSidebarSelectionChanged is called some times even same state
       // from sidebar and toolbar.
       return;
     }
 
     this.wasPanelVisibled = isPanelVisibled;
 
     if (this.isPanelVisible()) {
       await this.update();
       this.onSidebarResized(null, this.inspector.getSidebarSize());
       this.animationsFront.on("mutations", this.onAnimationsMutation);
+      this.inspector.on("new-root", this.onNavigate);
       this.inspector.selection.on("new-node-front", this.update);
       this.inspector.toolbox.on("inspector-sidebar-resized", this.onSidebarResized);
     } else {
       this.stopAnimationsCurrentTimeTimer();
       this.animationsFront.off("mutations", this.onAnimationsMutation);
+      this.inspector.off("new-root", this.onNavigate);
       this.inspector.selection.off("new-node-front", this.update);
       this.inspector.toolbox.off("inspector-sidebar-resized", this.onSidebarResized);
       this.setAnimationStateChangedListenerEnabled(false);
     }
   }
 
   onSidebarResized(size) {
     this.inspector.store.dispatch(updateSidebarSize(size));
--- a/devtools/client/inspector/animation/components/PlaybackRateSelector.js
+++ b/devtools/client/inspector/animation/components/PlaybackRateSelector.js
@@ -2,84 +2,72 @@
  * 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/. */
 
 "use strict";
 
 const { PureComponent } = require("devtools/client/shared/vendor/react");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+const { connect } = require("devtools/client/shared/vendor/react-redux");
 
 const { getFormatStr } = require("../utils/l10n");
 
 const PLAYBACK_RATES = [.1, .25, .5, 1, 2, 5, 10];
 
 class PlaybackRateSelector extends PureComponent {
   static get propTypes() {
     return {
       animations: PropTypes.arrayOf(PropTypes.object).isRequired,
+      playbackRates: PropTypes.arrayOf(PropTypes.number).isRequired,
       setAnimationsPlaybackRate: PropTypes.func.isRequired,
     };
   }
 
+  static getDerivedStateFromProps(props, state) {
+    const { animations, playbackRates } = props;
+
+    const currentPlaybackRates = sortAndUnique(animations.map(a => a.state.playbackRate));
+    const options =
+      sortAndUnique([...PLAYBACK_RATES, ...playbackRates, ...currentPlaybackRates]);
+
+    if (currentPlaybackRates.length === 1) {
+      return {
+        options,
+        selected: currentPlaybackRates[0],
+      };
+    }
+
+    // When the animations displayed have mixed playback rates, we can't
+    // select any of the predefined ones.
+    return {
+      options: ["", ...options],
+      selected: "",
+    };
+  }
+
   constructor(props) {
     super(props);
 
     this.state = {
       options: [],
       selected: 1,
     };
   }
 
-  componentWillMount() {
-    this.updateState(this.props);
-  }
-
-  componentWillReceiveProps(nextProps) {
-    this.updateState(nextProps);
-  }
-
-  getPlaybackRates(animations) {
-    return sortAndUnique(animations.map(a => a.state.playbackRate));
-  }
-
-  getSelectablePlaybackRates(animationsRates) {
-    return sortAndUnique(PLAYBACK_RATES.concat(animationsRates));
-  }
-
   onChange(e) {
     const { setAnimationsPlaybackRate } = this.props;
 
     if (!e.target.value) {
       return;
     }
 
     setAnimationsPlaybackRate(e.target.value);
   }
 
-  updateState(props) {
-    const { animations } = props;
-
-    let options;
-    let selected;
-    const rates = this.getPlaybackRates(animations);
-
-    if (rates.length === 1) {
-      options = this.getSelectablePlaybackRates(rates);
-      selected = rates[0];
-    } else {
-      // When the animations displayed have mixed playback rates, we can't
-      // select any of the predefined ones.
-      options = ["", ...PLAYBACK_RATES];
-      selected = "";
-    }
-
-    this.setState({ options, selected });
-  }
-
   render() {
     const { options, selected } = this.state;
 
     return dom.select(
       {
         className: "playback-rate-selector devtools-button",
         onChange: this.onChange.bind(this),
       },
@@ -95,9 +83,15 @@ class PlaybackRateSelector extends PureC
     );
   }
 }
 
 function sortAndUnique(array) {
   return [...new Set(array)].sort((a, b) => a > b);
 }
 
-module.exports = PlaybackRateSelector;
+const mapStateToProps = state => {
+  return {
+    playbackRates: state.animations.playbackRates,
+  };
+};
+
+module.exports = connect(mapStateToProps)(PlaybackRateSelector);
--- a/devtools/client/inspector/animation/reducers/animations.js
+++ b/devtools/client/inspector/animation/reducers/animations.js
@@ -4,27 +4,29 @@
 
 "use strict";
 
 const {
   UPDATE_ANIMATIONS,
   UPDATE_DETAIL_VISIBILITY,
   UPDATE_ELEMENT_PICKER_ENABLED,
   UPDATE_HIGHLIGHTED_NODE,
+  UPDATE_PLAYBACK_RATES,
   UPDATE_SELECTED_ANIMATION,
   UPDATE_SIDEBAR_SIZE,
 } = require("../actions/index");
 
 const TimeScale = require("../utils/timescale");
 
 const INITIAL_STATE = {
   animations: [],
   detailVisibility: false,
   elementPickerEnabled: false,
   highlightedNode: null,
+  playbackRates: [],
   selectedAnimation: null,
   sidebarSize: {
     height: 0,
     width: 0,
   },
   timeScale: null,
 };
 
@@ -34,19 +36,22 @@ const reducers = {
     let selectedAnimation = state.selectedAnimation;
 
     if (!state.selectedAnimation ||
         !animations.find(animation => animation.actorID === selectedAnimation.actorID)) {
       selectedAnimation = animations.length === 1 ? animations[0] : null;
       detailVisibility = !!selectedAnimation;
     }
 
+    const playbackRates = getPlaybackRates(state.playbackRates, animations);
+
     return Object.assign({}, state, {
       animations,
       detailVisibility,
+      playbackRates,
       selectedAnimation,
       timeScale: new TimeScale(animations),
     });
   },
 
   [UPDATE_DETAIL_VISIBILITY](state, { detailVisibility }) {
     const selectedAnimation =
       detailVisibility ? state.selectedAnimation : null;
@@ -64,28 +69,38 @@ const reducers = {
   },
 
   [UPDATE_HIGHLIGHTED_NODE](state, { highlightedNode }) {
     return Object.assign({}, state, {
       highlightedNode
     });
   },
 
+  [UPDATE_PLAYBACK_RATES](state) {
+    return Object.assign({}, state, {
+      playbackRates: getPlaybackRates([], state.animations),
+    });
+  },
+
   [UPDATE_SELECTED_ANIMATION](state, { selectedAnimation }) {
     const detailVisibility = !!selectedAnimation;
 
     return Object.assign({}, state, {
       detailVisibility,
       selectedAnimation
     });
   },
 
   [UPDATE_SIDEBAR_SIZE](state, { sidebarSize }) {
     return Object.assign({}, state, {
       sidebarSize
     });
   },
 };
 
+function getPlaybackRates(basePlaybackRate, animations) {
+  return [...new Set(animations.map(a => a.state.playbackRate).concat(basePlaybackRate))];
+}
+
 module.exports = function(state = INITIAL_STATE, action) {
   const reducer = reducers[action.type];
   return reducer ? reducer(state, action) : state;
 };
--- a/devtools/client/inspector/animation/test/browser_animation_playback-rate-selector.js
+++ b/devtools/client/inspector/animation/test/browser_animation_playback-rate-selector.js
@@ -13,25 +13,18 @@ add_task(async function() {
   await addTab(URL_ROOT + "doc_custom_playback_rate.html");
   const { animationInspector, inspector, panel } = await openAnimationInspector();
 
   info("Checking playback rate selector existence");
   const selectEl = panel.querySelector(".playback-rate-selector");
   ok(selectEl, "scrubber controller should exist");
 
   info("Checking playback rate existence which includes custom rate of animations");
-  const selectableRates = [0.1, 0.25, 0.5, 1, 1.5, 2, 5, 10];
-  is(selectEl.options.length, selectableRates.length,
-    `Length of options should be ${ selectableRates.length }`);
-  for (let i = 0; i < selectEl.options.length; i++) {
-    const optionEl = selectEl.options[i];
-    const selectableRate = selectableRates[i];
-    is(Number(optionEl.value), selectableRate,
-      `Option of index[${ i }] should be ${ selectableRate }`);
-  }
+  const expectedPlaybackRates = [0.1, 0.25, 0.5, 1, 1.5, 2, 5, 10];
+  assertPlaybackRateOptions(selectEl, expectedPlaybackRates);
 
   info("Checking selected playback rate");
   is(Number(selectEl.value), 1.5, "Selected option should be 1.5");
 
   info("Checking playback rate of animations");
   await clickOnPlaybackRateSelector(animationInspector, panel, 0.5);
   assertPlaybackRate(animationInspector, 0.5);
 
@@ -40,15 +33,31 @@ add_task(async function() {
   await clickOnPlaybackRateSelector(animationInspector, panel, 2);
   assertPlaybackRate(animationInspector, 2);
   await selectNodeAndWaitForAnimations("body", inspector);
   is(selectEl.value, "", "Selected option should be empty");
 
   info("Checking playback rate after re-setting");
   await clickOnPlaybackRateSelector(animationInspector, panel, 1);
   assertPlaybackRate(animationInspector, 1);
+
+  info("Checking whether custom playback rate exist " +
+       "after selecting another playback rate");
+  assertPlaybackRateOptions(selectEl, expectedPlaybackRates);
 });
 
 async function assertPlaybackRate(animationInspector, rate) {
   const isRateEqual =
     animationInspector.state.animations.every(({state}) => state.playbackRate === rate);
   ok(isRateEqual, `Playback rate of animations should be ${ rate }`);
 }
+
+function assertPlaybackRateOptions(selectEl, expectedPlaybackRates) {
+  is(selectEl.options.length, expectedPlaybackRates.length,
+    `Length of options should be ${ expectedPlaybackRates.length }`);
+
+  for (let i = 0; i < selectEl.options.length; i++) {
+    const optionEl = selectEl.options[i];
+    const expectedPlaybackRate = expectedPlaybackRates[i];
+    is(Number(optionEl.value), expectedPlaybackRate,
+      `Option of index[${ i }] should be ${ expectedPlaybackRate }`);
+  }
+}
--- a/dom/browser-element/mochitest/chrome.ini
+++ b/dom/browser-element/mochitest/chrome.ini
@@ -42,8 +42,9 @@ disabled = Bug 1458393
 [test_browserElement_inproc_GetScreenshotDppx.html]
 [test_browserElement_inproc_getWebManifest.html]
 [test_browserElement_inproc_NextPaint.html]
 [test_browserElement_inproc_PurgeHistory.html]
 [test_browserElement_inproc_ReloadPostRequest.html]
 disabled = no modal prompt on POST reload for chrome window
 [test_browserElement_inproc_SendEvent.html]
 [test_browserElement_inproc_Stop.html]
+skip-if = (os == "win" && !debug) #Bug 1345410
--- a/gfx/layers/wr/WebRenderBridgeParent.cpp
+++ b/gfx/layers/wr/WebRenderBridgeParent.cpp
@@ -891,17 +891,24 @@ WebRenderBridgeParent::RecvEmptyTransact
     // transactions inflight, then set sendDidComposite to false because we will
     // send the DidComposite message after the composite occurs.
     // If there are no pending transactions and we're not going to do a
     // composite, then we leave sendDidComposite as true so we just send
     // the DidComposite notification now.
     sendDidComposite = false;
   }
 
-  HoldPendingTransactionId(mWrEpoch, aTransactionId, aRefreshStartTime, aTxnStartTime, aFwdTime);
+  // Only register a value for CONTENT_FRAME_TIME telemetry if we actually drew
+  // something. It is for consistency with disabling WebRender.
+  HoldPendingTransactionId(mWrEpoch,
+                           aTransactionId,
+                           aRefreshStartTime,
+                           aTxnStartTime,
+                           aFwdTime,
+                           /* aUseForTelemetry */scheduleComposite);
 
   if (scheduleComposite) {
     ScheduleGenerateFrame();
   } else if (sendDidComposite) {
     // The only thing in the pending transaction id queue should be the entry
     // we just added, and now we're going to pretend we rendered it
     MOZ_ASSERT(mPendingTransactionIds.size() == 1);
     if (CompositorBridgeParent* cbp = GetRootCompositorBridgeParent()) {
@@ -1566,24 +1573,26 @@ WebRenderBridgeParent::MaybeGenerateFram
   mApi->SendTransaction(fastTxn);
 }
 
 void
 WebRenderBridgeParent::HoldPendingTransactionId(const wr::Epoch& aWrEpoch,
                                                 TransactionId aTransactionId,
                                                 const TimeStamp& aRefreshStartTime,
                                                 const TimeStamp& aTxnStartTime,
-                                                const TimeStamp& aFwdTime)
+                                                const TimeStamp& aFwdTime,
+                                                const bool aUseForTelemetry)
 {
   MOZ_ASSERT(aTransactionId > LastPendingTransactionId());
   mPendingTransactionIds.push(PendingTransactionId(aWrEpoch,
                                                    aTransactionId,
                                                    aRefreshStartTime,
                                                    aTxnStartTime,
-                                                   aFwdTime));
+                                                   aFwdTime,
+                                                   aUseForTelemetry));
 }
 
 TransactionId
 WebRenderBridgeParent::LastPendingTransactionId()
 {
   TransactionId id{0};
   if (!mPendingTransactionIds.empty()) {
     id = mPendingTransactionIds.back().mId;
@@ -1591,38 +1600,40 @@ WebRenderBridgeParent::LastPendingTransa
   return id;
 }
 
 TransactionId
 WebRenderBridgeParent::FlushTransactionIdsForEpoch(const wr::Epoch& aEpoch, const TimeStamp& aEndTime)
 {
   TransactionId id{0};
   while (!mPendingTransactionIds.empty()) {
-    if (aEpoch.mHandle < mPendingTransactionIds.front().mEpoch.mHandle) {
+    const auto& transactionId = mPendingTransactionIds.front();
+
+    if (aEpoch.mHandle < transactionId.mEpoch.mHandle) {
       break;
     }
 
-    if (!IsRootWebRenderBridgeParent() && !mVsyncRate.IsZero()) {
-      double latencyMs = (aEndTime - mPendingTransactionIds.front().mTxnStartTime).ToMilliseconds();
+    if (!IsRootWebRenderBridgeParent() && !mVsyncRate.IsZero() && transactionId.mUseForTelemetry) {
+      double latencyMs = (aEndTime - transactionId.mTxnStartTime).ToMilliseconds();
       double latencyNorm = latencyMs / mVsyncRate.ToMilliseconds();
       int32_t fracLatencyNorm = lround(latencyNorm * 100.0);
       Telemetry::Accumulate(Telemetry::CONTENT_FRAME_TIME, fracLatencyNorm);
     }
 
 #if defined(ENABLE_FRAME_LATENCY_LOG)
-    if (mPendingTransactionIds.front().mRefreshStartTime) {
-      int32_t latencyMs = lround((aEndTime - mPendingTransactionIds.front().mRefreshStartTime).ToMilliseconds());
+    if (transactionId.mRefreshStartTime) {
+      int32_t latencyMs = lround((aEndTime - transactionId.mRefreshStartTime).ToMilliseconds());
       printf_stderr("From transaction start to end of generate frame latencyMs %d this %p\n", latencyMs, this);
     }
-    if (mPendingTransactionIds.front().mFwdTime) {
-      int32_t latencyMs = lround((aEndTime - mPendingTransactionIds.front().mFwdTime).ToMilliseconds());
+    if (transactionId.mFwdTime) {
+      int32_t latencyMs = lround((aEndTime - transactionId.mFwdTime).ToMilliseconds());
       printf_stderr("From forwarding transaction to end of generate frame latencyMs %d this %p\n", latencyMs, this);
     }
 #endif
-    id = mPendingTransactionIds.front().mId;
+    id = transactionId.mId;
     mPendingTransactionIds.pop();
   }
   return id;
 }
 
 LayersId
 WebRenderBridgeParent::GetLayersId() const
 {
--- a/gfx/layers/wr/WebRenderBridgeParent.h
+++ b/gfx/layers/wr/WebRenderBridgeParent.h
@@ -147,17 +147,18 @@ public:
   void SendAsyncMessage(const InfallibleTArray<AsyncParentMessageData>& aMessage) override;
   void SendPendingAsyncMessages() override;
   void SetAboutToSendAsyncMessages() override;
 
   void HoldPendingTransactionId(const wr::Epoch& aWrEpoch,
                                 TransactionId aTransactionId,
                                 const TimeStamp& aRefreshStartTime,
                                 const TimeStamp& aTxnStartTime,
-                                const TimeStamp& aFwdTime);
+                                const TimeStamp& aFwdTime,
+                                const bool aUseForTelemetry = true);
   TransactionId LastPendingTransactionId();
   TransactionId FlushTransactionIdsForEpoch(const wr::Epoch& aEpoch, const TimeStamp& aEndTime);
 
   TextureFactoryIdentifier GetTextureFactoryIdentifier();
 
   void ExtractImageCompositeNotifications(nsTArray<ImageCompositeNotificationInfo>* aNotifications);
 
   wr::IdNamespace GetIdNamespace()
@@ -255,28 +256,31 @@ private:
   void MaybeGenerateFrame(bool aForceGenerateFrame);
 
 private:
   struct PendingTransactionId {
     PendingTransactionId(const wr::Epoch& aEpoch,
                          TransactionId aId,
                          const TimeStamp& aRefreshStartTime,
                          const TimeStamp& aTxnStartTime,
-                         const TimeStamp& aFwdTime)
+                         const TimeStamp& aFwdTime,
+                         const bool aUseForTelemetry)
       : mEpoch(aEpoch)
       , mId(aId)
       , mRefreshStartTime(aRefreshStartTime)
       , mTxnStartTime(aTxnStartTime)
       , mFwdTime(aFwdTime)
+      , mUseForTelemetry(aUseForTelemetry)
     {}
     wr::Epoch mEpoch;
     TransactionId mId;
     TimeStamp mRefreshStartTime;
     TimeStamp mTxnStartTime;
     TimeStamp mFwdTime;
+    bool mUseForTelemetry;
   };
 
   struct CompositorAnimationIdsForEpoch {
     CompositorAnimationIdsForEpoch(const wr::Epoch& aEpoch, InfallibleTArray<uint64_t>&& aIds)
       : mEpoch(aEpoch)
       , mIds(std::move(aIds))
     {
     }
--- a/js/public/TrackedOptimizationInfo.h
+++ b/js/public/TrackedOptimizationInfo.h
@@ -130,17 +130,16 @@ namespace JS {
     _(SetElemNonDenseNonTANotCached)                                    \
     _(NoSimdJitSupport)                                                 \
     _(SimdTypeNotOptimized)                                             \
     _(UnknownSimdProperty)                                              \
     _(NotModuleNamespace)                                               \
     _(UnknownProperty)                                                  \
     _(NoTemplateObject)                                                 \
     _(TemplateObjectIsUnboxedWithoutInlineElements)                     \
-    _(TemplateObjectIsPlainObjectWithDynamicSlots)                      \
     _(LengthTooBig)                                                     \
     _(SpeculationOnInputTypesFailed)                                    \
     _(RelationalCompare)                                                \
     _(OperandTypeNotBitwiseComparable)                                  \
     _(OperandMaybeEmulatesUndefined)                                    \
     _(LoosyUndefinedNullCompare)                                        \
     _(LoosyInt32BooleanCompare)                                         \
     _(CallsValueOf)                                                     \
--- a/js/src/jit-test/tests/ion/recover-objects.js
+++ b/js/src/jit-test/tests/ion/recover-objects.js
@@ -155,17 +155,17 @@ function dynamicSlots(i) {
         p31: i + 31, p32: i + 32, p33: i + 33, p34: i + 34, p35: i + 35, p36: i + 36, p37: i + 37, p38: i + 38, p39: i + 39, p40: i + 40,
         p41: i + 41, p42: i + 42, p43: i + 43, p44: i + 44, p45: i + 45, p46: i + 46, p47: i + 47, p48: i + 48, p49: i + 49, p50: i + 50
     };
     // Add a function call to capture a resumepoint at the end of the call or
     // inside the inlined block, such as the bailout does not rewind to the
     // beginning of the function.
     resumeHere(); bailout();
     assertEq(obj.p0 + obj.p10 + obj.p20 + obj.p30 + obj.p40, 5 * i + 100);
-    assertRecoveredOnBailout(obj, false);
+    assertRecoveredOnBailout(obj, true);
 }
 
 // Check that we can correctly recover allocations of new objects.
 function Point(x, y)
 {
     this.x = x;
     this.y = y;
 }
--- a/js/src/jit/BaselineCacheIRCompiler.cpp
+++ b/js/src/jit/BaselineCacheIRCompiler.cpp
@@ -2077,16 +2077,17 @@ BaselineCacheIRCompiler::init(CacheKind 
     size_t numInputs = writer_.numInputOperands();
 
     // Baseline passes the first 2 inputs in R0/R1, other Values are stored on
     // the stack.
     size_t numInputsInRegs = std::min(numInputs, size_t(2));
     AllocatableGeneralRegisterSet available(ICStubCompiler::availableGeneralRegs(numInputsInRegs));
 
     switch (kind) {
+      case CacheKind::NewObject:
       case CacheKind::GetIntrinsic:
         MOZ_ASSERT(numInputs == 0);
         break;
       case CacheKind::GetProp:
       case CacheKind::TypeOf:
       case CacheKind::GetIterator:
       case CacheKind::ToBool:
       case CacheKind::UnaryArith:
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -5183,11 +5183,76 @@ ICNewArray_Fallback::Compiler::generateS
 
     masm.push(R0.scratchReg()); // length
     masm.push(ICStubReg); // stub.
     masm.pushBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
 
     return tailCallVM(DoNewArrayInfo, masm);
 }
 
+//
+// NewObject_Fallback
+//
+static bool
+DoNewObject(JSContext* cx, void* payload, ICNewObject_Fallback* stub, MutableHandleValue res)
+{
+    SharedStubInfo info(cx, payload, stub->icEntry());
+
+    FallbackICSpew(cx, stub, "NewObject");
+
+    RootedObject obj(cx);
+
+    RootedObject templateObject(cx, stub->templateObject());
+    if (templateObject) {
+        MOZ_ASSERT(!templateObject->group()->maybePreliminaryObjectsDontCheckGeneration());
+        obj = NewObjectOperationWithTemplate(cx, templateObject);
+    } else {
+        HandleScript script = info.script();
+        jsbytecode* pc = info.pc();
+        obj = NewObjectOperation(cx, script, pc);
+
+        if (obj && !obj->isSingleton() &&
+            !obj->group()->maybePreliminaryObjectsDontCheckGeneration())
+        {
+            templateObject = NewObjectOperation(cx, script, pc, TenuredObject);
+            if (!templateObject)
+                return false;
+
+            if (!JitOptions.disableCacheIR) {
+                bool attached = false;
+                RootedScript script(cx, info.outerScript(cx));
+                NewObjectIRGenerator gen(cx, script, pc, stub->state().mode(), JSOp(*pc), templateObject);
+                if (gen.tryAttachStub()) {
+                    ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
+                                                                BaselineCacheIRStubKind::Regular,
+                                                                ICStubEngine::Baseline , script, stub, &attached);
+                    if (newStub)
+                        JitSpew(JitSpew_BaselineIC, "  NewObject Attached CacheIR stub");
+                }
+            }
+            stub->setTemplateObject(templateObject);
+        }
+    }
+
+    if (!obj)
+        return false;
+
+    res.setObject(*obj);
+    return true;
+}
+
+typedef bool(*DoNewObjectFn)(JSContext*, void*, ICNewObject_Fallback*, MutableHandleValue);
+static const VMFunction DoNewObjectInfo =
+    FunctionInfo<DoNewObjectFn>(DoNewObject, "DoNewObject", TailCall);
+
+bool
+ICNewObject_Fallback::Compiler::generateStubCode(MacroAssembler& masm)
+{
+    EmitRestoreTailCallReg(masm);
+
+    masm.push(ICStubReg); // stub.
+    pushStubPayload(masm, R0.scratchReg());
+
+    return tailCallVM(DoNewObjectInfo, masm);
+}
 
 } // namespace jit
 } // namespace js
--- a/js/src/jit/BaselineIC.h
+++ b/js/src/jit/BaselineIC.h
@@ -1602,16 +1602,52 @@ class ICNewArray_Fallback : public ICFal
     }
 
     void setTemplateGroup(ObjectGroup* group) {
         templateObject_ = nullptr;
         templateGroup_ = group;
     }
 };
 
+
+// JSOP_NEWOBJECT
+
+class ICNewObject_Fallback : public ICFallbackStub
+{
+    friend class ICStubSpace;
+
+    GCPtrObject templateObject_;
+
+    explicit ICNewObject_Fallback(JitCode* stubCode)
+      : ICFallbackStub(ICStub::NewObject_Fallback, stubCode), templateObject_(nullptr)
+    {}
+
+  public:
+    class Compiler : public ICStubCompiler {
+        MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override;
+
+      public:
+        explicit Compiler(JSContext* cx, Engine engine)
+          : ICStubCompiler(cx, ICStub::NewObject_Fallback, engine)
+        {}
+
+        ICStub* getStub(ICStubSpace* space) override {
+            return newStub<ICNewObject_Fallback>(space, getStubCode());
+        }
+    };
+
+    GCPtrObject& templateObject() {
+        return templateObject_;
+    }
+
+    void setTemplateObject(JSObject* obj) {
+        templateObject_ = obj;
+    }
+};
+
 inline bool
 IsCacheableDOMProxy(JSObject* obj)
 {
     if (!obj->is<ProxyObject>())
         return false;
 
     const BaseProxyHandler* handler = obj->as<ProxyObject>().handler();
     return handler->family() == GetDOMProxyHandlerFamily();
--- a/js/src/jit/CacheIR.cpp
+++ b/js/src/jit/CacheIR.cpp
@@ -5593,8 +5593,60 @@ BinaryArithIRGenerator::tryAttachBoolean
         break;
       default:
         MOZ_CRASH("Unhandled op in tryAttachInt32");
     }
 
     writer.returnFromIC();
     return true;
 }
+
+NewObjectIRGenerator::NewObjectIRGenerator(JSContext* cx, HandleScript script,
+                                           jsbytecode* pc, ICState::Mode mode, JSOp op,
+                                           HandleObject templateObj)
+  : IRGenerator(cx, script, pc, CacheKind::NewObject, mode),
+    op_(op),
+    templateObject_(templateObj)
+{
+    MOZ_ASSERT(templateObject_);
+}
+
+void
+NewObjectIRGenerator::trackAttached(const char* name)
+{
+#ifdef JS_CACHEIR_SPEW
+    if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
+        sp.opcodeProperty("op", op_);
+    }
+#endif
+}
+
+bool
+NewObjectIRGenerator::tryAttachStub()
+{
+    if (!templateObject_->is<UnboxedPlainObject>() &&
+        templateObject_->as<PlainObject>().hasDynamicSlots())
+    {
+        trackAttached(IRGenerator::NotAttached);
+        return false;
+    }
+
+    // Don't attach stub if group is pretenured, as the stub
+    // won't succeed.
+    AutoSweepObjectGroup sweep(templateObject_->group());
+    if (templateObject_->group()->shouldPreTenure(sweep)) {
+        trackAttached(IRGenerator::NotAttached);
+        return false;
+    }
+    // Stub doesn't support metadata builder
+    if (cx_->realm()->hasAllocationMetadataBuilder()) {
+        trackAttached(IRGenerator::NotAttached);
+        return false;
+    }
+
+    writer.guardNoAllocationMetadataBuilder();
+    writer.guardObjectGroupNotPretenured(templateObject_->group());
+    writer.loadNewObjectFromTemplateResult(templateObject_);
+    writer.returnFromIC();
+
+    trackAttached("NewObjectWithTemplate");
+    return true;
+}
\ No newline at end of file
--- a/js/src/jit/CacheIR.h
+++ b/js/src/jit/CacheIR.h
@@ -162,17 +162,18 @@ class TypedOperandId : public OperandId
     _(HasOwn)               \
     _(TypeOf)               \
     _(InstanceOf)           \
     _(GetIterator)          \
     _(Compare)              \
     _(ToBool)               \
     _(Call)                 \
     _(UnaryArith)           \
-    _(BinaryArith)
+    _(BinaryArith)          \
+    _(NewObject)
 
 enum class CacheKind : uint8_t
 {
 #define DEFINE_KIND(kind) kind,
     CACHE_IR_KINDS(DEFINE_KIND)
 #undef DEFINE_KIND
 };
 
@@ -216,16 +217,18 @@ extern const char* const CacheKindNames[
     _(GuardAndGetIndexFromString)         \
     _(GuardAndGetIterator)                \
     _(GuardHasGetterSetter)               \
     _(GuardGroupHasUnanalyzedNewScript)   \
     _(GuardIndexIsNonNegative)            \
     _(GuardTagNotEqual)                   \
     _(GuardXrayExpandoShapeAndDefaultProto) \
     _(GuardFunctionPrototype)             \
+    _(GuardNoAllocationMetadataBuilder)   \
+    _(GuardObjectGroupNotPretenured)      \
     _(LoadStackValue)                     \
     _(LoadObject)                         \
     _(LoadProto)                          \
     _(LoadEnclosingEnvironment)           \
     _(LoadWrapperTarget)                  \
     _(LoadValueTag)                       \
                                           \
     _(TruncateDoubleToUInt32)             \
@@ -314,16 +317,17 @@ extern const char* const CacheKindNames[
     _(Int32NotResult)                     \
     _(Int32NegationResult)                \
     _(DoubleNegationResult)               \
     _(LoadInt32TruthyResult)              \
     _(LoadDoubleTruthyResult)             \
     _(LoadStringTruthyResult)             \
     _(LoadObjectTruthyResult)             \
     _(LoadValueResult)                    \
+    _(LoadNewObjectFromTemplateResult)    \
                                           \
     _(CallStringSplitResult)              \
     _(CallStringConcatResult)             \
     _(CallStringObjectConcatResult)       \
                                           \
     _(CompareStringResult)                \
     _(CompareObjectResult)                \
     _(CompareSymbolResult)                \
@@ -641,16 +645,23 @@ class MOZ_RAII CacheIRWriter : public JS
         addStubField(uintptr_t(shapeWrapper), StubField::Type::JSObject);
     }
     // Guard rhs[slot] == prototypeObject
     void guardFunctionPrototype(ObjOperandId rhs, uint32_t slot, ObjOperandId protoId) {
         writeOpWithOperandId(CacheOp::GuardFunctionPrototype, rhs);
         writeOperandId(protoId);
         addStubField(slot, StubField::Type::RawWord);
     }
+    void guardNoAllocationMetadataBuilder() {
+        writeOp(CacheOp::GuardNoAllocationMetadataBuilder);
+    }
+    void guardObjectGroupNotPretenured(ObjectGroup* group) {
+        writeOp(CacheOp::GuardObjectGroupNotPretenured);
+        addStubField(uintptr_t(group), StubField::Type::ObjectGroup);
+    }
   private:
     // Use (or create) a specialization below to clarify what constaint the
     // group guard is implying.
     void guardGroup(ObjOperandId obj, ObjectGroup* group) {
         writeOpWithOperandId(CacheOp::GuardGroup, obj);
         addStubField(uintptr_t(group), StubField::Type::ObjectGroup);
     }
   public:
@@ -1245,16 +1256,26 @@ class MOZ_RAII CacheIRWriter : public JS
     }
     void loadObjectTruthyResult(ObjOperandId obj) {
         writeOpWithOperandId(CacheOp::LoadObjectTruthyResult, obj);
     }
     void loadValueResult(const Value& val) {
         writeOp(CacheOp::LoadValueResult);
         addStubField(val.asRawBits(), StubField::Type::Value);
     }
+    void loadNewObjectFromTemplateResult(JSObject* templateObj) {
+        writeOp(CacheOp::LoadNewObjectFromTemplateResult);
+        addStubField(uintptr_t(templateObj), StubField::Type::JSObject);
+        // Bake in a monotonically increasing number to ensure we differentiate
+        // between different baseline stubs that otherwise might share
+        // stub code.
+        uint64_t id = cx_->runtime()->jitRuntime()->nextDisambiguationId();
+        writeUint32Immediate(id & UINT32_MAX);
+        writeUint32Immediate(id >> 32);
+    }
     void callStringConcatResult(StringOperandId lhs, StringOperandId rhs) {
         writeOpWithOperandId(CacheOp::CallStringConcatResult, lhs);
         writeOperandId(rhs);
     }
     void callStringObjectConcatResult(ValOperandId lhs, ValOperandId rhs) {
         writeOpWithOperandId(CacheOp::CallStringObjectConcatResult, lhs);
         writeOperandId(rhs);
     }
@@ -1933,12 +1954,26 @@ class MOZ_RAII BinaryArithIRGenerator : 
   public:
     BinaryArithIRGenerator(JSContext* cx, HandleScript, jsbytecode* pc, ICState::Mode,
                            JSOp op, HandleValue lhs, HandleValue rhs, HandleValue res);
 
     bool tryAttachStub();
 
 };
 
+class MOZ_RAII NewObjectIRGenerator : public IRGenerator
+{
+    JSOp op_;
+    HandleObject templateObject_;
+
+    void trackAttached(const char* name);
+
+  public:
+    NewObjectIRGenerator(JSContext* cx, HandleScript, jsbytecode* pc, ICState::Mode,
+                         JSOp op, HandleObject templateObj);
+
+    bool tryAttachStub();
+};
+
 } // namespace jit
 } // namespace js
 
 #endif /* jit_CacheIR_h */
--- a/js/src/jit/CacheIRCompiler.cpp
+++ b/js/src/jit/CacheIRCompiler.cpp
@@ -2678,16 +2678,46 @@ CacheIRCompiler::emitGuardTagNotEqual()
     masm.branchTestNumber(Assembler::NotEqual, rhs, &done);
     masm.jump(failure->label());
 
     masm.bind(&done);
     return true;
 }
 
 bool
+CacheIRCompiler::emitGuardNoAllocationMetadataBuilder()
+{
+
+    FailurePath* failure;
+    if (!addFailurePath(&failure))
+        return false;
+
+    masm.branchPtr(Assembler::NotEqual, AbsoluteAddress(cx_->realm()->addressOfMetadataBuilder()),
+                   ImmWord(0), failure->label());
+
+    return true;
+}
+
+bool
+CacheIRCompiler::emitGuardObjectGroupNotPretenured()
+{
+    AutoScratchRegister scratch(allocator, masm);
+
+    FailurePath* failure;
+    if (!addFailurePath(&failure))
+        return false;
+
+    StubFieldOffset group(reader.stubOffset(), StubField::Type::ObjectGroup);
+    emitLoadStubField(group, scratch);
+
+    masm.branchIfPretenuredGroup(scratch, failure->label());
+    return true;
+}
+
+bool
 CacheIRCompiler::emitLoadDenseElementHoleResult()
 {
     AutoOutputRegister output(*this);
     Register obj = allocator.useRegister(masm, reader.objOperandId());
     Register index = allocator.useRegister(masm, reader.int32OperandId());
     AutoScratchRegister scratch1(allocator, masm);
     AutoScratchRegisterMaybeOutput scratch2(allocator, masm, output);
 
@@ -3107,16 +3137,38 @@ CacheIRCompiler::emitLoadObjectTruthyRes
     masm.xor32(Imm32(1), ReturnReg);
     masm.tagValue(JSVAL_TYPE_BOOLEAN, ReturnReg, output.valueReg());
 
     masm.bind(&done);
     return true;
 }
 
 bool
+CacheIRCompiler::emitLoadNewObjectFromTemplateResult()
+{
+    AutoOutputRegister output(*this);
+    AutoScratchRegister obj(allocator, masm);
+    AutoScratchRegisterMaybeOutput scratch(allocator, masm, output);
+
+    TemplateObject templateObj(objectStubFieldUnchecked(reader.stubOffset()));
+
+    // Consume the disambiguation id (2 halves)
+    mozilla::Unused << reader.uint32Immediate();
+    mozilla::Unused << reader.uint32Immediate();
+
+    FailurePath* failure;
+    if (!addFailurePath(&failure))
+        return false;
+
+    masm.createGCObject(obj, scratch, templateObj, gc::DefaultHeap, failure->label());
+    masm.tagValue(JSVAL_TYPE_OBJECT, obj, output.valueReg());
+    return true;
+}
+
+bool
 CacheIRCompiler::emitComparePointerResultShared(bool symbol)
 {
     AutoOutputRegister output(*this);
 
     Register left = symbol ? allocator.useRegister(masm, reader.symbolOperandId())
                            : allocator.useRegister(masm, reader.objOperandId());
     Register right = symbol ? allocator.useRegister(masm, reader.symbolOperandId())
                             : allocator.useRegister(masm, reader.objOperandId());
--- a/js/src/jit/CacheIRCompiler.h
+++ b/js/src/jit/CacheIRCompiler.h
@@ -41,16 +41,18 @@ namespace jit {
     _(GuardMagicValue)                    \
     _(GuardNoUnboxedExpando)              \
     _(GuardAndLoadUnboxedExpando)         \
     _(GuardNoDetachedTypedObjects)        \
     _(GuardNoDenseElements)               \
     _(GuardAndGetIndexFromString)         \
     _(GuardIndexIsNonNegative)            \
     _(GuardTagNotEqual)                   \
+    _(GuardNoAllocationMetadataBuilder)   \
+    _(GuardObjectGroupNotPretenured)           \
     _(LoadObject)                         \
     _(LoadProto)                          \
     _(LoadEnclosingEnvironment)           \
     _(LoadWrapperTarget)                  \
     _(LoadValueTag)                       \
     _(LoadDOMExpandoValue)                \
     _(LoadDOMExpandoValueIgnoreGeneration)\
     _(LoadUndefinedResult)                \
@@ -89,16 +91,17 @@ namespace jit {
     _(LoadTypedElementExistsResult)       \
     _(LoadTypedElementResult)             \
     _(LoadObjectResult)                   \
     _(LoadTypeOfObjectResult)             \
     _(LoadInt32TruthyResult)              \
     _(LoadDoubleTruthyResult)             \
     _(LoadStringTruthyResult)             \
     _(LoadObjectTruthyResult)             \
+    _(LoadNewObjectFromTemplateResult)    \
     _(CompareObjectResult)                \
     _(CompareSymbolResult)                \
     _(CompareInt32Result)                 \
     _(CompareDoubleResult)                \
     _(CompareObjectUndefinedNullResult)   \
     _(ArrayJoinResult)                    \
     _(CallPrintString)                    \
     _(Breakpoint)                         \
@@ -735,16 +738,23 @@ class MOZ_RAII CacheIRCompiler
     Shape* shapeStubField(uint32_t offset) {
         MOZ_ASSERT(stubFieldPolicy_ == StubFieldPolicy::Constant);
         return (Shape*)readStubWord(offset, StubField::Type::Shape);
     }
     JSObject* objectStubField(uint32_t offset) {
         MOZ_ASSERT(stubFieldPolicy_ == StubFieldPolicy::Constant);
         return (JSObject*)readStubWord(offset, StubField::Type::JSObject);
     }
+    // This accessor is for cases where the stubField policy is
+    // being respected through other means, so we don't check the
+    // policy here. (see LoadNewObjectFromTemplateResult)
+    JSObject* objectStubFieldUnchecked(uint32_t offset) {
+        return (JSObject*)writer_.readStubFieldForIon(offset,
+                                                      StubField::Type::JSObject).asWord();
+    }
     JSString* stringStubField(uint32_t offset) {
         MOZ_ASSERT(stubFieldPolicy_ == StubFieldPolicy::Constant);
         return (JSString*)readStubWord(offset, StubField::Type::String);
     }
     JS::Symbol* symbolStubField(uint32_t offset) {
         MOZ_ASSERT(stubFieldPolicy_ == StubFieldPolicy::Constant);
         return (JS::Symbol*)readStubWord(offset, StubField::Type::Symbol);
     }
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -423,16 +423,17 @@ CodeGenerator::visitOutOfLineICFallback(
 
         masm.jump(ool->rejoin());
         return;
       }
       case CacheKind::Call:
       case CacheKind::TypeOf:
       case CacheKind::ToBool:
       case CacheKind::GetIntrinsic:
+      case CacheKind::NewObject:
         MOZ_CRASH("Unsupported IC");
     }
     MOZ_CRASH();
 }
 
 StringObject*
 MNewStringObject::templateObj() const
 {
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -6307,22 +6307,16 @@ IonBuilder::newObjectTryTemplateObject(b
     if (canTrackOptimization)
         trackOptimizationAttempt(TrackedStrategy::NewObject_TemplateObject);
     if (!templateObject) {
         if (canTrackOptimization)
             trackOptimizationOutcome(TrackedOutcome::NoTemplateObject);
         return Ok();
     }
 
-    if (templateObject->is<PlainObject>() && templateObject->as<PlainObject>().hasDynamicSlots()) {
-        if (canTrackOptimization)
-            trackOptimizationOutcome(TrackedOutcome::TemplateObjectIsPlainObjectWithDynamicSlots);
-        return Ok();
-    }
-
     // Emit fastpath.
 
     MNewObject::Mode mode;
     if (JSOp(*pc) == JSOP_NEWOBJECT || JSOp(*pc) == JSOP_NEWINIT)
         mode = MNewObject::ObjectLiteral;
     else
         mode = MNewObject::ObjectCreate;
 
@@ -6338,50 +6332,16 @@ IonBuilder::newObjectTryTemplateObject(b
 
     if (canTrackOptimization)
         trackOptimizationSuccess();
     *emitted = true;
     return Ok();
 }
 
 AbortReasonOr<Ok>
-IonBuilder::newObjectTrySharedStub(bool* emitted)
-{
-    MOZ_ASSERT(*emitted == false);
-
-    // TODO: Support tracking optimizations for inlining a call and regular
-    // optimization tracking at the same time. Currently just drop optimization
-    // tracking when that happens.
-    bool canTrackOptimization = !IsCallPC(pc);
-
-    // Try to emit a shared stub cache.
-
-    if (JitOptions.disableSharedStubs)
-        return Ok();
-
-    if (canTrackOptimization)
-        trackOptimizationAttempt(TrackedStrategy::NewObject_SharedCache);
-
-    MInstruction* stub = MNullarySharedStub::New(alloc());
-    current->add(stub);
-    current->push(stub);
-
-    MOZ_TRY(resumeAfter(stub));
-
-    MUnbox* unbox = MUnbox::New(alloc(), current->pop(), MIRType::Object, MUnbox::Infallible);
-    current->add(unbox);
-    current->push(unbox);
-
-    if (canTrackOptimization)
-        trackOptimizationSuccess();
-    *emitted = true;
-    return Ok();
-}
-
-AbortReasonOr<Ok>
 IonBuilder::newObjectTryVM(bool* emitted, JSObject* templateObject)
 {
     // Emit a VM call.
     MOZ_ASSERT(JSOp(*pc) == JSOP_NEWOBJECT || JSOp(*pc) == JSOP_NEWINIT);
 
     trackOptimizationAttempt(TrackedStrategy::NewObject_Call);
 
     gc::InitialHeap heap = gc::DefaultHeap;
@@ -6409,22 +6369,17 @@ IonBuilder::newObjectTryVM(bool* emitted
 AbortReasonOr<Ok>
 IonBuilder::jsop_newobject()
 {
     bool emitted = false;
     startTrackingOptimizations();
 
     JSObject* templateObject = inspector->getTemplateObject(pc);
 
-    if (!forceInlineCaches()) {
-        MOZ_TRY(newObjectTryTemplateObject(&emitted, templateObject));
-        if (emitted)
-            return Ok();
-    }
-    MOZ_TRY(newObjectTrySharedStub(&emitted));
+    MOZ_TRY(newObjectTryTemplateObject(&emitted, templateObject));
     if (emitted)
         return Ok();
 
     MOZ_TRY(newObjectTryVM(&emitted, templateObject));
     if (emitted)
         return Ok();
 
     MOZ_CRASH("newobject should have been emited");
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -337,17 +337,16 @@ class IonBuilder
                                           MDefinition* right);
 
     // jsop_newarray helpers.
     AbortReasonOr<Ok> newArrayTryTemplateObject(bool* emitted, JSObject* templateObject,
                                                 uint32_t length);
     AbortReasonOr<Ok> newArrayTryVM(bool* emitted, JSObject* templateObject, uint32_t length);
 
     // jsop_newobject helpers.
-    AbortReasonOr<Ok> newObjectTrySharedStub(bool* emitted);
     AbortReasonOr<Ok> newObjectTryTemplateObject(bool* emitted, JSObject* templateObject);
     AbortReasonOr<Ok> newObjectTryVM(bool* emitted, JSObject* templateObject);
 
     // jsop_in/jsop_hasown helpers.
     AbortReasonOr<Ok> inTryDense(bool* emitted, MDefinition* obj, MDefinition* id);
     AbortReasonOr<Ok> hasTryNotDefined(bool* emitted, MDefinition* obj, MDefinition* id, bool ownProperty);
     AbortReasonOr<Ok> hasTryDefiniteSlotOrUnboxed(bool* emitted, MDefinition* obj, MDefinition* id);
 
--- a/js/src/jit/IonCacheIRCompiler.cpp
+++ b/js/src/jit/IonCacheIRCompiler.cpp
@@ -550,16 +550,17 @@ IonCacheIRCompiler::init()
         allocator.initInputLocation(0, ic->lhs());
         allocator.initInputLocation(1, ic->rhs());
         break;
       }
       case CacheKind::Call:
       case CacheKind::TypeOf:
       case CacheKind::ToBool:
       case CacheKind::GetIntrinsic:
+      case CacheKind::NewObject:
         MOZ_CRASH("Unsupported IC");
     }
 
     if (liveRegs_)
         liveFloatRegs_ = LiveFloatRegisterSet(liveRegs_->fpus());
 
     allocator.initAvailableRegs(available);
     allocator.initAvailableRegsAfterSpill();
--- a/js/src/jit/IonIC.cpp
+++ b/js/src/jit/IonIC.cpp
@@ -61,16 +61,17 @@ IonIC::scratchRegisterForEntryJump()
       case CacheKind::BinaryArith:
         return asBinaryArithIC()->output().scratchReg();
       case CacheKind::Compare:
         return asCompareIC()->output().scratchReg();
       case CacheKind::Call:
       case CacheKind::TypeOf:
       case CacheKind::ToBool:
       case CacheKind::GetIntrinsic:
+      case CacheKind::NewObject:
         MOZ_CRASH("Unsupported IC");
     }
 
     MOZ_CRASH("Invalid kind");
 }
 
 void
 IonIC::discardStubs(Zone* zone)
--- a/js/src/jit/JitRealm.h
+++ b/js/src/jit/JitRealm.h
@@ -158,16 +158,19 @@ class JitRuntime
                             mozilla::recordreplay::Behavior::DontPreserve> NumFinishedBuildersType;
     NumFinishedBuildersType numFinishedBuilders_;
 
     // List of Ion compilation waiting to get linked.
     using IonBuilderList = mozilla::LinkedList<js::jit::IonBuilder>;
     MainThreadData<IonBuilderList> ionLazyLinkList_;
     MainThreadData<size_t> ionLazyLinkListSize_;
 
+    // Counter used to help dismbiguate stubs in CacheIR
+    MainThreadData<uint64_t> disambiguationId_;
+
   private:
     void generateLazyLinkStub(MacroAssembler& masm);
     void generateInterpreterStub(MacroAssembler& masm);
     void generateProfilerExitFrameTailStub(MacroAssembler& masm, Label* profilerExitTail);
     void generateExceptionTailStub(MacroAssembler& masm, void* handler, Label* profilerExitTail);
     void generateBailoutTailStub(MacroAssembler& masm, Label* bailoutTail);
     void generateEnterJIT(JSContext* cx, MacroAssembler& masm);
     void generateArgumentsRectifier(MacroAssembler& masm);
@@ -324,16 +327,20 @@ class JitRuntime
     IonBuilderList& ionLazyLinkList(JSRuntime* rt);
 
     size_t ionLazyLinkListSize() const {
         return ionLazyLinkListSize_;
     }
 
     void ionLazyLinkListRemove(JSRuntime* rt, js::jit::IonBuilder* builder);
     void ionLazyLinkListAdd(JSRuntime* rt, js::jit::IonBuilder* builder);
+
+    uint64_t nextDisambiguationId() {
+        return disambiguationId_++;
+    }
 };
 
 enum class CacheKind : uint8_t;
 class CacheIRStubInfo;
 
 enum class ICStubEngine : uint8_t {
     // Baseline IC, see SharedIC.h and BaselineIC.h.
     Baseline = 0,
--- a/js/src/jit/MacroAssembler.cpp
+++ b/js/src/jit/MacroAssembler.cpp
@@ -3320,20 +3320,27 @@ MacroAssembler::branchIfObjGroupHasNoAdd
               ImmWord(0),
               label);
 }
 
 void
 MacroAssembler::branchIfPretenuredGroup(const ObjectGroup* group, Register scratch, Label* label)
 {
     movePtr(ImmGCPtr(group), scratch);
-    branchTest32(Assembler::NonZero, Address(scratch, ObjectGroup::offsetOfFlags()),
+    branchIfPretenuredGroup(scratch, label);
+}
+
+void
+MacroAssembler::branchIfPretenuredGroup(Register group, Label* label)
+{
+    branchTest32(Assembler::NonZero, Address(group, ObjectGroup::offsetOfFlags()),
                  Imm32(OBJECT_FLAG_PRE_TENURE), label);
 }
 
+
 void
 MacroAssembler::branchIfNonNativeObj(Register obj, Register scratch, Label* label)
 {
     loadObjClassUnsafe(obj, scratch);
     branchTest32(Assembler::NonZero, Address(scratch, Class::offsetOfFlags()),
                  Imm32(Class::NON_NATIVE), label);
 }
 
--- a/js/src/jit/MacroAssembler.h
+++ b/js/src/jit/MacroAssembler.h
@@ -1186,16 +1186,17 @@ class MacroAssembler : public MacroAssem
     inline void branchTestObjGroupUnsafe(Condition cond, Register obj, const ObjectGroup* group,
                                          Label* label);
 
     void branchTestObjCompartment(Condition cond, Register obj, const Address& compartment,
                                   Register scratch, Label* label);
     void branchTestObjCompartment(Condition cond, Register obj, const JS::Compartment* compartment,
                                   Register scratch, Label* label);
     void branchIfObjGroupHasNoAddendum(Register obj, Register scratch, Label* label);
+    void branchIfPretenuredGroup(Register group, Label* label);
     void branchIfPretenuredGroup(const ObjectGroup* group, Register scratch, Label* label);
 
     void branchIfNonNativeObj(Register obj, Register scratch, Label* label);
 
     void branchIfInlineTypedObject(Register obj, Register scratch, Label* label);
 
     inline void branchTestClassIsProxy(bool proxy, Register clasp, Label* label);
 
--- a/js/src/jit/SharedIC.cpp
+++ b/js/src/jit/SharedIC.cpp
@@ -1180,113 +1180,10 @@ ICUpdatedStub::addUpdateStubForValue(JSC
                 stub, group.get());
 
         addOptimizedUpdateStub(stub);
     }
 
     return true;
 }
 
-//
-// NewObject_Fallback
-//
-
-// Unlike typical baseline IC stubs, the code for NewObject_WithTemplate is
-// specialized for the template object being allocated.
-static JitCode*
-GenerateNewObjectWithTemplateCode(JSContext* cx, JSObject* templateObject)
-{
-    JitContext jctx(cx, nullptr);
-    StackMacroAssembler masm;
-#ifdef JS_CODEGEN_ARM
-    masm.setSecondScratchReg(BaselineSecondScratchReg);
-#endif
-
-    Label failure;
-    Register objReg = R0.scratchReg();
-    Register tempReg = R1.scratchReg();
-    masm.branchIfPretenuredGroup(templateObject->group(), tempReg, &failure);
-    masm.branchPtr(Assembler::NotEqual, AbsoluteAddress(cx->realm()->addressOfMetadataBuilder()),
-                   ImmWord(0), &failure);
-    TemplateObject templateObj(templateObject);
-    masm.createGCObject(objReg, tempReg, templateObj, gc::DefaultHeap, &failure);
-    masm.tagValue(JSVAL_TYPE_OBJECT, objReg, R0);
-
-    EmitReturnFromIC(masm);
-    masm.bind(&failure);
-    EmitStubGuardFailure(masm);
-
-    Linker linker(masm);
-    AutoFlushICache afc("GenerateNewObjectWithTemplateCode");
-    return linker.newCode(cx, CodeKind::Baseline);
-}
-
-static bool
-DoNewObject(JSContext* cx, void* payload, ICNewObject_Fallback* stub, MutableHandleValue res)
-{
-    SharedStubInfo info(cx, payload, stub->icEntry());
-
-    FallbackICSpew(cx, stub, "NewObject");
-
-    RootedObject obj(cx);
-
-    RootedObject templateObject(cx, stub->templateObject());
-    if (templateObject) {
-        MOZ_ASSERT(!templateObject->group()->maybePreliminaryObjectsDontCheckGeneration());
-        obj = NewObjectOperationWithTemplate(cx, templateObject);
-    } else {
-        HandleScript script = info.script();
-        jsbytecode* pc = info.pc();
-        obj = NewObjectOperation(cx, script, pc);
-
-        if (obj && !obj->isSingleton() &&
-            !obj->group()->maybePreliminaryObjectsDontCheckGeneration())
-        {
-            JSObject* templateObject = NewObjectOperation(cx, script, pc, TenuredObject);
-            if (!templateObject)
-                return false;
-
-            if (!stub->invalid() &&
-                (templateObject->is<UnboxedPlainObject>() ||
-                 !templateObject->as<PlainObject>().hasDynamicSlots()))
-            {
-                JitCode* code = GenerateNewObjectWithTemplateCode(cx, templateObject);
-                if (!code)
-                    return false;
-
-                ICStubSpace* space =
-                    ICStubCompiler::StubSpaceForStub(/* makesGCCalls = */ false, script,
-                                                     ICStubCompiler::Engine::Baseline);
-                ICStub* templateStub = ICStub::New<ICNewObject_WithTemplate>(cx, space, code);
-                if (!templateStub)
-                    return false;
-
-                stub->addNewStub(templateStub);
-            }
-
-            stub->setTemplateObject(templateObject);
-        }
-    }
-
-    if (!obj)
-        return false;
-
-    res.setObject(*obj);
-    return true;
-}
-
-typedef bool(*DoNewObjectFn)(JSContext*, void*, ICNewObject_Fallback*, MutableHandleValue);
-static const VMFunction DoNewObjectInfo =
-    FunctionInfo<DoNewObjectFn>(DoNewObject, "DoNewObject", TailCall);
-
-bool
-ICNewObject_Fallback::Compiler::generateStubCode(MacroAssembler& masm)
-{
-    EmitRestoreTailCallReg(masm);
-
-    masm.push(ICStubReg); // stub.
-    pushStubPayload(masm, R0.scratchReg());
-
-    return tailCallVM(DoNewObjectInfo, masm);
-}
-
 } // namespace jit
 } // namespace js
--- a/js/src/jit/SharedIC.h
+++ b/js/src/jit/SharedIC.h
@@ -1774,56 +1774,12 @@ ScalarTypeFromSimpleTypeDescrKey(uint32_
 
 inline ReferenceType
 ReferenceTypeFromSimpleTypeDescrKey(uint32_t key)
 {
     MOZ_ASSERT(!SimpleTypeDescrKeyIsScalar(key));
     return ReferenceType(key >> 1);
 }
 
-// JSOP_NEWOBJECT
-
-class ICNewObject_Fallback : public ICFallbackStub
-{
-    friend class ICStubSpace;
-
-    GCPtrObject templateObject_;
-
-    explicit ICNewObject_Fallback(JitCode* stubCode)
-      : ICFallbackStub(ICStub::NewObject_Fallback, stubCode), templateObject_(nullptr)
-    {}
-
-  public:
-    class Compiler : public ICStubCompiler {
-        MOZ_MUST_USE bool generateStubCode(MacroAssembler& masm) override;
-
-      public:
-        explicit Compiler(JSContext* cx, Engine engine)
-          : ICStubCompiler(cx, ICStub::NewObject_Fallback, engine)
-        {}
-
-        ICStub* getStub(ICStubSpace* space) override {
-            return newStub<ICNewObject_Fallback>(space, getStubCode());
-        }
-    };
-
-    GCPtrObject& templateObject() {
-        return templateObject_;
-    }
-
-    void setTemplateObject(JSObject* obj) {
-        templateObject_ = obj;
-    }
-};
-
-class ICNewObject_WithTemplate : public ICStub
-{
-    friend class ICStubSpace;
-
-    explicit ICNewObject_WithTemplate(JitCode* stubCode)
-      : ICStub(ICStub::NewObject_WithTemplate, stubCode)
-    {}
-};
-
 } // namespace jit
 } // namespace js
 
 #endif /* jit_SharedIC_h */
--- a/mobile/android/components/extensions/ext-utils.js
+++ b/mobile/android/components/extensions/ext-utils.js
@@ -1,16 +1,15 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
 ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
                                "resource://gre/modules/PrivateBrowsingUtils.jsm");
 
-/* globals TabBase, WindowBase, TabTrackerBase, WindowTrackerBase, TabManagerBase, WindowManagerBase */
 /* globals EventDispatcher */
 ChromeUtils.import("resource://gre/modules/Messaging.jsm");
 
 ChromeUtils.import("resource://gre/modules/ExtensionCommon.jsm");
 ChromeUtils.import("resource://gre/modules/ExtensionUtils.jsm");
 
 var {
   DefaultWeakMap,
@@ -167,16 +166,21 @@ class ProgressListenerWrapper {
 
 class WindowTracker extends WindowTrackerBase {
   constructor(...args) {
     super(...args);
 
     this.progressListeners = new DefaultWeakMap(() => new WeakMap());
   }
 
+  get topWindow() {
+    return Services.wm.getMostRecentWindow("navigator:browser") ||
+      Services.wm.getMostRecentWindow("navigator:geckoview");
+  }
+
   addProgressListener(window, listener) {
     let listeners = this.progressListeners.get(window);
     if (!listeners.has(listener)) {
       let wrapper = new ProgressListenerWrapper(window, listener);
       listeners.set(listener, wrapper);
     }
   }
 
--- a/mobile/android/modules/geckoview/GeckoViewTab.jsm
+++ b/mobile/android/modules/geckoview/GeckoViewTab.jsm
@@ -3,25 +3,37 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 var EXPORTED_SYMBOLS = ["GeckoViewTab"];
 
 ChromeUtils.import("resource://gre/modules/GeckoViewModule.jsm");
 
+// Based on the "Tab" prototype from mobile/android/chrome/content/browser.js
+class Tab {
+  constructor(id, browser) {
+    this.id = id;
+    this.browser = browser;
+  }
+
+  getActive() {
+    return this.browser.docShellIsActive;
+  }
+}
+
 // Stub BrowserApp implementation for WebExtensions support.
 class GeckoViewTab extends GeckoViewModule {
   onInit() {
-    this.browser.tab = { id: 0, browser: this.browser };
+    let tab = new Tab(0, this.browser);
 
     this.window.gBrowser = this.window.BrowserApp = {
       selectedBrowser: this.browser,
-      tabs: [this.browser.tab],
-      selectedTab: this.browser.tab,
+      tabs: [tab],
+      selectedTab: tab,
 
       getTabForId: function(aId) {
         return this.selectedTab;
       },
 
       getTabForBrowser: function(aBrowser) {
         return this.selectedTab;
       },
--- a/toolkit/themes/linux/global/menu.css
+++ b/toolkit/themes/linux/global/menu.css
@@ -102,20 +102,16 @@ menulist > menupopup > menu {
   margin-inline-end: 2px !important;
 }
 
 .menu-text {
   /* This is (18 + the size of end-padding on .menu-iconic-left)px */
   margin-inline-start: 21px !important;
 }
 
-menucaption > .menu-iconic-text {
-  margin-inline-start: 0 !important;
-}
-
 .menu-accel,
 .menu-iconic-accel {
   margin-top: 0px !important;
   margin-bottom: 0px !important;
   margin-inline-start: 7px !important;
 }
 
 .menu-accel-container {
--- a/toolkit/themes/windows/global/menu.css
+++ b/toolkit/themes/windows/global/menu.css
@@ -64,20 +64,16 @@ menucaption {
 }
 
 .menu-text,
 .menu-iconic-text {
   margin-inline-start: 2px !important;
   padding-inline-end: 2px;
 }
 
-menucaption > .menu-iconic-text {
-  padding-inline-start: 0 !important;
-}
-
 .menu-accel,
 .menu-iconic-accel {
   margin-inline-start: 0.74em !important;
   margin-inline-end: 1.35em !important;
 }
 
 .menu-iconic-left {
   min-width: 1.45em;
--- a/tools/lint/eslint/modules.json
+++ b/tools/lint/eslint/modules.json
@@ -50,17 +50,16 @@
   "DownloadList.jsm": ["DownloadList", "DownloadCombinedList", "DownloadSummary"],
   "engines.js": ["EngineManager", "SyncEngine", "Tracker", "Store", "Changeset"],
   "enginesync.js": ["EngineSynchronizer"],
   "evaluate.js": ["evaluate", "sandbox", "Sandboxes"],
   "event-emitter.js": ["EventEmitter"],
   "Extension.jsm": ["Extension", "ExtensionData"],
   "ExtensionAPI.jsm": ["ExtensionAPI", "ExtensionAPIs"],
   "ExtensionsUI.jsm": ["ExtensionsUI"],
-  "ExtensionTabs.jsm": ["TabTrackerBase", "TabManagerBase", "TabBase", "WindowTrackerBase", "WindowManagerBase", "WindowBase"],
   "extension-storage.js": ["ExtensionStorageEngine", "EncryptionRemoteTransformer", "KeyRingEncryptionRemoteTransformer"],
   "ExtensionXPCShellUtils.jsm": ["ExtensionTestUtils"],
   "NativeManifests.jsm": ["NativeManifests"],
   "fakeservices.js": ["FakeCryptoService", "FakeFilesystemService", "FakeGUIDService", "fakeSHA256HMAC"],
   "file_expandosharing.jsm": ["checkFromJSM"],
   "file_stringencoding.jsm": ["checkFromJSM"],
   "file_url.jsm": ["checkFromJSM"],
   "file_worker_url.jsm": ["checkFromJSM"],