merge autoland to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Mon, 12 Jun 2017 12:49:58 +0200
changeset 411556 8c7211cc49637cec922b1d4b141a44c7007f9bb6
parent 411529 27cad9749cddf68e11fdd4e5d73dad84a8f8cf23 (current diff)
parent 411555 3118b9ee9db8f10d53b9352efe3796b917b8930e (diff)
child 411561 f9605772a0c9098ed1bcaa98089b2c944ed69e9b
push id7391
push usermtabara@mozilla.com
push dateMon, 12 Jun 2017 13:08:53 +0000
treeherdermozilla-beta@2191d7f87e2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge autoland to mozilla-central a=merge
testing/web-platform/meta/web-animations/timing-model/animations/canceling-an-animation.html.ini
--- a/browser/components/uitour/UITour-lib.js
+++ b/browser/components/uitour/UITour-lib.js
@@ -102,19 +102,20 @@ if (typeof Mozilla == "undefined") {
    * <li>addons
    * <li>appMenu
    * <li>backForward
    * <li>bookmarks
    * <li>controlCenter-trackingUnblock
    * <li>controlCenter-trackingBlock
    * <li>customize
    * <li>devtools
+   * <li>forget
    * <li>help
    * <li>home
-   * <li>forget
+   * <li>library
    * <li>pocket
    * <li>privateWindow
    * <li>quit
    * <li>readerMode-urlBar
    * <li>search
    * <li>searchIcon
    * <li>searchPrefsLink
    * <li>selectedTabIcon
--- a/browser/components/uitour/UITour.jsm
+++ b/browser/components/uitour/UITour.jsm
@@ -162,27 +162,34 @@ this.UITour = {
           return button;
         }
         return aDocument.getElementById("appMenu-developer-button");
       },
       get widgetName() {
         return gPhotonStructure ? "appMenu-developer-button" : "developer-button";
       },
     }],
+    ["forget", {
+      allowAdd: true,
+      query: "#panic-button",
+      widgetName: "panic-button",
+    }],
     ["help", {
       query: (aDocument) => {
         let buttonId = gPhotonStructure ? "appMenu-help-button" : "PanelUI-help";
         return aDocument.getElementById(buttonId);
       }
     }],
     ["home",        {query: "#home-button"}],
