Merge mozilla-central to mozilla-inbound
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Tue, 21 Feb 2017 14:18:35 +0100
changeset 373090 0be3bfee0e9ea84ad2dcfcdaca010d97cd50c4f1
parent 373089 eb661732bcdec7e929eee1cbfb7f3fbab915ff1e (current diff)
parent 372984 b69d4316561aa125612509a202922455d2de03e5 (diff)
child 373091 e0ab3ae16c94d6bc42926c893c66138967c855f9
push id10863
push userjlorenzo@mozilla.com
push dateMon, 06 Mar 2017 23:02:23 +0000
treeherdermozilla-aurora@0931190cd725 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone54.0a1
Merge mozilla-central to mozilla-inbound
dom/ipc/ContentParent.cpp
js/src/old-configure.in
old-configure.in
--- a/accessible/ipc/win/ProxyAccessible.cpp
+++ b/accessible/ipc/win/ProxyAccessible.cpp
@@ -334,16 +334,17 @@ ConvertBSTRAttributesToArray(const nsASt
         tokens[eName].Truncate();
         tokens[eValue].Truncate();
         ++itr;
         continue;
       default:
         break;
     }
     tokens[state] += *itr;
+    ++itr;
   }
   return true;
 }
 
 void
 ProxyAccessible::Attributes(nsTArray<Attribute>* aAttrs) const
 {
   aAttrs->Clear();
--- a/accessible/tests/browser/e10s/browser.ini
+++ b/accessible/tests/browser/e10s/browser.ini
@@ -10,67 +10,42 @@ support-files =
   doc_treeupdate_whitespace.html
   !/accessible/tests/browser/shared-head.js
   !/accessible/tests/mochitest/*.js
   !/accessible/tests/mochitest/letters.gif
   !/accessible/tests/mochitest/moz.png
 
 # Caching tests
 [browser_caching_attributes.js]
-skip-if = e10s && os == 'win' # Bug 1288839
 [browser_caching_description.js]
-skip-if = e10s && os == 'win' && os_version == '5.1'
 [browser_caching_name.js]
 skip-if = e10s && os == 'win' && debug # Bug 1338034, leaks
 [browser_caching_relations.js]
-skip-if = e10s && os == 'win' && os_version == '5.1'
 [browser_caching_states.js]
-skip-if = e10s && os == 'win' && os_version == '5.1'
 [browser_caching_value.js]
-skip-if = e10s && os == 'win' && os_version == '5.1'
 
 # Events tests
 [browser_events_caretmove.js]
-skip-if = e10s && os == 'win' && os_version == '5.1'
 [browser_events_hide.js]
-skip-if = e10s && os == 'win' && os_version == '5.1'
 [browser_events_show.js]
-skip-if = e10s && os == 'win' && os_version == '5.1'
 [browser_events_statechange.js]
-skip-if = e10s && os == 'win' && os_version == '5.1'
 [browser_events_textchange.js]
-skip-if = e10s && os == 'win' && os_version == '5.1'
 
 # Tree update tests
 [browser_treeupdate_ariadialog.js]
-skip-if = e10s && os == 'win' && os_version == '5.1'
 [browser_treeupdate_ariaowns.js]
-skip-if = e10s && os == 'win' && os_version == '5.1'
 [browser_treeupdate_canvas.js]
-skip-if = e10s && os == 'win' && os_version == '5.1'
 [browser_treeupdate_cssoverflow.js]
-skip-if = e10s && os == 'win' && os_version == '5.1'
 [browser_treeupdate_doc.js]
 skip-if = e10s && os == 'win' # Bug 1288839
 [browser_treeupdate_gencontent.js]
-skip-if = e10s && os == 'win' && os_version == '5.1'
 [browser_treeupdate_hidden.js]
-skip-if = e10s && os == 'win' && os_version == '5.1'
 [browser_treeupdate_imagemap.js]
-skip-if = e10s && os == 'win' && os_version == '5.1'
 [browser_treeupdate_list.js]
-skip-if = e10s && os == 'win' && os_version == '5.1'
 [browser_treeupdate_list_editabledoc.js]
-skip-if = e10s && os == 'win' && os_version == '5.1'
 [browser_treeupdate_listener.js]
-skip-if = e10s && os == 'win' && os_version == '5.1'
 [browser_treeupdate_optgroup.js]
-skip-if = e10s && os == 'win' && os_version == '5.1'
 [browser_treeupdate_removal.js]
-skip-if = e10s && os == 'win' && os_version == '5.1'
 [browser_treeupdate_table.js]
-skip-if = e10s && os == 'win' && os_version == '5.1'
 [browser_treeupdate_textleaf.js]
-skip-if = e10s && os == 'win' && os_version == '5.1'
 [browser_treeupdate_visibility.js]
-skip-if = e10s && os == 'win' && os_version == '5.1'
 [browser_treeupdate_whitespace.js]
 skip-if = true # Failing due to incorrect index of test container children on document load.
--- a/browser/components/preferences/siteDataSettings.xul
+++ b/browser/components/preferences/siteDataSettings.xul
@@ -22,17 +22,17 @@
 
   <stringbundle id="bundlePreferences"
                 src="chrome://browser/locale/preferences/preferences.properties"/>
 
   <vbox flex="1">
     <description>&settings.description;</description>
     <separator class="thin"/>
 
-    <hbox id="searchBoxContainer">
+    <hbox id="searchBoxContainer" align="center">
       <label accesskey="&search.accesskey;" control="searchBox">&search.label;</label>
       <textbox id="searchBox" type="search" flex="1"/>
     </hbox>
     <separator class="thin"/>
 
     <richlistbox id="sitesList" orient="vertical" flex="1">
       <listheader>
         <treecol flex="4" width="50" label="&hostCol.label;" id="hostCol"/>
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -2282,23 +2282,19 @@ nsDocument::ResetStylesheetsToURI(nsIURI
     // filled the style set.  (This allows us to avoid calling
     // GetStyleBackendType() too early.)
     RemoveDocStyleSheetsFromStyleSets();
     RemoveStyleSheetsFromStyleSets(mOnDemandBuiltInUASheets, SheetType::Agent);
     RemoveStyleSheetsFromStyleSets(mAdditionalSheets[eAgentSheet], SheetType::Agent);
     RemoveStyleSheetsFromStyleSets(mAdditionalSheets[eUserSheet], SheetType::User);
     RemoveStyleSheetsFromStyleSets(mAdditionalSheets[eAuthorSheet], SheetType::Doc);
 
-    if (GetStyleBackendType() == StyleBackendType::Gecko) {
-      nsStyleSheetService *sheetService = nsStyleSheetService::GetInstance();
-      if (sheetService) {
-        RemoveStyleSheetsFromStyleSets(*sheetService->AuthorStyleSheets(), SheetType::Doc);
-      }
-    } else {
-      NS_WARNING("stylo: nsStyleSheetService doesn't handle ServoStyleSheets yet");
+    if (nsStyleSheetService* sheetService = nsStyleSheetService::GetInstance()) {
+      RemoveStyleSheetsFromStyleSets(
+        *sheetService->AuthorStyleSheets(GetStyleBackendType()), SheetType::Doc);
     }
 
     mStyleSetFilled = false;
   }
 
   // Release all the sheets
   mStyleSheets.Clear();
   mOnDemandBuiltInUASheets.Clear();
@@ -2355,33 +2351,29 @@ nsDocument::FillStyleSet(StyleSetHandle 
   MOZ_ASSERT(!mStyleSetFilled);
 
   for (StyleSheet* sheet : Reversed(mStyleSheets)) {
     if (sheet->IsApplicable()) {
       aStyleSet->AddDocStyleSheet(sheet, this);
     }
   }
 
-  if (aStyleSet->IsGecko()) {
-    nsStyleSheetService *sheetService = nsStyleSheetService::GetInstance();
-    if (sheetService) {
-      for (StyleSheet* sheet : *sheetService->AuthorStyleSheets()) {
-        aStyleSet->AppendStyleSheet(SheetType::Doc, sheet);
-      }
-    }
-
-    // Iterate backwards to maintain order
-    for (StyleSheet* sheet : Reversed(mOnDemandBuiltInUASheets)) {
-      if (sheet->IsApplicable()) {
-        aStyleSet->PrependStyleSheet(SheetType::Agent, sheet);
-      }
-    }
-  } else {
-    NS_WARNING("stylo: Not yet checking nsStyleSheetService for Servo-backed "
-               "documents. See bug 1290224");
+  if (nsStyleSheetService* sheetService = nsStyleSheetService::GetInstance()) {
+    nsTArray<RefPtr<StyleSheet>>& sheets =
+      *sheetService->AuthorStyleSheets(aStyleSet->BackendType());
+    for (StyleSheet* sheet : sheets) {
+      aStyleSet->AppendStyleSheet(SheetType::Doc, sheet);
+    }
+  }
+
+  // Iterate backwards to maintain order
+  for (StyleSheet* sheet : Reversed(mOnDemandBuiltInUASheets)) {
+    if (sheet->IsApplicable()) {
+      aStyleSet->PrependStyleSheet(SheetType::Agent, sheet);
+    }
   }
 
   AppendSheetsToStyleSet(aStyleSet, mAdditionalSheets[eAgentSheet],
                          SheetType::Agent);
   AppendSheetsToStyleSet(aStyleSet, mAdditionalSheets[eUserSheet],
                          SheetType::User);
   AppendSheetsToStyleSet(aStyleSet, mAdditionalSheets[eAuthorSheet],
                          SheetType::Doc);
--- a/dom/base/nsIDocumentInlines.h
+++ b/dom/base/nsIDocumentInlines.h
@@ -38,17 +38,18 @@ nsIDocument::FindDocStyleSheetInsertionP
     mozilla::StyleSheet* sheetHandle = sheet;
 
     // If the sheet is not owned by the document it can be an author
     // sheet registered at nsStyleSheetService or an additional author
     // sheet on the document, which means the new
     // doc sheet should end up before it.
     if (sheetDocIndex < 0) {
       if (sheetService) {
-        auto& authorSheets = *sheetService->AuthorStyleSheets();
+        auto& authorSheets =
+          *sheetService->AuthorStyleSheets(GetStyleBackendType());
         if (authorSheets.IndexOf(sheetHandle) != authorSheets.NoIndex) {
           break;
         }
       }
       if (sheetHandle == GetFirstAdditionalAuthorSheet()) {
         break;
       }
     }
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -2140,30 +2140,37 @@ ContentParent::InitInternal(ProcessPrior
       gpm->AddListener(this);
     }
   }
 
   nsStyleSheetService *sheetService = nsStyleSheetService::GetInstance();
   if (sheetService) {
     // This looks like a lot of work, but in a normal browser session we just
     // send two loads.
-
-    for (StyleSheet* sheet : *sheetService->AgentStyleSheets()) {
+    //
+    // The URIs of the Gecko and Servo sheets should be the same, so it
+    // shouldn't matter which we look at.  (The Servo sheets don't exist
+    // in non-MOZ-STYLO builds, though, so look at the Gecko ones.)
+
+    for (StyleSheet* sheet :
+           *sheetService->AgentStyleSheets(StyleBackendType::Gecko)) {
       URIParams uri;
       SerializeURI(sheet->GetSheetURI(), uri);
       Unused << SendLoadAndRegisterSheet(uri, nsIStyleSheetService::AGENT_SHEET);
     }
 
-    for (StyleSheet* sheet : *sheetService->UserStyleSheets()) {
+    for (StyleSheet* sheet :
+           *sheetService->UserStyleSheets(StyleBackendType::Gecko)) {
       URIParams uri;
       SerializeURI(sheet->GetSheetURI(), uri);
       Unused << SendLoadAndRegisterSheet(uri, nsIStyleSheetService::USER_SHEET);
     }
 
-    for (StyleSheet* sheet : *sheetService->AuthorStyleSheets()) {
+    for (StyleSheet* sheet :
+           *sheetService->AuthorStyleSheets(StyleBackendType::Gecko)) {
       URIParams uri;
       SerializeURI(sheet->GetSheetURI(), uri);
       Unused << SendLoadAndRegisterSheet(uri, nsIStyleSheetService::AUTHOR_SHEET);
     }
   }
 
 #ifdef MOZ_CONTENT_SANDBOX
   bool shouldSandbox = true;
--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -48,17 +48,17 @@ namespace mozilla {
 /**
  * This is a singleton which controls the number of decoders that can be
  * created concurrently. Before calling PDMFactory::CreateDecoder(), Alloc()
  * must be called to get a token object as a permission to create a decoder.
  * The token should stay alive until Shutdown() is called on the decoder.
  * The destructor of the token will restore the decoder count so it is available
  * for next calls of Alloc().
  */
-class DecoderAllocPolicy
+class GlobalAllocPolicy
 {
   using TrackType = TrackInfo::TrackType;
 
 public:
   class Token
   {
     NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Token)
   protected:
@@ -69,148 +69,272 @@ public:
 
   // Acquire a token for decoder creation. Thread-safe.
   auto Alloc() -> RefPtr<Promise>;
 
   // Called by ClearOnShutdown() to delete the singleton.
   void operator=(decltype(nullptr));
 
   // Get the singleton for the given track type. Thread-safe.
-  static DecoderAllocPolicy& Instance(TrackType aTrack);
+  static GlobalAllocPolicy& Instance(TrackType aTrack);
 
 private:
   class AutoDeallocToken;
   using PromisePrivate = Promise::Private;
-  DecoderAllocPolicy();
-  ~DecoderAllocPolicy();
+  GlobalAllocPolicy();
+  ~GlobalAllocPolicy();
   // Called by the destructor of TokenImpl to restore the decoder limit.
   void Dealloc();
   // Decrement the decoder limit and resolve a promise if available.
   void ResolvePromise(ReentrantMonitorAutoEnter& aProofOfLock);
 
   // Protect access to Instance().
   static StaticMutex sMutex;
 
   ReentrantMonitor mMonitor;
   // The number of decoders available for creation.
   int mDecoderLimit;
   // Requests to acquire tokens.
   std::queue<RefPtr<PromisePrivate>> mPromises;
 };
 
-StaticMutex DecoderAllocPolicy::sMutex;
-
-class DecoderAllocPolicy::AutoDeallocToken : public Token
+StaticMutex GlobalAllocPolicy::sMutex;
+
+class GlobalAllocPolicy::AutoDeallocToken : public Token
 {
 public:
-  explicit AutoDeallocToken(DecoderAllocPolicy& aPolicy) : mPolicy(aPolicy) { }
+  explicit AutoDeallocToken(GlobalAllocPolicy& aPolicy) : mPolicy(aPolicy) { }
 
 private:
   ~AutoDeallocToken()
   {
     mPolicy.Dealloc();
   }
 
-  DecoderAllocPolicy& mPolicy; // reference to a singleton object.
+  GlobalAllocPolicy& mPolicy; // reference to a singleton object.
 };
 
-DecoderAllocPolicy::DecoderAllocPolicy()
+GlobalAllocPolicy::GlobalAllocPolicy()
   : mMonitor("DecoderAllocPolicy::mMonitor")
   , mDecoderLimit(MediaPrefs::MediaDecoderLimit())
 {
   // Non DocGroup-version AbstractThread::MainThread is fine for
   // ClearOnShutdown().
   AbstractThread::MainThread()->Dispatch(NS_NewRunnableFunction([this] () {
     ClearOnShutdown(this, ShutdownPhase::ShutdownThreads);
   }));
 }
 
-DecoderAllocPolicy::~DecoderAllocPolicy()
+GlobalAllocPolicy::~GlobalAllocPolicy()
 {
   while (!mPromises.empty()) {
     RefPtr<PromisePrivate> p = mPromises.front().forget();
     mPromises.pop();
     p->Reject(true, __func__);
   }
 }
 
-DecoderAllocPolicy&
-DecoderAllocPolicy::Instance(TrackType aTrack)
+GlobalAllocPolicy&
+GlobalAllocPolicy::Instance(TrackType aTrack)
 {
   StaticMutexAutoLock lock(sMutex);
   if (aTrack == TrackType::kAudioTrack) {
-    static auto sAudioPolicy = new DecoderAllocPolicy();
+    static auto sAudioPolicy = new GlobalAllocPolicy();
     return *sAudioPolicy;
   } else {
-    static auto sVideoPolicy = new DecoderAllocPolicy();
+    static auto sVideoPolicy = new GlobalAllocPolicy();
     return *sVideoPolicy;
   }
 }
 
 auto
-DecoderAllocPolicy::Alloc() -> RefPtr<Promise>
+GlobalAllocPolicy::Alloc() -> RefPtr<Promise>
 {
   // No decoder limit set.
   if (mDecoderLimit < 0) {
     return Promise::CreateAndResolve(new Token(), __func__);
   }
 
   ReentrantMonitorAutoEnter mon(mMonitor);
   RefPtr<PromisePrivate> p = new PromisePrivate(__func__);
   mPromises.push(p);
   ResolvePromise(mon);
   return p.forget();
 }
 
 void
-DecoderAllocPolicy::Dealloc()
+GlobalAllocPolicy::Dealloc()
 {
   ReentrantMonitorAutoEnter mon(mMonitor);
   ++mDecoderLimit;
   ResolvePromise(mon);
 }
 
 void
-DecoderAllocPolicy::ResolvePromise(ReentrantMonitorAutoEnter& aProofOfLock)
+GlobalAllocPolicy::ResolvePromise(ReentrantMonitorAutoEnter& aProofOfLock)
 {
   MOZ_ASSERT(mDecoderLimit >= 0);
 
   if (mDecoderLimit > 0 && !mPromises.empty()) {
     --mDecoderLimit;
     RefPtr<PromisePrivate> p = mPromises.front().forget();
     mPromises.pop();
     p->Resolve(new AutoDeallocToken(*this), __func__);
   }
 }
 
 void
-DecoderAllocPolicy::operator=(std::nullptr_t)
+GlobalAllocPolicy::operator=(std::nullptr_t)
 {
   delete this;
 }
 
+/**
+ * This class addresses the concern of bug 1339310 comment 4 where the Widevine
+ * CDM doesn't support running multiple instances of a video decoder at once per
+ * CDM instance by sequencing the order of decoder creation and shutdown. Note
+ * this class addresses a different concern from that of GlobalAllocPolicy which
+ * controls a system-wide number of decoders while this class control a per-MFR
+ * number (which is one per CDM requirement).
+ */
+class LocalAllocPolicy
+{
+  using TrackType = TrackInfo::TrackType;
+  using Promise = GlobalAllocPolicy::Promise;
+  using Token = GlobalAllocPolicy::Token;
+
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(LocalAllocPolicy)
+
+public:
+  LocalAllocPolicy(TrackType aTrack, TaskQueue* aOwnerThread)
+    : mTrack(aTrack)
+    , mOwnerThread(aOwnerThread)
+  {
+  }
+
+  // Acquire a token for decoder creation. Note the resolved token will
+  // aggregate a GlobalAllocPolicy token to comply to its policy. Note
+  // this function shouldn't be called again until the returned promise
+  // is resolved or rejected.
+  RefPtr<Promise> Alloc();
+
+  // Cancel the request to GlobalAllocPolicy and reject the current token
+  // request. Note this must happen before mOwnerThread->BeginShutdown().
+  void Cancel();
+
+private:
+  /*
+   * An RAII class to manage LocalAllocPolicy::mDecoderLimit.
+   */
+  class AutoDeallocToken : public Token
+  {
+  public:
+    explicit AutoDeallocToken(LocalAllocPolicy* aOwner)
+      : mOwner(aOwner)
+    {
+      MOZ_DIAGNOSTIC_ASSERT(mOwner->mDecoderLimit > 0);
+      --mOwner->mDecoderLimit;
+    }
+    // Aggregate a GlobalAllocPolicy token to present a single instance of
+    // Token to the client so the client doesn't have to deal with
+    // GlobalAllocPolicy and LocalAllocPolicy separately.
+    void Append(Token* aToken)
+    {
+      mToken = aToken;
+    }
+  private:
+    // Release tokens allocated from GlobalAllocPolicy and LocalAllocPolicy
+    // and process next token request if any.
+    ~AutoDeallocToken()
+    {
+      mToken = nullptr; // Dealloc the global token.
+      ++mOwner->mDecoderLimit; // Dealloc the local token.
+      mOwner->ProcessRequest(); // Process next pending request.
+    }
+    RefPtr<LocalAllocPolicy> mOwner;
+    RefPtr<Token> mToken;
+  };
+
+  ~LocalAllocPolicy() { }
+  void ProcessRequest();
+
+  int mDecoderLimit = 1;
+  const TrackType mTrack;
+  RefPtr<TaskQueue> mOwnerThread;
+  MozPromiseHolder<Promise> mPendingPromise;
+  MozPromiseRequestHolder<Promise> mTokenRequest;
+};
+
+RefPtr<LocalAllocPolicy::Promise>
+LocalAllocPolicy::Alloc()
+{
+  MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
+  MOZ_DIAGNOSTIC_ASSERT(mPendingPromise.IsEmpty());
+  RefPtr<Promise> p = mPendingPromise.Ensure(__func__);
+  if (mDecoderLimit > 0) {
+    ProcessRequest();
+  }
+  return p.forget();
+}
+
+void
+LocalAllocPolicy::ProcessRequest()
+{
+  MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
+  MOZ_DIAGNOSTIC_ASSERT(mDecoderLimit > 0);
+
+  // No pending request.
+  if (mPendingPromise.IsEmpty()) {
+    return;
+  }
+
+  RefPtr<AutoDeallocToken> token = new AutoDeallocToken(this);
+  RefPtr<LocalAllocPolicy> self = this;
+
+  GlobalAllocPolicy::Instance(mTrack).Alloc()->Then(
+    mOwnerThread, __func__,
+    [self, token](Token* aToken) {
+      self->mTokenRequest.Complete();
+      token->Append(aToken);
+      self->mPendingPromise.Resolve(token, __func__);
+    },
+    [self, token]() {
+      self->mTokenRequest.Complete();
+      self->mPendingPromise.Reject(true, __func__);
+    })->Track(mTokenRequest);
+}
+
+void
+LocalAllocPolicy::Cancel()
+{
+  MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
+  mPendingPromise.RejectIfExists(true, __func__);
+  mTokenRequest.DisconnectIfExists();
+}
+
 class MediaFormatReader::DecoderFactory
 {
   using InitPromise = MediaDataDecoder::InitPromise;
-  using TokenPromise = DecoderAllocPolicy::Promise;
-  using Token = DecoderAllocPolicy::Token;
+  using TokenPromise = GlobalAllocPolicy::Promise;
+  using Token = GlobalAllocPolicy::Token;
 
 public:
   explicit DecoderFactory(MediaFormatReader* aOwner)
-    : mAudio(aOwner->mAudio, TrackInfo::kAudioTrack)
-    , mVideo(aOwner->mVideo, TrackInfo::kVideoTrack)
+    : mAudio(aOwner->mAudio, TrackInfo::kAudioTrack, aOwner->OwnerThread())
+    , mVideo(aOwner->mVideo, TrackInfo::kVideoTrack, aOwner->OwnerThread())
     , mOwner(WrapNotNull(aOwner)) { }
 
   void CreateDecoder(TrackType aTrack);
   // Shutdown any decoder pending initialization.
   RefPtr<ShutdownPromise> ShutdownDecoder(TrackType aTrack)
   {
     MOZ_ASSERT(aTrack == TrackInfo::kAudioTrack
                || aTrack == TrackInfo::kVideoTrack);
     auto& data = aTrack == TrackInfo::kAudioTrack ? mAudio : mVideo;
+    data.mPolicy->Cancel();
     data.mTokenRequest.DisconnectIfExists();
     data.mInitRequest.DisconnectIfExists();
     if (!data.mDecoder) {
       return ShutdownPromise::CreateAndResolve(true, __func__);
     }
     if (data.mShutdownRequest.Exists()) {
       // A shutdown is already in progress due to a prior initialization error,
       // return the existing promise.
@@ -230,23 +354,23 @@ private:
     None,
     WaitForToken,
     CreateDecoder,
     WaitForInit
   };
 
   struct Data
   {
-    Data(DecoderData& aOwnerData, TrackType aTrack)
+    Data(DecoderData& aOwnerData, TrackType aTrack, TaskQueue* aThread)
       : mOwnerData(aOwnerData)
       , mTrack(aTrack)
-      , mPolicy(DecoderAllocPolicy::Instance(aTrack)) { }
+      , mPolicy(new LocalAllocPolicy(aTrack, aThread)) { }
     DecoderData& mOwnerData;
     const TrackType mTrack;
-    DecoderAllocPolicy& mPolicy;
+    RefPtr<LocalAllocPolicy> mPolicy;
     Stage mStage = Stage::None;
     RefPtr<Token> mToken;
     RefPtr<MediaDataDecoder> mDecoder;
     MozPromiseRequestHolder<TokenPromise> mTokenRequest;
     MozPromiseRequestHolder<InitPromise> mInitRequest;
     MozPromiseRequestHolder<ShutdownPromise> mShutdownRequest;
     RefPtr<ShutdownPromise> mShutdownPromise;
   } mAudio, mVideo;
@@ -264,17 +388,17 @@ MediaFormatReader::DecoderFactory::Creat
 {
   MOZ_ASSERT(aTrack == TrackInfo::kAudioTrack
              || aTrack == TrackInfo::kVideoTrack);
   RunStage(aTrack == TrackInfo::kAudioTrack ? mAudio : mVideo);
 }
 
 class MediaFormatReader::DecoderFactory::Wrapper : public MediaDataDecoder
 {
-  using Token = DecoderAllocPolicy::Token;
+  using Token = GlobalAllocPolicy::Token;
 
 public:
   Wrapper(already_AddRefed<MediaDataDecoder> aDecoder,
           already_AddRefed<Token> aToken)
     : mDecoder(aDecoder), mToken(aToken) {}
 
   RefPtr<InitPromise> Init() override { return mDecoder->Init(); }
   RefPtr<DecodePromise> Decode(MediaRawData* aSample) override
@@ -319,17 +443,17 @@ private:
 };
 
 void
 MediaFormatReader::DecoderFactory::RunStage(Data& aData)
 {
   switch (aData.mStage) {
     case Stage::None: {
       MOZ_ASSERT(!aData.mToken);
-      aData.mPolicy.Alloc()->Then(
+      aData.mPolicy->Alloc()->Then(
         mOwner->OwnerThread(), __func__,
         [this, &aData] (Token* aToken) {
           aData.mTokenRequest.Complete();
           aData.mToken = aToken;
           aData.mStage = Stage::CreateDecoder;
           RunStage(aData);
         },
         [&aData] () {
--- a/dom/media/gmp/GMPCDMProxy.cpp
+++ b/dom/media/gmp/GMPCDMProxy.cpp
@@ -7,27 +7,25 @@
 #include "GMPCDMProxy.h"
 #include "mozilla/EMEUtils.h"
 #include "mozilla/PodOperations.h"
 
 #include "mozilla/dom/MediaKeys.h"
 #include "mozilla/dom/MediaKeySession.h"
 
 #include "mozIGeckoMediaPluginService.h"
-#include "nsContentCID.h"
-#include "nsIConsoleService.h"
 #include "nsPrintfCString.h"
-#include "nsServiceManagerUtils.h"
 #include "nsString.h"
 #include "prenv.h"
 #include "GMPCDMCallbackProxy.h"
 #include "GMPService.h"
 #include "MainThreadUtils.h"
 #include "MediaData.h"
 #include "DecryptJob.h"
+#include "GMPUtils.h"
 
 namespace mozilla {
 
 GMPCDMProxy::GMPCDMProxy(dom::MediaKeys* aKeys,
                          const nsAString& aKeySystem,
                          GMPCrashHelper* aCrashHelper,
                          bool aDistinctiveIdentifierRequired,
                          bool aPersistentStateRequired)
@@ -632,29 +630,16 @@ void
 GMPCDMProxy::OnDecrypted(uint32_t aId,
                          DecryptStatus aResult,
                          const nsTArray<uint8_t>& aDecryptedData)
 {
   MOZ_ASSERT(IsOnOwnerThread());
   gmp_Decrypted(aId, aResult, aDecryptedData);
 }
 
-static void
-LogToConsole(const nsAString& aMsg)
-{
-  nsCOMPtr<nsIConsoleService> console(
-    do_GetService("@mozilla.org/consoleservice;1"));
-  if (!console) {
-    NS_WARNING("Failed to log message to console.");
-    return;
-  }
-  nsAutoString msg(aMsg);
-  console->LogStringMessage(msg.get());
-}
-
 void
 GMPCDMProxy::OnSessionError(const nsAString& aSessionId,
                          nsresult aException,
                          uint32_t aSystemCode,
                          const nsAString& aMsg)
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (mKeys.IsNull()) {
--- a/dom/media/gmp/GMPService.h
+++ b/dom/media/gmp/GMPService.h
@@ -18,16 +18,17 @@
 #include "nsThreadUtils.h"
 #include "nsIDocument.h"
 #include "nsIWeakReference.h"
 #include "mozilla/AbstractThread.h"
 #include "nsClassHashtable.h"
 #include "nsISupportsImpl.h"
 #include "mozilla/MozPromise.h"
 #include "GMPContentParent.h"
+#include "GMPCrashHelper.h"
 
 template <class> struct already_AddRefed;
 
 namespace mozilla {
 
 class GMPCrashHelper;
 
 extern LogModule* GetGMPLog();
--- a/dom/media/gmp/GMPUtils.cpp
+++ b/dom/media/gmp/GMPUtils.cpp
@@ -7,16 +7,19 @@
 #include "GMPUtils.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsIFile.h"
 #include "nsCOMPtr.h"
 #include "nsLiteralString.h"
 #include "nsCRTGlue.h"
 #include "mozilla/Base64.h"
 #include "nsISimpleEnumerator.h"
+#include "prio.h"
+#include "nsIConsoleService.h"
+#include "mozIGeckoMediaPluginService.h"
 
 namespace mozilla {
 
 void
 SplitAt(const char* aDelims,
         const nsACString& aInput,
         nsTArray<nsCString>& aOutTokens)
 {
@@ -209,10 +212,22 @@ HaveGMPFor(const nsCString& aAPI,
 
   bool hasPlugin = false;
   if (NS_FAILED(mps->HasPluginForAPI(aAPI, &aTags, &hasPlugin))) {
     return false;
   }
   return hasPlugin;
 }
 
+void
+LogToConsole(const nsAString& aMsg)
+{
+  nsCOMPtr<nsIConsoleService> console(
+    do_GetService("@mozilla.org/consoleservice;1"));
+  if (!console) {
+    NS_WARNING("Failed to log message to console.");
+    return;
+  }
+  nsAutoString msg(aMsg);
+  console->LogStringMessage(msg.get());
+}
 
 } // namespace mozilla
--- a/dom/media/gmp/GMPUtils.h
+++ b/dom/media/gmp/GMPUtils.h
@@ -73,11 +73,14 @@ bool
 ReadIntoString(nsIFile* aFile,
                nsCString& aOutDst,
                size_t aMaxLength);
 
 bool
 HaveGMPFor(const nsCString& aAPI,
            nsTArray<nsCString>&& aTags);
 
+void
+LogToConsole(const nsAString& aMsg);
+
 } // namespace mozilla
 
 #endif
--- a/dom/media/gmp/widevine-adapter/WidevineAdapter.cpp
+++ b/dom/media/gmp/widevine-adapter/WidevineAdapter.cpp
@@ -39,17 +39,17 @@ GMPErr GMPCreateRecord(const char* aReco
 void
 WidevineAdapter::SetAdaptee(PRLibrary* aLib)
 {
   mLib = aLib;
 }
 
 void* GetCdmHost(int aHostInterfaceVersion, void* aUserData)
 {
-  Log("GetCdmHostFunc(%d, %p)", aHostInterfaceVersion, aUserData);
+  CDM_LOG("GetCdmHostFunc(%d, %p)", aHostInterfaceVersion, aUserData);
   WidevineDecryptor* decryptor = reinterpret_cast<WidevineDecryptor*>(aUserData);
   MOZ_ASSERT(decryptor);
   return static_cast<cdm::Host_8*>(decryptor);
 }
 
 #define STRINGIFY(s) _STRINGIFY(s)
 #define _STRINGIFY(s) #s
 
@@ -62,90 +62,90 @@ WidevineAdapter::GMPInit(const GMPPlatfo
   }
 
   auto init = reinterpret_cast<decltype(::INITIALIZE_CDM_MODULE)*>(
     PR_FindFunctionSymbol(mLib, STRINGIFY(INITIALIZE_CDM_MODULE)));
   if (!init) {
     return GMPGenericErr;
   }
 
-  Log(STRINGIFY(INITIALIZE_CDM_MODULE)"()");
+  CDM_LOG(STRINGIFY(INITIALIZE_CDM_MODULE)"()");
   init();
 
   return GMPNoErr;
 }
 
 GMPErr
 WidevineAdapter::GMPGetAPI(const char* aAPIName,
                            void* aHostAPI,
                            void** aPluginAPI,
                            uint32_t aDecryptorId)
 {
-  Log("WidevineAdapter::GMPGetAPI(%s, 0x%p, 0x%p, %u) this=0x%p",
-      aAPIName, aHostAPI, aPluginAPI, aDecryptorId, this);
+  CDM_LOG("WidevineAdapter::GMPGetAPI(%s, 0x%p, 0x%p, %u) this=0x%p",
+          aAPIName, aHostAPI, aPluginAPI, aDecryptorId, this);
   if (!strcmp(aAPIName, GMP_API_DECRYPTOR)) {
     if (WidevineDecryptor::GetInstance(aDecryptorId)) {
       // We only support one CDM instance per PGMPDecryptor. Fail!
-      Log("WidevineAdapter::GMPGetAPI() Tried to create more than once CDM per IPDL actor! FAIL!");
+      CDM_LOG("WidevineAdapter::GMPGetAPI() Tried to create more than once CDM per IPDL actor! FAIL!");
       return GMPQuotaExceededErr;
     }
     auto create = reinterpret_cast<decltype(::CreateCdmInstance)*>(
       PR_FindFunctionSymbol(mLib, "CreateCdmInstance"));
     if (!create) {
-      Log("WidevineAdapter::GMPGetAPI(%s, 0x%p, 0x%p, %u) this=0x%p FAILED to find CreateCdmInstance",
-        aAPIName, aHostAPI, aPluginAPI, aDecryptorId, this);
+      CDM_LOG("WidevineAdapter::GMPGetAPI(%s, 0x%p, 0x%p, %u) this=0x%p FAILED to find CreateCdmInstance",
+              aAPIName, aHostAPI, aPluginAPI, aDecryptorId, this);
       return GMPGenericErr;
     }
 
     auto* decryptor = new WidevineDecryptor();
 
     auto cdm = reinterpret_cast<cdm::ContentDecryptionModule*>(
       create(cdm::ContentDecryptionModule::kVersion,
              kEMEKeySystemWidevine.get(),
              kEMEKeySystemWidevine.Length(),
              &GetCdmHost,
              decryptor));
     if (!cdm) {
-      Log("WidevineAdapter::GMPGetAPI(%s, 0x%p, 0x%p, %u) this=0x%p FAILED to create cdm",
-          aAPIName, aHostAPI, aPluginAPI, aDecryptorId, this);
+      CDM_LOG("WidevineAdapter::GMPGetAPI(%s, 0x%p, 0x%p, %u) this=0x%p FAILED to create cdm",
+              aAPIName, aHostAPI, aPluginAPI, aDecryptorId, this);
       return GMPGenericErr;
     }
-    Log("cdm: 0x%p", cdm);
+    CDM_LOG("cdm: 0x%p", cdm);
     RefPtr<CDMWrapper> wrapper(new CDMWrapper(cdm, decryptor));
     decryptor->SetCDM(wrapper, aDecryptorId);
     *aPluginAPI = decryptor;
 
   } else if (!strcmp(aAPIName, GMP_API_VIDEO_DECODER)) {
     RefPtr<CDMWrapper> wrapper = WidevineDecryptor::GetInstance(aDecryptorId);
 
     // There is a possible race condition, where the decryptor will be destroyed
     // before we are able to create the video decoder, so we create a dummy
     // decoder to avoid crashing.
     if (!wrapper) {
-      Log("WidevineAdapter::GMPGetAPI(%s, 0x%p, 0x%p, %u) this=0x%p No cdm for video decoder. Using a DummyDecoder",
-          aAPIName, aHostAPI, aPluginAPI, aDecryptorId, this);
+      CDM_LOG("WidevineAdapter::GMPGetAPI(%s, 0x%p, 0x%p, %u) this=0x%p No cdm for video decoder. Using a DummyDecoder",
+              aAPIName, aHostAPI, aPluginAPI, aDecryptorId, this);
 
       *aPluginAPI = new WidevineDummyDecoder();
     } else {
       *aPluginAPI = new WidevineVideoDecoder(static_cast<GMPVideoHost*>(aHostAPI),
                                              wrapper);
     }
   }
   return *aPluginAPI ? GMPNoErr : GMPNotImplementedErr;
 }
 
 void
 WidevineAdapter::GMPShutdown()
 {
-  Log("WidevineAdapter::GMPShutdown()");
+  CDM_LOG("WidevineAdapter::GMPShutdown()");
 
   decltype(::DeinitializeCdmModule)* deinit;
   deinit = (decltype(deinit))(PR_FindFunctionSymbol(mLib, "DeinitializeCdmModule"));
   if (deinit) {
-    Log("DeinitializeCdmModule()");
+    CDM_LOG("DeinitializeCdmModule()");
     deinit();
   }
 }
 
 /* static */
 bool
 WidevineAdapter::Supports(int32_t aModuleVersion,
                           int32_t aInterfaceVersion,
--- a/dom/media/gmp/widevine-adapter/WidevineDecryptor.cpp
+++ b/dom/media/gmp/widevine-adapter/WidevineDecryptor.cpp
@@ -30,40 +30,40 @@ WidevineDecryptor::GetInstance(uint32_t 
   }
   return nullptr;
 }
 
 
 WidevineDecryptor::WidevineDecryptor()
   : mCallback(nullptr)
 {
-  Log("WidevineDecryptor created this=%p, instanceId=%u", this, mInstanceId);
+  CDM_LOG("WidevineDecryptor created this=%p, instanceId=%u", this, mInstanceId);
   AddRef(); // Released in DecryptingComplete().
 }
 
 WidevineDecryptor::~WidevineDecryptor()
 {
-  Log("WidevineDecryptor destroyed this=%p, instanceId=%u", this, mInstanceId);
+  CDM_LOG("WidevineDecryptor destroyed this=%p, instanceId=%u", this, mInstanceId);
 }
 
 void
 WidevineDecryptor::SetCDM(RefPtr<CDMWrapper> aCDM, uint32_t aInstanceId)
 {
   mCDM = aCDM;
   mInstanceId = aInstanceId;
   sDecryptors[mInstanceId] = aCDM.forget();
 }
 
 void
 WidevineDecryptor::Init(GMPDecryptorCallback* aCallback,
                         bool aDistinctiveIdentifierRequired,
                         bool aPersistentStateRequired)
 {
-  Log("WidevineDecryptor::Init() this=%p distinctiveId=%d persistentState=%d",
-      this, aDistinctiveIdentifierRequired, aPersistentStateRequired);
+  CDM_LOG("WidevineDecryptor::Init() this=%p distinctiveId=%d persistentState=%d",
+          this, aDistinctiveIdentifierRequired, aPersistentStateRequired);
   MOZ_ASSERT(aCallback);
   mCallback = aCallback;
   MOZ_ASSERT(mCDM);
   mDistinctiveIdentifierRequired = aDistinctiveIdentifierRequired;
   mPersistentStateRequired = aPersistentStateRequired;
   if (CDM()) {
     CDM()->Initialize(aDistinctiveIdentifierRequired,
                       aPersistentStateRequired);
@@ -87,17 +87,17 @@ void
 WidevineDecryptor::CreateSession(uint32_t aCreateSessionToken,
                                  uint32_t aPromiseId,
                                  const char* aInitDataType,
                                  uint32_t aInitDataTypeSize,
                                  const uint8_t* aInitData,
                                  uint32_t aInitDataSize,
                                  GMPSessionType aSessionType)
 {
-  Log("Decryptor::CreateSession(token=%d, pid=%d)", aCreateSessionToken, aPromiseId);
+  CDM_LOG("Decryptor::CreateSession(token=%d, pid=%d)", aCreateSessionToken, aPromiseId);
   InitDataType initDataType;
   if (!strcmp(aInitDataType, "cenc")) {
     initDataType = kCenc;
   } else if (!strcmp(aInitDataType, "webm")) {
     initDataType = kWebM;
   } else if (!strcmp(aInitDataType, "keyids")) {
     initDataType = kKeyIds;
   } else {
@@ -113,56 +113,56 @@ WidevineDecryptor::CreateSession(uint32_
                                          aInitData, aInitDataSize);
 }
 
 void
 WidevineDecryptor::LoadSession(uint32_t aPromiseId,
                                const char* aSessionId,
                                uint32_t aSessionIdLength)
 {
-  Log("Decryptor::LoadSession(pid=%d, %s)", aPromiseId, aSessionId);
+  CDM_LOG("Decryptor::LoadSession(pid=%d, %s)", aPromiseId, aSessionId);
   // TODO: session type??
   CDM()->LoadSession(aPromiseId, kPersistentLicense, aSessionId, aSessionIdLength);
 }
 
 void
 WidevineDecryptor::UpdateSession(uint32_t aPromiseId,
                                  const char* aSessionId,
                                  uint32_t aSessionIdLength,
                                  const uint8_t* aResponse,
                                  uint32_t aResponseSize)
 {
-  Log("Decryptor::UpdateSession(pid=%d, session=%s)", aPromiseId, aSessionId);
+  CDM_LOG("Decryptor::UpdateSession(pid=%d, session=%s)", aPromiseId, aSessionId);
   CDM()->UpdateSession(aPromiseId, aSessionId, aSessionIdLength, aResponse, aResponseSize);
 }
 
 void
 WidevineDecryptor::CloseSession(uint32_t aPromiseId,
                                 const char* aSessionId,
                                 uint32_t aSessionIdLength)
 {
-  Log("Decryptor::CloseSession(pid=%d, session=%s)", aPromiseId, aSessionId);
+  CDM_LOG("Decryptor::CloseSession(pid=%d, session=%s)", aPromiseId, aSessionId);
   CDM()->CloseSession(aPromiseId, aSessionId, aSessionIdLength);
 }
 
 void
 WidevineDecryptor::RemoveSession(uint32_t aPromiseId,
                                  const char* aSessionId,
                                  uint32_t aSessionIdLength)
 {
-  Log("Decryptor::RemoveSession(%s)", aSessionId);
+  CDM_LOG("Decryptor::RemoveSession(%s)", aSessionId);
   CDM()->RemoveSession(aPromiseId, aSessionId, aSessionIdLength);
 }
 
 void
 WidevineDecryptor::SetServerCertificate(uint32_t aPromiseId,
                                         const uint8_t* aServerCert,
                                         uint32_t aServerCertSize)
 {
-  Log("Decryptor::SetServerCertificate()");
+  CDM_LOG("Decryptor::SetServerCertificate()");
   CDM()->SetServerCertificate(aPromiseId, aServerCert, aServerCertSize);
 }
 
 class WidevineDecryptedBlock : public cdm::DecryptedBlock {
 public:
 
   WidevineDecryptedBlock()
     : mBuffer(nullptr)
@@ -226,17 +226,17 @@ WidevineDecryptor::ThrottleDecrypt(cdm::
 }
 
 void
 WidevineDecryptor::Decrypt(GMPBuffer* aBuffer,
                            GMPEncryptedBufferMetadata* aMetadata,
                            uint64_t aDurationUsecs)
 {
   if (!mCallback) {
-    Log("WidevineDecryptor::Decrypt() this=%p FAIL; !mCallback", this);
+    CDM_LOG("WidevineDecryptor::Decrypt() this=%p FAIL; !mCallback", this);
     return;
   }
 
   cdm::Time duration = double(aDurationUsecs) / USECS_PER_S;
   mPendingDecrypts.push({aBuffer, aMetadata, duration});
   ProcessDecrypts();
 }
 
@@ -286,31 +286,32 @@ WidevineDecryptor::DecryptBuffer(const P
 {
   GMPBuffer* buffer = aJob.mBuffer;
   const GMPEncryptedBufferMetadata* crypto = aJob.mMetadata;
   InputBuffer sample;
   nsTArray<SubsampleEntry> subsamples;
   InitInputBuffer(crypto, buffer->Id(), buffer->Data(), buffer->Size(), sample, subsamples);
   WidevineDecryptedBlock decrypted;
   Status rv = CDM()->Decrypt(sample, &decrypted);
-  Log("Decryptor::Decrypt(timestamp=%" PRId64 ") rv=%d sz=%d",
-      sample.timestamp, rv, decrypted.DecryptedBuffer()->Size());
+  CDM_LOG("Decryptor::Decrypt(timestamp=%" PRId64 ") rv=%d sz=%d",
+          sample.timestamp, rv, decrypted.DecryptedBuffer()->Size());
   if (rv == kSuccess) {
     buffer->Resize(decrypted.DecryptedBuffer()->Size());
     memcpy(buffer->Data(),
            decrypted.DecryptedBuffer()->Data(),
            decrypted.DecryptedBuffer()->Size());
   }
   mCallback->Decrypted(buffer, ToGMPErr(rv));
 }
 
 void
 WidevineDecryptor::DecryptingComplete()
 {
-  Log("WidevineDecryptor::DecryptingComplete() this=%p, instanceId=%u", this, mInstanceId);
+  CDM_LOG("WidevineDecryptor::DecryptingComplete() this=%p, instanceId=%u",
+          this, mInstanceId);
 
   // Ensure buffers are freed.
   while (!mPendingDecrypts.empty()) {
     PendingDecrypt& job = mPendingDecrypts.front();
     if (mCallback) {
       mCallback->Decrypted(job.mBuffer, GMPAbortedErr);
     }
     mPendingDecrypts.pop();
@@ -324,21 +325,21 @@ WidevineDecryptor::DecryptingComplete()
   sDecryptors.erase(mInstanceId);
   mCallback = nullptr;
   Release();
 }
 
 class WidevineBuffer : public cdm::Buffer {
 public:
   explicit WidevineBuffer(size_t aSize) {
-    Log("WidevineBuffer(size=%" PRIuSIZE ") created", aSize);
+    CDM_LOG("WidevineBuffer(size=%" PRIuSIZE ") created", aSize);
     mBuffer.SetLength(aSize);
   }
   ~WidevineBuffer() override {
-    Log("WidevineBuffer(size=%" PRIu32 ") destroyed", Size());
+    CDM_LOG("WidevineBuffer(size=%" PRIu32 ") destroyed", Size());
   }
   void Destroy() override { delete this; }
   uint32_t Capacity() const override { return mBuffer.Length(); };
   uint8_t* Data() override { return mBuffer.Elements(); }
   void SetSize(uint32_t aSize) override { mBuffer.SetLength(aSize); }
   uint32_t Size() const override { return mBuffer.Length(); }
 
 private:
@@ -346,17 +347,17 @@ private:
   void operator=(const WidevineBuffer&);
 
   nsTArray<uint8_t> mBuffer;
 };
 
 Buffer*
 WidevineDecryptor::Allocate(uint32_t aCapacity)
 {
-  Log("Decryptor::Allocate(capacity=%u)", aCapacity);
+  CDM_LOG("Decryptor::Allocate(capacity=%u)", aCapacity);
   return new WidevineBuffer(aCapacity);
 }
 
 class TimerTask : public GMPTask {
 public:
   TimerTask(WidevineDecryptor* aDecryptor,
             RefPtr<CDMWrapper> aCDM,
             void* aContext)
@@ -374,71 +375,71 @@ private:
   RefPtr<WidevineDecryptor> mDecryptor;
   RefPtr<CDMWrapper> mCDM;
   void* mContext;
 };
 
 void
 WidevineDecryptor::SetTimer(int64_t aDelayMs, void* aContext)
 {
-  Log("Decryptor::SetTimer(delay_ms=%" PRId64 ", context=0x%p)", aDelayMs, aContext);
+  CDM_LOG("Decryptor::SetTimer(delay_ms=%" PRId64 ", context=0x%p)", aDelayMs, aContext);
   if (mCDM) {
     GMPSetTimerOnMainThread(new TimerTask(this, mCDM, aContext), aDelayMs);
   }
 }
 
 Time
 WidevineDecryptor::GetCurrentWallTime()
 {
   GMPTimestamp gmpTime = 0;
   GMPGetCurrentTime(&gmpTime);
   double t = (double)gmpTime / 1e3;
-  Log("Decryptor::GetCurrentWallTime()= %lf", t);
+  CDM_LOG("Decryptor::GetCurrentWallTime()= %lf", t);
   return t;
 }
 
 void
 WidevineDecryptor::OnResolveNewSessionPromise(uint32_t aPromiseId,
                                               const char* aSessionId,
                                               uint32_t aSessionIdSize)
 {
   if (!mCallback) {
-    Log("Decryptor::OnResolveNewSessionPromise(aPromiseId=0x%d) FAIL; !mCallback", aPromiseId);
+    CDM_LOG("Decryptor::OnResolveNewSessionPromise(aPromiseId=0x%d) FAIL; !mCallback", aPromiseId);
     return;
   }
 
   // This is laid out in the API. If we fail to load a session we should
   // call OnResolveNewSessionPromise with nullptr as the sessionId.
   // We can safely assume this means that we have failed to load a session
   // as the other methods specify calling 'OnRejectPromise' when they fail.
   if (!aSessionId) {
-    Log("Decryptor::OnResolveNewSessionPromise(aPromiseId=0x%d) Failed to load session", aPromiseId);
+    CDM_LOG("Decryptor::OnResolveNewSessionPromise(aPromiseId=0x%d) Failed to load session", aPromiseId);
     mCallback->ResolveLoadSessionPromise(aPromiseId, false);
     return;
   }
 
-  Log("Decryptor::OnResolveNewSessionPromise(aPromiseId=0x%d)", aPromiseId);
+  CDM_LOG("Decryptor::OnResolveNewSessionPromise(aPromiseId=0x%d)", aPromiseId);
   auto iter = mPromiseIdToNewSessionTokens.find(aPromiseId);
   if (iter == mPromiseIdToNewSessionTokens.end()) {
-    Log("FAIL: Decryptor::OnResolveNewSessionPromise(aPromiseId=%d) unknown aPromiseId", aPromiseId);
+    CDM_LOG("FAIL: Decryptor::OnResolveNewSessionPromise(aPromiseId=%d) unknown aPromiseId", aPromiseId);
     return;
   }
   mCallback->SetSessionId(iter->second, aSessionId, aSessionIdSize);
   mCallback->ResolvePromise(aPromiseId);
   mPromiseIdToNewSessionTokens.erase(iter);
 }
 
 void
 WidevineDecryptor::OnResolvePromise(uint32_t aPromiseId)
 {
   if (!mCallback) {
-    Log("Decryptor::OnResolvePromise(aPromiseId=0x%d) FAIL; !mCallback", aPromiseId);
+    CDM_LOG("Decryptor::OnResolvePromise(aPromiseId=0x%d) FAIL; !mCallback", aPromiseId);
     return;
   }
-  Log("Decryptor::OnResolvePromise(aPromiseId=%d)", aPromiseId);
+  CDM_LOG("Decryptor::OnResolvePromise(aPromiseId=%d)", aPromiseId);
   mCallback->ResolvePromise(aPromiseId);
 }
 
 static GMPDOMException
 ToGMPDOMException(cdm::Error aError)
 {
   switch (aError) {
     case kNotSupportedError: return kGMPNotSupportedError;
@@ -460,22 +461,22 @@ ToGMPDOMException(cdm::Error aError)
 void
 WidevineDecryptor::OnRejectPromise(uint32_t aPromiseId,
                                    Error aError,
                                    uint32_t aSystemCode,
                                    const char* aErrorMessage,
                                    uint32_t aErrorMessageSize)
 {
   if (!mCallback) {
-    Log("Decryptor::OnRejectPromise(aPromiseId=%d, err=%d, sysCode=%u, msg=%s) FAIL; !mCallback",
-        aPromiseId, (int)aError, aSystemCode, aErrorMessage);
+    CDM_LOG("Decryptor::OnRejectPromise(aPromiseId=%d, err=%d, sysCode=%u, msg=%s) FAIL; !mCallback",
+            aPromiseId, (int)aError, aSystemCode, aErrorMessage);
     return;
   }
-  Log("Decryptor::OnRejectPromise(aPromiseId=%d, err=%d, sysCode=%u, msg=%s)",
-      aPromiseId, (int)aError, aSystemCode, aErrorMessage);
+  CDM_LOG("Decryptor::OnRejectPromise(aPromiseId=%d, err=%d, sysCode=%u, msg=%s)",
+          aPromiseId, (int)aError, aSystemCode, aErrorMessage);
   mCallback->RejectPromise(aPromiseId,
                            ToGMPDOMException(aError),
                            !aErrorMessageSize ? "" : aErrorMessage,
                            aErrorMessageSize);
 }
 
 static GMPSessionMessageType
 ToGMPMessageType(MessageType message_type)
@@ -493,20 +494,20 @@ WidevineDecryptor::OnSessionMessage(cons
                                     uint32_t aSessionIdSize,
                                     MessageType aMessageType,
                                     const char* aMessage,
                                     uint32_t aMessageSize,
                                     const char* aLegacyDestinationUrl,
                                     uint32_t aLegacyDestinationUrlLength)
 {
   if (!mCallback) {
-    Log("Decryptor::OnSessionMessage() FAIL; !mCallback");
+    CDM_LOG("Decryptor::OnSessionMessage() FAIL; !mCallback");
     return;
   }
-  Log("Decryptor::OnSessionMessage()");
+  CDM_LOG("Decryptor::OnSessionMessage()");
   mCallback->SessionMessage(aSessionId,
                             aSessionIdSize,
                             ToGMPMessageType(aMessageType),
                             reinterpret_cast<const uint8_t*>(aMessage),
                             aMessageSize);
 }
 
 static GMPMediaKeyStatus
@@ -527,20 +528,20 @@ ToGMPKeyStatus(KeyStatus aStatus)
 void
 WidevineDecryptor::OnSessionKeysChange(const char* aSessionId,
                                        uint32_t aSessionIdSize,
                                        bool aHasAdditionalUsableKey,
                                        const KeyInformation* aKeysInfo,
                                        uint32_t aKeysInfoCount)
 {
   if (!mCallback) {
-    Log("Decryptor::OnSessionKeysChange() FAIL; !mCallback");
+    CDM_LOG("Decryptor::OnSessionKeysChange() FAIL; !mCallback");
     return;
   }
-  Log("Decryptor::OnSessionKeysChange()");
+  CDM_LOG("Decryptor::OnSessionKeysChange()");
 
   nsTArray<GMPMediaKeyInfo> key_infos;
   for (uint32_t i = 0; i < aKeysInfoCount; i++) {
     key_infos.AppendElement(GMPMediaKeyInfo(aKeysInfo[i].key_id,
                                             aKeysInfo[i].key_id_size,
                                             ToGMPKeyStatus(aKeysInfo[i].status)));
   }
   mCallback->BatchedKeyStatusChanged(aSessionId, aSessionIdSize,
@@ -554,93 +555,93 @@ ToGMPTime(Time aCDMTime)
 }
 
 void
 WidevineDecryptor::OnExpirationChange(const char* aSessionId,
                                       uint32_t aSessionIdSize,
                                       Time aNewExpiryTime)
 {
   if (!mCallback) {
-    Log("Decryptor::OnExpirationChange(sid=%s) t=%lf FAIL; !mCallback",
-        aSessionId, aNewExpiryTime);
+    CDM_LOG("Decryptor::OnExpirationChange(sid=%s) t=%lf FAIL; !mCallback",
+            aSessionId, aNewExpiryTime);
     return;
   }
-  Log("Decryptor::OnExpirationChange(sid=%s) t=%lf", aSessionId, aNewExpiryTime);
+  CDM_LOG("Decryptor::OnExpirationChange(sid=%s) t=%lf", aSessionId, aNewExpiryTime);
   GMPTimestamp expiry = ToGMPTime(aNewExpiryTime);
   if (aNewExpiryTime == 0) {
     return;
   }
   mCallback->ExpirationChange(aSessionId, aSessionIdSize, expiry);
 }
 
 void
 WidevineDecryptor::OnSessionClosed(const char* aSessionId,
                                    uint32_t aSessionIdSize)
 {
   if (!mCallback) {
-    Log("Decryptor::OnSessionClosed(sid=%s) FAIL; !mCallback", aSessionId);
+    CDM_LOG("Decryptor::OnSessionClosed(sid=%s) FAIL; !mCallback", aSessionId);
     return;
   }
-  Log("Decryptor::OnSessionClosed(sid=%s)", aSessionId);
+  CDM_LOG("Decryptor::OnSessionClosed(sid=%s)", aSessionId);
   mCallback->SessionClosed(aSessionId, aSessionIdSize);
 }
 
 void
 WidevineDecryptor::OnLegacySessionError(const char* aSessionId,
                                         uint32_t aSessionIdLength,
                                         Error aError,
                                         uint32_t aSystemCode,
                                         const char* aErrorMessage,
                                         uint32_t aErrorMessageLength)
 {
   if (!mCallback) {
-    Log("Decryptor::OnLegacySessionError(sid=%s, error=%d) FAIL; !mCallback",
-        aSessionId, (int)aError);
+    CDM_LOG("Decryptor::OnLegacySessionError(sid=%s, error=%d) FAIL; !mCallback",
+            aSessionId, (int)aError);
     return;
   }
-  Log("Decryptor::OnLegacySessionError(sid=%s, error=%d)", aSessionId, (int)aError);
+  CDM_LOG("Decryptor::OnLegacySessionError(sid=%s, error=%d)", aSessionId, (int)aError);
   mCallback->SessionError(aSessionId,
                           aSessionIdLength,
                           ToGMPDOMException(aError),
                           aSystemCode,
                           aErrorMessage,
                           aErrorMessageLength);
 }
 
 void
 WidevineDecryptor::SendPlatformChallenge(const char* aServiceId,
                                          uint32_t aServiceIdSize,
                                          const char* aChallenge,
                                          uint32_t aChallengeSize)
 {
-  Log("Decryptor::SendPlatformChallenge(service_id=%s)", aServiceId);
+  CDM_LOG("Decryptor::SendPlatformChallenge(service_id=%s)", aServiceId);
 }
 
 void
 WidevineDecryptor::EnableOutputProtection(uint32_t aDesiredProtectionMask)
 {
-  Log("Decryptor::EnableOutputProtection(mask=0x%x)", aDesiredProtectionMask);
+  CDM_LOG("Decryptor::EnableOutputProtection(mask=0x%x)", aDesiredProtectionMask);
 }
 
 void
 WidevineDecryptor::QueryOutputProtectionStatus()
 {
-  Log("Decryptor::QueryOutputProtectionStatus()");
+  CDM_LOG("Decryptor::QueryOutputProtectionStatus()");
 }
 
 void
 WidevineDecryptor::OnDeferredInitializationDone(StreamType aStreamType,
                                                 Status aDecoderStatus)
 {
-  Log("Decryptor::OnDeferredInitializationDone()");
+  CDM_LOG("Decryptor::OnDeferredInitializationDone()");
 }
 
 FileIO*
 WidevineDecryptor::CreateFileIO(FileIOClient* aClient)
 {
-  Log("Decryptor::CreateFileIO()");
+  CDM_LOG("Decryptor::CreateFileIO()");
   if (!mPersistentStateRequired) {
     return nullptr;
   }
   return new WidevineFileIO(aClient);
 }
 
 } // namespace mozilla
--- a/dom/media/gmp/widevine-adapter/WidevineDummyDecoder.cpp
+++ b/dom/media/gmp/widevine-adapter/WidevineDummyDecoder.cpp
@@ -1,57 +1,57 @@
 #include "WidevineDummyDecoder.h"
 #include "WidevineUtils.h"
 
 using namespace cdm;
 
 namespace mozilla {
 WidevineDummyDecoder::WidevineDummyDecoder()
 {
-  Log("WidevineDummyDecoder created");
+  CDM_LOG("WidevineDummyDecoder created");
 }
 
 void WidevineDummyDecoder::InitDecode(const GMPVideoCodec & aCodecSettings,
                                       const uint8_t * aCodecSpecific,
                                       uint32_t aCodecSpecificLength,
                                       GMPVideoDecoderCallback * aCallback,
                                       int32_t aCoreCount)
 {
-  Log("WidevineDummyDecoder::InitDecode");
+  CDM_LOG("WidevineDummyDecoder::InitDecode");
 
   mCallback = aCallback;
   mCallback->Error(GMPErr::GMPNotImplementedErr);
 }
 
 void WidevineDummyDecoder::Decode(GMPVideoEncodedFrame * aInputFrame,
                                   bool aMissingFrames,
                                   const uint8_t * aCodecSpecificInfo,
                                   uint32_t aCodecSpecificInfoLength,
                                   int64_t aRenderTimeMs)
 {
-  Log("WidevineDummyDecoder::Decode");
+  CDM_LOG("WidevineDummyDecoder::Decode");
   mCallback->Error(GMPErr::GMPNotImplementedErr);
 }
 
 void WidevineDummyDecoder::Reset()
 {
-  Log("WidevineDummyDecoder::Reset");
+  CDM_LOG("WidevineDummyDecoder::Reset");
   mCallback->Error(GMPErr::GMPNotImplementedErr);
 }
 
 void WidevineDummyDecoder::Drain()
 {
-  Log("WidevineDummyDecoder::Drain");
+  CDM_LOG("WidevineDummyDecoder::Drain");
   mCallback->Error(GMPErr::GMPNotImplementedErr);
 }
 
 void WidevineDummyDecoder::DecodingComplete()
 {
-  Log("WidevineDummyDecoder::DecodingComplete");
+  CDM_LOG("WidevineDummyDecoder::DecodingComplete");
 
   mCallback = nullptr;
   delete this;
 }
 
 WidevineDummyDecoder::~WidevineDummyDecoder() {
-  Log("WidevineDummyDecoder destroyed");
+  CDM_LOG("WidevineDummyDecoder destroyed");
 }
 }
\ No newline at end of file
--- a/dom/media/gmp/widevine-adapter/WidevineFileIO.cpp
+++ b/dom/media/gmp/widevine-adapter/WidevineFileIO.cpp
@@ -8,57 +8,57 @@ namespace mozilla {
 
 void
 WidevineFileIO::Open(const char* aFilename, uint32_t aFilenameLength)
 {
   mName = std::string(aFilename, aFilename + aFilenameLength);
   GMPRecord* record = nullptr;
   GMPErr err = GMPCreateRecord(aFilename, aFilenameLength, &record, static_cast<GMPRecordClient*>(this));
   if (GMP_FAILED(err)) {
-    Log("WidevineFileIO::Open() '%s' GMPCreateRecord failed", mName.c_str());
+    CDM_LOG("WidevineFileIO::Open() '%s' GMPCreateRecord failed", mName.c_str());
     mClient->OnOpenComplete(FileIOClient::kError);
     return;
   }
   if (GMP_FAILED(record->Open())) {
-    Log("WidevineFileIO::Open() '%s' record open failed", mName.c_str());
+    CDM_LOG("WidevineFileIO::Open() '%s' record open failed", mName.c_str());
     mClient->OnOpenComplete(FileIOClient::kError);
     return;
   }
 
-  Log("WidevineFileIO::Open() '%s'", mName.c_str());
+  CDM_LOG("WidevineFileIO::Open() '%s'", mName.c_str());
   mRecord = record;
 }
 
 void
 WidevineFileIO::Read()
 {
   if (!mRecord) {
-    Log("WidevineFileIO::Read() '%s' used uninitialized!", mName.c_str());
+    CDM_LOG("WidevineFileIO::Read() '%s' used uninitialized!", mName.c_str());
     mClient->OnReadComplete(FileIOClient::kError, nullptr, 0);
     return;
   }
-  Log("WidevineFileIO::Read() '%s'", mName.c_str());
+  CDM_LOG("WidevineFileIO::Read() '%s'", mName.c_str());
   mRecord->Read();
 }
 
 void
 WidevineFileIO::Write(const uint8_t* aData, uint32_t aDataSize)
 {
   if (!mRecord) {
-    Log("WidevineFileIO::Write() '%s' used uninitialized!", mName.c_str());
+    CDM_LOG("WidevineFileIO::Write() '%s' used uninitialized!", mName.c_str());
     mClient->OnWriteComplete(FileIOClient::kError);
     return;
   }
   mRecord->Write(aData, aDataSize);
 }
 
 void
 WidevineFileIO::Close()
 {
-  Log("WidevineFileIO::Close() '%s'", mName.c_str());
+  CDM_LOG("WidevineFileIO::Close() '%s'", mName.c_str());
   if (mRecord) {
     mRecord->Close();
     mRecord = nullptr;
   }
   delete this;
 }
 
 static FileIOClient::Status
@@ -69,29 +69,29 @@ GMPToWidevineFileStatus(GMPErr aStatus)
     case GMPNoErr: return FileIOClient::kSuccess;
     default: return FileIOClient::kError;
   }
 }
 
 void
 WidevineFileIO::OpenComplete(GMPErr aStatus)
 {
-  Log("WidevineFileIO::OpenComplete() '%s' status=%d", mName.c_str(), aStatus);
+  CDM_LOG("WidevineFileIO::OpenComplete() '%s' status=%d", mName.c_str(), aStatus);
   mClient->OnOpenComplete(GMPToWidevineFileStatus(aStatus));
 }
 
 void
 WidevineFileIO::ReadComplete(GMPErr aStatus,
                              const uint8_t* aData,
                              uint32_t aDataSize)
 {
-  Log("WidevineFileIO::OnReadComplete() '%s' status=%d", mName.c_str(), aStatus);
+  CDM_LOG("WidevineFileIO::OnReadComplete() '%s' status=%d", mName.c_str(), aStatus);
   mClient->OnReadComplete(GMPToWidevineFileStatus(aStatus), aData, aDataSize);
 }
 
 void
 WidevineFileIO::WriteComplete(GMPErr aStatus)
 {
-  Log("WidevineFileIO::WriteComplete() '%s' status=%d", mName.c_str(), aStatus);
+  CDM_LOG("WidevineFileIO::WriteComplete() '%s' status=%d", mName.c_str(), aStatus);
   mClient->OnWriteComplete(GMPToWidevineFileStatus(aStatus));
 }
 
 } // namespace mozilla
--- a/dom/media/gmp/widevine-adapter/WidevineUtils.cpp
+++ b/dom/media/gmp/widevine-adapter/WidevineUtils.cpp
@@ -66,14 +66,14 @@ CDMWrapper::CDMWrapper(cdm::ContentDecry
   : mCDM(aCDM)
   , mDecryptor(aDecryptor)
 {
   MOZ_ASSERT(mCDM);
 }
 
 CDMWrapper::~CDMWrapper()
 {
-  Log("CDMWrapper destroying CDM=%p", mCDM);
+  CDM_LOG("CDMWrapper destroying CDM=%p", mCDM);
   mCDM->Destroy();
   mCDM = nullptr;
 }
 
 } // namespace mozilla
--- a/dom/media/gmp/widevine-adapter/WidevineUtils.h
+++ b/dom/media/gmp/widevine-adapter/WidevineUtils.h
@@ -15,28 +15,28 @@
 #include "mozilla/Logging.h"
 
 namespace mozilla {
 
 namespace detail {
 LogModule* GetCDMLog();
 } // namespace detail
 
-#define Log(...) MOZ_LOG(detail::GetCDMLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
+#define CDM_LOG(...) MOZ_LOG(detail::GetCDMLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
 
 #define ENSURE_TRUE(condition, rv) { \
   if (!(condition)) {\
-    Log("ENSURE_TRUE FAILED %s:%d", __FILE__, __LINE__); \
+    CDM_LOG("ENSURE_TRUE FAILED %s:%d", __FILE__, __LINE__); \
     return rv; \
   } \
 } \
 
 #define ENSURE_GMP_SUCCESS(err, rv) { \
   if (GMP_FAILED(err)) {\
-    Log("ENSURE_GMP_SUCCESS FAILED %s:%d", __FILE__, __LINE__); \
+    CDM_LOG("ENSURE_GMP_SUCCESS FAILED %s:%d", __FILE__, __LINE__); \
     return rv; \
     } \
 } \
 
 GMPErr
 ToGMPErr(cdm::Status aStatus);
 
 class WidevineDecryptor;
--- a/dom/media/gmp/widevine-adapter/WidevineVideoDecoder.cpp
+++ b/dom/media/gmp/widevine-adapter/WidevineVideoDecoder.cpp
@@ -22,25 +22,25 @@ WidevineVideoDecoder::WidevineVideoDecod
   , mSentInput(false)
   , mCodecType(kGMPVideoCodecInvalid)
   , mReturnOutputCallDepth(0)
   , mDrainPending(false)
   , mResetInProgress(false)
 {
   // Expect to start with a CDM wrapper, will release it in DecodingComplete().
   MOZ_ASSERT(mCDMWrapper);
-  Log("WidevineVideoDecoder created this=%p", this);
+  CDM_LOG("WidevineVideoDecoder created this=%p", this);
 
   // Corresponding Release is in DecodingComplete().
   AddRef();
 }
 
 WidevineVideoDecoder::~WidevineVideoDecoder()
 {
-  Log("WidevineVideoDecoder destroyed this=%p", this);
+  CDM_LOG("WidevineVideoDecoder destroyed this=%p", this);
 }
 
 static
 VideoDecoderConfig::VideoCodecProfile
 ToCDMH264Profile(uint8_t aProfile)
 {
   switch (aProfile) {
     case 66: return VideoDecoderConfig::kH264ProfileBaseline;
@@ -83,17 +83,17 @@ WidevineVideoDecoder::InitDecode(const G
   mExtraData->AppendElements(aCodecSpecific + 1, aCodecSpecificLength);
   config.extra_data = mExtraData->Elements();
   config.extra_data_size = mExtraData->Length();
   Status rv = CDM()->InitializeVideoDecoder(config);
   if (rv != kSuccess) {
     mCallback->Error(ToGMPErr(rv));
     return;
   }
-  Log("WidevineVideoDecoder::InitDecode() rv=%d", rv);
+  CDM_LOG("WidevineVideoDecoder::InitDecode() rv=%d", rv);
   mAnnexB = mp4_demuxer::AnnexB::ConvertExtraDataToAnnexB(mExtraData);
 }
 
 void
 WidevineVideoDecoder::Decode(GMPVideoEncodedFrame* aInputFrame,
                              bool aMissingFrames,
                              const uint8_t* aCodecSpecificInfo,
                              uint32_t aCodecSpecificInfoLength,
@@ -136,27 +136,27 @@ WidevineVideoDecoder::Decode(GMPVideoEnc
   if (raw->mKeyframe
       && !subsamples.IsEmpty()
       && mCodecType == kGMPVideoCodecH264) {
     subsamples[0].clear_bytes += mAnnexB->Length();
   }
 
   WidevineVideoFrame frame;
   Status rv = CDM()->DecryptAndDecodeFrame(sample, &frame);
-  Log("WidevineVideoDecoder::Decode(timestamp=%" PRId64 ") rv=%d", sample.timestamp,
-      rv);
+  CDM_LOG("WidevineVideoDecoder::Decode(timestamp=%" PRId64 ") rv=%d",
+          sample.timestamp, rv);
 
   // Destroy frame, so that the shmem is now free to be used to return
   // output to the Gecko process.
   aInputFrame->Destroy();
   aInputFrame = nullptr;
 
   if (rv == kSuccess) {
     if (!ReturnOutput(frame)) {
-      Log("WidevineVideoDecoder::Decode() Failed in ReturnOutput()");
+      CDM_LOG("WidevineVideoDecoder::Decode() Failed in ReturnOutput()");
       mCallback->Error(GMPDecodeErr);
       return;
     }
     // A reset should only be started at most at level mReturnOutputCallDepth 1,
     // and if it's started it should be finished by that call by the time
     // the it returns, so it should always be false by this point.
     MOZ_ASSERT(!mResetInProgress);
     // Only request more data if we don't have pending samples.
@@ -245,17 +245,17 @@ WidevineVideoDecoder::ReturnOutput(Widev
     // be in a reset at this stage -- this would indicate receiving decode
     // messages before completing our reset, which we should not.
     MOZ_ASSERT(!mResetInProgress);
     WidevineVideoFrame currentCDMFrame = Move(mFrameAllocationQueue.front());
     mFrameAllocationQueue.pop_front();
     GMPVideoFrame* f = nullptr;
     auto err = mVideoHost->CreateFrame(kGMPI420VideoFrame, &f);
     if (GMP_FAILED(err) || !f) {
-      Log("Failed to create i420 frame!\n");
+      CDM_LOG("Failed to create i420 frame!\n");
       return false;
     }
     auto gmpFrame = static_cast<GMPVideoi420Frame*>(f);
     FrameDestroyerHelper frameDestroyerHelper(gmpFrame);
     Size size = currentCDMFrame.Size();
     const int32_t yStride = currentCDMFrame.Stride(VideoFrame::kYPlane);
     const int32_t uStride = currentCDMFrame.Stride(VideoFrame::kUPlane);
     const int32_t vStride = currentCDMFrame.Stride(VideoFrame::kVPlane);
@@ -326,17 +326,17 @@ WidevineVideoDecoder::ReturnOutput(Widev
   }
 
   return true;
 }
 
 void
 WidevineVideoDecoder::Reset()
 {
-  Log("WidevineVideoDecoder::Reset() mSentInput=%d", mSentInput);
+  CDM_LOG("WidevineVideoDecoder::Reset() mSentInput=%d", mSentInput);
   // We shouldn't reset if a drain is pending.
   MOZ_ASSERT(!mDrainPending);
   mResetInProgress = true;
   if (mSentInput) {
     CDM()->ResetDecoder(kStreamTypeVideo);
   }
   // Remove queued frames, but do not reset mReturnOutputCallDepth, let the
   // ReturnOutput calls unwind and decrement the counter as needed.
@@ -355,57 +355,57 @@ WidevineVideoDecoder::CompleteReset()
   mCallback->ResetComplete();
   mSentInput = false;
   mResetInProgress = false;
 }
 
 void
 WidevineVideoDecoder::Drain()
 {
-  Log("WidevineVideoDecoder::Drain()");
+  CDM_LOG("WidevineVideoDecoder::Drain()");
   if (mReturnOutputCallDepth > 0) {
-    Log("Drain call is reentrant, postponing drain");
+    CDM_LOG("Drain call is reentrant, postponing drain");
     mDrainPending = true;
     return;
   }
 
   Status rv = kSuccess;
   while (rv == kSuccess) {
     WidevineVideoFrame frame;
     InputBuffer sample;
     Status rv = CDM()->DecryptAndDecodeFrame(sample, &frame);
-    Log("WidevineVideoDecoder::Drain();  DecryptAndDecodeFrame() rv=%d", rv);
+    CDM_LOG("WidevineVideoDecoder::Drain();  DecryptAndDecodeFrame() rv=%d", rv);
     if (frame.Format() == kUnknownVideoFormat) {
       break;
     }
     if (rv == kSuccess) {
       if (!ReturnOutput(frame)) {
-        Log("WidevineVideoDecoder::Decode() Failed in ReturnOutput()");
+        CDM_LOG("WidevineVideoDecoder::Decode() Failed in ReturnOutput()");
       }
     }
   }
   // Shouldn't be reset while draining.
   MOZ_ASSERT(!mResetInProgress);
 
   CDM()->ResetDecoder(kStreamTypeVideo);
   mDrainPending = false;
   mCallback->DrainComplete();
 }
 
 void
 WidevineVideoDecoder::DecodingComplete()
 {
-  Log("WidevineVideoDecoder::DecodingComplete()");
+  CDM_LOG("WidevineVideoDecoder::DecodingComplete()");
 
   if (mCDMWrapper) {
     // mCallback will be null if the decoder has not been fully initialized.
     if (mCallback) {
       CDM()->DeinitializeDecoder(kStreamTypeVideo);
     } else {
-      Log("WideVineDecoder::DecodingComplete() Decoder was not fully initialized!");
+      CDM_LOG("WideVineDecoder::DecodingComplete() Decoder was not fully initialized!");
     }
 
     mCDMWrapper = nullptr;
   }
 
   // Release that corresponds to AddRef() in constructor.
   Release();
 }
--- a/dom/media/gmp/widevine-adapter/WidevineVideoFrame.cpp
+++ b/dom/media/gmp/widevine-adapter/WidevineVideoFrame.cpp
@@ -13,113 +13,113 @@ using namespace cdm;
 namespace mozilla {
 
 WidevineVideoFrame::WidevineVideoFrame()
   : mFormat(kUnknownVideoFormat)
   , mSize(0,0)
   , mBuffer(nullptr)
   , mTimestamp(0)
 {
-  Log("WidevineVideoFrame::WidevineVideoFrame() this=%p", this);
+  CDM_LOG("WidevineVideoFrame::WidevineVideoFrame() this=%p", this);
   memset(mPlaneOffsets, 0, sizeof(mPlaneOffsets));
   memset(mPlaneStrides, 0, sizeof(mPlaneStrides));
 }
 
 WidevineVideoFrame::WidevineVideoFrame(WidevineVideoFrame&& aOther)
   : mFormat(aOther.mFormat)
   , mSize(aOther.mSize)
   , mBuffer(aOther.mBuffer)
   , mTimestamp(aOther.mTimestamp)
 {
-  Log("WidevineVideoFrame::WidevineVideoFrame(WidevineVideoFrame&&) this=%p, other=%p",
-      this, &aOther);
+  CDM_LOG("WidevineVideoFrame::WidevineVideoFrame(WidevineVideoFrame&&) this=%p, other=%p",
+          this, &aOther);
   memcpy(mPlaneOffsets, aOther.mPlaneOffsets, sizeof(mPlaneOffsets));
   memcpy(mPlaneStrides, aOther.mPlaneStrides, sizeof(mPlaneStrides));
   aOther.mBuffer = nullptr;
 }
 
 WidevineVideoFrame::~WidevineVideoFrame()
 {
   if (mBuffer) {
     mBuffer->Destroy();
     mBuffer = nullptr;
   }
 }
 
 void
 WidevineVideoFrame::SetFormat(cdm::VideoFormat aFormat)
 {
-  Log("WidevineVideoFrame::SetFormat(%d) this=%p", aFormat, this);
+  CDM_LOG("WidevineVideoFrame::SetFormat(%d) this=%p", aFormat, this);
   mFormat = aFormat;
 }
 
 cdm::VideoFormat
 WidevineVideoFrame::Format() const
 {
   return mFormat;
 }
 
 void
 WidevineVideoFrame::SetSize(cdm::Size aSize)
 {
-  Log("WidevineVideoFrame::SetSize(%d,%d) this=%p", aSize.width, aSize.height, this);
+  CDM_LOG("WidevineVideoFrame::SetSize(%d,%d) this=%p", aSize.width, aSize.height, this);
   mSize.width = aSize.width;
   mSize.height = aSize.height;
 }
 
 cdm::Size
 WidevineVideoFrame::Size() const
 {
   return mSize;
 }
 
 void
 WidevineVideoFrame::SetFrameBuffer(cdm::Buffer* aFrameBuffer)
 {
-  Log("WidevineVideoFrame::SetFrameBuffer(%p) this=%p", aFrameBuffer, this);
+  CDM_LOG("WidevineVideoFrame::SetFrameBuffer(%p) this=%p", aFrameBuffer, this);
   MOZ_ASSERT(!mBuffer);
   mBuffer = aFrameBuffer;
 }
 
 cdm::Buffer*
 WidevineVideoFrame::FrameBuffer()
 {
   return mBuffer;
 }
 
 void
 WidevineVideoFrame::SetPlaneOffset(cdm::VideoFrame::VideoPlane aPlane, uint32_t aOffset)
 {
-  Log("WidevineVideoFrame::SetPlaneOffset(%d, %d) this=%p", aPlane, aOffset, this);
+  CDM_LOG("WidevineVideoFrame::SetPlaneOffset(%d, %d) this=%p", aPlane, aOffset, this);
   mPlaneOffsets[aPlane] = aOffset;
 }
 
 uint32_t
 WidevineVideoFrame::PlaneOffset(cdm::VideoFrame::VideoPlane aPlane)
 {
   return mPlaneOffsets[aPlane];
 }
 
 void
 WidevineVideoFrame::SetStride(cdm::VideoFrame::VideoPlane aPlane, uint32_t aStride)
 {
-  Log("WidevineVideoFrame::SetStride(%d, %d) this=%p", aPlane, aStride, this);
+  CDM_LOG("WidevineVideoFrame::SetStride(%d, %d) this=%p", aPlane, aStride, this);
   mPlaneStrides[aPlane] = aStride;
 }
 
 uint32_t
 WidevineVideoFrame::Stride(cdm::VideoFrame::VideoPlane aPlane)
 {
   return mPlaneStrides[aPlane];
 }
 
 void
 WidevineVideoFrame::SetTimestamp(int64_t timestamp)
 {
-  Log("WidevineVideoFrame::SetTimestamp(%" PRId64 ") this=%p", timestamp, this);
+  CDM_LOG("WidevineVideoFrame::SetTimestamp(%" PRId64 ") this=%p", timestamp, this);
   mTimestamp = timestamp;
 }
 
 int64_t
 WidevineVideoFrame::Timestamp() const
 {
   return mTimestamp;
 }
--- a/dom/media/test/eme.js
+++ b/dom/media/test/eme.js
@@ -450,16 +450,17 @@ function SetupEME(test, token, params)
   });
   return v;
 }
 
 function SetupEMEPref(callback) {
   var prefs = [
     [ "media.mediasource.enabled", true ],
     [ "media.mediasource.webm.enabled", true ],
+    [ "media.eme.vp9-in-mp4.enabled", true ],
   ];
 
   if (SpecialPowers.Services.appinfo.name == "B2G" ||
       !manifestVideo().canPlayType("video/mp4")) {
     // XXX remove once we have mp4 PlatformDecoderModules on all platforms.
     prefs.push([ "media.use-blank-decoder", true ]);
   }
 
--- a/js/src/old-configure.in
+++ b/js/src/old-configure.in
@@ -695,20 +695,16 @@ case "$target" in
           if test -z `echo $CFLAGS | grep -i [-/]arch:` ; then
             CFLAGS="$CFLAGS -arch:SSE2"
           fi
           if test -z `echo $CXXFLAGS | grep -i [-/]arch:` ; then
             CXXFLAGS="$CXXFLAGS -arch:SSE2"
           fi
           changequote([,])
         fi
-        dnl VS2013+ requires -FS when parallel building by make -jN.
-        dnl If nothing, compiler sometimes causes C1041 error.
-        CFLAGS="$CFLAGS -FS"
-        CXXFLAGS="$CXXFLAGS -FS"
         dnl VS2013+ supports -Gw for better linker optimizations.
         dnl http://blogs.msdn.com/b/vcblog/archive/2013/09/11/introducing-gw-compiler-switch.aspx
         dnl Disabled on ASan because it causes false-positive ODR violations.
         if test -z "$MOZ_ASAN"; then
             CFLAGS="$CFLAGS -Gw"
             CXXFLAGS="$CXXFLAGS -Gw"
         fi
         # khuey says we can safely ignore MSVC warning C4251
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -1472,35 +1472,31 @@ PresShell::RemovePreferenceStyles()
 {
   if (mPrefStyleSheet) {
     mStyleSet->RemoveStyleSheet(SheetType::User, mPrefStyleSheet);
     mPrefStyleSheet = nullptr;
   }
 }
 
 void
-PresShell::AddUserSheet(nsISupports* aSheet)
-{
-  if (mStyleSet->IsServo()) {
-    NS_ERROR("stylo: nsStyleSheetService doesn't handle ServoStyleSheets yet");
-    return;
-  }
-
+PresShell::AddUserSheet(StyleSheet* aSheet)
+{
   // Make sure this does what nsDocumentViewer::CreateStyleSet does wrt
   // ordering. We want this new sheet to come after all the existing stylesheet
   // service sheets, but before other user sheets; see nsIStyleSheetService.idl
   // for the ordering.  Just remove and readd all the nsStyleSheetService
   // sheets.
   nsCOMPtr<nsIStyleSheetService> dummy =
     do_GetService(NS_STYLESHEETSERVICE_CONTRACTID);
 
   mStyleSet->BeginUpdate();
 
   nsStyleSheetService* sheetService = nsStyleSheetService::gInstance;
-  nsTArray<RefPtr<StyleSheet>>& userSheets = *sheetService->UserStyleSheets();
+  nsTArray<RefPtr<StyleSheet>>& userSheets =
+    *sheetService->UserStyleSheets(mStyleSet->BackendType());
   // Iterate forwards when removing so the searches for RemoveStyleSheet are as
   // short as possible.
   for (StyleSheet* sheet : userSheets) {
     mStyleSet->RemoveStyleSheet(SheetType::User, sheet);
   }
 
   // Now iterate backwards, so that the order of userSheets will be the same as
   // the order of sheets from it in the style set.
@@ -1509,64 +1505,44 @@ PresShell::AddUserSheet(nsISupports* aSh
   }
 
   mStyleSet->EndUpdate();
 
   RestyleForCSSRuleChanges();
 }
 
 void
-PresShell::AddAgentSheet(nsISupports* aSheet)
+PresShell::AddAgentSheet(StyleSheet* aSheet)
 {
   // Make sure this does what nsDocumentViewer::CreateStyleSet does
   // wrt ordering.
-  // XXXheycam This needs to work with ServoStyleSheets too.
-  RefPtr<CSSStyleSheet> sheet = do_QueryObject(aSheet);
-  if (!sheet) {
-    NS_ERROR("stylo: AddAgentSheet needs to support ServoStyleSheets");
-    return;
-  }
-
-  mStyleSet->AppendStyleSheet(SheetType::Agent, sheet);
+  mStyleSet->AppendStyleSheet(SheetType::Agent, aSheet);
   RestyleForCSSRuleChanges();
 }
 
 void
-PresShell::AddAuthorSheet(nsISupports* aSheet)
-{
-  // XXXheycam This needs to work with ServoStyleSheets too.
-  RefPtr<CSSStyleSheet> sheet = do_QueryObject(aSheet);
-  if (!sheet) {
-    NS_ERROR("stylo: AddAuthorSheet needs to support ServoStyleSheets");
-    return;
-  }
-
+PresShell::AddAuthorSheet(StyleSheet* aSheet)
+{
   // Document specific "additional" Author sheets should be stronger than the
   // ones added with the StyleSheetService.
   StyleSheet* firstAuthorSheet =
     mDocument->GetFirstAdditionalAuthorSheet();
   if (firstAuthorSheet) {
-    mStyleSet->InsertStyleSheetBefore(SheetType::Doc, sheet, firstAuthorSheet);
+    mStyleSet->InsertStyleSheetBefore(SheetType::Doc, aSheet, firstAuthorSheet);
   } else {
-    mStyleSet->AppendStyleSheet(SheetType::Doc, sheet);
+    mStyleSet->AppendStyleSheet(SheetType::Doc, aSheet);
   }
 
   RestyleForCSSRuleChanges();
 }
 
 void
-PresShell::RemoveSheet(SheetType aType, nsISupports* aSheet)
-{
-  RefPtr<CSSStyleSheet> sheet = do_QueryObject(aSheet);
-  if (!sheet) {
-    NS_ERROR("stylo: RemoveSheet needs to support ServoStyleSheets");
-    return;
-  }
-
-  mStyleSet->RemoveStyleSheet(aType, sheet);
+PresShell::RemoveSheet(SheetType aType, StyleSheet* aSheet)
+{
+  mStyleSet->RemoveStyleSheet(aType, aSheet);
   RestyleForCSSRuleChanges();
 }
 
 NS_IMETHODIMP
 PresShell::SetDisplaySelection(int16_t aToggle)
 {
   mSelection->SetDisplaySelection(aToggle);
   return NS_OK;
--- a/layout/base/PresShell.h
+++ b/layout/base/PresShell.h
@@ -563,20 +563,20 @@ protected:
                       const mozilla::LayoutDeviceIntPoint aPoint,
                       mozilla::LayoutDeviceIntRect* aScreenRect,
                       uint32_t aFlags);
 
   /**
    * Methods to handle changes to user and UA sheet lists that we get
    * notified about.
    */
-  void AddUserSheet(nsISupports* aSheet);
-  void AddAgentSheet(nsISupports* aSheet);
-  void AddAuthorSheet(nsISupports* aSheet);
-  void RemoveSheet(mozilla::SheetType aType, nsISupports* aSheet);
+  void AddUserSheet(StyleSheet* aSheet);
+  void AddAgentSheet(StyleSheet* aSheet);
+  void AddAuthorSheet(StyleSheet* aSheet);
+  void RemoveSheet(mozilla::SheetType aType, StyleSheet* aSheet);
 
   // Hide a view if it is a popup
   void HideViewIfPopup(nsView* aView);
 
   // Utility method to restore the root scrollframe state
   void RestoreRootScrollPosition();
 
   void MaybeReleaseCapturingContent();
--- a/layout/base/nsDocumentViewer.cpp
+++ b/layout/base/nsDocumentViewer.cpp
@@ -2398,29 +2398,25 @@ nsDocumentViewer::CreateStyleSet(nsIDocu
   } else {
     // SVG documents may have scrollbars and need the scrollbar styling.
     sheet = cache->MinimalXULSheet();
     if (sheet) {
       styleSet->PrependStyleSheet(SheetType::Agent, sheet);
     }
   }
 
-  if (styleSet->IsGecko()) {
-    nsStyleSheetService* sheetService = nsStyleSheetService::GetInstance();
-    if (sheetService) {
-      for (StyleSheet* sheet : *sheetService->AgentStyleSheets()) {
-        styleSet->AppendStyleSheet(SheetType::Agent, sheet);
-      }
-      for (StyleSheet* sheet : Reversed(*sheetService->UserStyleSheets())) {
-        styleSet->PrependStyleSheet(SheetType::User, sheet);
-      }
+  nsStyleSheetService* sheetService = nsStyleSheetService::GetInstance();
+  if (sheetService) {
+    for (StyleSheet* sheet : *sheetService->AgentStyleSheets(backendType)) {
+      styleSet->AppendStyleSheet(SheetType::Agent, sheet);
     }
-  } else {
-    NS_WARNING("stylo: Not yet checking nsStyleSheetService for Servo-backed "
-               "documents. See bug 1290224");
+    for (StyleSheet* sheet :
+           Reversed(*sheetService->UserStyleSheets(backendType))) {
+      styleSet->PrependStyleSheet(SheetType::User, sheet);
+    }
   }
 
   // Caller will handle calling EndUpdate, per contract.
   return styleSet;
 }
 
 NS_IMETHODIMP
 nsDocumentViewer::ClearHistoryEntry()
--- a/layout/base/nsStyleSheetService.cpp
+++ b/layout/base/nsStyleSheetService.cpp
@@ -77,26 +77,41 @@ nsStyleSheetService::RegisterFromEnumera
 
     nsCOMPtr<nsIURI> uri;
     NS_NewURI(getter_AddRefs(uri), spec);
     if (uri)
       LoadAndRegisterSheetInternal(uri, aSheetType);
   }
 }
 
+static bool
+SheetHasURI(StyleSheet* aSheet, nsIURI* aSheetURI)
+{
+  MOZ_ASSERT(aSheetURI);
+
+  bool result;
+  nsIURI* uri = aSheet->GetSheetURI();
+  return uri &&
+         NS_SUCCEEDED(uri->Equals(aSheetURI, &result)) &&
+         result;
+}
+
 int32_t
-nsStyleSheetService::FindSheetByURI(const nsTArray<RefPtr<StyleSheet>>& aSheets,
+nsStyleSheetService::FindSheetByURI(uint32_t aSheetType,
                                     nsIURI* aSheetURI)
 {
-  for (int32_t i = aSheets.Length() - 1; i >= 0; i-- ) {
-    bool bEqual;
-    nsIURI* uri = aSheets[i]->GetSheetURI();
-    if (uri
-        && NS_SUCCEEDED(uri->Equals(aSheetURI, &bEqual))
-        && bEqual) {
+  MOZ_ASSERT(mGeckoSheets[aSheetType].Length() ==
+               mServoSheets[aSheetType].Length());
+
+  SheetArray& sheets = mGeckoSheets[aSheetType];
+  for (int32_t i = sheets.Length() - 1; i >= 0; i-- ) {
+    if (SheetHasURI(sheets[i], aSheetURI)) {
+#ifdef MOZ_STYLO
+      MOZ_ASSERT(SheetHasURI(mServoSheets[aSheetType][i], aSheetURI));
+#endif
       return i;
     }
   }
 
   return -1;
 }
 
 nsresult
@@ -155,31 +170,32 @@ nsStyleSheetService::LoadAndRegisterShee
         u"the sheet, if it's a data URI.";
       consoleService->LogStringMessage(message);
     }
   }
 
   rv = LoadAndRegisterSheetInternal(aSheetURI, aSheetType);
   if (NS_SUCCEEDED(rv)) {
     // We're guaranteed that the new sheet is the last sheet in
-    // mSheets[aSheetType]
+    // m{Gecko,Servo}Sheets[aSheetType]
+
+    MOZ_ASSERT(mGeckoSheets[aSheetType].Length() ==
+                 mServoSheets[aSheetType].Length());
+
+    RefPtr<StyleSheet> geckoSheet = mGeckoSheets[aSheetType].LastElement();
+    RefPtr<StyleSheet> servoSheet = mServoSheets[aSheetType].LastElement();
 
-    // XXXheycam Once the nsStyleSheetService can hold ServoStyleSheets too,
-    // we'll need to include them in the notification.
-    RefPtr<StyleSheet> sheet = mSheets[aSheetType].LastElement();
-    if (sheet->IsGecko()) {
-      // Hold on to a copy of the registered PresShells.
-      nsTArray<nsCOMPtr<nsIPresShell>> toNotify(mPresShells);
-      for (nsIPresShell* presShell : toNotify) {
-        if (presShell->StyleSet() && presShell->StyleSet()->IsGecko()) {
-          presShell->NotifyStyleSheetServiceSheetAdded(sheet, aSheetType);
-        }
+    // Hold on to a copy of the registered PresShells.
+    nsTArray<nsCOMPtr<nsIPresShell>> toNotify(mPresShells);
+    for (nsIPresShell* presShell : toNotify) {
+      if (presShell->StyleSet()) {
+        StyleSheet* sheet = presShell->StyleSet()->IsGecko() ? geckoSheet
+                                                             : servoSheet;
+        presShell->NotifyStyleSheetServiceSheetAdded(sheet, aSheetType);
       }
-    } else {
-      NS_ERROR("stylo: can't notify observers of ServoStyleSheets");
     }
 
     if (XRE_IsParentProcess()) {
       nsTArray<dom::ContentParent*> children;
       dom::ContentParent::GetAll(children);
 
       if (children.IsEmpty()) {
         return rv;
@@ -191,16 +207,26 @@ nsStyleSheetService::LoadAndRegisterShee
       for (uint32_t i = 0; i < children.Length(); i++) {
         Unused << children[i]->SendLoadAndRegisterSheet(uri, aSheetType);
       }
     }
   }
   return rv;
 }
 
+static nsresult
+LoadSheet(nsIURI* aURI,
+          css::SheetParsingMode aParsingMode,
+          StyleBackendType aType,
+          RefPtr<StyleSheet>* aResult)
+{
+  RefPtr<css::Loader> loader = new css::Loader(aType);
+  return loader->LoadSheetSync(aURI, aParsingMode, true, aResult);
+}
+
 nsresult
 nsStyleSheetService::LoadAndRegisterSheetInternal(nsIURI *aSheetURI,
                                                   uint32_t aSheetType)
 {
   NS_ENSURE_ARG_POINTER(aSheetURI);
 
   css::SheetParsingMode parsingMode;
   switch (aSheetType) {
@@ -216,40 +242,48 @@ nsStyleSheetService::LoadAndRegisterShee
       parsingMode = css::eAuthorSheetFeatures;
       break;
 
     default:
       NS_WARNING("invalid sheet type argument");
       return NS_ERROR_INVALID_ARG;
   }
 
-  // XXXheycam We'll need to load and register both a Gecko- and Servo-backed
-  // style sheet.
-  RefPtr<css::Loader> loader = new css::Loader(StyleBackendType::Gecko);
+  nsresult rv;
+
+  RefPtr<StyleSheet> geckoSheet;
+  RefPtr<StyleSheet> servoSheet;
+
+  rv = LoadSheet(aSheetURI, parsingMode, StyleBackendType::Gecko, &geckoSheet);
+  NS_ENSURE_SUCCESS(rv, rv);
+  MOZ_ASSERT(geckoSheet);
 
-  RefPtr<StyleSheet> sheet;
-  nsresult rv = loader->LoadSheetSync(aSheetURI, parsingMode, true, &sheet);
+#ifdef MOZ_STYLO
+  rv = LoadSheet(aSheetURI, parsingMode, StyleBackendType::Servo, &servoSheet);
   NS_ENSURE_SUCCESS(rv, rv);
+  MOZ_ASSERT(servoSheet);
+#endif
 
-  mSheets[aSheetType].AppendElement(sheet);
+  mGeckoSheets[aSheetType].AppendElement(geckoSheet);
+  mServoSheets[aSheetType].AppendElement(servoSheet);
 
-  return rv;
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 nsStyleSheetService::SheetRegistered(nsIURI *sheetURI,
                                      uint32_t aSheetType, bool *_retval)
 {
   NS_ENSURE_ARG(aSheetType == AGENT_SHEET ||
                 aSheetType == USER_SHEET ||
                 aSheetType == AUTHOR_SHEET);
   NS_ENSURE_ARG_POINTER(sheetURI);
   NS_PRECONDITION(_retval, "Null out param");
 
-  *_retval = (FindSheetByURI(mSheets[aSheetType], sheetURI) >= 0);
+  *_retval = (FindSheetByURI(aSheetType, sheetURI) >= 0);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsStyleSheetService::PreloadSheet(nsIURI* aSheetURI, uint32_t aSheetType,
                                   nsIPreloadedStyleSheet** aSheet)
 {
@@ -289,33 +323,36 @@ nsStyleSheetService::PreloadSheet(nsIURI
 NS_IMETHODIMP
 nsStyleSheetService::UnregisterSheet(nsIURI *aSheetURI, uint32_t aSheetType)
 {
   NS_ENSURE_ARG(aSheetType == AGENT_SHEET ||
                 aSheetType == USER_SHEET ||
                 aSheetType == AUTHOR_SHEET);
   NS_ENSURE_ARG_POINTER(aSheetURI);
 
-  int32_t foundIndex = FindSheetByURI(mSheets[aSheetType], aSheetURI);
+  MOZ_ASSERT(mGeckoSheets[aSheetType].Length() ==
+               mServoSheets[aSheetType].Length());
+
+  int32_t foundIndex = FindSheetByURI(aSheetType, aSheetURI);
   NS_ENSURE_TRUE(foundIndex >= 0, NS_ERROR_INVALID_ARG);
-  RefPtr<StyleSheet> sheet = mSheets[aSheetType][foundIndex];
-  mSheets[aSheetType].RemoveElementAt(foundIndex);
+
+  RefPtr<StyleSheet> geckoSheet = mGeckoSheets[aSheetType][foundIndex];
+  RefPtr<StyleSheet> servoSheet = mServoSheets[aSheetType][foundIndex];
 
-  // XXXheycam Once the nsStyleSheetService can hold ServoStyleSheets too,
-  // we'll need to include them in the notification.
-  if (sheet->IsGecko()) {
-    // Hold on to a copy of the registered PresShells.
-    nsTArray<nsCOMPtr<nsIPresShell>> toNotify(mPresShells);
-    for (nsIPresShell* presShell : toNotify) {
-      if (presShell->StyleSet() && presShell->StyleSet()->IsGecko()) {
-        presShell->NotifyStyleSheetServiceSheetRemoved(sheet, aSheetType);
-      }
+  mGeckoSheets[aSheetType].RemoveElementAt(foundIndex);
+  mServoSheets[aSheetType].RemoveElementAt(foundIndex);
+
+  // Hold on to a copy of the registered PresShells.
+  nsTArray<nsCOMPtr<nsIPresShell>> toNotify(mPresShells);
+  for (nsIPresShell* presShell : toNotify) {
+    if (presShell->StyleSet()) {
+      StyleSheet* sheet = presShell->StyleSet()->IsGecko() ? geckoSheet
+                                                           : servoSheet;
+      presShell->NotifyStyleSheetServiceSheetRemoved(sheet, aSheetType);
     }
-  } else {
-    NS_ERROR("stylo: can't notify observers of ServoStyleSheets");
   }
 
   if (XRE_IsParentProcess()) {
     nsTArray<dom::ContentParent*> children;
     dom::ContentParent::GetAll(children);
 
     if (children.IsEmpty()) {
       return NS_OK;
@@ -360,20 +397,24 @@ nsStyleSheetService::CollectReports(nsIH
 
   return NS_OK;
 }
 
 size_t
 nsStyleSheetService::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
 {
   size_t n = aMallocSizeOf(this);
-  for (auto& sheetArray : mSheets) {
-    n += sheetArray.ShallowSizeOfExcludingThis(aMallocSizeOf);
-    for (StyleSheet* sheet : sheetArray) {
-      n += sheet->SizeOfIncludingThis(aMallocSizeOf);
+  for (auto* sheetArrays : { &mGeckoSheets, &mServoSheets }) {
+    for (auto& sheetArray : *sheetArrays) {
+      n += sheetArray.ShallowSizeOfExcludingThis(aMallocSizeOf);
+      for (StyleSheet* sheet : sheetArray) {
+        if (sheet) {
+          n += sheet->SizeOfIncludingThis(aMallocSizeOf);
+        }
+      }
     }
   }
   return n;
 }
 
 void
 nsStyleSheetService::RegisterPresShell(nsIPresShell* aPresShell)
 {
--- a/layout/base/nsStyleSheetService.h
+++ b/layout/base/nsStyleSheetService.h
@@ -8,16 +8,17 @@
 
 #ifndef nsStyleSheetService_h_
 #define nsStyleSheetService_h_
 
 #include "nsCOMArray.h"
 #include "nsCOMPtr.h"
 #include "nsIMemoryReporter.h"
 #include "nsIStyleSheetService.h"
+#include "mozilla/Array.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/StyleSheet.h"
 
 class nsICategoryManager;
 class nsIMemoryReporter;
 class nsIPresShell;
 class nsISimpleEnumerator;
@@ -28,62 +29,70 @@ class nsISimpleEnumerator;
 
 #define NS_STYLESHEETSERVICE_CONTRACTID \
   "@mozilla.org/content/style-sheet-service;1"
 
 class nsStyleSheetService final
   : public nsIStyleSheetService
   , public nsIMemoryReporter
 {
- public:
+public:
+  typedef nsTArray<RefPtr<mozilla::StyleSheet>> SheetArray;
+
   nsStyleSheetService();
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSISTYLESHEETSERVICE
   NS_DECL_NSIMEMORYREPORTER
 
   nsresult Init();
 
-  nsTArray<RefPtr<mozilla::StyleSheet>>* AgentStyleSheets()
+  SheetArray* AgentStyleSheets(mozilla::StyleBackendType aType)
   {
-    return &mSheets[AGENT_SHEET];
+    return &Sheets(aType)[AGENT_SHEET];
   }
-  nsTArray<RefPtr<mozilla::StyleSheet>>* UserStyleSheets()
+  SheetArray* UserStyleSheets(mozilla::StyleBackendType aType)
   {
-    return &mSheets[USER_SHEET];
+    return &Sheets(aType)[USER_SHEET];
   }
-  nsTArray<RefPtr<mozilla::StyleSheet>>* AuthorStyleSheets()
+  SheetArray* AuthorStyleSheets(mozilla::StyleBackendType aType)
   {
-    return &mSheets[AUTHOR_SHEET];
+    return &Sheets(aType)[AUTHOR_SHEET];
   }
 
   void RegisterPresShell(nsIPresShell* aPresShell);
   void UnregisterPresShell(nsIPresShell* aPresShell);
 
   size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
 
   static nsStyleSheetService *GetInstance();
   static nsStyleSheetService *gInstance;
 
- private:
+private:
   ~nsStyleSheetService();
 
   void RegisterFromEnumerator(nsICategoryManager  *aManager,
                                           const char          *aCategory,
                                           nsISimpleEnumerator *aEnumerator,
                                           uint32_t             aSheetType);
 
-  int32_t FindSheetByURI(const nsTArray<RefPtr<mozilla::StyleSheet>>& aSheets,
-                         nsIURI* aSheetURI);
+  int32_t FindSheetByURI(uint32_t aSheetType, nsIURI* aSheetURI);
 
   // Like LoadAndRegisterSheet, but doesn't notify.  If successful, the
   // new sheet will be the last sheet in mSheets[aSheetType].
   nsresult LoadAndRegisterSheetInternal(nsIURI *aSheetURI,
                                         uint32_t aSheetType);
 
-  nsTArray<RefPtr<mozilla::StyleSheet>> mSheets[3];
+  mozilla::Array<SheetArray, 3>& Sheets(mozilla::StyleBackendType aType)
+  {
+    return aType == mozilla::StyleBackendType::Gecko ? mGeckoSheets
+                                                     : mServoSheets;
+  }
+
+  mozilla::Array<SheetArray, 3> mGeckoSheets;
+  mozilla::Array<SheetArray, 3> mServoSheets;
 
   // Registered PresShells that will be notified when sheets are added and
   // removed from the style sheet service.
   nsTArray<nsCOMPtr<nsIPresShell>> mPresShells;
 };
 
 #endif
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -2049,20 +2049,39 @@ CachedBorderImageData::SetCachedSVGViewp
 }
 
 const mozilla::Maybe<nsSize>&
 CachedBorderImageData::GetCachedSVGViewportSize()
 {
   return mCachedSVGViewportSize;
 }
 
+struct PurgeCachedImagesTask : mozilla::Runnable
+{
+  NS_IMETHOD Run() final
+  {
+    mSubImages.Clear();
+    return NS_OK;
+  }
+
+  nsCOMArray<imgIContainer> mSubImages;
+};
+
 void
 CachedBorderImageData::PurgeCachedImages()
 {
-  mSubImages.Clear();
+  if (ServoStyleSet::IsInServoTraversal()) {
+    RefPtr<PurgeCachedImagesTask> task = new PurgeCachedImagesTask();
+    task->mSubImages.SwapElements(mSubImages);
+    // This will run the task immediately if we're already on the main thread,
+    // but that is fine.
+    NS_DispatchToMainThread(task.forget());
+  } else {
+    mSubImages.Clear();
+  }
 }
 
 void
 CachedBorderImageData::SetSubImage(uint8_t aIndex, imgIContainer* aSubImage)
 {
   mSubImages.ReplaceObjectAt(aSubImage, aIndex);
 }
 
--- a/layout/style/test/mochitest.ini
+++ b/layout/style/test/mochitest.ini
@@ -33,17 +33,16 @@ support-files =
   visited-lying-inner.html
   visited-pref-iframe.html
   xbl_bindings.xml
 
 [test_acid3_test46.html]
 [test_addSheet.html]
 support-files = additional_sheets_helper.html
 [test_additional_sheets.html]
-skip-if = stylo # bug 1337258
 support-files = additional_sheets_helper.html
 [test_align_justify_computed_values.html]
 [test_align_shorthand_serialization.html]
 skip-if = stylo # bug 1339656
 [test_all_shorthand.html]
 [test_animations.html]
 skip-if = (toolkit == 'android' || stylo) # bug 1339318 for stylo
 [test_animations_async_tests.html]
@@ -146,17 +145,16 @@ support-files = bug732209-css.sjs
 [test_bug829816.html]
 [test_bug874919.html]
 support-files = file_bug829816.css
 [test_bug887741_at-rules_in_declaration_lists.html]
 [test_bug892929.html]
 [test_bug1055933.html]
 support-files = file_bug1055933_circle-xxl.png
 [test_bug1089417.html]
-skip-if = stylo # bug 1323665
 support-files = file_bug1089417_iframe.html
 [test_bug1112014.html]
 [test_bug1203766.html]
 [test_bug1232829.html]
 [test_bug1292447.html]
 [test_cascade.html]
 [test_ch_ex_no_infloops.html]
 [test_change_hint_optimizations.html]
@@ -247,17 +245,16 @@ skip-if = android_version == '18' #debug
 [test_position_float_display.html]
 [test_position_sticky.html]
 [test_priority_preservation.html]
 [test_property_database.html]
 [test_property_syntax_errors.html]
 [test_pseudoelement_state.html]
 [test_pseudoelement_parsing.html]
 [test_redundant_font_download.html]
-skip-if = stylo # bug 1323665
 support-files = redundant_font_download.sjs
 [test_rem_unit.html]
 [test_restyles_in_smil_animation.html]
 [test_root_node_display.html]
 [test_rule_insertion.html]
 [test_rule_serialization.html]
 [test_rules_out_of_sheets.html]
 [test_selectors.html]
@@ -306,17 +303,16 @@ support-files = ../../reftests/fonts/mar
 [test_value_cloning.html]
 skip-if = toolkit == 'android' # bug 775227 for android
 [test_value_computation.html]
 skip-if = toolkit == 'android'
 [test_value_storage.html]
 [test_variable_serialization_computed.html]
 [test_variable_serialization_specified.html]
 [test_variables.html]
-skip-if = stylo # leak bug 1340457
 support-files = support/external-variable-url.css
 [test_video_object_fit.html]
 [test_viewport_units.html]
 [test_visited_image_loading.html]
 skip-if = (toolkit == 'android' || stylo) # TIMED_OUT for android, timeout bug 1328511 for stylo
 [test_visited_image_loading_empty.html]
 skip-if = (toolkit == 'android' || stylo) # TIMED_OUT for android, timeout bug 1328511 for stylo
 [test_visited_lying.html]
--- a/layout/style/test/stylo-failures.md
+++ b/layout/style/test/stylo-failures.md
@@ -23,17 +23,17 @@ In which
   accumulated, unlike `pattern`. And number of assertions cannot be "*".
 
 Any line which doesn't follow the format above would be ignored like comment.
 
 ## Failures
 
 * Media query support:
   * test_acid3_test46.html: @media support [13]
-  * test_bug1089417.html [1] **disabled**
+  * test_bug1089417.html [1]
   * test_bug418986-2.html: matchMedia support [3]
   * test_bug453896_deck.html: &lt;style media&gt; support [8]
   * test_media_queries.html [1]
   * test_media_queries_dynamic.html [17]
   * test_media_queries_dynamic_xbl.html [2]
   * test_webkit_device_pixel_ratio.html: -webkit-device-pixel-ratio [3]
 * test_all_shorthand.html: all shorthand servo/servo#15055 [*]
 * Animation support:
@@ -132,17 +132,17 @@ Any line which doesn't follow the format
   * test_value_storage.html `symbols(` [30]
   * ... `list-style-type` [60]
   * ... `'list-style'` [30]
 * test_default_computed_style.html: support of getDefaultComputedStyle [1]
 * @font-face support bug 1290237
   * test_descriptor_storage.html [1]
   * test_descriptor_syntax_errors.html [1]
   * test_font_face_parser.html `@font-face` [447]
-  * test_redundant_font_download.html [3] **disabled**
+  * test_redundant_font_download.html [3]
 * @namespace support:
   * test_namespace_rule.html [17]
 * test_dont_use_document_colors.html: support of disabling document color [21]
 * test_exposed_prop_accessors.html: mainly various unsupported properties [*]
 * test_extra_inherit_initial.html: CSS-wide keywords are accepted as part of value servo/servo#15054 [946]
 * flex-basis glue not implemented bug 1331529
   * test_flexbox_flex_shorthand.html `flex-basis` [28]
   * test_flexbox_layout.html [355]
@@ -561,16 +561,17 @@ Any line which doesn't follow the format
 * test_property_syntax_errors.html `'background'`: whether background shorthand should accept "text" [40]
 * test_inherit_computation.html `weight style`: whether font-synthesis should be reset by font w3c/csswg-drafts#1032 [8]
 
 ## Unknown / Unsure
 
 * border-width/padding failure
   * test_compute_data_with_start_struct.html `-width` [4]
   * ... `padding-` [4]
+* test_additional_sheets.html: one sub-test cascade order is wrong [1]
 * test_selectors.html `:nth-child`: &lt;an+b&gt; parsing difference [14]
 * test_selectors_on_anonymous_content.html: xbl and :nth-child [1]
 * test_variables.html `url`: url in custom property [1]
 * test_pseudoelement_state.html: doesn't seem to work at all, but only range-thumb fails... [4]
 
 ## Ignore
 
 * Ignore for now since should be mostly identical to test_value_storage.html
new file mode 100644
--- /dev/null
+++ b/netwerk/test/crashtests/1334468-1.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<!--
+user_pref("privacy.firstparty.isolate", true);
+-->
+<script>
+
+let RESTRICTED_CHARS = "\001\002\003\004\005\006\007" +
+                       "\010\011\012\013\014\015\016\017" +
+                       "\020\021\022\023\024\025\026\027" +
+                       "\030\031\032\033\034\035\036\037" +
+                       "/:*?\"<>|\\";
+
+function boom() {
+  for (let c of RESTRICTED_CHARS) {
+    window.location = 'http://s.s' + c;
+  }
+}
+
+</script>
+</head>
+<body onload="boom();"></body>
+</html>
--- a/netwerk/test/crashtests/crashtests.list
+++ b/netwerk/test/crashtests/crashtests.list
@@ -1,3 +1,4 @@
 load 785753-1.html
 load 785753-2.html
 load 1274044-1.html
+skip-if(Android) pref(privacy.firstparty.isolate,true) load 1334468-1.html
--- a/old-configure.in
+++ b/old-configure.in
@@ -981,20 +981,16 @@ case "$target" in
             dnl and doesn't have a separate arch for SSSE3
             SSSE3_FLAGS="-arch:SSE2"
         fi
         dnl clang-cl requires appropriate flags to enable SSSE3 support
         dnl on all architectures.
         if test -n "$CLANG_CL"; then
             SSSE3_FLAGS="-mssse3"
         fi
-        dnl VS2013+ requires -FS when parallel building by make -jN.
-        dnl If nothing, compiler sometimes causes C1041 error.
-        CFLAGS="$CFLAGS -FS"
-        CXXFLAGS="$CXXFLAGS -FS"
         dnl VS2013+ supports -Gw for better linker optimizations.
         dnl http://blogs.msdn.com/b/vcblog/archive/2013/09/11/introducing-gw-compiler-switch.aspx
         dnl Disabled on ASan because it causes false-positive ODR violations.
         if test -z "$MOZ_ASAN"; then
             CFLAGS="$CFLAGS -Gw"
             CXXFLAGS="$CXXFLAGS -Gw"
         fi
         # khuey says we can safely ignore MSVC warning C4251
--- a/servo/Cargo.lock
+++ b/servo/Cargo.lock
@@ -3205,17 +3205,17 @@ dependencies = [
  "url 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "uuid 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "webdriver 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "webrender"
 version = "0.17.0"
-source = "git+https://github.com/servo/webrender#bcd9ed5e74d657b3c9b6d62996e84c1a3ee7141b"
+source = "git+https://github.com/servo/webrender#4221987984718bfc6312f92df9501d8fd7a88ea8"
 dependencies = [
  "app_units 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "bincode 1.0.0-alpha2 (registry+https://github.com/rust-lang/crates.io-index)",
  "bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-graphics 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-text 4.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -3233,17 +3233,17 @@ dependencies = [
  "threadpool 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "time 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)",
  "webrender_traits 0.16.0 (git+https://github.com/servo/webrender)",
 ]
 
 [[package]]
 name = "webrender_traits"
 version = "0.16.0"
-source = "git+https://github.com/servo/webrender#bcd9ed5e74d657b3c9b6d62996e84c1a3ee7141b"
+source = "git+https://github.com/servo/webrender#4221987984718bfc6312f92df9501d8fd7a88ea8"
 dependencies = [
  "app_units 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-graphics 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "euclid 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "gleam 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)",
  "heapsize 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "ipc-channel 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
--- a/servo/components/servo/lib.rs
+++ b/servo/components/servo/lib.rs
@@ -178,20 +178,17 @@ impl<Window> Browser<Window> where Windo
                 enable_aa: opts.enable_text_antialiasing,
                 enable_profiler: opts.webrender_stats,
                 debug: opts.webrender_debug,
                 recorder: recorder,
                 precache_shaders: opts.precache_shaders,
                 enable_scrollbars: opts.output_file.is_none(),
                 renderer_kind: renderer_kind,
                 enable_subpixel_aa: opts.enable_subpixel_text_antialiasing,
-                clear_framebuffer: true,
-                clear_color: webrender_traits::ColorF::new(1.0, 1.0, 1.0, 1.0),
-                render_target_debug: false,
-                workers: None,
+                ..Default::default()
             }).expect("Unable to initialize webrender!")
         };
 
         // Important that this call is done in a single-threaded fashion, we
         // can't defer it after `create_constellation` has started.
         script::init();
 
         // Create the constellation, which maintains the engine
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -4157,16 +4157,34 @@
   "URLCLASSIFIER_PREFIX_MATCH": {
     "alert_emails": ["safebrowsing-telemetry@mozilla.org"],
     "expires_in_version": "58",
     "kind": "enumerated",
     "n_values": 4,
     "bug_numbers": [1298257],
     "description": "Classifier prefix matching result (0 = no match, 1 = match only V2, 2 = match only V4, 3 = match both V2 and V4)"
   },
+  "URLCLASSIFIER_POSITIVE_CACHE_DURATION": {
+    "alert_emails": ["safebrowsing-telemetry@mozilla.org"],
+    "expires_in_version": "60",
+    "kind": "exponential",
+    "high": 86400000,
+    "n_buckets": 50,
+    "bug_numbers": [1338082],
+    "description": "Positive cache duration (ms) received in fullhash response from any v4 provider"
+  },
+  "URLCLASSIFIER_NEGATIVE_CACHE_DURATION": {
+    "alert_emails": ["safebrowsing-telemetry@mozilla.org"],
+    "expires_in_version": "60",
+    "kind": "exponential",
+    "high": 86400000,
+    "n_buckets": 50,
+    "bug_numbers": [1338082],
+    "description": "Negative cache duration (ms) received in fullhash response from any v4 provider"
+  },
   "CSP_DOCUMENTS_COUNT": {
     "alert_emails": ["seceng@mozilla.com"],
     "bug_numbers": [1252829],
     "expires_in_version": "55",
     "kind": "count",
     "description": "Number of unique pages that contain a CSP"
   },
   "CSP_UNSAFE_INLINE_DOCUMENTS_COUNT": {
--- a/toolkit/components/url-classifier/nsUrlClassifierUtils.cpp
+++ b/toolkit/components/url-classifier/nsUrlClassifierUtils.cpp
@@ -450,29 +450,35 @@ nsUrlClassifierUtils::ParseFindFullHashR
   for (auto& m : r.matches()) {
     nsCString tableNames;
     nsresult rv = ConvertThreatTypeToListNames(m.threat_type(), tableNames);
     if (NS_FAILED(rv)) {
       hasUnknownThreatType = true;
       continue; // Ignore un-convertable threat type.
     }
     auto& hash = m.threat().hash();
+    auto cacheDuration = DurationToMs(m.cache_duration());
     aCallback->OnCompleteHashFound(nsCString(hash.c_str(), hash.length()),
-                                   tableNames,
-                                   DurationToMs(m.cache_duration()));
+                                   tableNames, cacheDuration);
+
+    Telemetry::Accumulate(Telemetry::URLCLASSIFIER_POSITIVE_CACHE_DURATION,
+                          cacheDuration);
   }
 
   auto minWaitDuration = DurationToMs(r.minimum_wait_duration());
   auto negCacheDuration = DurationToMs(r.negative_cache_duration());
 
   aCallback->OnResponseParsed(minWaitDuration, negCacheDuration);
 
   Telemetry::Accumulate(Telemetry::URLCLASSIFIER_COMPLETION_ERROR,
                         hasUnknownThreatType ? UNKNOWN_THREAT_TYPE : SUCCESS);
 
+  Telemetry::Accumulate(Telemetry::URLCLASSIFIER_NEGATIVE_CACHE_DURATION,
+                        negCacheDuration);
+
   return NS_OK;
 }
 
 //////////////////////////////////////////////////////////
 // nsIObserver
 
 NS_IMETHODIMP
 nsUrlClassifierUtils::Observe(nsISupports *aSubject, const char *aTopic,