-    ["forget", {
-      allowAdd: true,
-      query: "#panic-button",
-      widgetName: "panic-button",
+    ["library", {
+     query: (aDocument) => {
+        let buttonId = "appMenu-library-button";
+        return gPhotonStructure ?
+          aDocument.getElementById(buttonId) : null;
+      }
     }],
     ["pocket", {
       allowAdd: true,
       query: "#pocket-button",
       widgetName: "pocket-button",
     }],
     ["privateWindow", {
       query(aDocument) {
--- a/browser/components/uitour/test/browser_UITour_availableTargets.js
+++ b/browser/components/uitour/test/browser_UITour_availableTargets.js
@@ -1,33 +1,36 @@
 "use strict";
 
 var gTestTab;
 var gContentAPI;
 var gContentWindow;
 
 var hasPocket = Services.prefs.getBoolPref("extensions.pocket.enabled");
-var hasQuit = !Services.prefs.getBoolPref("browser.photon.structure.enabled") ||
+var isPhoton = Services.prefs.getBoolPref("browser.photon.structure.enabled");
+var hasQuit = !isPhoton ||
               false; // Update this with AppConstants.platform != "macosx" after bug 1368734 lands;
+var hasLibrary = isPhoton || false;
 
 requestLongerTimeout(2);
 add_task(setup_UITourTest);
 
 add_UITour_task(async function test_availableTargets() {
   let data = await getConfigurationPromise("availableTargets");
   ok_targets(data, [
     "accountStatus",
     "addons",
     "appMenu",
     "backForward",
     "bookmarks",
     "customize",
+    "devtools",
     "help",
     "home",
-    "devtools",
+      ...(hasLibrary ? ["library"] : []),
       ...(hasPocket ? ["pocket"] : []),
     "privateWindow",
       ...(hasQuit ? ["quit"] : []),
     "readerMode-urlBar",
     "search",
     "searchIcon",
     "trackingProtection",
     "urlbar",
@@ -46,16 +49,17 @@ add_UITour_task(async function test_avai
     "accountStatus",
     "addons",
     "appMenu",
     "backForward",
     "customize",
     "help",
     "devtools",
     "home",
+      ...(hasLibrary ? ["library"] : []),
       ...(hasPocket ? ["pocket"] : []),
     "privateWindow",
       ...(hasQuit ? ["quit"] : []),
     "readerMode-urlBar",
     "search",
     "searchIcon",
     "trackingProtection",
     "urlbar",
@@ -76,19 +80,20 @@ add_UITour_task(async function test_avai
   // Default minus "search" and "searchIcon"
   ok_targets(data, [
     "accountStatus",
     "addons",
     "appMenu",
     "backForward",
     "bookmarks",
     "customize",
+    "devtools",
     "help",
     "home",
-    "devtools",
+      ...(hasLibrary ? ["library"] : []),
       ...(hasPocket ? ["pocket"] : []),
     "privateWindow",
       ...(hasQuit ? ["quit"] : []),
     "readerMode-urlBar",
     "trackingProtection",
     "urlbar",
   ]);
 
--- a/browser/modules/WindowsJumpLists.jsm
+++ b/browser/modules/WindowsJumpLists.jsm
@@ -246,19 +246,25 @@ this.WinTaskbarJumpList =
       // Prior to building, delete removed items from history.
       this._clearHistory(removedItems);
       return true;
     }
     return false;
   },
 
   _commitBuild: function WTBJL__commitBuild() {
-    if (!this._hasPendingStatements() && !this._builder.commitListBuild()) {
-      this._builder.abortListBuild();
+    if (this._hasPendingStatements()) {
+      return;
     }
+
+    this._builder.commitListBuild(succeed => {
+      if (!succeed) {
+        this._builder.abortListBuild();
+      }
+    });
   },
 
   _buildTasks: function WTBJL__buildTasks() {
     var items = Cc["@mozilla.org/array;1"].
                 createInstance(Ci.nsIMutableArray);
     this._tasks.forEach(function(task) {
       if ((this._shuttingDown && !task.close) || (!this._shuttingDown && !task.open))
         return;
--- a/devtools/client/webconsole/new-console-output/test/chrome/test_render_perf.html
+++ b/devtools/client/webconsole/new-console-output/test/chrome/test_render_perf.html
@@ -175,17 +175,28 @@ window.onload = async function () {
   await testBulkLogging(wrapper);
 
   Services.profiler.AddMarker("Filtering Logging");
   await testFiltering(wrapper);
 
   ok(true, "Tests finished");
 
   let file = FileUtils.getFile("TmpD", [`test_render_perf_${Date.now()}.json`]);
-  info("PROFILE:\n\n\n\n\nSaving profile " + file.path + "\n\n\n\n\n");
   Services.profiler.dumpProfileToFile(file.path);
   Services.profiler.StopProfiler();
 
+  info(`
+
+SAVING PROFILE: ${file.path}
+
+To upload the profile, run the following commands:
+
+  gzip ${file.path}
+  curl 'https://profile-store.appspot.com/compressed-store' --compressed --data-binary @${file.path}.gz | awk '{print "Hosted at: https://perf-html.io/public/"$1}'
+
+
+`);
+
   SimpleTest.finish();
 };
 </script>
 </body>
 </html>
--- a/dom/animation/Animation.cpp
+++ b/dom/animation/Animation.cpp
@@ -1361,16 +1361,17 @@ Animation::ResetPendingTasks()
 {
   if (mPendingState == PendingState::NotPending) {
     return;
   }
 
   CancelPendingTasks();
   if (mReady) {
     mReady->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
+    mReady = nullptr;
   }
 }
 
 bool
 Animation::IsPossiblyOrphanedPendingAnimation() const
 {
   // Check if we are pending but might never start because we are not being
   // tracked.
--- a/dom/animation/test/mozilla/test_moz-prefixed-properties.html
+++ b/dom/animation/test/mozilla/test_moz-prefixed-properties.html
@@ -15,16 +15,19 @@
 const testcases = [
   {
     property: "-moz-box-align"
   },
   {
     property: "-moz-box-direction"
   },
   {
+    property: "-moz-box-ordinal-group"
+  },
+  {
     property: "-moz-box-orient",
     expectedValueMap: {
       "block-axis": "vertical",
       "inline-axis": "horizontal",
     }
   },
   {
     property: "-moz-box-pack"
@@ -35,27 +38,36 @@ const testcases = [
   {
     property: "-moz-orient"
   },
   {
     property: "-moz-osx-font-smoothing",
     pref: "layout.css.osx-font-smoothing.enabled"
   },
   {
+    property: "-moz-stack-sizing"
+  },
+  {
+    property: "-moz-text-size-adjust"
+  },
+  {
     property: "-moz-user-focus"
   },
   {
     property: "-moz-user-input"
   },
   {
     property: "-moz-user-modify"
   },
   {
     property: "-moz-window-dragging"
   },
+  {
+    property: "-moz-force-broken-image-icon"
+  },
 ];
 
 testcases.forEach(testcase => {
   if (testcase.pref && !IsCSSPropertyPrefEnabled(testcase.pref)) {
     return;
   }
 
   const property = gCSSProperties[testcase.property];
--- a/dom/events/IMEStateManager.cpp
+++ b/dom/events/IMEStateManager.cpp
@@ -272,18 +272,19 @@ IMEStateManager::OnDestroyPresContext(ns
   if (sTextCompositions) {
     TextCompositionArray::index_type i =
       sTextCompositions->IndexOf(aPresContext);
     if (i != TextCompositionArray::NoIndex) {
       MOZ_LOG(sISMLog, LogLevel::Debug,
         ("  OnDestroyPresContext(), "
          "removing TextComposition instance from the array (index=%" PRIuSIZE ")", i));
       // there should be only one composition per presContext object.
-      sTextCompositions->ElementAt(i)->Destroy();
+      RefPtr<TextComposition> composition = sTextCompositions->ElementAt(i);
       sTextCompositions->RemoveElementAt(i);
+      composition->Destroy();
       if (sTextCompositions->IndexOf(aPresContext) !=
             TextCompositionArray::NoIndex) {
         MOZ_LOG(sISMLog, LogLevel::Error,
           ("  OnDestroyPresContext(), FAILED to remove "
            "TextComposition instance from the array"));
         MOZ_CRASH("Failed to remove TextComposition instance from the array");
       }
     }
@@ -1288,20 +1289,21 @@ IMEStateManager::DispatchCompositionEven
   if ((!aIsSynthesized ||
        composition->WasNativeCompositionEndEventDiscarded()) &&
       aCompositionEvent->CausesDOMCompositionEndEvent()) {
     TextCompositionArray::index_type i =
       sTextCompositions->IndexOf(aCompositionEvent->mWidget);
     if (i != TextCompositionArray::NoIndex) {
       MOZ_LOG(sISMLog, LogLevel::Debug,
         ("  DispatchCompositionEvent(), "
-         "removing TextComposition from the array since NS_COMPOSTION_END "
+         "removing TextComposition from the array since eCompositionEnd "
          "was dispatched"));
-      sTextCompositions->ElementAt(i)->Destroy();
+      RefPtr<TextComposition> composition = sTextCompositions->ElementAt(i);
       sTextCompositions->RemoveElementAt(i);
+      composition->Destroy();
     }
   }
 }
 
 // static
 IMEContentObserver*
 IMEStateManager::GetActiveContentObserver()
 {
--- a/dom/events/TextComposition.cpp
+++ b/dom/events/TextComposition.cpp
@@ -65,26 +65,40 @@ TextComposition::TextComposition(nsPresC
   , mIsRequestingCommit(false)
   , mIsRequestingCancel(false)
   , mRequestedToCommitOrCancel(false)
   , mWasNativeCompositionEndEventDiscarded(false)
   , mAllowControlCharacters(
       Preferences::GetBool("dom.compositionevent.allow_control_characters",
                            false))
   , mWasCompositionStringEmpty(true)
+  , mHasDispatchedCompositionEvents(false)
 {
   MOZ_ASSERT(aCompositionEvent->mNativeIMEContext.IsValid());
 }
 
+TextComposition::~TextComposition()
+{
+  // WARNING: mPresContext may be destroying, so, be careful if you touch it.
+  if (NS_WARN_IF(mTabParent)) {
+    Destroy();
+  }
+}
+
 void
 TextComposition::Destroy()
 {
   mPresContext = nullptr;
   mNode = nullptr;
-  mTabParent = nullptr;
+  if (mTabParent) {
+    RefPtr<TabParent> tabParent = mTabParent.forget();
+    if (mHasDispatchedCompositionEvents) {
+      tabParent->OnDestroyTextComposition();
+    }
+  }
   // TODO: If the editor is still alive and this is held by it, we should tell
   //       this being destroyed for cleaning up the stuff.
 }
 
 bool
 TextComposition::IsValidStateForComposition(nsIWidget* aWidget) const
 {
   return !Destroyed() && aWidget && !aWidget->Destroyed() &&
@@ -146,16 +160,17 @@ void
 TextComposition::DispatchEvent(WidgetCompositionEvent* aDispatchEvent,
                                nsEventStatus* aStatus,
                                EventDispatchingCallback* aCallBack,
                                const WidgetCompositionEvent *aOriginalEvent)
 {
   nsPluginInstanceOwner::GeneratePluginEvent(aOriginalEvent,
                                              aDispatchEvent);
 
+  mHasDispatchedCompositionEvents = true;
   EventDispatcher::Dispatch(mNode, mPresContext,
                             aDispatchEvent, nullptr, aStatus, aCallBack);
 
   OnCompositionEventDispatched(aDispatchEvent);
 }
 
 void
 TextComposition::OnCompositionEventDiscarded(
@@ -244,16 +259,17 @@ TextComposition::DispatchCompositionEven
                    EventDispatchingCallback* aCallBack,
                    bool aIsSynthesized)
 {
   mWasCompositionStringEmpty = mString.IsEmpty();
 
   // If the content is a container of TabParent, composition should be in the
   // remote process.
   if (mTabParent) {
+    mHasDispatchedCompositionEvents = true;
     Unused << mTabParent->SendCompositionEvent(*aCompositionEvent);
     aCompositionEvent->StopPropagation();
     if (aCompositionEvent->CausesDOMTextEvent()) {
       mLastData = aCompositionEvent->mData;
       mLastRanges = aCompositionEvent->mRanges;
       // Although, the composition event hasn't been actually handled yet,
       // emulate an editor to be handling the composition event.
       EditorWillHandleCompositionChangeEvent(aCompositionEvent);
--- a/dom/events/TextComposition.h
+++ b/dom/events/TextComposition.h
@@ -173,20 +173,17 @@ public:
     RefPtr<TextComposition> mComposition;
     CompositionChangeEventHandlingMarker();
     CompositionChangeEventHandlingMarker(
       const CompositionChangeEventHandlingMarker& aOther);
   };
 
 private:
   // Private destructor, to discourage deletion outside of Release():
-  ~TextComposition()
-  {
-    // WARNING: mPresContext may be destroying, so, be careful if you touch it.
-  }
+  ~TextComposition();
 
   // sHandlingSelectionEvent is true while TextComposition sends a selection
   // event to ContentEventHandler.
   static bool sHandlingSelectionEvent;
 
   // This class holds nsPresContext weak.  This instance shouldn't block
   // destroying it.  When the presContext is being destroyed, it's notified to
   // IMEStateManager::OnDestroyPresContext(), and then, it destroy
@@ -257,31 +254,36 @@ private:
   // both composition string and data attribute of compositionupdate
   // and compositionend events.
   bool mAllowControlCharacters;
 
   // mWasCompositionStringEmpty is true if the composition string was empty
   // when DispatchCompositionEvent() is called.
   bool mWasCompositionStringEmpty;
 
+  // mHasDispatchedCompositionEvents is true if the instance has dispatched
+  // one or more composition events.
+  bool mHasDispatchedCompositionEvents;
+
   // Hide the default constructor and copy constructor.
   TextComposition()
     : mPresContext(nullptr)
     , mNativeContext(nullptr)
     , mCompositionStartOffset(0)
     , mTargetClauseOffsetInComposition(0)
     , mIsSynthesizedForTests(false)
     , mIsComposing(false)
     , mIsEditorHandlingEvent(false)
     , mIsRequestingCommit(false)
     , mIsRequestingCancel(false)
     , mRequestedToCommitOrCancel(false)
     , mWasNativeCompositionEndEventDiscarded(false)
     , mAllowControlCharacters(false)
     , mWasCompositionStringEmpty(true)
+    , mHasDispatchedCompositionEvents(false)
   {}
   TextComposition(const TextComposition& aOther);
 
   /**
    * GetEditor() returns nsIEditor pointer of mEditorWeak.
    */
   already_AddRefed<nsIEditor> GetEditor() const;
 
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -2461,17 +2461,27 @@ nsresult HTMLMediaElement::LoadResource(
       ReportLoadError("MediaLoadInvalidURI", params, ArrayLength(params));
       return rv;
     }
     SetupSrcMediaStreamPlayback(stream);
     return NS_OK;
   }
 
   if (mMediaSource) {
-    RefPtr<MediaSourceDecoder> decoder = new MediaSourceDecoder(this);
+    MediaDecoderInit decoderInit(
+      this,
+      mAudioChannel,
+      mMuted ? 0.0 : mVolume,
+      mPreservesPitch,
+      mPlaybackRate,
+      mPreloadAction == HTMLMediaElement::PRELOAD_METADATA,
+      mHasSuspendTaint,
+      HasAttr(kNameSpaceID_None, nsGkAtoms::loop));
+
+    RefPtr<MediaSourceDecoder> decoder = new MediaSourceDecoder(decoderInit);
     if (!mMediaSource->Attach(decoder)) {
       // TODO: Handle failure: run "If the media data cannot be fetched at
       // all, due to network errors, causing the user agent to give up
       // trying to fetch the resource" section of resource fetch algorithm.
       decoder->Shutdown();
       return NS_ERROR_FAILURE;
     }
     ChangeDelayLoadStatus(false);
@@ -4220,16 +4230,20 @@ HTMLMediaElement::AfterSetAttr(int32_t a
           CheckAutoplayDataReady();
         }
         // This attribute can affect AddRemoveSelfReference
         AddRemoveSelfReference();
         UpdatePreloadAction();
       }
     } else if (aName == nsGkAtoms::preload) {
       UpdatePreloadAction();
+    } else if (aName == nsGkAtoms::loop) {
+      if (mDecoder) {
+        mDecoder->SetLooping(!!aValue);
+      }
     }
   }
 
   // Since AfterMaybeChangeAttr may call DoLoad, make sure that it is called
   // *after* any possible changes to mSrcMediaSource.
   if (aValue) {
     AfterMaybeChangeAttr(aNameSpaceID, aName, aNotify);
   }
@@ -4585,17 +4599,28 @@ HTMLMediaElement::CanPlayType(const nsAS
 nsresult HTMLMediaElement::InitializeDecoderAsClone(MediaDecoder* aOriginal)
 {
   NS_ASSERTION(mLoadingSrc, "mLoadingSrc must already be set");
   NS_ASSERTION(mDecoder == nullptr, "Shouldn't have a decoder");
 
   MediaResource* originalResource = aOriginal->GetResource();
   if (!originalResource)
     return NS_ERROR_FAILURE;
-  RefPtr<MediaDecoder> decoder = aOriginal->Clone(this);
+
+  MediaDecoderInit decoderInit(
+    this,
+    mAudioChannel,
+    mMuted ? 0.0 : mVolume,
+    mPreservesPitch,
+    mPlaybackRate,
+    mPreloadAction == HTMLMediaElement::PRELOAD_METADATA,
+    mHasSuspendTaint,
+    HasAttr(kNameSpaceID_None, nsGkAtoms::loop));
+
+  RefPtr<MediaDecoder> decoder = aOriginal->Clone(decoderInit);
   if (!decoder)
     return NS_ERROR_FAILURE;
 
   LOG(LogLevel::Debug, ("%p Cloned decoder %p from %p", this, decoder.get(), aOriginal));
 
   RefPtr<MediaResource> resource =
     originalResource->CloneData(decoder->GetResourceCallback());
 
@@ -4614,18 +4639,28 @@ nsresult HTMLMediaElement::InitializeDec
   NS_ASSERTION(mLoadingSrc, "mLoadingSrc must already be set");
 
   nsAutoCString mimeType;
 
   aChannel->GetContentType(mimeType);
   NS_ASSERTION(!mimeType.IsEmpty(), "We should have the Content-Type.");
 
   DecoderDoctorDiagnostics diagnostics;
+  MediaDecoderInit decoderInit(
+    this,
+    mAudioChannel,
+    mMuted ? 0.0 : mVolume,
+    mPreservesPitch,
+    mPlaybackRate,
+    mPreloadAction == HTMLMediaElement::PRELOAD_METADATA,
+    mHasSuspendTaint,
+    HasAttr(kNameSpaceID_None, nsGkAtoms::loop));
+
   RefPtr<MediaDecoder> decoder =
-    DecoderTraits::CreateDecoder(mimeType, this, &diagnostics);
+    DecoderTraits::CreateDecoder(mimeType, decoderInit, &diagnostics);
   diagnostics.StoreFormatDiagnostics(OwnerDoc(),
                                      NS_ConvertASCIItoUTF16(mimeType),
                                      decoder != nullptr,
                                      __func__);
   if (!decoder) {
     nsAutoString src;
     GetCurrentSrc(src);
     NS_ConvertUTF8toUTF16 mimeUTF16(mimeType);
@@ -4664,25 +4699,16 @@ nsresult HTMLMediaElement::FinishDecoder
 
   // Set mDecoder now so if methods like GetCurrentSrc get called between
   // here and Load(), they work.
   SetDecoder(aDecoder);
 
   // Tell the decoder about its MediaResource now so things like principals are
   // available immediately.
   mDecoder->SetResource(aStream);
-  mDecoder->SetAudioChannel(mAudioChannel);
-  mDecoder->SetVolume(mMuted ? 0.0 : mVolume);
-  mDecoder->SetPreservesPitch(mPreservesPitch);
-  mDecoder->SetPlaybackRate(mPlaybackRate);
-  if (mPreloadAction == HTMLMediaElement::PRELOAD_METADATA) {
-    mDecoder->SetMinimizePrerollUntilPlaybackStarts();
-  }
-  // Notify the decoder of suspend taint.
-  mDecoder->SetSuspendTaint(mHasSuspendTaint);
   // Notify the decoder of the initial activity status.
   NotifyDecoderActivityChanges();
 
   // Update decoder principal before we start decoding, since it
   // can affect how we feed data to MediaStreams
   NotifyDecoderPrincipalChanged();
 
   nsresult rv = aDecoder->Load(aListener);
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -2062,16 +2062,22 @@ TabParent::SendPasteTransferable(const I
                                  const bool& aIsPrivateData,
                                  const IPC::Principal& aRequestingPrincipal)
 {
   return PBrowserParent::SendPasteTransferable(aDataTransfer,
                                                aIsPrivateData,
                                                aRequestingPrincipal);
 }
 
+void
+TabParent::OnDestroyTextComposition()
+{
+  mContentCache.OnDestroyTextComposition();
+}
+
 /*static*/ TabParent*
 TabParent::GetFrom(nsFrameLoader* aFrameLoader)
 {
   if (!aFrameLoader) {
     return nullptr;
   }
   PBrowserParent* remoteBrowser = aFrameLoader->GetRemoteBrowser();
   return static_cast<TabParent*>(remoteBrowser);
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -502,16 +502,22 @@ public:
   bool SendCompositionEvent(mozilla::WidgetCompositionEvent& aEvent);
 
   bool SendSelectionEvent(mozilla::WidgetSelectionEvent& aEvent);
 
   bool SendPasteTransferable(const IPCDataTransfer& aDataTransfer,
                              const bool& aIsPrivateData,
                              const IPC::Principal& aRequestingPrincipal);
 
+  /**
+   * OnDestroyTextComposition() should be called when TextComposition instance
+   * which dispatched composition events to this instance is being destroyed.
+   */
+  void OnDestroyTextComposition();
+
   static TabParent* GetFrom(nsFrameLoader* aFrameLoader);
 
   static TabParent* GetFrom(nsIFrameLoader* aFrameLoader);
 
   static TabParent* GetFrom(nsITabParent* aTabParent);
 
   static TabParent* GetFrom(PBrowserParent* aTabParent);
 
--- a/dom/media/ADTSDecoder.cpp
+++ b/dom/media/ADTSDecoder.cpp
@@ -9,22 +9,22 @@
 #include "MediaContainerType.h"
 #include "MediaDecoderStateMachine.h"
 #include "MediaFormatReader.h"
 #include "PDMFactory.h"
 
 namespace mozilla {
 
 MediaDecoder*
-ADTSDecoder::Clone(MediaDecoderOwner* aOwner)
+ADTSDecoder::Clone(MediaDecoderInit& aInit)
 {
   if (!IsEnabled())
     return nullptr;
 
-  return new ADTSDecoder(aOwner);
+  return new ADTSDecoder(aInit);
 }
 
 MediaDecoderStateMachine*
 ADTSDecoder::CreateStateMachine()
 {
   RefPtr<MediaDecoderReader> reader =
       new MediaFormatReader(this, new ADTSDemuxer(GetResource()));
   return new MediaDecoderStateMachine(this, reader);
--- a/dom/media/ADTSDecoder.h
+++ b/dom/media/ADTSDecoder.h
@@ -12,18 +12,18 @@
 namespace mozilla {
 
 class MediaContainerType;
 
 class ADTSDecoder : public MediaDecoder
 {
 public:
   // MediaDecoder interface.
-  explicit ADTSDecoder(MediaDecoderOwner* aOwner) : MediaDecoder(aOwner) {}
-  MediaDecoder* Clone(MediaDecoderOwner* aOwner) override;
+  explicit ADTSDecoder(MediaDecoderInit& aInit) : MediaDecoder(aInit) {}
+  MediaDecoder* Clone(MediaDecoderInit& aInit) override;
   MediaDecoderStateMachine* CreateStateMachine() override;
 
   // Returns true if the ADTS backend is pref'ed on, and we're running on a
   // platform that is likely to have decoders for the format.
   static bool IsEnabled();
   static bool IsSupportedType(const MediaContainerType& aContainerType);
 };
 
--- a/dom/media/AbstractMediaDecoder.h
+++ b/dom/media/AbstractMediaDecoder.h
@@ -41,20 +41,16 @@ static inline bool IsCurrentThread(nsITh
 
 /**
  * The AbstractMediaDecoder class describes the public interface for a media decoder
  * and is used by the MediaReader classes.
  */
 class AbstractMediaDecoder : public nsIObserver
 {
 public:
-  // A special version of the above for the ogg decoder that is allowed to be
-  // called cross-thread.
-  virtual bool IsOggDecoderShutdown() { return false; }
-
   // Get the current MediaResource being used. Its URI will be returned
   // by currentSrc. Returns what was passed to Load(), if Load() has been called.
   virtual MediaResource* GetResource() const = 0;
 
   // Increments the parsed, decoded and dropped frame counters by the passed in
   // counts.
   // Can be called on any thread.
   virtual void NotifyDecodedFrames(const FrameStatisticsData& aStats) = 0;
--- a/dom/media/DecoderTraits.cpp
+++ b/dom/media/DecoderTraits.cpp
@@ -252,81 +252,81 @@ bool DecoderTraits::ShouldHandleMediaTyp
 
   return CanHandleMediaType(*containerType, aDiagnostics) != CANPLAY_NO;
 }
 
 // Instantiates but does not initialize decoder.
 static
 already_AddRefed<MediaDecoder>
 InstantiateDecoder(const MediaContainerType& aType,
-                   MediaDecoderOwner* aOwner,
+                   MediaDecoderInit& aInit,
                    DecoderDoctorDiagnostics* aDiagnostics)
 {
   MOZ_ASSERT(NS_IsMainThread());
   RefPtr<MediaDecoder> decoder;
 
 #ifdef MOZ_FMP4
   if (MP4Decoder::IsSupportedType(aType, aDiagnostics)) {
-    decoder = new MP4Decoder(aOwner);
+    decoder = new MP4Decoder(aInit);
     return decoder.forget();
   }
 #endif
   if (MP3Decoder::IsSupportedType(aType)) {
-    decoder = new MP3Decoder(aOwner);
+    decoder = new MP3Decoder(aInit);
     return decoder.forget();
   }
   if (ADTSDecoder::IsSupportedType(aType)) {
-    decoder = new ADTSDecoder(aOwner);
+    decoder = new ADTSDecoder(aInit);
     return decoder.forget();
   }
   if (OggDecoder::IsSupportedType(aType)) {
-    decoder = new OggDecoder(aOwner);
+    decoder = new OggDecoder(aInit);
     return decoder.forget();
   }
   if (WaveDecoder::IsSupportedType(aType)) {
-    decoder = new WaveDecoder(aOwner);
+    decoder = new WaveDecoder(aInit);
     return decoder.forget();
   }
   if (FlacDecoder::IsSupportedType(aType)) {
-    decoder = new FlacDecoder(aOwner);
+    decoder = new FlacDecoder(aInit);
     return decoder.forget();
   }
 #ifdef MOZ_ANDROID_OMX
   if (MediaDecoder::IsAndroidMediaPluginEnabled() &&
       EnsureAndroidMediaPluginHost()->FindDecoder(aType, nullptr)) {
-    decoder = new AndroidMediaDecoder(aOwner, aType);
+    decoder = new AndroidMediaDecoder(aInit, aType);
     return decoder.forget();
   }
 #endif
 
   if (WebMDecoder::IsSupportedType(aType)) {
-    decoder = new WebMDecoder(aOwner);
+    decoder = new WebMDecoder(aInit);
     return decoder.forget();
   }
 
   if (IsHttpLiveStreamingType(aType)) {
     // We don't have an HLS decoder.
     Telemetry::Accumulate(Telemetry::MEDIA_HLS_DECODER_SUCCESS, false);
   }
 
   return nullptr;
 }
 
 /* static */
 already_AddRefed<MediaDecoder>
 DecoderTraits::CreateDecoder(const nsACString& aType,
-                             MediaDecoderOwner* aOwner,
+                             MediaDecoderInit& aInit,
                              DecoderDoctorDiagnostics* aDiagnostics)
 {
   MOZ_ASSERT(NS_IsMainThread());
   Maybe<MediaContainerType> type = MakeMediaContainerType(aType);
   if (!type) {
     return nullptr;
   }
-  return InstantiateDecoder(*type, aOwner, aDiagnostics);
+  return InstantiateDecoder(*type, aInit, aDiagnostics);
 }
 
 /* static */
 MediaDecoderReader*
 DecoderTraits::CreateReader(const MediaContainerType& aType,
                             AbstractMediaDecoder* aDecoder)
 {
   MOZ_ASSERT(NS_IsMainThread());
--- a/dom/media/DecoderTraits.h
+++ b/dom/media/DecoderTraits.h
@@ -13,16 +13,17 @@ class nsAString;
 class nsACString;
 
 namespace mozilla {
 
 class AbstractMediaDecoder;
 class DecoderDoctorDiagnostics;
 class MediaContainerType;
 class MediaDecoder;
+struct MediaDecoderInit;
 class MediaDecoderOwner;
 class MediaDecoderReader;
 
 enum CanPlayStatus {
   CANPLAY_NO,
   CANPLAY_MAYBE,
   CANPLAY_YES
 };
@@ -38,17 +39,17 @@ public:
   // for the type is more limited than appears in the wild, we should return
   // false here even if CanHandleMediaType would return true.
   static bool ShouldHandleMediaType(const char* aMIMEType,
                                     DecoderDoctorDiagnostics* aDiagnostics);
 
   // Create a decoder for the given aType. Returns null if we
   // were unable to create the decoder.
   static already_AddRefed<MediaDecoder> CreateDecoder(const nsACString& aType,
-                                                      MediaDecoderOwner* aOwner,
+                                                      MediaDecoderInit& aInit,
                                                       DecoderDoctorDiagnostics* aDiagnostics);
 
   // Create a reader for thew given MIME type aType. Returns null
   // if we were unable to create the reader.
   static MediaDecoderReader* CreateReader(const MediaContainerType& aType,
                                           AbstractMediaDecoder* aDecoder);
 
   // Returns true if MIME type aType is supported in video documents,
--- a/dom/media/MP3Decoder.cpp
+++ b/dom/media/MP3Decoder.cpp
@@ -10,21 +10,21 @@
 #include "MediaDecoderStateMachine.h"
 #include "MediaFormatReader.h"
 #include "MP3Demuxer.h"
 #include "PDMFactory.h"
 
 namespace mozilla {
 
 MediaDecoder*
-MP3Decoder::Clone(MediaDecoderOwner* aOwner) {
+MP3Decoder::Clone(MediaDecoderInit& aInit) {
   if (!IsEnabled()) {
     return nullptr;
   }
-  return new MP3Decoder(aOwner);
+  return new MP3Decoder(aInit);
 }
 
 MediaDecoderStateMachine*
 MP3Decoder::CreateStateMachine() {
   RefPtr<MediaDecoderReader> reader =
       new MediaFormatReader(this, new mp3::MP3Demuxer(GetResource()));
   return new MediaDecoderStateMachine(this, reader);
 }
--- a/dom/media/MP3Decoder.h
+++ b/dom/media/MP3Decoder.h
@@ -11,18 +11,18 @@
 namespace mozilla {
 
 class MediaContainerType;
 
 class MP3Decoder : public MediaDecoder
 {
 public:
   // MediaDecoder interface.
-  explicit MP3Decoder(MediaDecoderOwner* aOwner) : MediaDecoder(aOwner) {}
-  MediaDecoder* Clone(MediaDecoderOwner* aOwner) override;
+  explicit MP3Decoder(MediaDecoderInit& aInit) : MediaDecoder(aInit) {}
+  MediaDecoder* Clone(MediaDecoderInit& aInit) override;
   MediaDecoderStateMachine* CreateStateMachine() override;
 
   // Returns true if the MP3 backend is preffed on, and we're running on a
   // platform that is likely to have decoders for the format.
   static bool IsEnabled();
   static bool IsSupportedType(const MediaContainerType& aContainerType);
 };
 
--- a/dom/media/MediaDecoder.cpp
+++ b/dom/media/MediaDecoder.cpp
@@ -361,80 +361,77 @@ MediaDecoder::SetInfinite(bool aInfinite
 bool
 MediaDecoder::IsInfinite() const
 {
   MOZ_ASSERT(NS_IsMainThread());
   return mInfiniteStream;
 }
 
 #define INIT_MIRROR(name, val) \
-  name(aOwner->AbstractMainThread(), val, "MediaDecoder::" #name " (Mirror)")
+  name(mOwner->AbstractMainThread(), val, "MediaDecoder::" #name " (Mirror)")
 #define INIT_CANONICAL(name, val) \
-  name(aOwner->AbstractMainThread(), val, "MediaDecoder::" #name " (Canonical)")
+  name(mOwner->AbstractMainThread(), val, "MediaDecoder::" #name " (Canonical)")
 
-MediaDecoder::MediaDecoder(MediaDecoderOwner* aOwner)
-  : mWatchManager(this, aOwner->AbstractMainThread())
+MediaDecoder::MediaDecoder(MediaDecoderInit& aInit)
+  : mWatchManager(this, aInit.mOwner->AbstractMainThread())
   , mLogicalPosition(0.0)
   , mDuration(std::numeric_limits<double>::quiet_NaN())
-  , mResourceCallback(new ResourceCallback(aOwner->AbstractMainThread()))
+  , mResourceCallback(new ResourceCallback(aInit.mOwner->AbstractMainThread()))
   , mCDMProxyPromise(mCDMProxyPromiseHolder.Ensure(__func__))
   , mIgnoreProgressData(false)
   , mInfiniteStream(false)
-  , mOwner(aOwner)
-  , mAbstractMainThread(aOwner->AbstractMainThread())
+  , mOwner(aInit.mOwner)
+  , mAbstractMainThread(aInit.mOwner->AbstractMainThread())
   , mFrameStats(new FrameStatistics())
-  , mVideoFrameContainer(aOwner->GetVideoFrameContainer())
+  , mVideoFrameContainer(aInit.mOwner->GetVideoFrameContainer())
   , mPinnedForSeek(false)
-  , mMinimizePreroll(false)
+  , mAudioChannel(aInit.mAudioChannel)
+  , mMinimizePreroll(aInit.mMinimizePreroll)
   , mFiredMetadataLoaded(false)
   , mIsDocumentVisible(false)
   , mElementVisibility(Visibility::UNTRACKED)
   , mIsElementInTree(false)
   , mForcedHidden(false)
-  , mHasSuspendTaint(false)
-  , INIT_MIRROR(mStateMachineIsShutdown, true)
+  , mHasSuspendTaint(aInit.mHasSuspendTaint)
+  , mPlaybackRate(aInit.mPlaybackRate)
   , INIT_MIRROR(mBuffered, TimeIntervals())
   , INIT_MIRROR(mNextFrameStatus, MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE)
   , INIT_MIRROR(mCurrentPosition, TimeUnit::Zero())
   , INIT_MIRROR(mStateMachineDuration, NullableTimeUnit())
   , INIT_MIRROR(mPlaybackPosition, 0)
   , INIT_MIRROR(mIsAudioDataAudible, false)
-  , INIT_CANONICAL(mVolume, 0.0)
-  , INIT_CANONICAL(mPreservesPitch, true)
+  , INIT_CANONICAL(mVolume, aInit.mVolume)
+  , INIT_CANONICAL(mPreservesPitch, aInit.mPreservesPitch)
+  , INIT_CANONICAL(mLooping, aInit.mLooping)
   , INIT_CANONICAL(mExplicitDuration, Maybe<double>())
   , INIT_CANONICAL(mPlayState, PLAY_STATE_LOADING)
   , INIT_CANONICAL(mNextState, PLAY_STATE_PAUSED)
   , INIT_CANONICAL(mLogicallySeeking, false)
   , INIT_CANONICAL(mSameOriginMedia, false)
   , INIT_CANONICAL(mMediaPrincipalHandle, PRINCIPAL_HANDLE_NONE)
   , INIT_CANONICAL(mPlaybackBytesPerSecond, 0.0)
   , INIT_CANONICAL(mPlaybackRateReliable, true)
   , INIT_CANONICAL(mDecoderPosition, 0)
   , mTelemetryReported(false)
-  , mIsMediaElement(!!aOwner->GetMediaElement())
-  , mElement(aOwner->GetMediaElement())
+  , mIsMediaElement(!!mOwner->GetMediaElement())
+  , mElement(mOwner->GetMediaElement())
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mAbstractMainThread);
   MediaMemoryTracker::AddMediaDecoder(this);
 
-  mAudioChannel = AudioChannelService::GetDefaultAudioChannel();
   mResourceCallback->Connect(this);
 
   //
   // Initialize watchers.
   //
 
   // mDuration
   mWatchManager.Watch(mStateMachineDuration, &MediaDecoder::DurationChanged);
 
-  // mStateMachineIsShutdown
-  mWatchManager.Watch(mStateMachineIsShutdown,
-                      &MediaDecoder::ShutdownBitChanged);
-
   // readyState
   mWatchManager.Watch(mPlayState, &MediaDecoder::UpdateReadyState);
   mWatchManager.Watch(mNextFrameStatus, &MediaDecoder::UpdateReadyState);
   // ReadyState computation depends on MediaDecoder::CanPlayThrough, which
   // depends on the download rate.
   mWatchManager.Watch(mBuffered, &MediaDecoder::UpdateReadyState);
 
   // mLogicalPosition
@@ -676,28 +673,16 @@ MediaDecoder::SetStateMachineParameters(
   mOnPlaybackErrorEvent = mDecoderStateMachine->OnPlaybackErrorEvent().Connect(
     mAbstractMainThread, this, &MediaDecoder::OnPlaybackErrorEvent);
   mOnDecoderDoctorEvent = mDecoderStateMachine->OnDecoderDoctorEvent().Connect(
     mAbstractMainThread, this, &MediaDecoder::OnDecoderDoctorEvent);
   mOnMediaNotSeekable = mDecoderStateMachine->OnMediaNotSeekable().Connect(
     mAbstractMainThread, this, &MediaDecoder::OnMediaNotSeekable);
 }
 
-void
-MediaDecoder::SetMinimizePrerollUntilPlaybackStarts()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  LOG("SetMinimizePrerollUntilPlaybackStarts()");
-  mMinimizePreroll = true;
-
-  // This needs to be called before we init the state machine, otherwise it will
-  // have no effect.
-  MOZ_DIAGNOSTIC_ASSERT(!mDecoderStateMachine);
-}
-
 nsresult
 MediaDecoder::Play()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   NS_ASSERTION(mDecoderStateMachine != nullptr, "Should have state machine.");
   if (mPlaybackRate == 0) {
     return NS_OK;
@@ -1458,36 +1443,41 @@ MediaDecoder::SetPlaybackRate(double aPl
 void
 MediaDecoder::SetPreservesPitch(bool aPreservesPitch)
 {
   MOZ_ASSERT(NS_IsMainThread());
   mPreservesPitch = aPreservesPitch;
 }
 
 void
+MediaDecoder::SetLooping(bool aLooping)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  mLooping = aLooping;
+}
+
+void
 MediaDecoder::ConnectMirrors(MediaDecoderStateMachine* aObject)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aObject);
   mStateMachineDuration.Connect(aObject->CanonicalDuration());
   mBuffered.Connect(aObject->CanonicalBuffered());
-  mStateMachineIsShutdown.Connect(aObject->CanonicalIsShutdown());
   mNextFrameStatus.Connect(aObject->CanonicalNextFrameStatus());
   mCurrentPosition.Connect(aObject->CanonicalCurrentPosition());
   mPlaybackPosition.Connect(aObject->CanonicalPlaybackOffset());
   mIsAudioDataAudible.Connect(aObject->CanonicalIsAudioDataAudible());
 }
 
 void
 MediaDecoder::DisconnectMirrors()
 {
   MOZ_ASSERT(NS_IsMainThread());
   mStateMachineDuration.DisconnectIfConnected();
   mBuffered.DisconnectIfConnected();
-  mStateMachineIsShutdown.DisconnectIfConnected();
   mNextFrameStatus.DisconnectIfConnected();
   mCurrentPosition.DisconnectIfConnected();
   mPlaybackPosition.DisconnectIfConnected();
   mIsAudioDataAudible.DisconnectIfConnected();
 }
 
 void
 MediaDecoder::SetStateMachine(MediaDecoderStateMachine* aStateMachine)
--- a/dom/media/MediaDecoder.h
+++ b/dom/media/MediaDecoder.h
@@ -52,16 +52,47 @@ enum class MediaEventType : int8_t;
 enum class Visibility : uint8_t;
 
 // GetCurrentTime is defined in winbase.h as zero argument macro forwarding to
 // GetTickCount() and conflicts with MediaDecoder::GetCurrentTime implementation.
 #ifdef GetCurrentTime
 #undef GetCurrentTime
 #endif
 
+struct MediaDecoderInit
+{
+  MediaDecoderOwner* const mOwner;
+  const dom::AudioChannel mAudioChannel;
+  const double mVolume;
+  const bool mPreservesPitch;
+  const double mPlaybackRate;
+  const bool mMinimizePreroll;
+  const bool mHasSuspendTaint;
+  const bool mLooping;
+
+  MediaDecoderInit(MediaDecoderOwner* aOwner,
+                   dom::AudioChannel aAudioChannel,
+                   double aVolume,
+                   bool aPreservesPitch,
+                   double aPlaybackRate,
+                   bool aMinimizePreroll,
+                   bool aHasSuspendTaint,
+                   bool aLooping)
+    : mOwner(aOwner)
+    , mAudioChannel(aAudioChannel)
+    , mVolume(aVolume)
+    , mPreservesPitch(aPreservesPitch)
+    , mPlaybackRate(aPlaybackRate)
+    , mMinimizePreroll(aMinimizePreroll)
+    , mHasSuspendTaint(aHasSuspendTaint)
+    , mLooping(aLooping)
+  {
+  }
+};
+
 class MediaDecoder : public AbstractMediaDecoder
 {
 public:
   // Used to register with MediaResource to receive notifications which will
   // be forwarded to MediaDecoder.
   class ResourceCallback : public MediaResourceCallback
   {
     // Throttle calls to MediaDecoder::NotifyDataArrived()
@@ -112,25 +143,25 @@ public:
     PLAY_STATE_PLAYING,
     PLAY_STATE_ENDED,
     PLAY_STATE_SHUTDOWN
   };
 
   // Must be called exactly once, on the main thread, during startup.
   static void InitStatics();
 
-  explicit MediaDecoder(MediaDecoderOwner* aOwner);
+  explicit MediaDecoder(MediaDecoderInit& aInit);
 
   // Return a callback object used to register with MediaResource to receive
   // notifications.
   MediaResourceCallback* GetResourceCallback() const;
 
   // Create a new decoder of the same type as this one.
   // Subclasses must implement this.
-  virtual MediaDecoder* Clone(MediaDecoderOwner* aOwner) = 0;
+  virtual MediaDecoder* Clone(MediaDecoderInit& aInit) = 0;
   // Create a new state machine to run this decoder.
   // Subclasses must implement this.
   virtual MediaDecoderStateMachine* CreateStateMachine() = 0;
 
   // Cleanup internal data structures. Must be called on the main
   // thread by the owning object before that object disposes of this object.
   virtual void Shutdown();
 
@@ -190,25 +221,21 @@ public:
   // Notify activity of the decoder owner is changed.
   virtual void NotifyOwnerActivityChanged(bool aIsDocumentVisible,
                                           Visibility aElementVisibility,
                                           bool aIsElementInTree);
 
   // Pause video playback.
   virtual void Pause();
   // Adjust the speed of the playback, optionally with pitch correction,
-  virtual void SetVolume(double aVolume);
-
-  virtual void SetPlaybackRate(double aPlaybackRate);
-  void SetPreservesPitch(bool aPreservesPitch);
+  void SetVolume(double aVolume);
 
-  // Directs the decoder to not preroll extra samples until the media is
-  // played. This reduces the memory overhead of media elements that may
-  // not be played. Note that seeking also doesn't cause us start prerolling.
-  void SetMinimizePrerollUntilPlaybackStarts();
+  void SetPlaybackRate(double aPlaybackRate);
+  void SetPreservesPitch(bool aPreservesPitch);
+  void SetLooping(bool aLooping);
 
   bool GetMinimizePreroll() const { return mMinimizePreroll; }
 
   // All MediaStream-related data is protected by mReentrantMonitor.
   // We have at most one DecodedStreamData per MediaDecoder. Its stream
   // is used as the input for each ProcessedMediaStream created by calls to
   // captureStream(UntilEnded). Seeking creates a new source stream, as does
   // replaying after the input as ended. In the latter case, the new source is
@@ -352,17 +379,16 @@ private:
 
   // The actual playback rate computation. The monitor must be held.
   void ComputePlaybackRate();
 
   // Returns true if we can play the entire media through without stopping
   // to buffer, given the current download and playback rates.
   virtual bool CanPlayThrough();
 
-  void SetAudioChannel(dom::AudioChannel aChannel) { mAudioChannel = aChannel; }
   dom::AudioChannel GetAudioChannel() { return mAudioChannel; }
 
   // Called from HTMLMediaElement when owner document activity changes
   virtual void SetElementVisibility(bool aIsDocumentVisible,
                                     Visibility aElementVisibility,
                                     bool aIsElementInTree);
 
   // Force override the visible state to hidden.
@@ -511,19 +537,16 @@ protected:
 
   // Called by the state machine to notify the decoder that the duration
   // has changed.
   void DurationChanged();
 
   // State-watching manager.
   WatchManager<MediaDecoder> mWatchManager;
 
-  // Used by the ogg decoder to watch mStateMachineIsShutdown.
-  virtual void ShutdownBitChanged() {}
-
   double ExplicitDuration() { return mExplicitDuration.Ref().ref(); }
 
   void SetExplicitDuration(double aValue)
   {
     MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
     mExplicitDuration.Set(Some(aValue));
 
     // We Invoke DurationChanged explicitly, rather than using a watcher, so
@@ -663,23 +686,23 @@ protected:
   MediaChannelStatistics mPlaybackStatistics;
 
   // True when our media stream has been pinned. We pin the stream
   // while seeking.
   bool mPinnedForSeek;
 
   // Be assigned from media element during the initialization and pass to
   // AudioStream Class.
-  dom::AudioChannel mAudioChannel;
+  const dom::AudioChannel mAudioChannel;
 
   // True if the decoder has been directed to minimize its preroll before
   // playback starts. After the first time playback starts, we don't attempt
   // to minimize preroll, as we assume the user is likely to keep playing,
   // or play the media again.
-  bool mMinimizePreroll;
+  const bool mMinimizePreroll;
 
   // True if we've already fired metadataloaded.
   bool mFiredMetadataLoaded;
 
   // True if the media is seekable (i.e. supports random access).
   bool mMediaSeekable = true;
 
   // True if the media is only seekable within its buffered ranges
@@ -713,18 +736,18 @@ protected:
   MediaEventListener mFirstFrameLoadedListener;
 
   MediaEventListener mOnPlaybackEvent;
   MediaEventListener mOnPlaybackErrorEvent;
   MediaEventListener mOnDecoderDoctorEvent;
   MediaEventListener mOnMediaNotSeekable;
 
 protected:
-  // Whether the state machine is shut down.
-  Mirror<bool> mStateMachineIsShutdown;
+  // PlaybackRate and pitch preservation status we should start at.
+  double mPlaybackRate;
 
   // Buffered range, mirrored from the reader.
   Mirror<media::TimeIntervals> mBuffered;
 
   // NextFrameStatus, mirrored from the state machine.
   Mirror<MediaDecoderOwner::NextFrameStatus> mNextFrameStatus;
 
   // NB: Don't use mCurrentPosition directly, but rather CurrentPosition().
@@ -740,20 +763,19 @@ protected:
   Mirror<int64_t> mPlaybackPosition;
 
   // Used to distinguish whether the audio is producing sound.
   Mirror<bool> mIsAudioDataAudible;
 
   // Volume of playback.  0.0 = muted. 1.0 = full volume.
   Canonical<double> mVolume;
 
-  // PlaybackRate and pitch preservation status we should start at.
-  double mPlaybackRate = 1;
+  Canonical<bool> mPreservesPitch;
 
-  Canonical<bool> mPreservesPitch;
+  Canonical<bool> mLooping;
 
   // Media duration set explicitly by JS. At present, this is only ever present
   // for MSE.
   Canonical<Maybe<double>> mExplicitDuration;
 
   // Set to one of the valid play states.
   // This can only be changed on the main thread while holding the decoder
   // monitor. Thus, it can be safely read while holding the decoder monitor
@@ -790,16 +812,20 @@ protected:
 
 public:
   AbstractCanonical<media::NullableTimeUnit>* CanonicalDurationOrNull() override;
   AbstractCanonical<double>* CanonicalVolume() { return &mVolume; }
   AbstractCanonical<bool>* CanonicalPreservesPitch()
   {
     return &mPreservesPitch;
   }
+  AbstractCanonical<bool>* CanonicalLooping()
+  {
+    return &mLooping;
+  }
   AbstractCanonical<Maybe<double>>* CanonicalExplicitDuration()
   {
     return &mExplicitDuration;
   }
   AbstractCanonical<PlayState>* CanonicalPlayState() { return &mPlayState; }
   AbstractCanonical<PlayState>* CanonicalNextPlayState() { return &mNextState; }
   AbstractCanonical<bool>* CanonicalLogicallySeeking()
   {
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -1905,18 +1905,21 @@ class MediaDecoderStateMachine::Complete
 public:
   explicit CompletedState(Master* aPtr) : StateObject(aPtr) { }
 
   void Enter()
   {
     // TODO : use more approriate way to decide whether need to release
     // resource in bug1367983.
 #ifndef MOZ_WIDGET_ANDROID
-    // We've decoded all samples. We don't need decoders anymore.
-    Reader()->ReleaseResources();
+    if (!mMaster->mLooping) {
+      // We've decoded all samples.
+      // We don't need decoders anymore if not looping.
+      Reader()->ReleaseResources();
+    }
 #endif
     bool hasNextFrame = (!mMaster->HasAudio() || !mMaster->mAudioCompleted)
                         && (!mMaster->HasVideo() || !mMaster->mVideoCompleted);
 
     mMaster->UpdateNextFrameStatus(
       hasNextFrame ? MediaDecoderOwner::NEXT_FRAME_AVAILABLE
                    : MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE);
 
@@ -2634,17 +2637,16 @@ BufferingState::HandleEndOfVideo()
 }
 
 RefPtr<ShutdownPromise>
 MediaDecoderStateMachine::
 ShutdownState::Enter()
 {
   auto master = mMaster;
 
-  master->mIsShutdown = true;
   master->mDelayedScheduler.Reset();
 
   // Shutdown happens while decode timer is active, we need to disconnect and
   // dispose of the timer.
   master->CancelSuspendTimer();
 
   master->mCDMProxyPromise.DisconnectIfExists();
 
@@ -2669,24 +2671,24 @@ ShutdownState::Enter()
 
   // Disconnect canonicals and mirrors before shutting down our task queue.
   master->mBuffered.DisconnectIfConnected();
   master->mExplicitDuration.DisconnectIfConnected();
   master->mPlayState.DisconnectIfConnected();
   master->mNextPlayState.DisconnectIfConnected();
   master->mVolume.DisconnectIfConnected();
   master->mPreservesPitch.DisconnectIfConnected();
+  master->mLooping.DisconnectIfConnected();
   master->mSameOriginMedia.DisconnectIfConnected();
   master->mMediaPrincipalHandle.DisconnectIfConnected();
   master->mPlaybackBytesPerSecond.DisconnectIfConnected();
   master->mPlaybackRateReliable.DisconnectIfConnected();
   master->mDecoderPosition.DisconnectIfConnected();
 
   master->mDuration.DisconnectAll();
-  master->mIsShutdown.DisconnectAll();
   master->mNextFrameStatus.DisconnectAll();
   master->mCurrentPosition.DisconnectAll();
   master->mPlaybackOffset.DisconnectAll();
   master->mIsAudioDataAudible.DisconnectAll();
 
   // Shut down the watch manager to stop further notifications.
   master->mWatchManager.Shutdown();
 
@@ -2732,23 +2734,23 @@ MediaDecoderStateMachine::MediaDecoderSt
   mVideoDecodeMode(VideoDecodeMode::Normal),
   mIsMSE(aDecoder->IsMSE()),
   INIT_MIRROR(mBuffered, TimeIntervals()),
   INIT_MIRROR(mExplicitDuration, Maybe<double>()),
   INIT_MIRROR(mPlayState, MediaDecoder::PLAY_STATE_LOADING),
   INIT_MIRROR(mNextPlayState, MediaDecoder::PLAY_STATE_PAUSED),
   INIT_MIRROR(mVolume, 1.0),
   INIT_MIRROR(mPreservesPitch, true),
+  INIT_MIRROR(mLooping, false),
   INIT_MIRROR(mSameOriginMedia, false),
   INIT_MIRROR(mMediaPrincipalHandle, PRINCIPAL_HANDLE_NONE),
   INIT_MIRROR(mPlaybackBytesPerSecond, 0.0),
   INIT_MIRROR(mPlaybackRateReliable, true),
   INIT_MIRROR(mDecoderPosition, 0),
   INIT_CANONICAL(mDuration, NullableTimeUnit()),
-  INIT_CANONICAL(mIsShutdown, false),
   INIT_CANONICAL(mNextFrameStatus, MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE),
   INIT_CANONICAL(mCurrentPosition, TimeUnit::Zero()),
   INIT_CANONICAL(mPlaybackOffset, 0),
   INIT_CANONICAL(mIsAudioDataAudible, false)
 #ifdef XP_WIN
   , mShouldUseHiResTimers(Preferences::GetBool("media.hi-res-timers.enabled", true))
 #endif
 {
@@ -2779,16 +2781,17 @@ MediaDecoderStateMachine::Initialization
 
   // Connect mirrors.
   mBuffered.Connect(mReader->CanonicalBuffered());
   mExplicitDuration.Connect(aDecoder->CanonicalExplicitDuration());
   mPlayState.Connect(aDecoder->CanonicalPlayState());
   mNextPlayState.Connect(aDecoder->CanonicalNextPlayState());
   mVolume.Connect(aDecoder->CanonicalVolume());
   mPreservesPitch.Connect(aDecoder->CanonicalPreservesPitch());
+  mLooping.Connect(aDecoder->CanonicalLooping());
   mSameOriginMedia.Connect(aDecoder->CanonicalSameOriginMedia());
   mMediaPrincipalHandle.Connect(aDecoder->CanonicalMediaPrincipalHandle());
   mPlaybackBytesPerSecond.Connect(aDecoder->CanonicalPlaybackBytesPerSecond());
   mPlaybackRateReliable.Connect(aDecoder->CanonicalPlaybackRateReliable());
   mDecoderPosition.Connect(aDecoder->CanonicalDecoderPosition());
 
   // Initialize watchers.
   mWatchManager.Watch(mBuffered,
@@ -3214,21 +3217,16 @@ void MediaDecoderStateMachine::BufferedR
   }
 }
 
 RefPtr<MediaDecoder::SeekPromise>
 MediaDecoderStateMachine::Seek(const SeekTarget& aTarget)
 {
   MOZ_ASSERT(OnTaskQueue());
 
-  if (IsShutdown()) {
-    return MediaDecoder::SeekPromise::CreateAndReject(/* aIgnored = */ true,
-                                                      __func__);
-  }
-
   // We need to be able to seek in some way
   if (!mMediaSeekable && !mMediaSeekableOnlyInBufferedRanges) {
     LOGW("Seek() should not be called on a non-seekable media");
     return MediaDecoder::SeekPromise::CreateAndReject(/* aIgnored = */ true,
                                                       __func__);
   }
 
   if (aTarget.IsNextFrame() && !HasVideo()) {
@@ -3497,17 +3495,16 @@ MediaDecoderStateMachine::HasLowBuffered
   media::TimeInterval interval(start, end);
   return !mBuffered.Ref().Contains(interval);
 }
 
 void
 MediaDecoderStateMachine::DecodeError(const MediaResult& aError)
 {
   MOZ_ASSERT(OnTaskQueue());
-  MOZ_ASSERT(!IsShutdown());
   LOGW("Decode error");
   // Notify the decode error and MediaDecoder will shut down MDSM.
   mOnPlaybackErrorEvent.Notify(aError);
 }
 
 void
 MediaDecoderStateMachine::EnqueueFirstFrameLoadedEvent()
 {
@@ -3759,23 +3756,16 @@ MediaDecoderStateMachine::SetPlaybackRat
 }
 
 void MediaDecoderStateMachine::PreservesPitchChanged()
 {
   MOZ_ASSERT(OnTaskQueue());
   mMediaSink->SetPreservesPitch(mPreservesPitch);
 }
 
-bool
-MediaDecoderStateMachine::IsShutdown() const
-{
-  MOZ_ASSERT(OnTaskQueue());
-  return mIsShutdown;
-}
-
 TimeUnit
 MediaDecoderStateMachine::AudioEndTime() const
 {
   MOZ_ASSERT(OnTaskQueue());
   if (mMediaSink->IsStarted()) {
     return mMediaSink->GetEndTime(TrackInfo::kAudioTrack);
   }
   return TimeUnit::Zero();
--- a/dom/media/MediaDecoderStateMachine.h
+++ b/dom/media/MediaDecoderStateMachine.h
@@ -300,19 +300,16 @@ private:
   // Invokes ScheduleStateMachine to run in |aTime|,
   // unless it's already scheduled to run earlier, in which case the
   // request is discarded.
   void ScheduleStateMachineIn(const media::TimeUnit& aTime);
 
   bool HaveEnoughDecodedAudio();
   bool HaveEnoughDecodedVideo();
 
-  // True if shutdown process has begun.
-  bool IsShutdown() const;
-
   // Returns true if we're currently playing. The decoder monitor must
   // be held.
   bool IsPlaying() const;
 
   // Sets mMediaSeekable to false.
   void SetMediaNotSeekable();
 
   // Resets all states related to decoding and aborts all pending requests
@@ -700,16 +697,20 @@ private:
   Mirror<MediaDecoder::PlayState> mNextPlayState;
 
   // Volume of playback. 0.0 = muted. 1.0 = full volume.
   Mirror<double> mVolume;
 
   // Pitch preservation for the playback rate.
   Mirror<bool> mPreservesPitch;
 
+  // Whether to seek back to the start of the media resource
+  // upon reaching the end.
+  Mirror<bool> mLooping;
+
   // True if the media is same-origin with the element. Data can only be
   // passed to MediaStreams when this is true.
   Mirror<bool> mSameOriginMedia;
 
   // An identifier for the principal of the media. Used to track when
   // main-thread induced principal changes get reflected on MSG thread.
   Mirror<PrincipalHandle> mMediaPrincipalHandle;
 
@@ -722,19 +723,16 @@ private:
   // Current decoding position in the stream.
   Mirror<int64_t> mDecoderPosition;
 
 
   // Duration of the media. This is guaranteed to be non-null after we finish
   // decoding the first frame.
   Canonical<media::NullableTimeUnit> mDuration;
 
-  // Whether we're currently in or transitioning to shutdown state.
-  Canonical<bool> mIsShutdown;
-
   // The status of our next frame. Mirrored on the main thread and used to
   // compute ready state.
   Canonical<NextFrameStatus> mNextFrameStatus;
 
   // The time of the current frame, corresponding to the "current
   // playback position" in HTML5. This is referenced from 0, which is the initial
   // playback position.
   Canonical<media::TimeUnit> mCurrentPosition;
@@ -747,17 +745,16 @@ private:
 
 public:
   AbstractCanonical<media::TimeIntervals>* CanonicalBuffered() const;
 
   AbstractCanonical<media::NullableTimeUnit>* CanonicalDuration()
   {
     return &mDuration;
   }
-  AbstractCanonical<bool>* CanonicalIsShutdown() { return &mIsShutdown; }
   AbstractCanonical<NextFrameStatus>* CanonicalNextFrameStatus()
   {
     return &mNextFrameStatus;
   }
   AbstractCanonical<media::TimeUnit>* CanonicalCurrentPosition()
   {
     return &mCurrentPosition;
   }
--- a/dom/media/android/AndroidMediaDecoder.cpp
+++ b/dom/media/android/AndroidMediaDecoder.cpp
@@ -5,19 +5,19 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "MediaDecoderStateMachine.h"
 #include "AndroidMediaDecoder.h"
 #include "AndroidMediaReader.h"
 
 namespace mozilla {
 
-AndroidMediaDecoder::AndroidMediaDecoder(MediaDecoderOwner* aOwner,
+AndroidMediaDecoder::AndroidMediaDecoder(MediaDecoderInit& aInit,
                                          const MediaContainerType& aType)
-  : MediaDecoder(aOwner), mType(aType)
+  : MediaDecoder(aInit), mType(aType)
 {
 }
 
 MediaDecoderStateMachine* AndroidMediaDecoder::CreateStateMachine()
 {
   return new MediaDecoderStateMachine(this, new AndroidMediaReader(this, mType));
 }
 
--- a/dom/media/android/AndroidMediaDecoder.h
+++ b/dom/media/android/AndroidMediaDecoder.h
@@ -11,19 +11,19 @@
 #include "MediaContainerType.h"
 
 namespace mozilla {
 
 class AndroidMediaDecoder : public MediaDecoder
 {
   MediaContainerType mType;
 public:
-  AndroidMediaDecoder(MediaDecoderOwner* aOwner, const MediaContainerType& aType);
+  AndroidMediaDecoder(MediaDecoderInit& aInit, const MediaContainerType& aType);
 
-  MediaDecoder* Clone(MediaDecoderOwner* aOwner) override {
-    return new AndroidMediaDecoder(aOwner, mType);
+  MediaDecoder* Clone(MediaDecoderInit& aInit) override {
+    return new AndroidMediaDecoder(aInit, mType);
   }
   MediaDecoderStateMachine* CreateStateMachine() override;
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/media/flac/FlacDecoder.cpp
+++ b/dom/media/flac/FlacDecoder.cpp
@@ -9,23 +9,23 @@
 #include "MediaContainerType.h"
 #include "MediaDecoderStateMachine.h"
 #include "MediaFormatReader.h"
 #include "MediaPrefs.h"
 
 namespace mozilla {
 
 MediaDecoder*
-FlacDecoder::Clone(MediaDecoderOwner* aOwner)
+FlacDecoder::Clone(MediaDecoderInit& aInit)
 {
   if (!IsEnabled()) {
     return nullptr;
   }
 
-  return new FlacDecoder(aOwner);
+  return new FlacDecoder(aInit);
 }
 
 MediaDecoderStateMachine*
 FlacDecoder::CreateStateMachine()
 {
   RefPtr<MediaDecoderReader> reader =
       new MediaFormatReader(this, new FlacDemuxer(GetResource()));
   return new MediaDecoderStateMachine(this, reader);
--- a/dom/media/flac/FlacDecoder.h
+++ b/dom/media/flac/FlacDecoder.h
@@ -12,18 +12,18 @@
 namespace mozilla {
 
 class MediaContainerType;
 
 class FlacDecoder : public MediaDecoder
 {
 public:
   // MediaDecoder interface.
-  explicit FlacDecoder(MediaDecoderOwner* aOwner) : MediaDecoder(aOwner) {}
-  MediaDecoder* Clone(MediaDecoderOwner* aOwner) override;
+  explicit FlacDecoder(MediaDecoderInit& aInit) : MediaDecoder(aInit) {}
+  MediaDecoder* Clone(MediaDecoderInit& aInit) override;
   MediaDecoderStateMachine* CreateStateMachine() override;
 
   // Returns true if the Flac backend is pref'ed on, and we're running on a
   // platform that is likely to have decoders for the format.
   static bool IsEnabled();
   static bool IsSupportedType(const MediaContainerType& aContainerType);
 };
 
--- a/dom/media/fmp4/MP4Decoder.cpp
+++ b/dom/media/fmp4/MP4Decoder.cpp
@@ -20,18 +20,18 @@
 #include "nsIGfxInfo.h"
 #endif
 #include "mozilla/layers/LayersTypes.h"
 
 #include "PDMFactory.h"
 
 namespace mozilla {
 
-MP4Decoder::MP4Decoder(MediaDecoderOwner* aOwner)
-  : MediaDecoder(aOwner)
+MP4Decoder::MP4Decoder(MediaDecoderInit& aInit)
+  : MediaDecoder(aInit)
 {
 }
 
 MediaDecoderStateMachine* MP4Decoder::CreateStateMachine()
 {
   mReader =
     new MediaFormatReader(this,
                           new MP4Demuxer(GetResource()),
--- a/dom/media/fmp4/MP4Decoder.h
+++ b/dom/media/fmp4/MP4Decoder.h
@@ -14,23 +14,23 @@
 namespace mozilla {
 
 class MediaContainerType;
 
 // Decoder that uses a bundled MP4 demuxer and platform decoders to play MP4.
 class MP4Decoder : public MediaDecoder
 {
 public:
-  explicit MP4Decoder(MediaDecoderOwner* aOwner);
+  explicit MP4Decoder(MediaDecoderInit& aInit);
 
-  MediaDecoder* Clone(MediaDecoderOwner* aOwner) override {
+  MediaDecoder* Clone(MediaDecoderInit& aInit) override {
     if (!IsEnabled()) {
       return nullptr;
     }
-    return new MP4Decoder(aOwner);
+    return new MP4Decoder(aInit);
   }
 
   MediaDecoderStateMachine* CreateStateMachine() override;
 
   // Returns true if aContainerType is an MP4 type that we think we can render
   // with the a platform decoder backend.
   // If provided, codecs are checked for support.
   static bool IsSupportedType(const MediaContainerType& aContainerType,
--- a/dom/media/mediasource/MediaSourceDecoder.cpp
+++ b/dom/media/mediasource/MediaSourceDecoder.cpp
@@ -21,26 +21,26 @@ extern mozilla::LogModule* GetMediaSourc
 
 #define MSE_DEBUG(arg, ...) MOZ_LOG(GetMediaSourceLog(), mozilla::LogLevel::Debug, ("MediaSourceDecoder(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
 #define MSE_DEBUGV(arg, ...) MOZ_LOG(GetMediaSourceLog(), mozilla::LogLevel::Verbose, ("MediaSourceDecoder(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
 
 using namespace mozilla::media;
 
 namespace mozilla {
 
-MediaSourceDecoder::MediaSourceDecoder(dom::HTMLMediaElement* aElement)
-  : MediaDecoder(aElement)
+MediaSourceDecoder::MediaSourceDecoder(MediaDecoderInit& aInit)
+  : MediaDecoder(aInit)
   , mMediaSource(nullptr)
   , mEnded(false)
 {
   mExplicitDuration.Set(Some(UnspecifiedNaN<double>()));
 }
 
 MediaDecoder*
-MediaSourceDecoder::Clone(MediaDecoderOwner* aOwner)
+MediaSourceDecoder::Clone(MediaDecoderInit& aInit)
 {
   // TODO: Sort out cloning.
   return nullptr;
 }
 
 MediaDecoderStateMachine*
 MediaSourceDecoder::CreateStateMachine()
 {
--- a/dom/media/mediasource/MediaSourceDecoder.h
+++ b/dom/media/mediasource/MediaSourceDecoder.h
@@ -30,19 +30,19 @@ namespace dom {
 class HTMLMediaElement;
 class MediaSource;
 
 } // namespace dom
 
 class MediaSourceDecoder : public MediaDecoder
 {
 public:
-  explicit MediaSourceDecoder(dom::HTMLMediaElement* aElement);
+  explicit MediaSourceDecoder(MediaDecoderInit& aInit);
 
-  MediaDecoder* Clone(MediaDecoderOwner* aOwner) override;
+  MediaDecoder* Clone(MediaDecoderInit& aInit) override;
   MediaDecoderStateMachine* CreateStateMachine() override;
   nsresult Load(nsIStreamListener**) override;
   media::TimeIntervals GetSeekable() override;
   media::TimeIntervals GetBuffered() override;
 
   void Shutdown() override;
 
   static already_AddRefed<MediaResource> CreateResource(nsIPrincipal* aPrincipal = nullptr);
--- a/dom/media/ogg/OggDecoder.h
+++ b/dom/media/ogg/OggDecoder.h
@@ -10,51 +10,29 @@
 
 namespace mozilla {
 
 class MediaContainerType;
 
 class OggDecoder : public MediaDecoder
 {
 public:
-  explicit OggDecoder(MediaDecoderOwner* aOwner)
-    : MediaDecoder(aOwner)
-    , mShutdownBitMonitor("mShutdownBitMonitor")
-    , mShutdownBit(false)
+  explicit OggDecoder(MediaDecoderInit& aInit)
+    : MediaDecoder(aInit)
   {}
 
-  MediaDecoder* Clone(MediaDecoderOwner* aOwner) override {
+  MediaDecoder* Clone(MediaDecoderInit& aInit) override {
     if (!IsOggEnabled()) {
       return nullptr;
     }
-    return new OggDecoder(aOwner);
+    return new OggDecoder(aInit);
   }
   MediaDecoderStateMachine* CreateStateMachine() override;
 
-  // For yucky legacy reasons, the ogg decoder needs to do a cross-thread read
-  // to check for shutdown while it hogs its own task queue. We don't want to
-  // protect the general state with a lock, so we make a special copy and a
-  // special-purpose lock. This method may be called on any thread.
-  bool IsOggDecoderShutdown() override
-  {
-    MonitorAutoLock lock(mShutdownBitMonitor);
-    return mShutdownBit;
-  }
-
   // Returns true if aContainerType is an Ogg type that we think we can render
   // with an enabled platform decoder backend.
   // If provided, codecs are checked for support.
   static bool IsSupportedType(const MediaContainerType& aContainerType);
-
-protected:
-  void ShutdownBitChanged() override
-  {
-    MonitorAutoLock lock(mShutdownBitMonitor);
-    mShutdownBit = mStateMachineIsShutdown;
-  }
-
-  Monitor mShutdownBitMonitor;
-  bool mShutdownBit;
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/media/wave/WaveDecoder.cpp
+++ b/dom/media/wave/WaveDecoder.cpp
@@ -9,19 +9,19 @@
 #include "MediaDecoderStateMachine.h"
 #include "WaveDecoder.h"
 #include "MediaFormatReader.h"
 #include "PDMFactory.h"
 
 namespace mozilla {
 
 MediaDecoder*
-WaveDecoder::Clone(MediaDecoderOwner* aOwner)
+WaveDecoder::Clone(MediaDecoderInit& aInit)
 {
-  return new WaveDecoder(aOwner);
+  return new WaveDecoder(aInit);
 }
 
 MediaDecoderStateMachine*
 WaveDecoder::CreateStateMachine()
 {
   return new MediaDecoderStateMachine(
     this, new MediaFormatReader(this, new WAVDemuxer(GetResource())));
 }
--- a/dom/media/wave/WaveDecoder.h
+++ b/dom/media/wave/WaveDecoder.h
@@ -11,18 +11,18 @@
 namespace mozilla {
 
 class MediaContainerType;
 
 class WaveDecoder : public MediaDecoder
 {
 public:
   // MediaDecoder interface.
-  explicit WaveDecoder(MediaDecoderOwner* aOwner) : MediaDecoder(aOwner) {}
-  MediaDecoder* Clone(MediaDecoderOwner* aOwner) override;
+  explicit WaveDecoder(MediaDecoderInit& aInit) : MediaDecoder(aInit) {}
+  MediaDecoder* Clone(MediaDecoderInit& aInit) override;
   MediaDecoderStateMachine* CreateStateMachine() override;
 
   // Returns true if the Wave backend is pref'ed on, and we're running on a
   // platform that is likely to have decoders for the format.
   static bool IsSupportedType(const MediaContainerType& aContainerType);
 };
 
 } // namespace mozilla
--- a/dom/media/webm/WebMDecoder.h
+++ b/dom/media/webm/WebMDecoder.h
@@ -11,22 +11,22 @@
 
 namespace mozilla {
 
 class MediaContainerType;
 
 class WebMDecoder : public MediaDecoder
 {
 public:
-  explicit WebMDecoder(MediaDecoderOwner* aOwner) : MediaDecoder(aOwner) {}
-  MediaDecoder* Clone(MediaDecoderOwner* aOwner) override {
+  explicit WebMDecoder(MediaDecoderInit& aInit) : MediaDecoder(aInit) {}
+  MediaDecoder* Clone(MediaDecoderInit& aInit) override {
     if (!IsWebMEnabled()) {
       return nullptr;
     }
-    return new WebMDecoder(aOwner);
+    return new WebMDecoder(aInit);
   }
   MediaDecoderStateMachine* CreateStateMachine() override;
 
   // Returns true if aContainerType is a WebM type that we think we can render
   // with an enabled platform decoder backend.
   // If provided, codecs are checked for support.
   static bool IsSupportedType(const MediaContainerType& aContainerType);
 
--- a/gfx/layers/wr/WebRenderBridgeParent.cpp
+++ b/gfx/layers/wr/WebRenderBridgeParent.cpp
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/layers/WebRenderBridgeParent.h"
 
 #include "apz/src/AsyncPanZoomController.h"
 #include "CompositableHost.h"
 #include "gfxPrefs.h"
+#include "GeckoProfiler.h"
 #include "GLContext.h"
 #include "GLContextProvider.h"
 #include "mozilla/Range.h"
 #include "mozilla/layers/AnimationHelper.h"
 #include "mozilla/layers/APZCTreeManager.h"
 #include "mozilla/layers/Compositor.h"
 #include "mozilla/layers/CompositorBridgeParent.h"
 #include "mozilla/layers/CompositorThread.h"
@@ -307,16 +308,17 @@ WebRenderBridgeParent::HandleDPEnd(const
                                  InfallibleTArray<OpDestroy>&& aToDestroy,
                                  const uint64_t& aFwdTransactionId,
                                  const uint64_t& aTransactionId,
                                  const WrSize& aContentSize,
                                  const ByteBuffer& dl,
                                  const WrBuiltDisplayListDescriptor& dlDesc,
                                  const WebRenderScrollData& aScrollData)
 {
+  GeckoProfilerTracingRAII tracer("Paint", "DPTransaction");
   UpdateFwdTransactionId(aFwdTransactionId);
   AutoClearReadLocks clearLocks(mReadLocks);
 
   if (mDestroyed) {
     for (const auto& op : aToDestroy) {
       DestroyActor(op);
     }
     return;
@@ -831,16 +833,17 @@ WebRenderBridgeParent::SampleAnimations(
       }
     }
   }
 }
 
 void
 WebRenderBridgeParent::CompositeToTarget(gfx::DrawTarget* aTarget, const gfx::IntRect* aRect)
 {
+  GeckoProfilerTracingRAII tracer("Paint", "CompositeToTraget");
   if (mPaused) {
     return;
   }
 
   const uint32_t maxPendingFrameCount = 2;
 
   if (!mForceRendering &&
       wr::RenderThread::Get()->GetPendingFrameCount(mApi->GetId()) > maxPendingFrameCount) {
--- a/gfx/layers/wr/WebRenderLayerManager.cpp
+++ b/gfx/layers/wr/WebRenderLayerManager.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WebRenderLayerManager.h"
 
 #include "gfxPrefs.h"
+#include "GeckoProfiler.h"
 #include "LayersLogging.h"
 #include "mozilla/layers/CompositorBridgeChild.h"
 #include "mozilla/layers/StackingContextHelper.h"
 #include "mozilla/layers/TextureClient.h"
 #include "mozilla/layers/WebRenderBridgeChild.h"
 #include "WebRenderCanvasLayer.h"
 #include "WebRenderColorLayer.h"
 #include "WebRenderContainerLayer.h"
@@ -174,16 +175,17 @@ WebRenderLayerManager::EndTransaction(Dr
   EndTransactionInternal(aCallback, aCallbackData, aFlags);
 }
 
 bool
 WebRenderLayerManager::EndTransactionInternal(DrawPaintedLayerCallback aCallback,
                                               void* aCallbackData,
                                               EndTransactionFlags aFlags)
 {
+  GeckoProfilerTracingRAII tracer("Paint", "RenderLayers");
   mPaintedLayerCallback = aCallback;
   mPaintedLayerCallbackData = aCallbackData;
   mTransactionIncomplete = false;
 
   if (gfxPrefs::LayersDump()) {
     this->Dump();
   }
 
@@ -221,17 +223,21 @@ WebRenderLayerManager::EndTransactionInt
     if (mRoot) {
       PopulateScrollData(scrollData, mRoot.get());
     }
   }
 
   bool sync = mTarget != nullptr;
   mLatestTransactionId = mTransactionIdAllocator->GetTransactionId();
 
-  WrBridge()->DPEnd(builder, size.ToUnknownSize(), sync, mLatestTransactionId, scrollData);
+  {
+    GeckoProfilerTracingRAII
+      tracer("Paint", sync ? "ForwardDPTransactionSync":"ForwardDPTransaction");
+    WrBridge()->DPEnd(builder, size.ToUnknownSize(), sync, mLatestTransactionId, scrollData);
+  }
 
   MakeSnapshotIfRequired(size);
   mNeedsComposite = false;
 
   ClearDisplayItemLayers();
 
   // this may result in Layers being deleted, which results in
   // PLayer::Send__delete__() and DeallocShmem()
--- a/gfx/webrender_bindings/RenderThread.cpp
+++ b/gfx/webrender_bindings/RenderThread.cpp
@@ -1,22 +1,23 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+#include "base/task.h"
+#include "GeckoProfiler.h"
 #include "RenderThread.h"
 #include "nsThreadUtils.h"
 #include "mozilla/layers/CompositorThread.h"
 #include "mozilla/layers/CompositorBridgeParent.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/webrender/RendererOGL.h"
 #include "mozilla/webrender/RenderTextureHost.h"
 #include "mozilla/widget/CompositorWidget.h"
-#include "base/task.h"
 
 namespace mozilla {
 namespace wr {
 
 static StaticRefPtr<RenderThread> sRenderThread;
 
 RenderThread::RenderThread(base::Thread* aThread)
   : mThread(aThread)
@@ -173,16 +174,17 @@ NotifyDidRender(layers::CompositorBridge
     aBridge->NotifyDidCompositeToPipeline(pipeline, epoch, aStart, aEnd);
   }
   wr_rendered_epochs_delete(aEpochs);
 }
 
 void
 RenderThread::UpdateAndRender(wr::WindowId aWindowId)
 {
+  GeckoProfilerTracingRAII tracer("Paint", "Composite");
   MOZ_ASSERT(IsInRenderThread());
 
   auto it = mRenderers.find(aWindowId);
   MOZ_ASSERT(it != mRenderers.end());
   if (it == mRenderers.end()) {
     return;
   }
 
--- a/layout/reftests/svg/smil/anim-gradient-attr-presence-01-ref.svg
+++ b/layout/reftests/svg/smil/anim-gradient-attr-presence-01-ref.svg
@@ -7,130 +7,130 @@
   <!-- 1. gradientUnits -->
   <defs>
     <linearGradient id="gradientUnits" x1="0" y1="0" x2="100" y2="0"
       gradientUnits="userSpaceOnUse">
       <stop offset="0%" stop-color="#F60" />
       <stop offset="100%" stop-color="#FF6" />
     </linearGradient>
   </defs>
-  <rect width="100" height="100" fill="url(#gradientUnits)"/>
-  <g transform="translate(100)">
-    <rect width="100" height="100" fill="url(#gradientUnits)"/>
+  <rect width="100px" height="100px" fill="url(#gradientUnits)"/>
+  <g transform="translate(100px)">
+    <rect width="100px" height="100px" fill="url(#gradientUnits)"/>
   </g>
   <!-- 2. gradientTransform -->
   <defs>
     <linearGradient id="gradientTransform"
       gradientTransform="rotate(90 0.5 0.5)">
       <stop offset="0%" stop-color="#F60" />
       <stop offset="100%" stop-color="#FF6" />
     </linearGradient>
   </defs>
-  <g transform="translate(0 100)">
-    <rect width="100" height="100" fill="url(#gradientTransform)"/>
-    <g transform="translate(100)">
-      <rect width="100" height="100" fill="url(#gradientTransform)"/>
+  <g transform="translate(0px 100px)">
+    <rect width="100px" height="100px" fill="url(#gradientTransform)"/>
+    <g transform="translate(100px)">
+      <rect width="100px" height="100px" fill="url(#gradientTransform)"/>
     </g>
   </g>
   <!-- 3. x1, x2 -->
   <defs>
     <linearGradient id="x1x2" x1="40%" x2="60%">
       <stop offset="0%" stop-color="#F60" />
       <stop offset="100%" stop-color="#FF6" />
     </linearGradient>
   </defs>
-  <g transform="translate(0 200)">
-    <rect width="100" height="100" fill="url(#x1x2)"/>
-    <g transform="translate(100)">
-      <rect width="100" height="100" fill="url(#x1x2)"/>
+  <g transform="translate(0px 200px)">
+    <rect width="100px" height="100px" fill="url(#x1x2)"/>
+    <g transform="translate(100px)">
+      <rect width="100px" height="100px" fill="url(#x1x2)"/>
     </g>
   </g>
   <!-- 4. y1, y2 -->
   <defs>
     <linearGradient id="y1y2" x2="0%" y1="40%" y2="60%">
       <stop offset="0%" stop-color="#F60" />
       <stop offset="100%" stop-color="#FF6" />
     </linearGradient>
   </defs>
-  <g transform="translate(0 300)">
-    <rect width="100" height="100" fill="url(#y1y2)"/>
-    <g transform="translate(100)">
-      <rect width="100" height="100" fill="url(#y1y2)"/>
+  <g transform="translate(0px 300px)">
+    <rect width="100px" height="100px" fill="url(#y1y2)"/>
+    <g transform="translate(100px)">
+      <rect width="100px" height="100px" fill="url(#y1y2)"/>
     </g>
   </g>
   <!-- 5. cx, cy -->
   <defs>
     <radialGradient id="cxcy" cx="0%" cy="100%">
       <stop offset="0%" stop-color="#F60" />
       <stop offset="100%" stop-color="#FF6" />
     </radialGradient>
   </defs>
-  <g transform="translate(0 400)">
-    <rect width="100" height="100" fill="url(#cxcy)"/>
-    <g transform="translate(100)">
-      <rect width="100" height="100" fill="url(#cxcy)"/>
+  <g transform="translate(0px 400px)">
+    <rect width="100px" height="100px" fill="url(#cxcy)"/>
+    <g transform="translate(100px)">
+      <rect width="100px" height="100px" fill="url(#cxcy)"/>
     </g>
   </g>
   <!-- 6. r -->
   <defs>
     <radialGradient id="r" r="100%">
       <stop offset="0%" stop-color="#F60" />
       <stop offset="100%" stop-color="#FF6" />
     </radialGradient>
   </defs>
-  <g transform="translate(0 500)">
-    <rect width="100" height="100" fill="url(#r)"/>
-    <g transform="translate(100)">
-      <rect width="100" height="100" fill="url(#r)"/>
+  <g transform="translate(0px 500px)">
+    <rect width="100px" height="100px" fill="url(#r)"/>
+    <g transform="translate(100px)">
+      <rect width="100px" height="100px" fill="url(#r)"/>
     </g>
   </g>
   <!-- 7. fx, fy -->
   <defs>
     <radialGradient id="fxfy" fx="20%" fy="80%">
       <stop offset="0%" stop-color="#F60" />
       <stop offset="100%" stop-color="#FF6" />
     </radialGradient>
   </defs>
-  <g transform="translate(0 600)">
-    <rect width="100" height="100" fill="url(#fxfy)"/>
-    <g transform="translate(100)">
-      <rect width="100" height="100" fill="url(#fxfy)"/>
+  <g transform="translate(0px 600px)">
+    <rect width="100px" height="100px" fill="url(#fxfy)"/>
+    <g transform="translate(100px)">
+      <rect width="100px" height="100px" fill="url(#fxfy)"/>
     </g>
   </g>
   <!-- 8. spreadMethod -->
   <defs>
     <linearGradient id="spreadMethod" x1="50%" spreadMethod="reflect">
       <stop offset="0%" stop-color="#F60" />
       <stop offset="100%" stop-color="#FF6" />
     </linearGradient>
   </defs>
-  <g transform="translate(0 700)">
-    <rect width="100" height="100" fill="url(#spreadMethod)"/>
-    <g transform="translate(100)">
-      <rect width="100" height="100" fill="url(#spreadMethod)"/>
+  <g transform="translate(0px 700px)">
+    <rect width="100px" height="100px" fill="url(#spreadMethod)"/>
+    <g transform="translate(100px)">
+      <rect width="100px" height="100px" fill="url(#spreadMethod)"/>
     </g>
   </g>
   <!-- 9. xlink:href -->
   <defs>
     <linearGradient id="xlink" xlink:href="#xlinkRef"/>
     <linearGradient id="xlinkRef">
       <stop offset="0%" stop-color="#F60" />
       <stop offset="100%" stop-color="#FF6" />
     </linearGradient>
   </defs>
-  <g transform="translate(0 800)">
-    <rect width="100" height="100" fill="url(#xlink)"/>
-    <g transform="translate(100)">
-      <rect width="100" height="100" fill="url(#xlinkRef)"/>
+  <g transform="translate(0px 800px)">
+    <rect width="100px" height="100" fill="url(#xlink)"/>
+    <g transform="translate(100px)">
+      <rect width="100px" height="100px" fill="url(#xlinkRef)"/>
     </g>
   </g>
   <!-- We were getting random but very minor anti-aliasing differences on OSX
        and OSX64 along the edges of these gradients so we draw a thick stroke
        over all the edges. -->
-  <path stroke="black" stroke-width="3" stroke-linecap="square" fill="none"
+  <path stroke="black" stroke-width="3px" stroke-linecap="square" fill="none"
     d="M0 0H200V900H0V0
        M0 100H200
        M0 200H200
        M0 300H200
        M0 400H200
        M0 500H200
        M0 600H200
        M0 700H200
--- a/layout/reftests/svg/smil/anim-gradient-attr-presence-01.svg
+++ b/layout/reftests/svg/smil/anim-gradient-attr-presence-01.svg
@@ -31,157 +31,157 @@
   <defs>
     <linearGradient xlink:href="#gradientUnitsRef" id="gradientUnits"/>
     <linearGradient id="gradientUnitsRef" x1="0" y1="0" x2="100" y2="0">
       <set attributeName="gradientUnits" to="userSpaceOnUse"/>
       <stop offset="0%" stop-color="#F60" />
       <stop offset="100%" stop-color="#FF6" />
     </linearGradient>
   </defs>
-  <rect width="100" height="100" fill="url(#gradientUnits)"/>
-  <g transform="translate(100)">
-    <rect width="100" height="100" fill="url(#gradientUnitsRef)"/>
+  <rect width="100px" height="100px" fill="url(#gradientUnits)"/>
+  <g transform="translate(100px)">
+    <rect width="100px" height="100px" fill="url(#gradientUnitsRef)"/>
   </g>
   <!-- 2. gradientTransform: defaults to identity -->
   <defs>
     <linearGradient xlink:href="#gradientTransformRef" id="gradientTransform"/>
     <linearGradient id="gradientTransformRef">
       <animateTransform attributeName="gradientTransform" type="rotate"
         values="90 0.5 0.5" fill="freeze"/>
       <stop offset="0%" stop-color="#F60" />
       <stop offset="100%" stop-color="#FF6" />
     </linearGradient>
   </defs>
-  <g transform="translate(0 100)">
-    <rect width="100" height="100" fill="url(#gradientTransform)"/>
-    <g transform="translate(100)">
-      <rect width="100" height="100" fill="url(#gradientTransformRef)"/>
+  <g transform="translate(0px 100px)">
+    <rect width="100px" height="100px" fill="url(#gradientTransform)"/>
+    <g transform="translate(100px)">
+      <rect width="100px" height="100px" fill="url(#gradientTransformRef)"/>
     </g>
   </g>
   <!-- 3. x1: defaults to 0%
           x2: defaults to 100% -->
   <defs>
     <linearGradient xlink:href="#x1x2Ref" id="x1x2"/>
     <linearGradient id="x1x2Ref">
       <set attributeName="x1" to="40%"/>
       <set attributeName="x2" to="60%"/>
       <stop offset="0%" stop-color="#F60" />
       <stop offset="100%" stop-color="#FF6" />
     </linearGradient>
   </defs>
-  <g transform="translate(0 200)">
-    <rect width="100" height="100" fill="url(#x1x2)"/>
-    <g transform="translate(100)">
-      <rect width="100" height="100" fill="url(#x1x2Ref)"/>
+  <g transform="translate(0px 200px)">
+    <rect width="100px" height="100px" fill="url(#x1x2)"/>
+    <g transform="translate(100px)">
+      <rect width="100px" height="100px" fill="url(#x1x2Ref)"/>
     </g>
   </g>
   <!-- 4. y1: defaults to 0%
           y2: defaults to 0% -->
   <defs>
     <linearGradient xlink:href="#y1y2Ref" id="y1y2"/>
     <linearGradient id="y1y2Ref" x2="0%">
       <set attributeName="y1" to="40%"/>
       <set attributeName="y2" to="60%"/>
       <stop offset="0%" stop-color="#F60" />
       <stop offset="100%" stop-color="#FF6" />
     </linearGradient>
   </defs>
-  <g transform="translate(0 300)">
-    <rect width="100" height="100" fill="url(#y1y2)"/>
-    <g transform="translate(100)">
-      <rect width="100" height="100" fill="url(#y1y2Ref)"/>
+  <g transform="translate(0px 300px)">
+    <rect width="100px" height="100px" fill="url(#y1y2)"/>
+    <g transform="translate(100px)">
+      <rect width="100px" height="100px" fill="url(#y1y2Ref)"/>
     </g>
   </g>
   <!-- 5. cx: defaults to 50%
           cy: defaults to 50% -->
   <defs>
     <radialGradient xlink:href="#cxcyRef" id="cxcy"/>
     <radialGradient id="cxcyRef">
       <set attributeName="cx" to="0%"/>
       <set attributeName="cy" to="100%"/>
       <stop offset="0%" stop-color="#F60" />
       <stop offset="100%" stop-color="#FF6" />
     </radialGradient>
   </defs>
-  <g transform="translate(0 400)">
-    <rect width="100" height="100" fill="url(#cxcy)"/>
-    <g transform="translate(100)">
-      <rect width="100" height="100" fill="url(#cxcyRef)"/>
+  <g transform="translate(0px 400px)">
+    <rect width="100px" height="100px" fill="url(#cxcy)"/>
+    <g transform="translate(100px)">
+      <rect width="100px" height="100px" fill="url(#cxcyRef)"/>
     </g>
   </g>
   <!-- 6. r: defaults to 50% -->
   <defs>
     <radialGradient xlink:href="#rRef" id="r"/>
     <radialGradient id="rRef">
       <set attributeName="r" to="100%"/>
       <stop offset="0%" stop-color="#F60" />
       <stop offset="100%" stop-color="#FF6" />
     </radialGradient>
   </defs>
-  <g transform="translate(0 500)">
-    <rect width="100" height="100" fill="url(#r)"/>
-    <g transform="translate(100)">
-      <rect width="100" height="100" fill="url(#rRef)"/>
+  <g transform="translate(0px 500px)">
+    <rect width="100px" height="100px" fill="url(#r)"/>
+    <g transform="translate(100px)">
+      <rect width="100px" height="100px" fill="url(#rRef)"/>
     </g>
   </g>
   <!-- 7. fx: defaults to cx
           fy: defaults to cy -->
   <defs>
     <radialGradient xlink:href="#fxfyRef" id="fxfy"/>
     <radialGradient id="fxfyRef">
       <set attributeName="fx" to="20%"/>
       <set attributeName="fy" to="80%"/>
       <stop offset="0%" stop-color="#F60" />
       <stop offset="100%" stop-color="#FF6" />
     </radialGradient>
   </defs>
-  <g transform="translate(0 600)">
-    <rect width="100" height="100" fill="url(#fxfy)"/>
-    <g transform="translate(100)">
-      <rect width="100" height="100" fill="url(#fxfyRef)"/>
+  <g transform="translate(0px 600px)">
+    <rect width="100px" height="100px" fill="url(#fxfy)"/>
+    <g transform="translate(100px)">
+      <rect width="100px" height="100px" fill="url(#fxfyRef)"/>
     </g>
   </g>
   <!-- 8. spreadMethod: defaults to pad -->
   <defs>
     <linearGradient xlink:href="#spreadMethodRef" id="spreadMethod"/>
     <linearGradient id="spreadMethodRef" x1="50%">
       <set attributeName="spreadMethod" to="reflect"/>
       <stop offset="0%" stop-color="#F60" />
       <stop offset="100%" stop-color="#FF6" />
     </linearGradient>
   </defs>
-  <g transform="translate(0 700)">
-    <rect width="100" height="100" fill="url(#spreadMethod)"/>
-    <g transform="translate(100)">
-      <rect width="100" height="100" fill="url(#spreadMethodRef)"/>
+  <g transform="translate(0px 700px)">
+    <rect width="100px" height="100px" fill="url(#spreadMethod)"/>
+    <g transform="translate(100px)">
+      <rect width="100px" height="100px" fill="url(#spreadMethodRef)"/>
     </g>
   </g>
   <!-- 9. xlink:href
     This attribute is not affected by bug 608161 but we test it here for
     completeness.
     -->
   <defs>
     <linearGradient id="xlink">
       <set attributeName="xlink:href" to="#xlinkRef"/>
     </linearGradient>
     <linearGradient id="xlinkRef">
       <stop offset="0%" stop-color="#F60" />
       <stop offset="100%" stop-color="#FF6" />
     </linearGradient>
   </defs>
-  <g transform="translate(0 800)">
-    <rect width="100" height="100" fill="url(#xlink)"/>
-    <g transform="translate(100)">
-      <rect width="100" height="100" fill="url(#xlinkRef)"/>
+  <g transform="translate(0px 800px)">
+    <rect width="100px" height="100px" fill="url(#xlink)"/>
+    <g transform="translate(100px)">
+      <rect width="100px" height="100px" fill="url(#xlinkRef)"/>
     </g>
   </g>
   <!-- We were getting random but very minor anti-aliasing differences on OSX
        and OSX64 along the edges of these gradients so we draw a thick stroke
        over all the edges. -->
-  <path stroke="black" stroke-width="3" stroke-linecap="square" fill="none"
+  <path stroke="black" stroke-width="3px" stroke-linecap="square" fill="none"
     d="M0 0H200V900H0V0
        M0 100H200
        M0 200H200
        M0 300H200
        M0 400H200
        M0 500H200
        M0 600H200
        M0 700H200
--- a/media/libcubeb/prefer-pulse-rust.patch
+++ b/media/libcubeb/prefer-pulse-rust.patch
@@ -1,14 +1,14 @@
 diff --git a/media/libcubeb/src/cubeb.c b/media/libcubeb/src/cubeb.c
 index 1db59240e335..241f3cfed041 100644
 --- a/media/libcubeb/src/cubeb.c
 +++ b/media/libcubeb/src/cubeb.c
 @@ -166,6 +166,9 @@ cubeb_init(cubeb ** context, char const * context_name, char const * backend_nam
       * to override all other choices
       */
      init_oneshot,
-+#if defined(USE_PULSE_RUST)
++#if defined(NIGHTLY_BUILD) && defined(USE_PULSE_RUST)
 +    pulse_rust_init,
 +#endif
  #if defined(USE_PULSE)
      pulse_init,
  #endif
--- a/media/libcubeb/src/cubeb.c
+++ b/media/libcubeb/src/cubeb.c
@@ -161,17 +161,17 @@ cubeb_init(cubeb ** context, char const 
   }
 
   int (* default_init[])(cubeb **, char const *) = {
     /*
      * init_oneshot must be at the top to allow user
      * to override all other choices
      */
     init_oneshot,
-#if defined(USE_PULSE_RUST)
+#if defined(NIGHTLY_BUILD) && defined(USE_PULSE_RUST)
     pulse_rust_init,
 #endif
 #if defined(USE_PULSE)
     pulse_init,
 #endif
 #if defined(USE_JACK)
     jack_init,
 #endif
--- a/servo/components/config/opts.rs
+++ b/servo/components/config/opts.rs
@@ -475,17 +475,17 @@ fn default_user_agent_string(agent: User
 const DEFAULT_USER_AGENT: UserAgent = UserAgent::Android;
 
 #[cfg(not(target_os = "android"))]
 const DEFAULT_USER_AGENT: UserAgent = UserAgent::Desktop;
 
 pub fn default_opts() -> Opts {
     Opts {
         is_running_problem_test: false,
-        url: Some(ServoUrl::parse("about:blank").unwrap()),
+        url: None,
         tile_size: 512,
         device_pixels_per_px: None,
         time_profiling: None,
         time_profiler_trace_path: None,
         mem_profiler_period: None,
         nonincremental_layout: false,
         userscripts: None,
         user_stylesheets: Vec::new(),
@@ -626,40 +626,34 @@ pub fn from_cmdline_args(args: &[String]
         }
     }
 
     if debug_options.help {
         print_debug_usage(app_name)
     }
 
     let cwd = env::current_dir().unwrap();
-    let homepage_pref = PREFS.get("shell.homepage");
     let url_opt = if !opt_match.free.is_empty() {
         Some(&opt_match.free[0][..])
     } else {
-        homepage_pref.as_string()
+        None
     };
     let is_running_problem_test =
         url_opt
         .as_ref()
         .map_or(false, |url|
              url.starts_with("http://web-platform.test:8000/2dcontext/drawing-images-to-the-canvas/") ||
              url.starts_with("http://web-platform.test:8000/_mozilla/mozilla/canvas/") ||
              url.starts_with("http://web-platform.test:8000/_mozilla/css/canvas_over_area.html"));
 
-    let url = match url_opt {
-        Some(url_string) => {
-            parse_url_or_filename(&cwd, url_string)
-                .unwrap_or_else(|()| args_fail("URL parsing failed"))
-        },
-        None => {
-            print_usage(app_name, &opts);
-            args_fail("servo asks that you provide a URL")
-        }
-    };
+    let url_opt = url_opt.and_then(|url_string| parse_url_or_filename(&cwd, url_string)
+                                   .or_else(|error| {
+                                       warn!("URL parsing failed ({:?}).", error);
+                                       Err(error)
+                                   }).ok());
 
     let tile_size: usize = match opt_match.opt_str("s") {
         Some(tile_size_str) => tile_size_str.parse()
             .unwrap_or_else(|err| args_fail(&format!("Error parsing option: -s ({})", err))),
         None => 512,
     };
 
     let device_pixels_per_px = opt_match.opt_str("device-pixel-ratio").map(|dppx_str|
@@ -770,17 +764,17 @@ pub fn from_cmdline_args(args: &[String]
     let do_not_use_native_titlebar =
         opt_match.opt_present("b") ||
         !PREFS.get("shell.native-titlebar.enabled").as_boolean().unwrap();
 
     let is_printing_version = opt_match.opt_present("v") || opt_match.opt_present("version");
 
     let opts = Opts {
         is_running_problem_test: is_running_problem_test,
-        url: Some(url),
+        url: url_opt,
         tile_size: tile_size,
         device_pixels_per_px: device_pixels_per_px,
         time_profiling: time_profiling,
         time_profiler_trace_path: opt_match.opt_str("profiler-trace-path"),
         mem_profiler_period: mem_profiler_period,
         nonincremental_layout: nonincremental_layout,
         userscripts: opt_match.opt_default("userscripts", ""),
         user_stylesheets: user_stylesheets,
--- a/servo/components/servo/lib.rs
+++ b/servo/components/servo/lib.rs
@@ -117,17 +117,17 @@ pub use servo_url as url;
 /// loop to pump messages between the embedding application and
 /// various browser components.
 pub struct Browser<Window: WindowMethods + 'static> {
     compositor: IOCompositor<Window>,
     constellation_chan: Sender<ConstellationMsg>,
 }
 
 impl<Window> Browser<Window> where Window: WindowMethods + 'static {
-    pub fn new(window: Rc<Window>) -> Browser<Window> {
+    pub fn new(window: Rc<Window>, target_url: ServoUrl) -> Browser<Window> {
         // Global configuration options, parsed from the command line.
         let opts = opts::get();
 
         // Make sure the gl context is made current.
         window.prepare_for_composite(0, 0);
 
         // Get both endpoints of a special channel for communication between
         // the client window and the compositor. This channel is unique because
@@ -198,17 +198,17 @@ impl<Window> Browser<Window> where Windo
         // can't defer it after `create_constellation` has started.
         script::init();
 
         // Create the constellation, which maintains the engine
         // pipelines, including the script and layout threads, as well
         // as the navigation context.
         let (constellation_chan, sw_senders) = create_constellation(opts.user_agent.clone(),
                                                                     opts.config_dir.clone(),
-                                                                    opts.url.clone(),
+                                                                    target_url,
                                                                     compositor_proxy.clone_compositor_proxy(),
                                                                     time_profiler_chan.clone(),
                                                                     mem_profiler_chan.clone(),
                                                                     debugger_chan,
                                                                     devtools_chan,
                                                                     supports_clipboard,
                                                                     &webrender,
                                                                     webrender_api_sender.clone());
@@ -282,17 +282,17 @@ fn create_compositor_channel(event_loop_
         },
      CompositorReceiver {
          receiver: receiver
      })
 }
 
 fn create_constellation(user_agent: Cow<'static, str>,
                         config_dir: Option<PathBuf>,
-                        url: Option<ServoUrl>,
+                        url: ServoUrl,
                         compositor_proxy: CompositorProxy,
                         time_profiler_chan: time::ProfilerChan,
                         mem_profiler_chan: mem::ProfilerChan,
                         debugger_chan: Option<debugger::Sender>,
                         devtools_chan: Option<Sender<devtools_traits::DevtoolsControlMsg>>,
                         supports_clipboard: bool,
                         webrender: &webrender::Renderer,
                         webrender_api_sender: webrender_traits::RenderApiSender)
@@ -332,19 +332,17 @@ fn create_constellation(user_agent: Cow<
         let (mut handler, sender) = WebVRCompositorHandler::new();
         let webvr_thread = WebVRThread::spawn(constellation_chan.clone(), sender);
         handler.set_webvr_thread_sender(webvr_thread.clone());
 
         webrender.set_vr_compositor_handler(handler);
         constellation_chan.send(ConstellationMsg::SetWebVRThread(webvr_thread)).unwrap();
     }
 
-    if let Some(url) = url {
-        constellation_chan.send(ConstellationMsg::InitLoadUrl(url)).unwrap();
-    };
+    constellation_chan.send(ConstellationMsg::InitLoadUrl(url)).unwrap();
 
     // channels to communicate with Service Worker Manager
     let sw_senders = SWManagerSenders {
         swmanager_sender: from_swmanager_sender,
         resource_sender: resource_sender
     };
 
     (constellation_chan, sw_senders)
--- a/servo/ports/cef/browser.rs
+++ b/servo/ports/cef/browser.rs
@@ -4,16 +4,18 @@
 
 use browser_host::{ServoCefBrowserHost, ServoCefBrowserHostExtensions};
 use eutil::Downcast;
 use frame::{ServoCefFrame, ServoCefFrameExtensions};
 use interfaces::{CefBrowser, CefBrowserHost, CefClient, CefFrame, CefRequestContext};
 use interfaces::{cef_browser_t, cef_browser_host_t, cef_client_t, cef_frame_t};
 use interfaces::{cef_request_context_t};
 use servo::Browser;
+use servo::servo_config::prefs::PREFS;
+use servo::servo_url::ServoUrl;
 use types::{cef_browser_settings_t, cef_string_t, cef_window_info_t, cef_window_handle_t};
 use window;
 use wrappers::CefWrap;
 
 use compositing::windowing::{WindowNavigateMsg, WindowEvent};
 use glutin_app;
 use libc::c_int;
 use std::cell::{Cell, RefCell};
@@ -125,17 +127,19 @@ impl ServoCefBrowser {
     pub fn new(window_info: &cef_window_info_t, client: CefClient) -> ServoCefBrowser {
         let frame = ServoCefFrame::new().as_cef_interface();
         let host = ServoCefBrowserHost::new(client.clone()).as_cef_interface();
         let mut window_handle: cef_window_handle_t = get_null_window_handle();
 
         let (glutin_window, servo_browser) = if window_info.windowless_rendering_enabled == 0 {
             let parent_window = glutin_app::WindowID::new(window_info.parent_window as *mut _);
             let glutin_window = glutin_app::create_window(Some(parent_window));
-            let servo_browser = Browser::new(glutin_window.clone());
+            let home_url = ServoUrl::parse(PREFS.get("shell.homepage").as_string()
+                    .unwrap_or("about:blank")).unwrap();
+            let servo_browser = Browser::new(glutin_window.clone(), home_url);
             window_handle = glutin_window.platform_window().window as cef_window_handle_t;
             (Some(glutin_window), ServoBrowser::OnScreen(servo_browser))
         } else {
             (None, ServoBrowser::Invalid)
         };
 
         let id = ID_COUNTER.with(|counter| {
             counter.fetch_add(1, Ordering::SeqCst)
@@ -166,17 +170,19 @@ pub trait ServoCefBrowserExtensions {
     fn pinch_zoom_level(&self) -> f32;
 }
 
 impl ServoCefBrowserExtensions for CefBrowser {
     fn init(&self, window_info: &cef_window_info_t) {
         if window_info.windowless_rendering_enabled != 0 {
             let window = window::Window::new(window_info.width, window_info.height);
             window.set_browser(self.clone());
-            let servo_browser = Browser::new(window.clone());
+            let home_url = ServoUrl::parse(PREFS.get("shell.homepage").as_string()
+                    .unwrap_or("about:blank")).unwrap();
+            let servo_browser = Browser::new(window.clone(), home_url);
             *self.downcast().servo_browser.borrow_mut() = ServoBrowser::OffScreen(servo_browser);
         }
 
         self.downcast().host.set_browser((*self).clone());
         self.downcast().frame.set_browser((*self).clone());
         if window_info.windowless_rendering_enabled == 0 {
             self.downcast().host.initialize_compositing();
         }
--- a/servo/ports/servo/main.rs
+++ b/servo/ports/servo/main.rs
@@ -32,16 +32,18 @@ extern crate sig;
 
 use backtrace::Backtrace;
 use servo::Browser;
 use servo::compositing::windowing::WindowEvent;
 #[cfg(target_os = "android")]
 use servo::config;
 use servo::config::opts::{self, ArgumentParsingResult};
 use servo::config::servo_version;
+use servo::servo_config::prefs::PREFS;
+use servo::servo_url::ServoUrl;
 use std::env;
 use std::panic;
 use std::process;
 use std::rc::Rc;
 use std::thread;
 
 pub mod platform {
     #[cfg(target_os = "macos")]
@@ -138,20 +140,26 @@ fn main() {
 
     if opts::get().is_printing_version {
         println!("{}", servo_version());
         process::exit(0);
     }
 
     let window = app::create_window(None);
 
+    // If the url is not provided, we fallback to the homepage in PREFS,
+    // or a blank page in case the homepage is not set either.
+    let target_url = opts::get().url.clone()
+                .unwrap_or(ServoUrl::parse(PREFS.get("shell.homepage").as_string()
+                    .unwrap_or("about:blank")).unwrap());
+
     // Our wrapper around `Browser` that also implements some
     // callbacks required by the glutin window implementation.
     let mut browser = BrowserWrapper {
-        browser: Browser::new(window.clone()),
+        browser: Browser::new(window.clone(), target_url)
     };
 
     browser.browser.setup_logging();
 
     register_glutin_resize_handler(&window, &mut browser);
 
     browser.browser.handle_events(vec![WindowEvent::InitializeCompositing]);
 
--- a/testing/web-platform/meta/MANIFEST.json
+++ b/testing/web-platform/meta/MANIFEST.json
@@ -220234,17 +220234,17 @@
    "5ef1183a4d3e12ad3edfe678c9fa002e7edce888",
    "support"
   ],
   "web-animations/resources/keyframe-utils.js": [
    "ff5700466b5af6ffaad824437d6566003a22e25b",
    "support"
   ],
   "web-animations/testcommon.js": [
-   "b12f7087f2adec5ce2a2b8e07be2f24d68aa9df2",
+   "d057ad66c4561ef32f83770e4948f2019da89d48",
    "support"
   ],
   "web-animations/timing-model/animation-effects/active-time.html": [
    "42eb1a23e89ae60ccd0a3664a9a583df1eb30d49",
    "testharness"
   ],
   "web-animations/timing-model/animation-effects/current-iteration.html": [
    "b08a35ae832ce33da7fe7fee22e589a6b85a6353",
deleted file mode 100644
--- a/testing/web-platform/meta/web-animations/timing-model/animations/canceling-an-animation.html.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-[canceling-an-animation.html]
-  type: testharness
-  [The ready promise should be replaced when the animation is canceled]
-    expected: FAIL
-
--- a/widget/ContentCache.cpp
+++ b/widget/ContentCache.cpp
@@ -1157,32 +1157,38 @@ void
 ContentCacheInParent::OnEventNeedingAckHandled(nsIWidget* aWidget,
                                                 EventMessage aMessage)
 {
   // This is called when the child process receives WidgetCompositionEvent or
   // WidgetSelectionEvent.
 
   MOZ_LOG(sContentCacheLog, LogLevel::Info,
     ("0x%p OnEventNeedingAckHandled(aWidget=0x%p, "
-     "aMessage=%s), mPendingEventsNeedingAck=%u, mPendingCompositionCount=%" PRIu8,
-     this, aWidget, ToChar(aMessage), mPendingEventsNeedingAck, mPendingCompositionCount));
-
-  if (WidgetCompositionEvent::IsFollowedByCompositionEnd(aMessage)) {
-    MOZ_RELEASE_ASSERT(mPendingCompositionCount > 0);
-    mPendingCompositionCount--;
-  }
+     "aMessage=%s), mPendingEventsNeedingAck=%u",
+     this, aWidget, ToChar(aMessage), mPendingEventsNeedingAck));
 
   MOZ_RELEASE_ASSERT(mPendingEventsNeedingAck > 0);
   if (--mPendingEventsNeedingAck) {
     return;
   }
 
   FlushPendingNotifications(aWidget);
 }
 
+void
+ContentCacheInParent::OnDestroyTextComposition()
+{
+  MOZ_LOG(sContentCacheLog, LogLevel::Info,
+    ("0x%p OnDestroyTextComposition(), "
+     "mPendingEventsNeedingAck=%u, mPendingCompositionCount=%" PRIu8,
+     this, mPendingEventsNeedingAck, mPendingCompositionCount));
+  MOZ_RELEASE_ASSERT(mPendingCompositionCount > 0);
+  mPendingCompositionCount--;
+}
+
 bool
 ContentCacheInParent::RequestIMEToCommitComposition(nsIWidget* aWidget,
                                                     bool aCancel,
                                                     nsAString& aCommittedString)
 {
   MOZ_LOG(sContentCacheLog, LogLevel::Info,
     ("0x%p RequestToCommitComposition(aWidget=%p, "
      "aCancel=%s), mWidgetHasComposition=%s, mCommitStringByRequest=%p",
--- a/widget/ContentCache.h
+++ b/widget/ContentCache.h
@@ -370,16 +370,23 @@ public:
    *
    * WARNING: This may send notifications to IME.  That might cause destroying
    *          TabParent or aWidget.  Therefore, the caller must not destroy
    *          this instance during a call of this method.
    */
   void OnEventNeedingAckHandled(nsIWidget* aWidget, EventMessage aMessage);
 
   /**
+   * OnDestroyTextComposition() should be called when TextComposition instance
+   * which dispatched composition events to the owner of this instance is being
+   * destroyed.
+   */
+  void OnDestroyTextComposition();
+
+  /**
    * RequestIMEToCommitComposition() requests aWidget to commit or cancel
    * composition.  If it's handled synchronously, this returns true.
    *
    * @param aWidget     The widget to be requested to commit or cancel
    *                    the composition.
    * @param aCancel     When the caller tries to cancel the composition, true.
    *                    Otherwise, i.e., tries to commit the composition, false.
    * @param aCommittedString    The committed string (i.e., the last data of
--- a/widget/nsIJumpListBuilder.idl
+++ b/widget/nsIJumpListBuilder.idl
@@ -3,16 +3,22 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 
 interface nsIArray;
 interface nsIMutableArray;
 
+[scriptable, function, uuid(5131a62a-e99f-4631-9138-751f8aad1ae4)]
+interface nsIJumpListCommittedCallback : nsISupports
+{
+  void done(in boolean result);
+};
+
 [scriptable, uuid(1FE6A9CD-2B18-4dd5-A176-C2B32FA4F683)]
 interface nsIJumpListBuilder : nsISupports
 {
   /**
    * JumpLists
    *
    * Jump lists are built and then applied. Modifying an applied jump list is not
    * permitted. Callers should begin the creation of a new jump list using
@@ -131,19 +137,21 @@ interface nsIJumpListBuilder : nsISuppor
   /**
    * Aborts and clears the current jump list build.
    */
   void abortListBuild();
 
   /**
    * Commits the current jump list build to the Taskbar.
    *
-   * @returns true if the operation completed successfully.
+   * @param callback
+   *        Receives one argument, which is true if the operation completed
+   *        successfully, otherwise it is false.
    */
-  boolean commitListBuild();
+  void commitListBuild([optional] in nsIJumpListCommittedCallback callback);
 
   /**
    * Deletes any currently applied taskbar jump list for this application.
    * Common uses would be the enabling of a privacy mode and uninstallation.
    *
    * @returns true if the operation completed successfully.
    *
    * @throw NS_ERROR_UNEXPECTED on internal errors.
--- a/widget/tests/unit/test_taskbar_jumplistitems.js
+++ b/widget/tests/unit/test_taskbar_jumplistitems.js
@@ -169,17 +169,17 @@ function test_shortcuts()
     sc.app = handlerApp;
     do_check_eq(sc.app.detailedDescription, "TestApp detailed description.");
     do_check_eq(sc.app.name, "TestApp");
     do_check_true(sc.app.parameterExists("-test"));
     do_check_false(sc.app.parameterExists("-notset"));
   }
 }
 
-function test_jumplist()
+async function test_jumplist()
 {
   // Jump lists can't register links unless the application is the default
   // protocol handler for the protocol of the link, so we skip off testing
   // those in these tests. We'll init the jump list for the xpc shell harness,
   // add a task item, and commit it.
  
   // not compiled in
   if (Ci.nsIWinTaskbar == null)
@@ -220,42 +220,55 @@ function test_jumplist()
   handlerApp.detailedDescription = "Testing detailed description.";
 
   var dirSvc = Cc["@mozilla.org/file/directory_service;1"].
                   getService(Ci.nsIProperties).
                   QueryInterface(Ci.nsIDirectoryService);
   var notepad = dirSvc.get("WinD", Ci.nsIFile);
   notepad.append("notepad.exe");
   if (notepad.exists()) {
+    // To ensure "profile-before-change" will fire before
+    // "xpcom-shutdown-threads"
+    do_get_profile();
+
     handlerApp.executable = notepad;
     sc.app = handlerApp;
     items.appendElement(sc);
 
     var removed = Cc["@mozilla.org/array;1"]
                   .createInstance(Ci.nsIMutableArray);
     do_check_true(builder.initListBuild(removed));
     do_check_true(builder.addListToBuild(builder.JUMPLIST_CATEGORY_TASKS, items));
     do_check_true(builder.addListToBuild(builder.JUMPLIST_CATEGORY_RECENT));
     do_check_true(builder.addListToBuild(builder.JUMPLIST_CATEGORY_FREQUENT));
-    do_check_true(builder.commitListBuild());
+    let rv = new Promise((resolve) => {
+      builder.commitListBuild(resolve);
+    });
+    do_check_true(await rv);
 
     builder.deleteActiveList();
 
     do_check_true(builder.initListBuild(removed));
-    do_check_true(builder.addListToBuild(builder.JUMPLIST_CATEGORY_CUSTOM, items, "Custom List"));
-    do_check_true(builder.commitListBuild());
+    do_check_true(builder.addListToBuild(builder.JUMPLIST_CATEGORY_CUSTOMLIST, items, "Custom List"));
+    rv = new Promise((resolve) => {
+      builder.commitListBuild(resolve);
+    });
+    do_check_true(await rv);
 
     builder.deleteActiveList();
   }
 }
 
 function run_test()
 {
   if (mozinfo.os != "win") {
     return;
   }
   test_basics();
   test_separator();
   test_hashes();
   test_links();
   test_shortcuts();
-  test_jumplist();
+
+  run_next_test();
 }
+
+add_task(test_jumplist);
--- a/widget/windows/JumpListBuilder.cpp
+++ b/widget/windows/JumpListBuilder.cpp
@@ -15,39 +15,98 @@
 #include "WinTaskbar.h"
 #include "nsDirectoryServiceUtils.h"
 #include "nsISimpleEnumerator.h"
 #include "mozilla/Preferences.h"
 #include "nsStringStream.h"
 #include "nsThreadUtils.h"
 #include "mozilla/LazyIdleThread.h"
 #include "nsIObserverService.h"
+#include "mozilla/Unused.h"
 
 #include "WinUtils.h"
 
 // The amount of time, in milliseconds, that our IO thread will stay alive after the last event it processes.
 #define DEFAULT_THREAD_TIMEOUT_MS 30000
 
 namespace mozilla {
 namespace widget {
 
 static NS_DEFINE_CID(kJumpListItemCID,     NS_WIN_JUMPLISTITEM_CID);
 static NS_DEFINE_CID(kJumpListLinkCID,     NS_WIN_JUMPLISTLINK_CID);
 static NS_DEFINE_CID(kJumpListShortcutCID, NS_WIN_JUMPLISTSHORTCUT_CID);
 
 // defined in WinTaskbar.cpp
 extern const wchar_t *gMozillaJumpListIDGeneric;
 
-bool JumpListBuilder::sBuildingList = false;
+Atomic<bool> JumpListBuilder::sBuildingList(false);
 const char kPrefTaskbarEnabled[] = "browser.taskbar.lists.enabled";
 
 NS_IMPL_ISUPPORTS(JumpListBuilder, nsIJumpListBuilder, nsIObserver)
 #define TOPIC_PROFILE_BEFORE_CHANGE "profile-before-change"
 #define TOPIC_CLEAR_PRIVATE_DATA "clear-private-data"
 
+
+namespace detail {
+
+class DoneCommitListBuildCallback : public nsIRunnable
+{
+  NS_DECL_THREADSAFE_ISUPPORTS
+
+public:
+  DoneCommitListBuildCallback(nsIJumpListCommittedCallback* aCallback,
+                              JumpListBuilder* aBuilder)
+    : mCallback(aCallback)
+    , mBuilder(aBuilder)
+    , mResult(false)
+  {
+  }
+
+  NS_IMETHOD Run() override
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    if (mCallback) {
+      Unused << mCallback->Done(mResult);
+    }
+    // Ensure we are releasing on the main thread.
+    Destroy();
+    return NS_OK;
+  }
+
+  void SetResult(bool aResult)
+  {
+    mResult = aResult;
+  }
+
+private:
+  ~DoneCommitListBuildCallback()
+  {
+    // Destructor does not always call on the main thread.
+    MOZ_ASSERT(!mCallback);
+    MOZ_ASSERT(!mBuilder);
+  }
+
+  void Destroy()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    mCallback = nullptr;
+    mBuilder = nullptr;
+  }
+
+  // These two references MUST be released on the main thread.
+  RefPtr<nsIJumpListCommittedCallback> mCallback;
+  RefPtr<JumpListBuilder> mBuilder;
+  bool mResult;
+};
+
+NS_IMPL_ISUPPORTS(DoneCommitListBuildCallback, nsIRunnable);
+
+} // namespace detail
+
+
 JumpListBuilder::JumpListBuilder() :
   mMaxItems(0),
   mHasCommit(false)
 {
   ::CoInitialize(nullptr);
 
   CoCreateInstance(CLSID_DestinationList, nullptr, CLSCTX_INPROC_SERVER,
                    IID_ICustomDestinationList, getter_AddRefs(mJumpListMgr));
@@ -402,33 +461,51 @@ NS_IMETHODIMP JumpListBuilder::AbortList
     return NS_ERROR_NOT_AVAILABLE;
 
   mJumpListMgr->AbortList();
   sBuildingList = false;
 
   return NS_OK;
 }
 
-NS_IMETHODIMP JumpListBuilder::CommitListBuild(bool *_retval)
+NS_IMETHODIMP JumpListBuilder::CommitListBuild(nsIJumpListCommittedCallback* aCallback)
 {
-  *_retval = false;
-
   if (!mJumpListMgr)
     return NS_ERROR_NOT_AVAILABLE;
 
+  // Also holds a strong reference to this to prevent use-after-free.
+  RefPtr<detail::DoneCommitListBuildCallback> callback =
+    new detail::DoneCommitListBuildCallback(aCallback, this);
+
+  // The builder has a strong reference in the callback already, so we do not
+  // need to do it for this runnable again.
+  RefPtr<nsIRunnable> event =
+    NewNonOwningRunnableMethod<RefPtr<detail::DoneCommitListBuildCallback>>
+      ("JumpListBuilder::DoCommitListBuild", this,
+       &JumpListBuilder::DoCommitListBuild, Move(callback));
+  Unused << mIOThread->Dispatch(event, NS_DISPATCH_NORMAL);
+
+  return NS_OK;
+}
+
+void JumpListBuilder::DoCommitListBuild(RefPtr<detail::DoneCommitListBuildCallback> aCallback)
+{
+  MOZ_ASSERT(mJumpListMgr);
+  MOZ_ASSERT(aCallback);
+
   HRESULT hr = mJumpListMgr->CommitList();
   sBuildingList = false;
 
-  // XXX We might want some specific error data here.
   if (SUCCEEDED(hr)) {
-    *_retval = true;
     mHasCommit = true;
   }
 
-  return NS_OK;
+  // XXX We might want some specific error data here.
+  aCallback->SetResult(SUCCEEDED(hr));
+  Unused << NS_DispatchToMainThread(aCallback);
 }
 
 NS_IMETHODIMP JumpListBuilder::DeleteActiveList(bool *_retval)
 {
   *_retval = false;
 
   if (!mJumpListMgr)
     return NS_ERROR_NOT_AVAILABLE;
--- a/widget/windows/JumpListBuilder.h
+++ b/widget/windows/JumpListBuilder.h
@@ -21,41 +21,46 @@
 #include "nsIJumpListItem.h"
 #include "JumpListItem.h"
 #include "nsIObserver.h"
 #include "mozilla/Attributes.h"
 
 namespace mozilla {
 namespace widget {
 
+namespace detail {
+class DoneCommitListBuildCallback;
+} // namespace detail
+
 class JumpListBuilder : public nsIJumpListBuilder, 
                         public nsIObserver
 {
   virtual ~JumpListBuilder();
 
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIJUMPLISTBUILDER
   NS_DECL_NSIOBSERVER
 
   JumpListBuilder();
 
 protected:
-  static bool sBuildingList; 
+  static Atomic<bool> sBuildingList;
 
 private:
   RefPtr<ICustomDestinationList> mJumpListMgr;
   uint32_t mMaxItems;
   bool mHasCommit;
   nsCOMPtr<nsIThread> mIOThread;
 
   bool IsSeparator(nsCOMPtr<nsIJumpListItem>& item);
   nsresult TransferIObjectArrayToIMutableArray(IObjectArray *objArray, nsIMutableArray *removedItems);
   nsresult RemoveIconCacheForItems(nsIMutableArray *removedItems);
   nsresult RemoveIconCacheForAllItems();
+  void DoCommitListBuild(RefPtr<detail::DoneCommitListBuildCallback> aCallback);
 
   friend class WinTaskbar;
 };
 
 } // namespace widget
 } // namespace mozilla
 
 #endif /* __JumpListBuilder_h__ */
--- a/widget/windows/WinUtils.cpp
+++ b/widget/windows/WinUtils.cpp
@@ -1456,30 +1456,34 @@ AsyncDeleteIconFromDisk::~AsyncDeleteIco
 
 AsyncDeleteAllFaviconsFromDisk::
   AsyncDeleteAllFaviconsFromDisk(bool aIgnoreRecent)
   : mIgnoreRecent(aIgnoreRecent)
 {
   // We can't call FaviconHelper::GetICOCacheSecondsTimeout() on non-main
   // threads, as it reads a pref, so cache its value here.
   mIcoNoDeleteSeconds = FaviconHelper::GetICOCacheSecondsTimeout() + 600;
+
+  // Prepare the profile directory cache on the main thread, to ensure we wont
+  // do this on non-main threads.
+  Unused << NS_GetSpecialDirectory("ProfLDS",
+    getter_AddRefs(mJumpListCacheDir));
 }
 
 NS_IMETHODIMP AsyncDeleteAllFaviconsFromDisk::Run()
 {
+  if (!mJumpListCacheDir) {
+    return NS_ERROR_FAILURE;
+  }
   // Construct the path of our jump list cache
-  nsCOMPtr<nsIFile> jumpListCacheDir;
-  nsresult rv = NS_GetSpecialDirectory("ProfLDS", 
-    getter_AddRefs(jumpListCacheDir));
-  NS_ENSURE_SUCCESS(rv, rv);
-  rv = jumpListCacheDir->AppendNative(
+  nsresult rv = mJumpListCacheDir->AppendNative(
       nsDependentCString(FaviconHelper::kJumpListCacheDir));
   NS_ENSURE_SUCCESS(rv, rv);
   nsCOMPtr<nsISimpleEnumerator> entries;
-  rv = jumpListCacheDir->GetDirectoryEntries(getter_AddRefs(entries));
+  rv = mJumpListCacheDir->GetDirectoryEntries(getter_AddRefs(entries));
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Loop through each directory entry and remove all ICO files found
   do {
     bool hasMore = false;
     if (NS_FAILED(entries->HasMoreElements(&hasMore)) || !hasMore)
       break;
 
--- a/widget/windows/WinUtils.h
+++ b/widget/windows/WinUtils.h
@@ -568,16 +568,17 @@ public:
 
   explicit AsyncDeleteAllFaviconsFromDisk(bool aIgnoreRecent = false);
 
 private:
   virtual ~AsyncDeleteAllFaviconsFromDisk();
 
   int32_t mIcoNoDeleteSeconds;
   bool mIgnoreRecent;
+  nsCOMPtr<nsIFile> mJumpListCacheDir;
 };
 
 class FaviconHelper
 {
 public:
   static const char kJumpListCacheDir[];
   static const char kShortcutCacheDir[];
   static nsresult ObtainCachedIconFile(nsCOMPtr<nsIURI> aFaviconPageURI,
--- a/xpcom/threads/MozPromise.h
+++ b/xpcom/threads/MozPromise.h
@@ -170,42 +170,31 @@ class MozPromise : public MozPromiseBase
     return static_cast<R>(aX);
   }
 
 public:
   typedef ResolveValueT ResolveValueType;
   typedef RejectValueT RejectValueType;
   class ResolveOrRejectValue
   {
-    template <int, typename T>
-    struct Holder
-    {
-      template <typename... Args>
-      explicit Holder(Args&&... aArgs) : mData(Forward<Args>(aArgs)...) { }
-      T mData;
-    };
-
-    // Ensure Holder<0, T1> and Holder<1, T2> are different types
-    // which is required by Variant.
-    using ResolveValueHolder = Holder<0, ResolveValueType>;
-    using RejectValueHolder = Holder<1, RejectValueType>;
-
   public:
     template<typename ResolveValueType_>
     void SetResolve(ResolveValueType_&& aResolveValue)
     {
       MOZ_ASSERT(IsNothing());
-      mValue = AsVariant(ResolveValueHolder(Forward<ResolveValueType_>(aResolveValue)));
+      mValue = Storage(VariantIndex<ResolveIndex>{},
+                       Forward<ResolveValueType_>(aResolveValue));
     }
 
     template<typename RejectValueType_>
     void SetReject(RejectValueType_&& aRejectValue)
     {
       MOZ_ASSERT(IsNothing());
-      mValue = AsVariant(RejectValueHolder(Forward<RejectValueType_>(aRejectValue)));
+      mValue = Storage(VariantIndex<RejectIndex>{},
+                       Forward<RejectValueType_>(aRejectValue));
     }
 
     template<typename ResolveValueType_>
     static ResolveOrRejectValue MakeResolve(ResolveValueType_&& aResolveValue)
     {
       ResolveOrRejectValue val;
       val.SetResolve(Forward<ResolveValueType_>(aResolveValue));
       return val;
@@ -214,39 +203,41 @@ public:
     template<typename RejectValueType_>
     static ResolveOrRejectValue MakeReject(RejectValueType_&& aRejectValue)
     {
       ResolveOrRejectValue val;
       val.SetReject(Forward<RejectValueType_>(aRejectValue));
       return val;
     }
 
-    bool IsResolve() const { return mValue.template is<ResolveValueHolder>(); }
-    bool IsReject() const { return mValue.template is<RejectValueHolder>(); }
-    bool IsNothing() const { return mValue.template is<Nothing>(); }
+    bool IsResolve() const { return mValue.template is<ResolveIndex>(); }
+    bool IsReject() const { return mValue.template is<RejectIndex>(); }
+    bool IsNothing() const { return mValue.template is<NothingIndex>(); }
 
     const ResolveValueType& ResolveValue() const
     {
-      return mValue.template as<ResolveValueHolder>().mData;
+      return mValue.template as<ResolveIndex>();
     }
     ResolveValueType& ResolveValue()
     {
-      return mValue.template as<ResolveValueHolder>().mData;
+      return mValue.template as<ResolveIndex>();
     }
     const RejectValueType& RejectValue() const
     {
-      return mValue.template as<RejectValueHolder>().mData;
+      return mValue.template as<RejectIndex>();
     }
     RejectValueType& RejectValue()
     {
-      return mValue.template as<RejectValueHolder>().mData;
+      return mValue.template as<RejectIndex>();
     }
 
   private:
-    Variant<Nothing, ResolveValueHolder, RejectValueHolder> mValue = AsVariant(Nothing{});
+    enum { NothingIndex, ResolveIndex, RejectIndex };
+    using Storage = Variant<Nothing, ResolveValueType, RejectValueType>;
+    Storage mValue = Storage(VariantIndex<NothingIndex>{});
   };
 
 protected:
   // MozPromise is the public type, and never constructed directly. Construct
   // a MozPromise::Private, defined below.
   MozPromise(const char* aCreationSite, bool aIsCompletionPromise)
     : mCreationSite(aCreationSite)
     , mMutex("MozPromise Mutex")