Merge inbound to central, a=merge
authorWes Kocher <wkocher@mozilla.com>
Mon, 16 May 2016 14:19:52 -0700
changeset 297539 a884b96685aa13b65601feddb24e5f85ba861561
parent 297497 e9533997fcef31fa5d2db464c6ceec0ca6ea560f (current diff)
parent 297538 349f5ef87e5be1c7be0a24c60895c0efe3fa2495 (diff)
child 297540 3780a3a6b83aeda143f9562829c830410a0c961e
child 297580 44f718bb2282512003fa09eb9a796ff4b580c957
child 297702 ae689711c21875e2f732c97264e35527c7f6b963
push id30262
push userkwierso@gmail.com
push dateMon, 16 May 2016 21:19:59 +0000
treeherdermozilla-central@a884b96685aa [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone49.0a1
first release with
nightly linux32
a884b96685aa / 49.0a1 / 20160517030211 / files
nightly linux64
a884b96685aa / 49.0a1 / 20160517030211 / files
nightly mac
a884b96685aa / 49.0a1 / 20160517030211 / files
nightly win32
a884b96685aa / 49.0a1 / 20160517030211 / files
nightly win64
a884b96685aa / 49.0a1 / 20160517030211 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to central, a=merge
--- a/browser/base/content/browser-fullScreen.js
+++ b/browser/base/content/browser-fullScreen.js
@@ -126,20 +126,22 @@ var FullScreen = {
         // to get its corresponding browser here.
         let browser;
         if (event.target == gBrowser) {
           browser = event.originalTarget;
         } else {
           let topWin = event.target.ownerDocument.defaultView.top;
           browser = gBrowser.getBrowserForContentWindow(topWin);
         }
+        TelemetryStopwatch.start("FULLSCREEN_CHANGE_MS");
         this.enterDomFullscreen(browser);
         break;
       }
       case "MozDOMFullscreen:Exited":
+        TelemetryStopwatch.start("FULLSCREEN_CHANGE_MS");
         this.cleanupDomFullscreen();
         break;
     }
   },
 
   receiveMessage: function(aMessage) {
     let browser = aMessage.target;
     switch (aMessage.name) {
@@ -152,16 +154,17 @@ var FullScreen = {
         break;
       }
       case "DOMFullscreen:Exit": {
         this._windowUtils.remoteFrameFullscreenReverted();
         break;
       }
       case "DOMFullscreen:Painted": {
         Services.obs.notifyObservers(window, "fullscreen-painted", "");
+        TelemetryStopwatch.finish("FULLSCREEN_CHANGE_MS");
         break;
       }
     }
   },
 
   enterDomFullscreen : function(aBrowser) {
     if (!document.fullscreenElement) {
       return;
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -263,17 +263,17 @@ tags = mcb
 [browser_bug832435.js]
 [browser_bug839103.js]
 [browser_bug880101.js]
 [browser_bug882977.js]
 [browser_bug902156.js]
 tags = mcb
 [browser_bug906190.js]
 tags = mcb
-skip-if = buildapp == "mulet" || e10s # Bug 1093642 - test manipulates content and relies on content focus
+skip-if = buildapp == "mulet" # Bug 1093642 - test manipulates content and relies on content focus
 [browser_mixedContentFromOnunload.js]
 tags = mcb
 [browser_mixedContentFramesOnHttp.js]
 tags = mcb
 [browser_bug970746.js]
 [browser_bug1015721.js]
 skip-if = os == 'win'
 [browser_bug1064280_changeUrlInPinnedTab.js]
--- a/browser/base/content/test/general/browser_bug906190.js
+++ b/browser/base/content/test/general/browser_bug906190.js
@@ -187,45 +187,54 @@ add_task(function* test_same_origin_meta
   }, true);
 });
 
 /**
  * 5. - Load a html page which has mixed content
  *    - Doorhanger to disable protection appears - we disable it
  *    - Load a new page from the same origin in a new tab simulating a click
  *    - Redirect to another page from the same origin using 302 redirect
- *    - Doorhanger >> APPEARS << , but should >> NOT << appear again!
- *    - FOLLOW UP BUG 914860!
  */
 add_task(function* test_same_origin_302redirect_same_origin() {
   // the sjs files returns a 302 redirect- note, same origins
   yield doTest(gHttpTestRoot1 + "file_bug906190_1.html",
                gHttpTestRoot1 + "file_bug906190.sjs", function* () {
     // The doorhanger should appear but activeBlocked should be >> NOT << true.
     // Currently it is >> TRUE << - see follow up bug 914860
-    todo(!gIdentityHandler._identityBox.classList.contains("mixedActiveBlocked"),
-         "OK: Mixed Content is NOT being blocked");
+    ok(!gIdentityHandler._identityBox.classList.contains("mixedActiveBlocked"),
+       "OK: Mixed Content is NOT being blocked");
 
-    todo_is(content.document.getElementById('mctestdiv').innerHTML,
-            "Mixed Content Blocker disabled", "OK: Executed mixed script");
+    is(content.document.getElementById('mctestdiv').innerHTML,
+       "Mixed Content Blocker disabled", "OK: Executed mixed script");
   });
 });
 
 /**
  * 6. - Load a html page which has mixed content
  *    - Doorhanger to disable protection appears - we disable it
  *    - Load a new page from the same origin in a new tab simulating a click
  *    - Redirect to another page from a different origin using 302 redirect
- *    - Doorhanger >> SHOULD << appear again!
  */
 add_task(function* test_same_origin_302redirect_different_origin() {
   // the sjs files returns a 302 redirect - note, different origins
   yield doTest(gHttpTestRoot2 + "file_bug906190_1.html",
                gHttpTestRoot2 + "file_bug906190.sjs", function* () {
     // The doorhanger should appear and activeBlocked should be >> TRUE <<.
     yield assertMixedContentBlockingState(gBrowser, {
       activeLoaded: false, activeBlocked: true, passiveLoaded: false,
     });
 
     is(content.document.getElementById('mctestdiv').innerHTML,
        "Mixed Content Blocker enabled", "OK: Blocked mixed script");
   });
 });
+
+/**
+ * 7. - Test memory leak issue on redirection error. See Bug 1269426.
+ */
+add_task(function* test_bad_redirection() {
+  // the sjs files returns a 302 redirect - note, different origins
+  yield doTest(gHttpTestRoot2 + "file_bug906190_1.html",
+               gHttpTestRoot2 + "file_bug906190.sjs?bad-redirection=1", function* () {
+    // Nothing to do. Just see if memory leak is reported in the end.
+    ok(true, "Nothing to do");
+  });
+});
--- a/browser/base/content/test/general/file_bug906190.sjs
+++ b/browser/base/content/test/general/file_bug906190.sjs
@@ -1,11 +1,17 @@
 function handleRequest(request, response) {
   var page = "<!DOCTYPE html><html><body>bug 906190</body></html>";
   var path = "https://test1.example.com/browser/browser/base/content/test/general/";
-  var url = path + "file_bug906190_redirected.html";
+  var url;
+
+  if (request.queryString.includes('bad-redirection=1')) {
+    url = path + "this_page_does_not_exist.html";
+  } else {
+    url = path + "file_bug906190_redirected.html";
+  }
 
   response.setHeader("Cache-Control", "no-cache", false);
   response.setHeader("Content-Type", "text/html", false);
   response.setStatusLine(request.httpVersion, "302", "Found");
   response.setHeader("Location", url, false);
   response.write(page);
 }
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -7348,16 +7348,29 @@ nsDocShell::OnLocationChange(nsIWebProgr
 void
 nsDocShell::OnRedirectStateChange(nsIChannel* aOldChannel,
                                   nsIChannel* aNewChannel,
                                   uint32_t aRedirectFlags,
                                   uint32_t aStateFlags)
 {
   NS_ASSERTION(aStateFlags & STATE_REDIRECTING,
                "Calling OnRedirectStateChange when there is no redirect");
+
+  // If mixed content is allowed for the old channel, we forward
+  // the permission to the new channel if it has the same origin
+  // as the old one.
+  if (mMixedContentChannel && mMixedContentChannel == aOldChannel) {
+    nsresult rv = nsContentUtils::CheckSameOrigin(mMixedContentChannel, aNewChannel);
+    if (NS_SUCCEEDED(rv)) {
+      SetMixedContentChannel(aNewChannel); // Same origin: forward permission.
+    } else {
+      SetMixedContentChannel(nullptr); // Different origin: clear mMixedContentChannel.
+    }
+  }
+
   if (!(aStateFlags & STATE_IS_DOCUMENT)) {
     return;  // not a toplevel document
   }
 
   nsCOMPtr<nsIURI> oldURI, newURI;
   aOldChannel->GetURI(getter_AddRefs(oldURI));
   aNewChannel->GetURI(getter_AddRefs(newURI));
   if (!oldURI || !newURI) {
--- a/dom/animation/KeyframeEffect.cpp
+++ b/dom/animation/KeyframeEffect.cpp
@@ -685,16 +685,24 @@ KeyframeEffectReadOnly::SetIsRunningOnCo
       if (aIsRunning) {
         property.mPerformanceWarning.reset();
       }
       return;
     }
   }
 }
 
+void
+KeyframeEffectReadOnly::ResetIsRunningOnCompositor()
+{
+  for (AnimationProperty& property : mProperties) {
+    property.mIsRunningOnCompositor = false;
+  }
+}
+
 KeyframeEffectReadOnly::~KeyframeEffectReadOnly()
 {
 }
 
 static Maybe<OwningAnimationTarget>
 ConvertTarget(const Nullable<ElementOrCSSPseudoElement>& aTarget)
 {
   // Return value optimization.
@@ -746,24 +754,16 @@ KeyframeEffectReadOnly::ConstructKeyfram
   if (aRv.Failed()) {
     return nullptr;
   }
 
   return effect.forget();
 }
 
 void
-KeyframeEffectReadOnly::ResetIsRunningOnCompositor()
-{
-  for (AnimationProperty& property : mProperties) {
-    property.mIsRunningOnCompositor = false;
-  }
-}
-
-void
 KeyframeEffectReadOnly::ResetWinsInCascade()
 {
   for (AnimationProperty& property : mProperties) {
     property.mWinsInCascade = false;
   }
 }
 
 void
--- a/dom/animation/KeyframeEffect.h
+++ b/dom/animation/KeyframeEffect.h
@@ -307,16 +307,17 @@ public:
   // AnimationEffect for the current time except any properties already
   // contained in |aSetProperties|.
   // Any updated properties are added to |aSetProperties|.
   void ComposeStyle(RefPtr<AnimValuesStyleRule>& aStyleRule,
                     nsCSSPropertySet& aSetProperties);
   // Returns true if at least one property is being animated on compositor.
   bool IsRunningOnCompositor() const;
   void SetIsRunningOnCompositor(nsCSSProperty aProperty, bool aIsRunning);
+  void ResetIsRunningOnCompositor();
 
   // Returns true if this effect, applied to |aFrame|, contains properties
   // that mean we shouldn't run transform compositor animations on this element.
   //
   // For example, if we have an animation of geometric properties like 'left'
   // and 'top' on an element, we force all 'transform' animations running at
   // the same time on the same element to run on the main thread.
   //
@@ -347,17 +348,16 @@ protected:
   template<class KeyframeEffectType, class OptionsType>
   static already_AddRefed<KeyframeEffectType>
   ConstructKeyframeEffect(const GlobalObject& aGlobal,
                           const Nullable<ElementOrCSSPseudoElement>& aTarget,
                           JS::Handle<JSObject*> aKeyframes,
                           const OptionsType& aOptions,
                           ErrorResult& aRv);
 
-  void ResetIsRunningOnCompositor();
   void ResetWinsInCascade();
 
   // This effect is registered with its target element so long as:
   //
   // (a) It has a target element, and
   // (b) It is "relevant" (i.e. yet to finish but not idle, or finished but
   //     filling forwards)
   //
--- a/dom/animation/test/chrome/test_restyles.html
+++ b/dom/animation/test/chrome/test_restyles.html
@@ -416,19 +416,19 @@ waitForAllPaints(function() {
 
     // We need to wait a frame to apply display:none style.
     yield waitForFrame();
 
     is(animation.playState, 'running',
        'Opacity script animations keep running even when the target element ' +
        'has "display: none" style');
 
-    todo(!animation.isRunningOnCompositor,
-         'Opacity script animations on "display:none" element should not ' +
-         'run on the compositor');
+    ok(!animation.isRunningOnCompositor,
+       'Opacity script animations on "display:none" element should not ' +
+       'run on the compositor');
 
     var markers = yield observeStyling(5);
     is(markers.length, 0,
        'Opacity script animations on "display: none" element should not ' +
        'update styles');
 
     div.style.display = '';
 
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -98,16 +98,21 @@
 #include "mozilla/WebBrowserPersistLocalDocument.h"
 
 #include "nsPrincipal.h"
 
 #ifdef MOZ_XUL
 #include "nsXULPopupManager.h"
 #endif
 
+#ifdef NS_PRINTING
+#include "mozilla/embedding/printingui/PrintingParent.h"
+#include "nsIWebBrowserPrint.h"
+#endif
+
 using namespace mozilla;
 using namespace mozilla::hal;
 using namespace mozilla::dom;
 using namespace mozilla::dom::ipc;
 using namespace mozilla::layers;
 using namespace mozilla::layout;
 typedef FrameMetrics::ViewID ViewID;
 
@@ -3097,16 +3102,56 @@ nsFrameLoader::RequestNotifyLayerTreeCle
     new AsyncEventDispatcher(mOwnerContent,
                              NS_LITERAL_STRING("MozLayerTreeCleared"),
                              true, false);
   event->PostDOMEvent();
 
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsFrameLoader::Print(nsIPrintSettings* aPrintSettings,
+                     nsIWebProgressListener* aProgressListener)
+{
+#if defined(NS_PRINTING)
+  if (mRemoteBrowser) {
+    RefPtr<embedding::PrintingParent> printingParent =
+      mRemoteBrowser->Manager()->AsContentParent()->GetPrintingParent();
+
+    embedding::PrintData printData;
+    nsresult rv = printingParent->SerializeAndEnsureRemotePrintJob(
+      aPrintSettings, aProgressListener, nullptr, &printData);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    bool success = mRemoteBrowser->SendPrint(printData);
+    return success ? NS_OK : NS_ERROR_FAILURE;
+  }
+
+  if (mDocShell) {
+    nsCOMPtr<nsIContentViewer> viewer;
+    mDocShell->GetContentViewer(getter_AddRefs(viewer));
+    if (!viewer) {
+      return NS_ERROR_FAILURE;
+    }
+
+    nsCOMPtr<nsIWebBrowserPrint> webBrowserPrint = do_QueryInterface(viewer);
+    if (!webBrowserPrint) {
+      return NS_ERROR_FAILURE;
+    }
+
+    return webBrowserPrint->Print(aPrintSettings, aProgressListener);
+  }
+
+  return NS_ERROR_FAILURE;
+#endif
+  return NS_OK;
+}
+
 /* [infallible] */ NS_IMETHODIMP
 nsFrameLoader::SetVisible(bool aVisible)
 {
   if (mVisible == aVisible) {
     return NS_OK;
   }
 
   mVisible = aVisible;
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -6243,16 +6243,17 @@ private:
   static const char* const kPaintedTopic;
 
   RefPtr<nsGlobalWindow> mWindow;
   nsCOMPtr<nsIWidget> mWidget;
   nsCOMPtr<nsIScreen> mScreen;
   nsCOMPtr<nsITimer> mTimer;
   nsCOMPtr<nsISupports> mTransitionData;
 
+  TimeStamp mFullscreenChangeStartTime;
   FullscreenTransitionDuration mDuration;
   Stage mStage;
   bool mFullscreen;
 };
 
 const char* const
 FullscreenTransitionTask::kPaintedTopic = "fullscreen-painted";
 
@@ -6269,16 +6270,17 @@ FullscreenTransitionTask::Run()
   }
   if (stage == eBeforeToggle) {
     PROFILER_MARKER("Fullscreen transition start");
     mWidget->PerformFullscreenTransition(nsIWidget::eBeforeFullscreenToggle,
                                          mDuration.mFadeIn, mTransitionData,
                                          this);
   } else if (stage == eToggleFullscreen) {
     PROFILER_MARKER("Fullscreen toggle start");
+    mFullscreenChangeStartTime = TimeStamp::Now();
     if (MOZ_UNLIKELY(mWindow->mFullScreen != mFullscreen)) {
       // This could happen in theory if several fullscreen requests in
       // different direction happen continuously in a short time. We
       // need to ensure the fullscreen state matches our target here,
       // otherwise the widget would change the window state as if we
       // toggle for Fullscreen Mode instead of Fullscreen API.
       NS_WARNING("The fullscreen state of the window does not match");
       mWindow->mFullScreen = mFullscreen;
@@ -6305,16 +6307,18 @@ FullscreenTransitionTask::Run()
     // powerful, layout could take a long time, in which case, staying
     // in black screen for that long could hurt user experience even
     // more than exposing an intermediate state.
     mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
     uint32_t timeout =
       Preferences::GetUint("full-screen-api.transition.timeout", 500);
     mTimer->Init(observer, timeout, nsITimer::TYPE_ONE_SHOT);
   } else if (stage == eAfterToggle) {
+    Telemetry::AccumulateTimeDelta(Telemetry::FULLSCREEN_TRANSITION_BLACK_MS,
+                                   mFullscreenChangeStartTime);
     mWidget->PerformFullscreenTransition(nsIWidget::eAfterFullscreenToggle,
                                          mDuration.mFadeOut, mTransitionData,
                                          this);
   } else if (stage == eEnd) {
     PROFILER_MARKER("Fullscreen transition end");
   }
   return NS_OK;
 }
--- a/dom/base/nsIFrameLoader.idl
+++ b/dom/base/nsIFrameLoader.idl
@@ -11,16 +11,18 @@ interface nsIDocShell;
 interface nsIURI;
 interface nsIFrame;
 interface nsSubDocumentFrame;
 interface nsIMessageSender;
 interface nsIVariant;
 interface nsIDOMElement;
 interface nsITabParent;
 interface nsILoadContext;
+interface nsIPrintSettings;
+interface nsIWebProgressListener;
 
 [scriptable, builtinclass, uuid(1645af04-1bc7-4363-8f2c-eb9679220ab1)]
 interface nsIFrameLoader : nsISupports
 {
   /**
    * Get the docshell from the frame loader.
    */
   readonly attribute nsIDocShell docShell;
@@ -135,16 +137,26 @@ interface nsIFrameLoader : nsISupports
    * Request an event when the layer tree from the remote tab becomes
    * available or unavailable. When this happens, a mozLayerTreeReady
    * or mozLayerTreeCleared event is fired.
    */
   void requestNotifyLayerTreeReady();
   void requestNotifyLayerTreeCleared();
 
   /**
+   * Print the current document.
+   *
+   * @param aPrintSettings optional print settings to use; printSilent can be
+   *                       set to prevent prompting.
+   * @param aProgressListener optional print progress listener.
+   */
+  void print(in nsIPrintSettings aPrintSettings,
+             in nsIWebProgressListener aProgressListener);
+
+  /**
    * The default event mode automatically forwards the events
    * handled in EventStateManager::HandleCrossProcessEvent to
    * the child content process when these events are targeted to
    * the remote browser element.
    *
    * Used primarly for input events (mouse, keyboard)
    */
   const unsigned long EVENT_MODE_NORMAL_DISPATCH = 0x00000000;
--- a/dom/camera/DOMCameraControl.cpp
+++ b/dom/camera/DOMCameraControl.cpp
@@ -1181,19 +1181,18 @@ nsDOMCameraControl::NotifyRecordingStatu
     mAudioChannelAgent = do_CreateInstance("@mozilla.org/audiochannelagent;1");
     if (!mAudioChannelAgent) {
       return NS_ERROR_UNEXPECTED;
     }
 
     // Camera app will stop recording when it falls to the background, so no callback is necessary.
     mAudioChannelAgent->Init(mWindow, (int32_t)AudioChannel::Content, nullptr);
     // Video recording doesn't output any sound, so it's not necessary to check canPlay.
-    float volume = 0.0;
-    bool muted = true;
-    rv = mAudioChannelAgent->NotifyStartedPlaying(&volume, &muted);
+    AudioPlaybackConfig config;
+    rv = mAudioChannelAgent->NotifyStartedPlaying(&config, AudioChannelService::AudibleState::eAudible);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
 #endif
   return rv;
 }
 
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -102,16 +102,17 @@
 #include "nsAnonymousTemporaryFile.h"
 #include "nsISpellChecker.h"
 #include "nsClipboardProxy.h"
 #include "nsISystemMessageCache.h"
 #include "nsDirectoryService.h"
 #include "nsDirectoryServiceUtils.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsContentPermissionHelper.h"
+#include "nsPrintingProxy.h"
 
 #include "IHistory.h"
 #include "nsNetUtil.h"
 
 #include "base/message_loop.h"
 #include "base/process_util.h"
 #include "base/task.h"
 
@@ -706,16 +707,21 @@ ContentChild::Init(MessageLoop* aIOLoop,
   }
 #endif
 
 #ifdef MOZ_NUWA_PROCESS
   if (IsNuwaProcess()) {
     NuwaAddConstructor(ResetTransports, nullptr);
   }
 #endif
+#ifdef NS_PRINTING
+  // Force the creation of the nsPrintingProxy so that it's IPC counterpart,
+  // PrintingParent, is always available for printing initiated from the parent.
+  RefPtr<nsPrintingProxy> printingProxy = nsPrintingProxy::GetInstance();
+#endif
 
   return true;
 }
 
 void
 ContentChild::InitProcessAttributes()
 {
 #ifdef MOZ_WIDGET_GONK
@@ -925,23 +931,23 @@ ContentChild::ProvideWindowCommon(TabChi
     return NS_ERROR_ABORT;
   }
 
   if (layersId == 0) { // if renderFrame is invalid.
     PRenderFrameChild::Send__delete__(renderFrame);
     renderFrame = nullptr;
   }
 
-  ShowInfo showInfo(EmptyString(), false, false, true, 0, 0);
+  ShowInfo showInfo(EmptyString(), false, false, true, false, 0, 0);
   auto* opener = nsPIDOMWindowOuter::From(aParent);
   nsIDocShell* openerShell;
   if (opener && (openerShell = opener->GetDocShell())) {
     nsCOMPtr<nsILoadContext> context = do_QueryInterface(openerShell);
     showInfo = ShowInfo(EmptyString(), false,
-                        context->UsePrivateBrowsing(), true,
+                        context->UsePrivateBrowsing(), true, false,
                         aTabOpener->mDPI, aTabOpener->mDefaultScale);
   }
 
   // Unfortunately we don't get a window unless we've shown the frame.  That's
   // pretty bogus; see bug 763602.
   newChild->DoFakeShow(textureFactoryIdentifier, layersId, renderFrame,
                        showInfo);
 
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -81,16 +81,17 @@
 #include "mozilla/ipc/PFileDescriptorSetParent.h"
 #include "mozilla/ipc/PSendStreamParent.h"
 #include "mozilla/ipc/SendStreamAlloc.h"
 #include "mozilla/ipc/TestShellParent.h"
 #include "mozilla/ipc/InputStreamUtils.h"
 #include "mozilla/jsipc/CrossProcessObjectWrappers.h"
 #include "mozilla/layers/PAPZParent.h"
 #include "mozilla/layers/CompositorBridgeParent.h"
+#include "mozilla/layers/CompositorThread.h"
 #include "mozilla/layers/ImageBridgeParent.h"
 #include "mozilla/layers/SharedBufferManagerParent.h"
 #include "mozilla/layout/RenderFrameParent.h"
 #include "mozilla/LookAndFeel.h"
 #include "mozilla/media/MediaParent.h"
 #include "mozilla/Move.h"
 #include "mozilla/net/NeckoParent.h"
 #include "mozilla/plugins/PluginBridge.h"
@@ -2553,17 +2554,17 @@ ContentParent::InitInternal(ProcessPrior
     // NB: internally, this will send an IPC message to the child
     // process to get it to create the CompositorBridgeChild.  This
     // message goes through the regular IPC queue for this
     // channel, so delivery will happen-before any other messages
     // we send.  The CompositorBridgeChild must be created before any
     // PBrowsers are created, because they rely on the Compositor
     // already being around.  (Creation is async, so can't happen
     // on demand.)
-    bool useOffMainThreadCompositing = !!CompositorBridgeParent::CompositorLoop();
+    bool useOffMainThreadCompositing = !!CompositorThreadHolder::Loop();
     if (useOffMainThreadCompositing) {
       DebugOnly<bool> opened = PCompositorBridge::Open(this);
       MOZ_ASSERT(opened);
 
       opened = PImageBridge::Open(this);
       MOZ_ASSERT(opened);
 
       opened = gfx::PVRManager::Open(this);
@@ -3814,34 +3815,52 @@ ContentParent::DeallocPNeckoParent(PNeck
   delete necko;
   return true;
 }
 
 PPrintingParent*
 ContentParent::AllocPPrintingParent()
 {
 #ifdef NS_PRINTING
-  return new PrintingParent();
+  MOZ_ASSERT(!mPrintingParent,
+             "Only one PrintingParent should be created per process.");
+
+  // Create the printing singleton for this process.
+  mPrintingParent = new PrintingParent();
+  return mPrintingParent.get();
 #else
+  MOZ_ASSERT_UNREACHABLE("Should never be created if no printing.");
   return nullptr;
 #endif
 }
 
 bool
-ContentParent::RecvPPrintingConstructor(PPrintingParent* aActor)
-{
+ContentParent::DeallocPPrintingParent(PPrintingParent* printing)
+{
+#ifdef NS_PRINTING
+  MOZ_ASSERT(mPrintingParent == printing,
+             "Only one PrintingParent should have been created per process.");
+
+  mPrintingParent = nullptr;
+#else
+  MOZ_ASSERT_UNREACHABLE("Should never have been created if no printing.");
+#endif
   return true;
 }
 
-bool
-ContentParent::DeallocPPrintingParent(PPrintingParent* printing)
-{
-  delete printing;
-  return true;
-}
+#ifdef NS_PRINTING
+already_AddRefed<embedding::PrintingParent>
+ContentParent::GetPrintingParent()
+{
+  MOZ_ASSERT(mPrintingParent);
+
+  RefPtr<embedding::PrintingParent> printingParent = mPrintingParent;
+  return printingParent.forget();
+}
+#endif
 
 PSendStreamParent*
 ContentParent::AllocPSendStreamParent()
 {
   return mozilla::ipc::AllocPSendStreamParent();
 }
 
 bool
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -44,16 +44,20 @@ class PRemoteSpellcheckEngineParent;
 class ProfileGatherer;
 #endif
 
 #if defined(XP_LINUX) && defined(MOZ_CONTENT_SANDBOX)
 class SandboxBroker;
 class SandboxBrokerPolicyFactory;
 #endif
 
+namespace embedding {
+class PrintingParent;
+}
+
 namespace ipc {
 class OptionalURIParams;
 class PFileDescriptorSetParent;
 class URIParams;
 class TestShellParent;
 } // namespace ipc
 
 namespace jsipc {
@@ -375,19 +379,24 @@ public:
 
   virtual bool RecvPNeckoConstructor(PNeckoParent* aActor) override
   {
     return PContentParent::RecvPNeckoConstructor(aActor);
   }
 
   virtual PPrintingParent* AllocPPrintingParent() override;
 
-  virtual bool RecvPPrintingConstructor(PPrintingParent* aActor) override;
+  virtual bool DeallocPPrintingParent(PPrintingParent* aActor) override;
 
-  virtual bool DeallocPPrintingParent(PPrintingParent* aActor) override;
+#if defined(NS_PRINTING)
+  /**
+   * @return the PrintingParent for this ContentParent.
+   */
+  already_AddRefed<embedding::PrintingParent> GetPrintingParent();
+#endif
 
   virtual PSendStreamParent* AllocPSendStreamParent() override;
   virtual bool DeallocPSendStreamParent(PSendStreamParent* aActor) override;
 
   virtual PScreenManagerParent*
   AllocPScreenManagerParent(uint32_t* aNumberOfScreens,
                             float* aSystemDefaultScale,
                             bool* aSuccess) override;
@@ -1195,16 +1204,20 @@ private:
 
   UniquePtr<gfx::DriverCrashGuard> mDriverCrashGuard;
 
 #if defined(XP_LINUX) && defined(MOZ_CONTENT_SANDBOX)
   mozilla::UniquePtr<SandboxBroker> mSandboxBroker;
   static mozilla::UniquePtr<SandboxBrokerPolicyFactory>
       sSandboxBrokerPolicyFactory;
 #endif
+
+#ifdef NS_PRINTING
+  RefPtr<embedding::PrintingParent> mPrintingParent;
+#endif
 };
 
 } // namespace dom
 } // namespace mozilla
 
 class ParentIdleListener : public nsIObserver
 {
   friend class mozilla::dom::ContentParent;
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -10,20 +10,22 @@ include protocol PColorPicker;
 include protocol PContent;
 include protocol PContentBridge;
 include protocol PDocAccessible;
 include protocol PDocumentRenderer;
 include protocol PFilePicker;
 include protocol PIndexedDBPermissionRequest;
 include protocol PRenderFrame;
 include protocol PPluginWidget;
+include protocol PRemotePrintJob;
 include DOMTypes;
 include JavaScriptTypes;
 include URIParams;
 include BrowserConfiguration;
+include PPrintingTypes;
 include PTabContext;
 
 
 using class IPC::Principal from "mozilla/dom/PermissionMessageUtils.h";
 using class mozilla::gfx::Matrix from "mozilla/gfx/Matrix.h";
 using struct gfxSize from "gfxPoint.h";
 using CSSRect from "Units.h";
 using CSSSize from "Units.h";
@@ -87,16 +89,17 @@ union MaybeNativeKeyBinding
 };
 
 struct ShowInfo
 {
   nsString name;
   bool fullscreenAllowed;
   bool isPrivate;
   bool fakeShowInfo;
+  bool isTransparent;
   float dpi;
   double defaultScale;
 };
 
 prio(normal upto urgent) sync protocol PBrowser
 {
     manager PContent or PContentBridge;
 
@@ -776,16 +779,23 @@ child:
      * @param aKeyEventData      The key event which was posted to the parent
      *                           process.
      * @param aIsConsumed        true if aKeyEventData is consumed in the
      *                           parent process.  Otherwise, false.
      */
     async HandledWindowedPluginKeyEvent(NativeEventData aKeyEventData,
                                         bool aIsConsumed);
 
+    /**
+     * Tell the child to print the current page with the given settings.
+     *
+     * @param aPrintData the serialized settings to print with
+     */
+    async Print(PrintData aPrintData);
+
 /*
  * FIXME: write protocol!
 
 state LIVE:
     send LoadURL goto LIVE;
 //etc.
     send Destroy goto DYING;
 
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -107,16 +107,23 @@
 #include "nsNetUtil.h"
 #include "nsIPermissionManager.h"
 #include "nsIURILoader.h"
 #include "nsIScriptError.h"
 #include "mozilla/EventForwards.h"
 #include "nsDeviceContext.h"
 #include "FrameLayerBuilder.h"
 
+#ifdef NS_PRINTING
+#include "nsIPrintSession.h"
+#include "nsIPrintSettings.h"
+#include "nsIPrintSettingsService.h"
+#include "nsIWebBrowserPrint.h"
+#endif
+
 #define BROWSER_ELEMENT_CHILD_SCRIPT \
     NS_LITERAL_STRING("chrome://global/content/BrowserElementChild.js")
 
 #define TABC_LOG(...)
 // #define TABC_LOG(...) printf_stderr("TABC: " __VA_ARGS__)
 
 using namespace mozilla;
 using namespace mozilla::dom;
@@ -586,16 +593,17 @@ TabChild::TabChild(nsIContentChild* aMan
   , mOrientation(eScreenOrientation_PortraitPrimary)
   , mUpdateHitRegion(false)
   , mIgnoreKeyPressEvent(false)
   , mHasValidInnerSize(false)
   , mDestroyed(false)
   , mUniqueId(aTabId)
   , mDPI(0)
   , mDefaultScale(0)
+  , mIsTransparent(false)
   , mIPCOpen(true)
   , mParentIsActive(false)
   , mDidSetRealShowInfo(false)
   , mDidLoadURLInit(false)
   , mAPZChild(nullptr)
 {
   // In the general case having the TabParent tell us if APZ is enabled or not
   // doesn't really work because the TabParent itself may not have a reference
@@ -1298,40 +1306,16 @@ TabChild::SetProcessNameToAppName()
     NS_WARNING("Failed to retrieve app name");
     return;
   }
 
   ContentChild::GetSingleton()->SetProcessName(appName, true);
 }
 
 bool
-TabChild::IsRootContentDocument() const
-{
-    // A TabChild is a "root content document" if it's
-    //
-    //  - <iframe mozapp> not inside another <iframe mozapp>,
-    //  - <iframe mozbrowser> (not mozapp), or
-    //  - a vanilla remote frame (<html:iframe remote=true> or <xul:browser
-    //    remote=true>).
-    //
-    // Put another way, an iframe is /not/ a "root content document" iff it's a
-    // mozapp inside a mozapp.  (This corresponds exactly to !HasAppOwnerApp.)
-    //
-    // Note that we're lying through our teeth here (thus the scare quotes).
-    // <html:iframe remote=true> or <xul:browser remote=true> inside another
-    // content iframe is not actually a root content document, but we say it is.
-    //
-    // We do this because we make a remote frame opaque iff
-    // IsRootContentDocument(), and making vanilla remote frames transparent
-    // breaks our remote reftests.
-
-    return !HasAppOwnerApp();
-}
-
-bool
 TabChild::RecvLoadURL(const nsCString& aURI,
                       const BrowserConfiguration& aConfiguration,
                       const ShowInfo& aInfo)
 {
   if (!mDidLoadURLInit) {
     mDidLoadURLInit = true;
     if (!InitTabChildGlobal()) {
       return false;
@@ -1559,16 +1543,17 @@ TabChild::ApplyShowInfo(const ShowInfo& 
         } else {
           context->SetUsePrivateBrowsing(true);
         }
       }
     }
   }
   mDPI = aInfo.dpi();
   mDefaultScale = aInfo.defaultScale();
+  mIsTransparent = aInfo.isTransparent();
 }
 
 #ifdef MOZ_WIDGET_GONK
 void
 TabChild::MaybeRequestPreinitCamera()
 {
     // Check if this tab is an app (not a browser frame) and will use the
     // `camera` permission,
@@ -2453,16 +2438,55 @@ TabChild::RecvSetUseGlobalHistory(const 
   if (NS_FAILED(rv)) {
     NS_WARNING("Failed to set UseGlobalHistory on TabChild docShell");
   }
 
   return true;
 }
 
 bool
+TabChild::RecvPrint(const PrintData& aPrintData)
+{
+#ifdef NS_PRINTING
+  nsCOMPtr<nsIWebBrowserPrint> webBrowserPrint = do_GetInterface(mWebNav);
+  if (NS_WARN_IF(!webBrowserPrint)) {
+    return true;
+  }
+
+  nsCOMPtr<nsIPrintSettingsService> printSettingsSvc =
+    do_GetService("@mozilla.org/gfx/printsettings-service;1");
+  if (NS_WARN_IF(!printSettingsSvc)) {
+    return true;
+  }
+
+  nsCOMPtr<nsIPrintSettings> printSettings;
+  nsresult rv =
+    printSettingsSvc->GetNewPrintSettings(getter_AddRefs(printSettings));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return true;
+  }
+
+  nsCOMPtr<nsIPrintSession>  printSession =
+    do_CreateInstance("@mozilla.org/gfx/printsession;1", &rv);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return true;
+  }
+
+  printSettings->SetPrintSession(printSession);
+  printSettingsSvc->DeserializeToPrintSettings(aPrintData, printSettings);
+  rv = webBrowserPrint->Print(printSettings, nullptr);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return true;
+  }
+
+#endif
+  return true;
+}
+
+bool
 TabChild::RecvDestroy()
 {
   MOZ_ASSERT(mDestroyed == false);
   mDestroyed = true;
 
   nsTArray<PContentPermissionRequestChild*> childArray =
       nsContentPermissionUtils::GetContentPermissionRequestChildById(GetTabId());
 
@@ -2989,17 +3013,17 @@ TabChild::InvalidateLayers()
 
   RefPtr<LayerManager> lm = mPuppetWidget->GetLayerManager();
   FrameLayerBuilder::InvalidateAllLayers(lm);
 }
 
 void
 TabChild::CompositorUpdated(const TextureFactoryIdentifier& aNewIdentifier)
 {
-  gfxPlatform::GetPlatform()->UpdateRenderModeIfDeviceReset();
+  gfxPlatform::GetPlatform()->CompositorUpdated();
 
   RefPtr<LayerManager> lm = mPuppetWidget->GetLayerManager();
   ClientLayerManager* clm = lm->AsClientLayerManager();
 
   mTextureFactoryIdentifier = aNewIdentifier;
   clm->UpdateTextureFactoryIdentifier(aNewIdentifier);
   FrameLayerBuilder::InvalidateAllLayers(clm);
 }
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -189,22 +189,22 @@ public:
                                        const mozilla::layers::FrameMetrics::ViewID& aViewId,
                                        const Maybe<mozilla::layers::ZoomConstraints>& aConstraints) = 0;
 
   virtual ScreenIntSize GetInnerSize() = 0;
 
   // Get the Document for the top-level window in this tab.
   already_AddRefed<nsIDocument> GetDocument() const;
 
+  // Get the pres-shell of the document for the top-level window in this tab.
+  already_AddRefed<nsIPresShell> GetPresShell() const;
+
 protected:
   virtual ~TabChildBase();
 
-  // Get the pres-shell of the document for the top-level window in this tab.
-  already_AddRefed<nsIPresShell> GetPresShell() const;
-
   // Wraps up a JSON object as a structured clone and sends it to the browser
   // chrome script.
   //
   // XXX/bug 780335: Do the work the browser chrome script does in C++ instead
   // so we don't need things like this.
   void DispatchMessageManagerMessage(const nsAString& aMessageName,
                                      const nsAString& aJSONData);
 
@@ -260,18 +260,16 @@ public:
    */
   static void PreloadSlowThings();
 
   /** Return a TabChild with the given attributes. */
   static already_AddRefed<TabChild>
   Create(nsIContentChild* aManager, const TabId& aTabId,
          const TabContext& aContext, uint32_t aChromeFlags);
 
-  bool IsRootContentDocument() const;
-
   // Let managees query if it is safe to send messages.
   bool IsDestroyed() const{ return mDestroyed; }
 
   const TabId GetTabId() const
   {
     MOZ_ASSERT(mUniqueId != 0);
     return mUniqueId;
   }
@@ -471,16 +469,18 @@ public:
 
   virtual PuppetWidget* WebWidget() override { return mPuppetWidget; }
 
   /** Return the DPI of the widget this TabChild draws to. */
   void GetDPI(float* aDPI);
 
   void GetDefaultScale(double *aScale);
 
+  bool IsTransparent() const { return mIsTransparent; }
+
   void GetMaxTouchPoints(uint32_t* aTouchPoints);
 
   ScreenOrientationInternal GetOrientation() const { return mOrientation; }
 
   void SetBackgroundColor(const nscolor& aColor);
 
   void NotifyPainted();
 
@@ -575,16 +575,18 @@ public:
                                                   const bool& aMuted) override;
 
   virtual bool RecvSetUseGlobalHistory(const bool& aUse) override;
 
   virtual bool RecvHandledWindowedPluginKeyEvent(
                  const mozilla::NativeEventData& aKeyEventData,
                  const bool& aIsConsumed) override;
 
+  virtual bool RecvPrint(const PrintData& aPrintData) override;
+
   /**
    * Native widget remoting protocol for use with windowed plugins with e10s.
    */
   PPluginWidgetChild* AllocPPluginWidgetChild() override;
 
   bool DeallocPPluginWidgetChild(PPluginWidgetChild* aActor) override;
 
   nsresult CreatePluginWidget(nsIWidget* aParent, nsIWidget** aOut);
@@ -746,16 +748,18 @@ private:
   // Position of tab, relative to parent widget (typically the window)
   LayoutDeviceIntPoint mChromeDisp;
   TabId mUniqueId;
 
   friend class ContentChild;
   float mDPI;
   double mDefaultScale;
 
+  bool mIsTransparent;
+
   bool mIPCOpen;
   bool mParentIsActive;
   bool mAsyncPanZoomEnabled;
   CSSSize mUnscaledInnerSize;
   bool mDidSetRealShowInfo;
   bool mDidLoadURLInit;
 
   AutoTArray<bool, NUMBER_OF_AUDIO_CHANNELS> mAudioChannelsActive;
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -3320,22 +3320,25 @@ TabParent::GetShowInfo()
   TryCacheDPIAndScale();
   if (mFrameElement) {
     nsAutoString name;
     mFrameElement->GetAttr(kNameSpaceID_None, nsGkAtoms::name, name);
     bool allowFullscreen =
       mFrameElement->HasAttr(kNameSpaceID_None, nsGkAtoms::allowfullscreen) ||
       mFrameElement->HasAttr(kNameSpaceID_None, nsGkAtoms::mozallowfullscreen);
     bool isPrivate = mFrameElement->HasAttr(kNameSpaceID_None, nsGkAtoms::mozprivatebrowsing);
+    bool isTransparent =
+      nsContentUtils::IsChromeDoc(mFrameElement->OwnerDoc()) &&
+      mFrameElement->HasAttr(kNameSpaceID_None, nsGkAtoms::transparent);
     return ShowInfo(name, allowFullscreen, isPrivate, false,
-                    mDPI, mDefaultScale.scale);
+                    isTransparent, mDPI, mDefaultScale.scale);
   }
 
   return ShowInfo(EmptyString(), false, false, false,
-                  mDPI, mDefaultScale.scale);
+                  false, mDPI, mDefaultScale.scale);
 }
 
 void
 TabParent::AudioChannelChangeNotification(nsPIDOMWindowOuter* aWindow,
                                           AudioChannel aAudioChannel,
                                           float aVolume,
                                           bool aMuted)
 {
--- a/dom/media/gmp/widevine-adapter/WidevineDecryptor.cpp
+++ b/dom/media/gmp/widevine-adapter/WidevineDecryptor.cpp
@@ -182,16 +182,17 @@ WidevineDecryptor::Decrypt(GMPBuffer* aB
   mCallback->Decrypted(aBuffer, ToGMPErr(rv));
 }
 
 void
 WidevineDecryptor::DecryptingComplete()
 {
   Log("WidevineDecryptor::DecryptingComplete() this=%p", this);
   mCDM = nullptr;
+  mCallback = nullptr;
   Release();
 }
 
 class WidevineBuffer : public cdm::Buffer {
 public:
   WidevineBuffer(size_t aSize) {
     Log("WidevineBuffer(size=" PRIuSIZE ") created", aSize);
     mBuffer.SetLength(aSize);
--- a/dom/media/mediasource/TrackBuffersManager.cpp
+++ b/dom/media/mediasource/TrackBuffersManager.cpp
@@ -1964,44 +1964,70 @@ TrackBuffersManager::SkipToNextRandomAcc
       break;
     }
     parsed++;
   }
 
   return parsed;
 }
 
+const MediaRawData*
+TrackBuffersManager::GetSample(TrackInfo::TrackType aTrack,
+                               size_t aIndex,
+                               const TimeUnit& aExpectedDts,
+                               const TimeUnit& aExpectedPts,
+                               const TimeUnit& aFuzz)
+{
+  const TrackBuffer& track = GetTrackBuffer(aTrack);
+
+  if (aIndex >= track.Length()) {
+    // reached the end.
+    return nullptr;
+  }
+
+  const RefPtr<MediaRawData>& sample = track[aIndex];
+  if (!aIndex || sample->mTimecode <= (aExpectedDts + aFuzz).ToMicroseconds() ||
+      sample->mTime <= (aExpectedPts + aFuzz).ToMicroseconds()) {
+    return sample;
+  }
+
+  // Gap is too big. End of Stream or Waiting for Data.
+  // TODO, check that we have continuous data based on the sanitized buffered
+  // range instead.
+  return nullptr;
+}
+
 already_AddRefed<MediaRawData>
 TrackBuffersManager::GetSample(TrackInfo::TrackType aTrack,
                                const TimeUnit& aFuzz,
                                bool& aError)
 {
   MOZ_ASSERT(OnTaskQueue());
   auto& trackData = GetTracksData(aTrack);
   const TrackBuffer& track = GetTrackBuffer(aTrack);
 
   aError = false;
 
-  if (!track.Length() ||
-      (trackData.mNextGetSampleIndex.isSome() &&
-       trackData.mNextGetSampleIndex.ref() >= track.Length())) {
+  if (!track.Length()) {
     return nullptr;
   }
   if (trackData.mNextGetSampleIndex.isNothing() &&
       trackData.mNextSampleTimecode == TimeUnit()) {
     // First demux, get first sample.
     trackData.mNextGetSampleIndex = Some(0u);
   }
 
   if (trackData.mNextGetSampleIndex.isSome()) {
-    const RefPtr<MediaRawData>& sample =
-      track[trackData.mNextGetSampleIndex.ref()];
-    if (trackData.mNextGetSampleIndex.ref() &&
-        sample->mTimecode > (trackData.mNextSampleTimecode + aFuzz).ToMicroseconds()) {
-      // Gap is too big. End of Stream or Waiting for Data.
+    const MediaRawData* sample =
+      GetSample(aTrack,
+                trackData.mNextGetSampleIndex.ref(),
+                trackData.mNextSampleTimecode,
+                trackData.mNextSampleTime,
+                aFuzz);
+    if (!sample) {
       return nullptr;
     }
 
     RefPtr<MediaRawData> p = sample->Clone();
     if (!p) {
       aError = true;
       return nullptr;
     }
@@ -2073,28 +2099,30 @@ TrackBuffersManager::GetNextRandomAccess
                                               const TimeUnit& aFuzz)
 {
   auto& trackData = GetTracksData(aTrack);
   MOZ_ASSERT(trackData.mNextGetSampleIndex.isSome());
   const TrackBuffersManager::TrackBuffer& track = GetTrackBuffer(aTrack);
 
   uint32_t i = trackData.mNextGetSampleIndex.ref();
   TimeUnit nextSampleTimecode = trackData.mNextSampleTimecode;
+  TimeUnit nextSampleTime = trackData.mNextSampleTime;
 
   for (; i < track.Length(); i++) {
-    const RefPtr<MediaRawData>& sample = track[i];
-    if (sample->mTimecode > (nextSampleTimecode + aFuzz).ToMicroseconds()) {
-      // Gap is too big. End of Stream or Waiting for Data.
+    const MediaRawData* sample =
+      GetSample(aTrack, i, nextSampleTimecode, nextSampleTime, aFuzz);
+    if (!sample) {
       break;
     }
     if (sample->mKeyframe) {
       return TimeUnit::FromMicroseconds(sample->mTime);
     }
     nextSampleTimecode =
       TimeUnit::FromMicroseconds(sample->mTimecode + sample->mDuration);
+    nextSampleTime = TimeUnit::FromMicroseconds(sample->GetEndTime());
   }
   return TimeUnit::FromInfinity();
 }
 
 void
 TrackBuffersManager::TrackData::AddSizeOfResources(MediaSourceDecoder::ResourceSizes* aSizes)
 {
   for (TrackBuffer& buffer : mBuffers) {
--- a/dom/media/mediasource/TrackBuffersManager.h
+++ b/dom/media/mediasource/TrackBuffersManager.h
@@ -335,16 +335,21 @@ private:
                     const media::TimeIntervals& aIntervals,
                     TrackData& aTrackData);
   void RemoveFrames(const media::TimeIntervals& aIntervals,
                     TrackData& aTrackData,
                     uint32_t aStartIndex);
   // Find index of sample. Return a negative value if not found.
   uint32_t FindSampleIndex(const TrackBuffer& aTrackBuffer,
                            const media::TimeInterval& aInterval);
+  const MediaRawData* GetSample(TrackInfo::TrackType aTrack,
+                                size_t aIndex,
+                                const media::TimeUnit& aExpectedDts,
+                                const media::TimeUnit& aExpectedPts,
+                                const media::TimeUnit& aFuzz);
   void UpdateBufferedRanges();
   void RejectProcessing(nsresult aRejectValue, const char* aName);
   void ResolveProcessing(bool aResolveValue, const char* aName);
   MozPromiseRequestHolder<CodedFrameProcessingPromise> mProcessingRequest;
   MozPromiseHolder<CodedFrameProcessingPromise> mProcessingPromise;
 
   // Trackbuffers definition.
   nsTArray<TrackData*> GetTracksList();
--- a/dom/media/systemservices/MediaSystemResourceService.cpp
+++ b/dom/media/systemservices/MediaSystemResourceService.cpp
@@ -1,16 +1,16 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "MediaSystemResourceManagerParent.h"
-#include "mozilla/layers/CompositorBridgeParent.h"
+#include "mozilla/layers/CompositorThread.h"
 #include "mozilla/unused.h"
 
 #include "MediaSystemResourceService.h"
 
 using namespace mozilla::layers;
 
 namespace mozilla {
 
@@ -41,17 +41,17 @@ MediaSystemResourceService::Shutdown()
     sSingleton->Destroy();
     sSingleton = nullptr;
   }
 }
 
 MediaSystemResourceService::MediaSystemResourceService()
   : mDestroyed(false)
 {
-  MOZ_ASSERT(CompositorBridgeParent::IsInCompositorThread());
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
 #ifdef MOZ_WIDGET_GONK
   // The maximum number of hardware resoureces available.
   // XXX need to hange to a dynamic way.
   enum
   {
     VIDEO_DECODER_COUNT = 1,
     VIDEO_ENCODER_COUNT = 1
   };
@@ -77,17 +77,17 @@ MediaSystemResourceService::Destroy()
 }
 
 void
 MediaSystemResourceService::Acquire(media::MediaSystemResourceManagerParent* aParent,
                                     uint32_t aId,
                                     MediaSystemResourceType aResourceType,
                                     bool aWillWait)
 {
-  MOZ_ASSERT(CompositorBridgeParent::IsInCompositorThread());
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   MOZ_ASSERT(aParent);
 
   if (mDestroyed) {
     return;
   }
 
   MediaSystemResource* resource = mResources.Get(static_cast<uint32_t>(aResourceType));
 
@@ -118,17 +118,17 @@ MediaSystemResourceService::Acquire(medi
     MediaSystemResourceRequest(aParent, aId));
 }
 
 void
 MediaSystemResourceService::ReleaseResource(media::MediaSystemResourceManagerParent* aParent,
                                             uint32_t aId,
                                             MediaSystemResourceType aResourceType)
 {
-  MOZ_ASSERT(CompositorBridgeParent::IsInCompositorThread());
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   MOZ_ASSERT(aParent);
 
   if (mDestroyed) {
     return;
   }
 
   MediaSystemResource* resource = mResources.Get(static_cast<uint32_t>(aResourceType));
 
--- a/embedding/components/printingui/ipc/PPrinting.ipdl
+++ b/embedding/components/printingui/ipc/PPrinting.ipdl
@@ -18,16 +18,17 @@ sync protocol PPrinting
   manager PContent;
   manages PPrintProgressDialog;
   manages PPrintSettingsDialog;
   manages PRemotePrintJob;
 
 parent:
   sync ShowProgress(PBrowser browser,
                     PPrintProgressDialog printProgressDialog,
+                    PRemotePrintJob remotePrintJob,
                     bool isForPrinting)
     returns(bool notifyOnOpen,
             nsresult rv);
 
   async ShowPrintDialog(PPrintSettingsDialog dialog,
                         PBrowser browser,
                         PrintData settings);
 
--- a/embedding/components/printingui/ipc/PrintingParent.cpp
+++ b/embedding/components/printingui/ipc/PrintingParent.cpp
@@ -1,40 +1,42 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/TabParent.h"
+#include "mozilla/unused.h"
 #include "nsIContent.h"
 #include "nsIDocument.h"
 #include "nsIDOMWindow.h"
 #include "nsIPrintingPromptService.h"
-#include "nsIPrintOptions.h"
 #include "nsIPrintProgressParams.h"
 #include "nsIPrintSettingsService.h"
 #include "nsIServiceManager.h"
+#include "nsServiceManagerUtils.h"
 #include "nsIWebProgressListener.h"
 #include "PrintingParent.h"
 #include "PrintDataUtils.h"
 #include "PrintProgressDialogParent.h"
 #include "PrintSettingsDialogParent.h"
 #include "mozilla/layout/RemotePrintJobParent.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::layout;
 
 namespace mozilla {
 namespace embedding {
 bool
 PrintingParent::RecvShowProgress(PBrowserParent* parent,
                                  PPrintProgressDialogParent* printProgressDialog,
+                                 PRemotePrintJobParent* remotePrintJob,
                                  const bool& isForPrinting,
                                  bool* notifyOnOpen,
                                  nsresult* result)
 {
   *result = NS_ERROR_FAILURE;
   *notifyOnOpen = false;
 
   nsCOMPtr<nsPIDOMWindowOuter> parentWin = DOMWindowFromBrowserParent(parent);
@@ -57,17 +59,25 @@ PrintingParent::RecvShowProgress(PBrowse
 
   *result = pps->ShowProgress(parentWin, nullptr, nullptr, observer,
                               isForPrinting,
                               getter_AddRefs(printProgressListener),
                               getter_AddRefs(printProgressParams),
                               notifyOnOpen);
   NS_ENSURE_SUCCESS(*result, true);
 
-  dialogParent->SetWebProgressListener(printProgressListener);
+  if (remotePrintJob) {
+    // If we have a RemotePrintJob use that as a more general forwarder for
+    // print progress listeners.
+    static_cast<RemotePrintJobParent*>(remotePrintJob)
+      ->RegisterListener(printProgressListener);
+  } else {
+    dialogParent->SetWebProgressListener(printProgressListener);
+  }
+
   dialogParent->SetPrintProgressParams(printProgressParams);
 
   return true;
 }
 
 nsresult
 PrintingParent::ShowPrintDialog(PBrowserParent* aParent,
                                 const PrintData& aData,
@@ -83,35 +93,32 @@ PrintingParent::ShowPrintDialog(PBrowser
     return NS_ERROR_FAILURE;
   }
 
   // The initSettings we got can be wrapped using
   // PrintDataUtils' MockWebBrowserPrint, which implements enough of
   // nsIWebBrowserPrint to keep the dialogs happy.
   nsCOMPtr<nsIWebBrowserPrint> wbp = new MockWebBrowserPrint(aData);
 
-  nsresult rv;
-  nsCOMPtr<nsIPrintOptions> po = do_GetService("@mozilla.org/gfx/printsettings-service;1", &rv);
+  nsCOMPtr<nsIPrintSettings> settings;
+  nsresult rv = mPrintSettingsSvc->GetNewPrintSettings(getter_AddRefs(settings));
   NS_ENSURE_SUCCESS(rv, rv);
 
-  nsCOMPtr<nsIPrintSettings> settings;
-  rv = po->CreatePrintSettings(getter_AddRefs(settings));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = po->DeserializeToPrintSettings(aData, settings);
+  rv = mPrintSettingsSvc->DeserializeToPrintSettings(aData, settings);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = pps->ShowPrintDialog(parentWin, wbp, settings);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  // And send it back.
-  rv = po->SerializeToPrintData(settings, nullptr, aResult);
-
-  PRemotePrintJobParent* remotePrintJob = new RemotePrintJobParent(settings);
-  aResult->remotePrintJobParent() = SendPRemotePrintJobConstructor(remotePrintJob);
+  // Serialize back to aResult. Use the existing RemotePrintJob if we have one
+  // otherwise SerializeAndEnsureRemotePrintJob() will create a new one.
+  RemotePrintJobParent* remotePrintJob =
+    static_cast<RemotePrintJobParent*>(aData.remotePrintJobParent());
+  rv = SerializeAndEnsureRemotePrintJob(settings, nullptr, remotePrintJob,
+                                        aResult);
 
   return rv;
 }
 
 bool
 PrintingParent::RecvShowPrintDialog(PPrintSettingsDialogParent* aDialog,
                                     PBrowserParent* aParent,
                                     const PrintData& aData)
@@ -132,31 +139,26 @@ PrintingParent::RecvShowPrintDialog(PPri
 }
 
 bool
 PrintingParent::RecvSavePrintSettings(const PrintData& aData,
                                       const bool& aUsePrinterNamePrefix,
                                       const uint32_t& aFlags,
                                       nsresult* aResult)
 {
-  nsCOMPtr<nsIPrintSettingsService> pss =
-    do_GetService("@mozilla.org/gfx/printsettings-service;1", aResult);
-  NS_ENSURE_SUCCESS(*aResult, true);
-
-  nsCOMPtr<nsIPrintOptions> po = do_QueryInterface(pss, aResult);
+  nsCOMPtr<nsIPrintSettings> settings;
+  *aResult = mPrintSettingsSvc->GetNewPrintSettings(getter_AddRefs(settings));
   NS_ENSURE_SUCCESS(*aResult, true);
 
-  nsCOMPtr<nsIPrintSettings> settings;
-  *aResult = po->CreatePrintSettings(getter_AddRefs(settings));
+  *aResult = mPrintSettingsSvc->DeserializeToPrintSettings(aData, settings);
   NS_ENSURE_SUCCESS(*aResult, true);
 
-  *aResult = po->DeserializeToPrintSettings(aData, settings);
-  NS_ENSURE_SUCCESS(*aResult, true);
-
-  *aResult = pss->SavePrintSettingsToPrefs(settings, aUsePrinterNamePrefix, aFlags);
+  *aResult = mPrintSettingsSvc->SavePrintSettingsToPrefs(settings,
+                                                        aUsePrinterNamePrefix,
+                                                        aFlags);
 
   return true;
 }
 
 PPrintProgressDialogParent*
 PrintingParent::AllocPPrintProgressDialogParent()
 {
   PrintProgressDialogParent* actor = new PrintProgressDialogParent();
@@ -233,21 +235,65 @@ PrintingParent::DOMWindowFromBrowserPare
   nsCOMPtr<nsPIDOMWindowOuter> parentWin = frame->OwnerDoc()->GetWindow();
   if (!parentWin) {
     return nullptr;
   }
 
   return parentWin;
 }
 
+nsresult
+PrintingParent::SerializeAndEnsureRemotePrintJob(
+  nsIPrintSettings* aPrintSettings, nsIWebProgressListener* aListener,
+  layout::RemotePrintJobParent* aRemotePrintJob, PrintData* aPrintData)
+{
+  MOZ_ASSERT(aPrintData);
+
+  nsresult rv;
+  nsCOMPtr<nsIPrintSettings> printSettings;
+  if (aPrintSettings) {
+    printSettings = aPrintSettings;
+  } else {
+    rv = mPrintSettingsSvc->GetNewPrintSettings(getter_AddRefs(printSettings));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+  }
+
+  rv = mPrintSettingsSvc->SerializeToPrintData(aPrintSettings, nullptr,
+                                               aPrintData);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  RemotePrintJobParent* remotePrintJob;
+  if (aRemotePrintJob) {
+    remotePrintJob = aRemotePrintJob;
+    aPrintData->remotePrintJobParent() = remotePrintJob;
+  } else {
+    remotePrintJob = new RemotePrintJobParent(aPrintSettings);
+    aPrintData->remotePrintJobParent() =
+      SendPRemotePrintJobConstructor(remotePrintJob);
+  }
+  if (aListener) {
+    remotePrintJob->RegisterListener(aListener);
+  }
+
+  return NS_OK;
+}
+
 PrintingParent::PrintingParent()
 {
-    MOZ_COUNT_CTOR(PrintingParent);
+  MOZ_COUNT_CTOR(PrintingParent);
+
+  mPrintSettingsSvc =
+    do_GetService("@mozilla.org/gfx/printsettings-service;1");
+  MOZ_ASSERT(mPrintSettingsSvc);
 }
 
 PrintingParent::~PrintingParent()
 {
-    MOZ_COUNT_DTOR(PrintingParent);
+  MOZ_COUNT_DTOR(PrintingParent);
 }
 
 } // namespace embedding
 } // namespace mozilla
 
--- a/embedding/components/printingui/ipc/PrintingParent.h
+++ b/embedding/components/printingui/ipc/PrintingParent.h
@@ -5,33 +5,39 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_embedding_PrintingParent_h
 #define mozilla_embedding_PrintingParent_h
 
 #include "mozilla/dom/PBrowserParent.h"
 #include "mozilla/embedding/PPrintingParent.h"
 
+class nsIPrintSettingsService;
+class nsIWebProgressListener;
 class nsPIDOMWindowOuter;
 class PPrintProgressDialogParent;
 class PPrintSettingsDialogParent;
 
 namespace mozilla {
 namespace layout {
 class PRemotePrintJobParent;
+class RemotePrintJobParent;
 }
 
 namespace embedding {
 
 class PrintingParent final : public PPrintingParent
 {
 public:
+    NS_INLINE_DECL_REFCOUNTING(PrintingParent)
+
     virtual bool
     RecvShowProgress(PBrowserParent* parent,
                      PPrintProgressDialogParent* printProgressDialog,
+                     PRemotePrintJobParent* remotePrintJob,
                      const bool& isForPrinting,
                      bool* notifyOnOpen,
                      nsresult* result);
     virtual bool
     RecvShowPrintDialog(PPrintSettingsDialogParent* aDialog,
                         PBrowserParent* aParent,
                         const PrintData& aData);
 
@@ -58,24 +64,46 @@ public:
 
     virtual bool
     DeallocPRemotePrintJobParent(PRemotePrintJobParent* aActor);
 
     virtual void
     ActorDestroy(ActorDestroyReason aWhy);
 
     MOZ_IMPLICIT PrintingParent();
+
+    /**
+     * Serialize nsIPrintSettings to PrintData ready for sending to a child
+     * process. A RemotePrintJob will be created and added to the PrintData.
+     * An optional progress listener can be given, which will be registered
+     * with the RemotePrintJob, so that progress can be tracked in the parent.
+     *
+     * @param aPrintSettings optional print settings to serialize, otherwise a
+     *                       default print settings will be used.
+     * @param aProgressListener optional print progress listener.
+     * @param aRemotePrintJob optional remote print job, so that an existing
+     *                        one can be used.
+     * @param aPrintData PrintData to populate.
+     */
+    nsresult
+    SerializeAndEnsureRemotePrintJob(nsIPrintSettings* aPrintSettings,
+                                     nsIWebProgressListener* aListener,
+                                     layout::RemotePrintJobParent* aRemotePrintJob,
+                                     PrintData* aPrintData);
+
+private:
     virtual ~PrintingParent();
 
-private:
     nsPIDOMWindowOuter*
     DOMWindowFromBrowserParent(PBrowserParent* parent);
 
     nsresult
     ShowPrintDialog(PBrowserParent* parent,
                     const PrintData& data,
                     PrintData* result);
+
+    nsCOMPtr<nsIPrintSettingsService> mPrintSettingsSvc;
 };
 
 } // namespace embedding
 } // namespace mozilla
 
 #endif
--- a/embedding/components/printingui/ipc/nsPrintingProxy.cpp
+++ b/embedding/components/printingui/ipc/nsPrintingProxy.cpp
@@ -9,20 +9,23 @@
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/TabChild.h"
 #include "mozilla/layout/RemotePrintJobChild.h"
 #include "mozilla/unused.h"
 #include "nsIDocShell.h"
 #include "nsIDocShellTreeOwner.h"
 #include "nsIPrintingPromptService.h"
+#include "nsIPrintSession.h"
 #include "nsPIDOMWindow.h"
 #include "nsPrintOptionsImpl.h"
+#include "nsServiceManagerUtils.h"
 #include "PrintDataUtils.h"
 #include "PrintProgressDialogChild.h"
+#include "PrintSettingsDialogChild.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::embedding;
 using namespace mozilla::layout;
 
 static StaticRefPtr<nsPrintingProxy> sPrintingProxyInstance;
 
@@ -82,22 +85,23 @@ nsPrintingProxy::ShowPrintDialog(mozIDOM
 
   nsCOMPtr<nsITabChild> tabchild = docShell->GetTabChild();
   NS_ENSURE_STATE(tabchild);
 
   TabChild* pBrowser = static_cast<TabChild*>(tabchild.get());
 
   // Next, serialize the nsIWebBrowserPrint and nsIPrintSettings we were given.
   nsresult rv = NS_OK;
-  nsCOMPtr<nsIPrintOptions> po =
+  nsCOMPtr<nsIPrintSettingsService> printSettingsSvc =
     do_GetService("@mozilla.org/gfx/printsettings-service;1", &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   PrintData inSettings;
-  rv = po->SerializeToPrintData(printSettings, webBrowserPrint, &inSettings);
+  rv = printSettingsSvc->SerializeToPrintData(printSettings, webBrowserPrint,
+                                              &inSettings);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Now, the waiting game. The parent process should be showing
   // the printing dialog soon. In the meantime, we need to spin a
   // nested event loop while we wait for the results of the dialog
   // to be returned to us.
 
   RefPtr<PrintSettingsDialogChild> dialog = new PrintSettingsDialogChild();
@@ -107,17 +111,18 @@ nsPrintingProxy::ShowPrintDialog(mozIDOM
 
   while(!dialog->returned()) {
     NS_ProcessNextEvent(nullptr, true);
   }
 
   rv = dialog->result();
   NS_ENSURE_SUCCESS(rv, rv);
 
-  rv = po->DeserializeToPrintSettings(dialog->data(), printSettings);
+  rv = printSettingsSvc->DeserializeToPrintSettings(dialog->data(),
+                                                    printSettings);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsPrintingProxy::ShowProgress(mozIDOMWindowProxy*      parent,
                               nsIWebBrowserPrint*      webBrowserPrint,    // ok to be null
                               nsIPrintSettings*        printSettings,      // ok to be null
                               nsIObserver*             openDialogObserver, // ok to be null
@@ -140,24 +145,39 @@ nsPrintingProxy::ShowProgress(mozIDOMWin
   nsCOMPtr<nsITabChild> tabchild = docShell->GetTabChild();
   TabChild* pBrowser = static_cast<TabChild*>(tabchild.get());
 
   RefPtr<PrintProgressDialogChild> dialogChild =
     new PrintProgressDialogChild(openDialogObserver);
 
   SendPPrintProgressDialogConstructor(dialogChild);
 
+  // Get the RemotePrintJob if we have one available.
+  RefPtr<mozilla::layout::RemotePrintJobChild> remotePrintJob;
+  if (printSettings) {
+    nsCOMPtr<nsIPrintSession> printSession;
+    nsresult rv = printSettings->GetPrintSession(getter_AddRefs(printSession));
+    if (NS_SUCCEEDED(rv) && printSession) {
+      printSession->GetRemotePrintJob(getter_AddRefs(remotePrintJob));
+    }
+  }
+
   nsresult rv = NS_OK;
-  mozilla::Unused << SendShowProgress(pBrowser, dialogChild,
+  mozilla::Unused << SendShowProgress(pBrowser, dialogChild, remotePrintJob,
                                       isForPrinting, notifyOnOpen, &rv);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
-  NS_ADDREF(*webProgressListener = dialogChild);
+  // If we have a RemotePrintJob that will be being used as a more general
+  // forwarder for print progress listeners. Once we always have one we can
+  // remove the interface from PrintProgressDialogChild.
+  if (!remotePrintJob) {
+    NS_ADDREF(*webProgressListener = dialogChild);
+  }
   NS_ADDREF(*printProgressParams = dialogChild);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsPrintingProxy::ShowPageSetup(mozIDOMWindowProxy *parent,
                                nsIPrintSettings *printSettings,
@@ -175,22 +195,22 @@ nsPrintingProxy::ShowPrinterProperties(m
 }
 
 nsresult
 nsPrintingProxy::SavePrintSettings(nsIPrintSettings* aPS,
                                    bool aUsePrinterNamePrefix,
                                    uint32_t aFlags)
 {
   nsresult rv;
-  nsCOMPtr<nsIPrintOptions> po =
+  nsCOMPtr<nsIPrintSettingsService> printSettingsSvc =
     do_GetService("@mozilla.org/gfx/printsettings-service;1", &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   PrintData settings;
-  rv = po->SerializeToPrintData(aPS, nullptr, &settings);
+  rv = printSettingsSvc->SerializeToPrintData(aPS, nullptr, &settings);
   NS_ENSURE_SUCCESS(rv, rv);
 
   Unused << SendSavePrintSettings(settings, aUsePrinterNamePrefix, aFlags,
                                   &rv);
   return rv;
 }
 
 PPrintProgressDialogChild*
--- a/gfx/layers/Compositor.cpp
+++ b/gfx/layers/Compositor.cpp
@@ -2,16 +2,17 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/layers/Compositor.h"
 #include "base/message_loop.h"          // for MessageLoop
 #include "mozilla/layers/CompositorBridgeParent.h"  // for CompositorBridgeParent
 #include "mozilla/layers/Effects.h"     // for Effect, EffectChain, etc
+#include "mozilla/layers/CompositorThread.h"
 #include "mozilla/mozalloc.h"           // for operator delete, etc
 #include "gfx2DGlue.h"
 #include "nsAppRunner.h"
 
 #if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
 #include "libdisplay/GonkDisplay.h"     // for GonkDisplay
 #include <ui/Fence.h>
 #include "nsWindow.h"
@@ -20,18 +21,18 @@
 
 namespace mozilla {
 
 namespace layers {
 
 /* static */ void
 Compositor::AssertOnCompositorThread()
 {
-  MOZ_ASSERT(!CompositorBridgeParent::CompositorLoop() ||
-             CompositorBridgeParent::CompositorLoop() == MessageLoop::current(),
+  MOZ_ASSERT(!CompositorThreadHolder::Loop() ||
+             CompositorThreadHolder::Loop() == MessageLoop::current(),
              "Can only call this from the compositor thread!");
 }
 
 bool
 Compositor::ShouldDrawDiagnostics(DiagnosticFlags aFlags)
 {
   if ((aFlags & DiagnosticFlags::TILE) && !(mDiagnosticTypes & DiagnosticTypes::TILE_BORDERS)) {
     return false;
--- a/gfx/layers/Compositor.h
+++ b/gfx/layers/Compositor.h
@@ -190,16 +190,18 @@ protected:
 public:
   NS_INLINE_DECL_REFCOUNTING(Compositor)
 
   explicit Compositor(widget::CompositorWidgetProxy* aWidget,
                       CompositorBridgeParent* aParent = nullptr)
     : mCompositorID(0)
     , mDiagnosticTypes(DiagnosticTypes::NO_DIAGNOSTIC)
     , mParent(aParent)
+    , mPixelsPerFrame(0)
+    , mPixelsFilled(0)
     , mScreenRotation(ROTATION_0)
     , mWidget(aWidget)
   {
   }
 
   virtual already_AddRefed<DataTextureSource> CreateDataTextureSource(TextureFlags aFlags = TextureFlags::NO_FLAGS) = 0;
 
   virtual already_AddRefed<DataTextureSource>
--- a/gfx/layers/LayerScope.cpp
+++ b/gfx/layers/LayerScope.cpp
@@ -12,17 +12,17 @@
 #include "Effects.h"
 #include "mozilla/Endian.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/TimeStamp.h"
 
 #include "TexturePoolOGL.h"
 #include "mozilla/layers/CompositorOGL.h"
-#include "mozilla/layers/CompositorBridgeParent.h"
+#include "mozilla/layers/CompositorThread.h"
 #include "mozilla/layers/LayerManagerComposite.h"
 #include "mozilla/layers/TextureHostOGL.h"
 
 #include "gfxContext.h"
 #include "gfxUtils.h"
 #include "gfxPrefs.h"
 #include "nsIWidget.h"
 
@@ -1894,17 +1894,17 @@ LayerScope::SendLayerDump(UniquePtr<Pack
         new DebugGLLayersData(Move(aPacket)));
 }
 
 /*static*/
 bool
 LayerScope::CheckSendable()
 {
     // Only compositor threads check LayerScope status
-    MOZ_ASSERT(CompositorBridgeParent::IsInCompositorThread() || gIsGtest);
+    MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread() || gIsGtest);
 
     if (!gfxPrefs::LayerScopeEnabled()) {
         return false;
     }
     if (!gLayerScopeManager.GetSocketManager()) {
         Init();
         return false;
     }
--- a/gfx/layers/client/CanvasClient.cpp
+++ b/gfx/layers/client/CanvasClient.cpp
@@ -499,16 +499,24 @@ CanvasClientSharedSurface::Updated()
   //            implementation of the WebVR 1.0 API, which will enable
   //            the inputFrameID to be passed through Javascript with
   //            the new VRDisplay API.
   t->mInputFrameID = VRManagerChild::Get()->GetInputFrameID();
   forwarder->UseTextures(this, textures);
 }
 
 void
+CanvasClientSharedSurface::OnDetach() {
+  if (mShSurfClient) {
+    mShSurfClient->CancelWaitForCompositorRecycle();
+  }
+  ClearSurfaces();
+}
+
+void
 CanvasClientSharedSurface::ClearSurfaces()
 {
   mFront = nullptr;
   mNewFront = nullptr;
   mShSurfClient = nullptr;
   mReadbackClient = nullptr;
 }
 
--- a/gfx/layers/client/CanvasClient.h
+++ b/gfx/layers/client/CanvasClient.h
@@ -155,19 +155,17 @@ public:
   virtual void Update(gfx::IntSize aSize,
                       ClientCanvasLayer* aLayer) override;
   void UpdateRenderer(gfx::IntSize aSize, Renderer& aRenderer);
 
   virtual void UpdateAsync(AsyncCanvasRenderer* aRenderer) override;
 
   virtual void Updated() override;
 
-  virtual void OnDetach() override {
-    ClearSurfaces();
-  }
+  virtual void OnDetach() override;
 };
 
 /**
  * Used for OMT<canvas> uploads using the image bridge protocol.
  * Actual CanvasClient is on the ImageBridgeChild thread, so we
  * only forward its AsyncID here
  */
 class CanvasClientBridge final : public CanvasClient
--- a/gfx/layers/client/ClientLayerManager.cpp
+++ b/gfx/layers/client/ClientLayerManager.cpp
@@ -200,16 +200,17 @@ ClientLayerManager::BeginTransactionWith
   Log();
 #endif
 
   NS_ASSERTION(!InTransaction(), "Nested transactions not allowed");
   mPhase = PHASE_CONSTRUCTION;
 
   if (DependsOnStaleDevice()) {
     FrameLayerBuilder::InvalidateAllLayers(this);
+    mDeviceCounter = gfxPlatform::GetPlatform()->GetDeviceCounter();
   }
 
   MOZ_ASSERT(mKeepAlive.IsEmpty(), "uncommitted txn?");
 
   // If the last transaction was incomplete (a failed DoEmptyTransaction),
   // don't signal a new transaction to ShadowLayerForwarder. Carry on adding
   // to the previous transaction.
   dom::ScreenOrientationInternal orientation;
--- a/gfx/layers/composite/AsyncCompositionManager.cpp
+++ b/gfx/layers/composite/AsyncCompositionManager.cpp
@@ -18,16 +18,17 @@
 #include "mozilla/dom/AnimationEffectReadOnlyBinding.h" // for dom::FillMode
 #include "mozilla/gfx/BaseRect.h"       // for BaseRect
 #include "mozilla/gfx/Point.h"          // for RoundedToInt, PointTyped
 #include "mozilla/gfx/Rect.h"           // for RoundedToInt, RectTyped
 #include "mozilla/gfx/ScaleFactor.h"    // for ScaleFactor
 #include "mozilla/layers/APZUtils.h"    // for CompleteAsyncTransform
 #include "mozilla/layers/Compositor.h"  // for Compositor
 #include "mozilla/layers/CompositorBridgeParent.h" // for CompositorBridgeParent, etc
+#include "mozilla/layers/CompositorThread.h"
 #include "mozilla/layers/LayerAnimationUtils.h" // for TimingFunctionToComputedTimingFunction
 #include "mozilla/layers/LayerMetricsWrapper.h" // for LayerMetricsWrapper
 #include "nsCoord.h"                    // for NSAppUnitsToFloatPixels, etc
 #include "nsDebug.h"                    // for NS_ASSERTION, etc
 #include "nsDeviceContext.h"            // for nsDeviceContext
 #include "nsDisplayList.h"              // for nsDisplayTransform, etc
 #include "nsMathUtils.h"                // for NS_round
 #include "nsPoint.h"                    // for nsPoint
@@ -701,17 +702,17 @@ SampleAPZAnimations(const LayerMetricsWr
 
   return activeAnimations;
 }
 
 void
 AsyncCompositionManager::RecordShadowTransforms(Layer* aLayer)
 {
   MOZ_ASSERT(gfxPrefs::CollectScrollTransforms());
-  MOZ_ASSERT(CompositorBridgeParent::IsInCompositorThread());
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
 
   ForEachNodePostOrder<ForwardIterator>(
       aLayer,
       [this] (Layer* layer)
       {
         for (uint32_t i = 0; i < layer->GetScrollMetadataCount(); i++) {
           AsyncPanZoomController* apzc = layer->GetAsyncPanZoomController(i);
           if (!apzc) {
@@ -1434,17 +1435,17 @@ AsyncCompositionManager::TransformScroll
                             fixedLayerMargins, nullptr);
 
   ExpandRootClipRect(aLayer, fixedLayerMargins);
 }
 
 void
 AsyncCompositionManager::GetFrameUniformity(FrameUniformityData* aOutData)
 {
-  MOZ_ASSERT(CompositorBridgeParent::IsInCompositorThread());
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   mLayerTransformRecorder.EndTest(aOutData);
 }
 
 bool
 AsyncCompositionManager::TransformShadowTree(TimeStamp aCurrentFrame,
                                              TransformsToSkip aSkip)
 {
   PROFILER_LABEL("AsyncCompositionManager", "TransformShadowTree",
--- a/gfx/layers/ipc/CompositorBridgeChild.cpp
+++ b/gfx/layers/ipc/CompositorBridgeChild.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set sw=2 ts=2 et tw=80 : */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/layers/CompositorBridgeChild.h"
 #include "mozilla/layers/CompositorBridgeParent.h"
+#include "mozilla/layers/CompositorThread.h"
 #include <stddef.h>                     // for size_t
 #include "ClientLayerManager.h"         // for ClientLayerManager
 #include "base/message_loop.h"          // for MessageLoop
 #include "base/task.h"                  // for NewRunnableMethod, etc
 #include "mozilla/layers/LayerTransactionChild.h"
 #include "mozilla/layers/PLayerTransactionChild.h"
 #include "mozilla/mozalloc.h"           // for operator new, etc
 #include "nsDebug.h"                    // for NS_RUNTIMEABORT
@@ -175,17 +176,17 @@ CompositorBridgeChild::Create(Transport*
 
 bool
 CompositorBridgeChild::OpenSameProcess(CompositorBridgeParent* aParent)
 {
   MOZ_ASSERT(aParent);
 
   mCompositorBridgeParent = aParent;
   mCanSend = Open(mCompositorBridgeParent->GetIPCChannel(),
-                  CompositorBridgeParent::CompositorLoop(),
+                  CompositorThreadHolder::Loop(),
                   ipc::ChildSide);
   return mCanSend;
 }
 
 /*static*/ CompositorBridgeChild*
 CompositorBridgeChild::Get()
 {
   // This is only expected to be used in child processes.
--- a/gfx/layers/ipc/CompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CompositorBridgeParent.cpp
@@ -33,16 +33,17 @@
 #include "mozilla/ipc/Transport.h"      // for Transport
 #include "mozilla/layers/APZCTreeManager.h"  // for APZCTreeManager
 #include "mozilla/layers/APZThreadUtils.h"  // for APZCTreeManager
 #include "mozilla/layers/AsyncCompositionManager.h"
 #include "mozilla/layers/BasicCompositor.h"  // for BasicCompositor
 #include "mozilla/layers/Compositor.h"  // for Compositor
 #include "mozilla/layers/CompositorLRU.h"  // for CompositorLRU
 #include "mozilla/layers/CompositorOGL.h"  // for CompositorOGL
+#include "mozilla/layers/CompositorThread.h"
 #include "mozilla/layers/CompositorTypes.h"
 #include "mozilla/layers/FrameUniformityData.h"
 #include "mozilla/layers/ImageBridgeParent.h"
 #include "mozilla/layers/LayerManagerComposite.h"
 #include "mozilla/layers/LayersTypes.h"
 #include "mozilla/layers/PLayerTransactionParent.h"
 #include "mozilla/layers/RemoteContentController.h"
 #include "mozilla/layers/ShadowLayersManager.h" // for ShadowLayersManager
@@ -85,21 +86,16 @@
 #ifdef MOZ_ANDROID_APZ
 #include "AndroidBridge.h"
 #endif
 
 #include "LayerScope.h"
 
 namespace mozilla {
 
-namespace gfx {
-// See VRManagerChild.cpp
-void ReleaseVRManagerParentSingleton();
-} // namespace gfx
-
 namespace layers {
 
 using namespace mozilla::ipc;
 using namespace mozilla::gfx;
 using namespace std;
 
 using base::ProcessId;
 using base::Thread;
@@ -150,109 +146,40 @@ CompositorBridgeParent::ForEachIndirectL
 /**
   * A global map referencing each compositor by ID.
   *
   * This map is used by the ImageBridge protocol to trigger
   * compositions without having to keep references to the
   * compositor
   */
 typedef map<uint64_t,CompositorBridgeParent*> CompositorMap;
-static CompositorMap* sCompositorMap;
-
-static void CreateCompositorMap()
+static StaticAutoPtr<CompositorMap> sCompositorMap;
+
+void
+CompositorBridgeParent::Initialize()
 {
+  EnsureLayerTreeMapReady();
+
   MOZ_ASSERT(!sCompositorMap);
   sCompositorMap = new CompositorMap;
 }
 
-static void DestroyCompositorMap()
+void
+CompositorBridgeParent::Shutdown()
 {
   MOZ_ASSERT(sCompositorMap);
   MOZ_ASSERT(sCompositorMap->empty());
-  delete sCompositorMap;
   sCompositorMap = nullptr;
 }
 
-// See ImageBridgeChild.cpp
-void ReleaseImageBridgeParentSingleton();
-
-CompositorThreadHolder::CompositorThreadHolder()
-  : mCompositorThread(CreateCompositorThread())
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_COUNT_CTOR(CompositorThreadHolder);
-}
-
-CompositorThreadHolder::~CompositorThreadHolder()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  MOZ_COUNT_DTOR(CompositorThreadHolder);
-
-  DestroyCompositorThread(mCompositorThread);
-}
-
-static StaticRefPtr<CompositorThreadHolder> sCompositorThreadHolder;
-static bool sFinishedCompositorShutDown = false;
-
-CompositorThreadHolder* GetCompositorThreadHolder()
-{
-  return sCompositorThreadHolder;
-}
-
-/* static */ Thread*
-CompositorThreadHolder::CreateCompositorThread()
+void
+CompositorBridgeParent::FinishShutdown()
 {
-  MOZ_ASSERT(NS_IsMainThread());
-
-  MOZ_ASSERT(!sCompositorThreadHolder, "The compositor thread has already been started!");
-
-  Thread* compositorThread = new Thread("Compositor");
-
-  Thread::Options options;
-  /* Timeout values are powers-of-two to enable us get better data.
-     128ms is chosen for transient hangs because 8Hz should be the minimally
-     acceptable goal for Compositor responsiveness (normal goal is 60Hz). */
-  options.transient_hang_timeout = 128; // milliseconds
-  /* 2048ms is chosen for permanent hangs because it's longer than most
-   * Compositor hangs seen in the wild, but is short enough to not miss getting
-   * native hang stacks. */
-  options.permanent_hang_timeout = 2048; // milliseconds
-#if defined(_WIN32)
-  /* With d3d9 the compositor thread creates native ui, see DeviceManagerD3D9. As
-   * such the thread is a gui thread, and must process a windows message queue or
-   * risk deadlocks. Chromium message loop TYPE_UI does exactly what we need. */
-  options.message_loop_type = MessageLoop::TYPE_UI;
-#endif
-
-  if (!compositorThread->StartWithOptions(options)) {
-    delete compositorThread;
-    return nullptr;
-  }
-
-  EnsureLayerTreeMapReady();
-  CreateCompositorMap();
-
-  return compositorThread;
-}
-
-/* static */ void
-CompositorThreadHolder::DestroyCompositorThread(Thread* aCompositorThread)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  MOZ_ASSERT(!sCompositorThreadHolder, "We shouldn't be destroying the compositor thread yet.");
-
-  DestroyCompositorMap();
-  delete aCompositorThread;
-  sFinishedCompositorShutDown = true;
-}
-
-static Thread* CompositorThread() {
-  return sCompositorThreadHolder ? sCompositorThreadHolder->GetCompositorThread() : nullptr;
+  // TODO: this should be empty by now...
+  sIndirectLayerTrees.clear();
 }
 
 static void SetThreadPriority()
 {
   hal::SetCurrentThreadPriority(hal::THREAD_PRIORITY_COMPOSITOR);
 }
 
 #ifdef COMPOSITOR_PERFORMANCE_WARNING
@@ -353,17 +280,17 @@ CompositorVsyncScheduler::~CompositorVsy
 
 #ifdef MOZ_WIDGET_GONK
 #if ANDROID_VERSION >= 19
 void
 CompositorVsyncScheduler::SetDisplay(bool aDisplayEnable)
 {
   // SetDisplay() is usually called from nsScreenManager at main thread. Post
   // to compositor thread if needs.
-  if (!CompositorBridgeParent::IsInCompositorThread()) {
+  if (!CompositorThreadHolder::IsInCompositorThread()) {
     MOZ_ASSERT(NS_IsMainThread());
     MonitorAutoLock lock(mSetDisplayMonitor);
     RefPtr<CancelableRunnable> task =
       NewCancelableRunnableMethod<bool>(this, &CompositorVsyncScheduler::SetDisplay, aDisplayEnable);
     mSetDisplayTask = task;
     ScheduleTask(task.forget(), 0);
     return;
   } else {
@@ -380,17 +307,17 @@ CompositorVsyncScheduler::SetDisplay(boo
     CancelCurrentSetNeedsCompositeTask();
     CancelCurrentCompositeTask();
   }
 }
 
 void
 CompositorVsyncScheduler::CancelSetDisplayTask()
 {
-  MOZ_ASSERT(CompositorBridgeParent::IsInCompositorThread());
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   MonitorAutoLock lock(mSetDisplayMonitor);
   if (mSetDisplayTask) {
     mSetDisplayTask->Cancel();
     mSetDisplayTask = nullptr;
   }
 
   // CancelSetDisplayTask is only be called in clean-up process, so
   // mDisplayEnabled could be false there.
@@ -401,17 +328,17 @@ CompositorVsyncScheduler::CancelSetDispl
 
 void
 CompositorVsyncScheduler::Destroy()
 {
   if (!mVsyncObserver) {
     // Destroy was already called on this object.
     return;
   }
-  MOZ_ASSERT(CompositorBridgeParent::IsInCompositorThread());
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   UnobserveVsync();
   mVsyncObserver->Destroy();
   mVsyncObserver = nullptr;
 
 #ifdef MOZ_WIDGET_GONK
 #if ANDROID_VERSION >= 19
   CancelSetDisplayTask();
 #endif
@@ -432,17 +359,17 @@ CompositorVsyncScheduler::PostCompositeT
     mCurrentCompositeTask = task;
     ScheduleTask(task.forget(), 0);
   }
 }
 
 void
 CompositorVsyncScheduler::ScheduleComposition()
 {
-  MOZ_ASSERT(CompositorBridgeParent::IsInCompositorThread());
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   if (mAsapScheduling) {
     // Used only for performance testing purposes
     PostCompositeTask(TimeStamp::Now());
 #ifdef MOZ_WIDGET_ANDROID
   } else if (mNeedsComposite >= 2 && mIsObservingVsync) {
     // uh-oh, we already requested a composite at least twice so far, and a
     // composite hasn't happened yet. It is possible that the vsync observation
     // is blocked on the main thread, so let's just composite ASAP and not
@@ -454,17 +381,17 @@ CompositorVsyncScheduler::ScheduleCompos
   } else {
     SetNeedsComposite();
   }
 }
 
 void
 CompositorVsyncScheduler::CancelCurrentSetNeedsCompositeTask()
 {
-  MOZ_ASSERT(CompositorBridgeParent::IsInCompositorThread());
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   MonitorAutoLock lock(mSetNeedsCompositeMonitor);
   if (mSetNeedsCompositeTask) {
     mSetNeedsCompositeTask->Cancel();
     mSetNeedsCompositeTask = nullptr;
   }
   mNeedsComposite = 0;
 }
 
@@ -473,17 +400,17 @@ CompositorVsyncScheduler::CancelCurrentS
  * If a composite takes 17 ms, do we composite ASAP or wait until next vsync?
  * If a layer transaction comes after vsync, do we composite ASAP or wait until
  * next vsync?
  * How many skipped vsync events until we stop listening to vsync events?
  */
 void
 CompositorVsyncScheduler::SetNeedsComposite()
 {
-  if (!CompositorBridgeParent::IsInCompositorThread()) {
+  if (!CompositorThreadHolder::IsInCompositorThread()) {
     MonitorAutoLock lock(mSetNeedsCompositeMonitor);
     RefPtr<CancelableRunnable> task =
       NewCancelableRunnableMethod(this, &CompositorVsyncScheduler::SetNeedsComposite);
     mSetNeedsCompositeTask = task;
     ScheduleTask(task.forget(), 0);
     return;
   } else {
     MonitorAutoLock lock(mSetNeedsCompositeMonitor);
@@ -504,37 +431,37 @@ CompositorVsyncScheduler::SetNeedsCompos
     ObserveVsync();
   }
 }
 
 bool
 CompositorVsyncScheduler::NotifyVsync(TimeStamp aVsyncTimestamp)
 {
   // Called from the vsync dispatch thread
-  MOZ_ASSERT(!CompositorBridgeParent::IsInCompositorThread());
+  MOZ_ASSERT(!CompositorThreadHolder::IsInCompositorThread());
   MOZ_ASSERT(!NS_IsMainThread());
   PostCompositeTask(aVsyncTimestamp);
   return true;
 }
 
 void
 CompositorVsyncScheduler::CancelCurrentCompositeTask()
 {
-  MOZ_ASSERT(CompositorBridgeParent::IsInCompositorThread() || NS_IsMainThread());
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread() || NS_IsMainThread());
   MonitorAutoLock lock(mCurrentCompositeTaskMonitor);
   if (mCurrentCompositeTask) {
     mCurrentCompositeTask->Cancel();
     mCurrentCompositeTask = nullptr;
   }
 }
 
 void
 CompositorVsyncScheduler::Composite(TimeStamp aVsyncTimestamp)
 {
-  MOZ_ASSERT(CompositorBridgeParent::IsInCompositorThread());
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   {
     MonitorAutoLock lock(mCurrentCompositeTaskMonitor);
     mCurrentCompositeTask = nullptr;
   }
 
   if ((aVsyncTimestamp < mLastCompose) && !mAsapScheduling) {
     // We can sometimes get vsync timestamps that are in the past
     // compared to the last compose with force composites.
@@ -567,128 +494,100 @@ CompositorVsyncScheduler::OnForceCompose
    * where we receive many sync RecvFlushComposites. We also get vsync notifications which
    * will increment mVsyncNotificationsSkipped because a composite just occurred. After
    * enough vsyncs and RecvFlushComposites occurred, we will disable vsync. Then at the next
    * ScheduleComposite, we will enable vsync, then get a RecvFlushComposite, which will
    * force us to unobserve vsync again. On some platforms, enabling/disabling vsync is not
    * free and this oscillating behavior causes a performance hit. In order to avoid this problem,
    * we reset the mVsyncNotificationsSkipped counter to keep vsync enabled.
    */
-  MOZ_ASSERT(CompositorBridgeParent::IsInCompositorThread());
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   mVsyncNotificationsSkipped = 0;
 }
 
 void
 CompositorVsyncScheduler::ForceComposeToTarget(gfx::DrawTarget* aTarget, const IntRect* aRect)
 {
-  MOZ_ASSERT(CompositorBridgeParent::IsInCompositorThread());
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   OnForceComposeToTarget();
   mLastCompose = TimeStamp::Now();
   ComposeToTarget(aTarget, aRect);
 }
 
 bool
 CompositorVsyncScheduler::NeedsComposite()
 {
-  MOZ_ASSERT(CompositorBridgeParent::IsInCompositorThread());
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   return mNeedsComposite;
 }
 
 void
 CompositorVsyncScheduler::ObserveVsync()
 {
-  MOZ_ASSERT(CompositorBridgeParent::IsInCompositorThread());
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   mCompositorVsyncDispatcher->SetCompositorVsyncObserver(mVsyncObserver);
   mIsObservingVsync = true;
 }
 
 void
 CompositorVsyncScheduler::UnobserveVsync()
 {
-  MOZ_ASSERT(CompositorBridgeParent::IsInCompositorThread());
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   mCompositorVsyncDispatcher->SetCompositorVsyncObserver(nullptr);
   mIsObservingVsync = false;
 }
 
 void
 CompositorVsyncScheduler::DispatchTouchEvents(TimeStamp aVsyncTimestamp)
 {
 #ifdef MOZ_WIDGET_GONK
   GeckoTouchDispatcher::GetInstance()->NotifyVsync(aVsyncTimestamp);
 #endif
 }
 
 void
 CompositorVsyncScheduler::DispatchVREvents(TimeStamp aVsyncTimestamp)
 {
-  MOZ_ASSERT(CompositorBridgeParent::IsInCompositorThread());
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
 
   VRManager* vm = VRManager::Get();
   vm->NotifyVsync(aVsyncTimestamp);
 }
 
-void CompositorBridgeParent::StartUp()
-{
-  MOZ_ASSERT(NS_IsMainThread(), "Should be on the main Thread!");
-  MOZ_ASSERT(!sCompositorThreadHolder, "The compositor thread has already been started!");
-
-  sCompositorThreadHolder = new CompositorThreadHolder();
-}
-
-void CompositorBridgeParent::ShutDown()
-{
-  MOZ_ASSERT(NS_IsMainThread(), "Should be on the main Thread!");
-  MOZ_ASSERT(sCompositorThreadHolder, "The compositor thread has already been shut down!");
-
-  ReleaseImageBridgeParentSingleton();
-  ReleaseVRManagerParentSingleton();
-  MediaSystemResourceService::Shutdown();
-
-  sCompositorThreadHolder = nullptr;
-
-  // No locking is needed around sFinishedCompositorShutDown because it is only
-  // ever accessed on the main thread.
-  while (!sFinishedCompositorShutDown) {
-    NS_ProcessNextEvent(nullptr, true);
-  }
-
-  // TODO: this should be empty by now...
-  sIndirectLayerTrees.clear();
-}
-
-MessageLoop* CompositorBridgeParent::CompositorLoop()
-{
-  return CompositorThread() ? CompositorThread()->message_loop() : nullptr;
-}
-
 void
 CompositorVsyncScheduler::ScheduleTask(already_AddRefed<CancelableRunnable> aTask,
                                        int aTime)
 {
-  MOZ_ASSERT(CompositorBridgeParent::CompositorLoop());
+  MOZ_ASSERT(CompositorThreadHolder::Loop());
   MOZ_ASSERT(aTime >= 0);
-  CompositorBridgeParent::CompositorLoop()->PostDelayedTask(Move(aTask), aTime);
+  CompositorThreadHolder::Loop()->PostDelayedTask(Move(aTask), aTime);
 }
 
 void
 CompositorVsyncScheduler::ResumeComposition()
 {
-  MOZ_ASSERT(CompositorBridgeParent::IsInCompositorThread());
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   mLastCompose = TimeStamp::Now();
   ComposeToTarget(nullptr);
 }
 
 void
 CompositorVsyncScheduler::ComposeToTarget(gfx::DrawTarget* aTarget, const IntRect* aRect)
 {
-  MOZ_ASSERT(CompositorBridgeParent::IsInCompositorThread());
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   MOZ_ASSERT(mCompositorBridgeParent);
   mCompositorBridgeParent->CompositeToTarget(aTarget, aRect);
 }
 
+static inline MessageLoop*
+CompositorLoop()
+{
+  return CompositorThreadHolder::Loop();
+}
+
 CompositorBridgeParent::CompositorBridgeParent(widget::CompositorWidgetProxy* aWidget,
                                                CSSToLayoutDeviceScale aScale,
                                                bool aUseAPZ,
                                                bool aUseExternalSurfaceSize,
                                                int aSurfaceWidth, int aSurfaceHeight)
   : mWidgetProxy(aWidget)
   , mIsTesting(false)
   , mPendingTransaction(0)
@@ -696,17 +595,17 @@ CompositorBridgeParent::CompositorBridge
   , mUseExternalSurfaceSize(aUseExternalSurfaceSize)
   , mEGLSurfaceSize(aSurfaceWidth, aSurfaceHeight)
   , mPauseCompositionMonitor("PauseCompositionMonitor")
   , mResumeCompositionMonitor("ResumeCompositionMonitor")
   , mResetCompositorMonitor("ResetCompositorMonitor")
   , mRootLayerTreeID(AllocateLayerTreeId())
   , mOverrideComposeReadiness(false)
   , mForceCompositionTask(nullptr)
-  , mCompositorThreadHolder(sCompositorThreadHolder)
+  , mCompositorThreadHolder(CompositorThreadHolder::GetSingleton())
   , mCompositorScheduler(nullptr)
 #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
   , mLastPluginUpdateLayerTreeId(0)
   , mDeferPluginWindows(false)
   , mPluginWindowsHidden(false)
 #endif
 {
   MOZ_ASSERT(NS_IsMainThread());
@@ -739,22 +638,16 @@ CompositorBridgeParent::CompositorBridge
 
   mCompositorScheduler = new CompositorVsyncScheduler(this, aWidget);
   LayerScope::SetPixelScale(aScale.scale);
 
   // mSelfRef is cleared in DeferredDestroy.
   mSelfRef = this;
 }
 
-bool
-CompositorBridgeParent::IsInCompositorThread()
-{
-  return CompositorThread() && CompositorThread()->thread_id() == PlatformThread::CurrentId();
-}
-
 uint64_t
 CompositorBridgeParent::RootLayerTreeId()
 {
   return mRootLayerTreeID;
 }
 
 CompositorBridgeParent::~CompositorBridgeParent()
 {
@@ -1010,17 +903,17 @@ CompositorBridgeParent::InvalidateOnComp
 {
   MOZ_ASSERT(CompositorLoop());
   CompositorLoop()->PostTask(NewRunnableMethod(this, &CompositorBridgeParent::Invalidate));
 }
 
 void
 CompositorBridgeParent::PauseComposition()
 {
-  MOZ_ASSERT(IsInCompositorThread(),
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread(),
              "PauseComposition() can only be called on the compositor thread");
 
   MonitorAutoLock lock(mPauseCompositionMonitor);
 
   if (!mPaused) {
     mPaused = true;
 
     mCompositor->Pause();
@@ -1031,17 +924,17 @@ CompositorBridgeParent::PauseComposition
 
   // if anyone's waiting to make sure that composition really got paused, tell them
   lock.NotifyAll();
 }
 
 void
 CompositorBridgeParent::ResumeComposition()
 {
-  MOZ_ASSERT(IsInCompositorThread(),
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread(),
              "ResumeComposition() can only be called on the compositor thread");
 
   MonitorAutoLock lock(mResumeCompositionMonitor);
 
   if (!mCompositor->Resume()) {
 #ifdef MOZ_WIDGET_ANDROID
     // We can't get a surface. This could be because the activity changed between
     // the time resume was scheduled and now.
@@ -1165,17 +1058,17 @@ CompositorBridgeParent::NotifyShadowTree
   if (aScheduleComposite) {
     ScheduleComposition();
   }
 }
 
 void
 CompositorBridgeParent::ScheduleComposition()
 {
-  MOZ_ASSERT(IsInCompositorThread());
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   if (mPaused) {
     return;
   }
 
   mCompositorScheduler->ScheduleComposition();
 }
 
 // Go down the composite layer tree, setting properties to match their
@@ -1208,17 +1101,17 @@ CompositorBridgeParent::SetShadowPropert
 
 void
 CompositorBridgeParent::CompositeToTarget(DrawTarget* aTarget, const gfx::IntRect* aRect)
 {
   profiler_tracing("Paint", "Composite", TRACING_INTERVAL_START);
   PROFILER_LABEL("CompositorBridgeParent", "Composite",
     js::ProfileEntry::Category::GRAPHICS);
 
-  MOZ_ASSERT(IsInCompositorThread(),
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread(),
              "Composite can only be called on the compositor thread");
   TimeStamp start = TimeStamp::Now();
 
 #ifdef COMPOSITOR_PERFORMANCE_WARNING
   TimeDuration scheduleDelta = TimeStamp::Now() - mCompositorScheduler->GetExpectedComposeStartTime();
   if (scheduleDelta > TimeDuration::FromMilliseconds(2) ||
       scheduleDelta < TimeDuration::FromMilliseconds(-2)) {
     printf_stderr("Compositor: Compose starting off schedule by %4.1f ms\n",
@@ -1368,17 +1261,17 @@ CompositorBridgeParent::CanComposite()
          mLayerManager->GetRoot() &&
          !mPaused;
 }
 
 void
 CompositorBridgeParent::ScheduleRotationOnCompositorThread(const TargetConfig& aTargetConfig,
                                                      bool aIsFirstPaint)
 {
-  MOZ_ASSERT(IsInCompositorThread());
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
 
   if (!aIsFirstPaint &&
       !mCompositionManager->IsFirstPaint() &&
       mCompositionManager->RequiresReorientation(aTargetConfig.orientation())) {
     if (mForceCompositionTask != nullptr) {
       mForceCompositionTask->Cancel();
     }
     RefPtr<CancelableRunnable> task =
@@ -1867,27 +1760,27 @@ CompositorBridgeParent::ComputeRenderInt
 
   return 1.0f;
 }
 
 static void
 InsertVsyncProfilerMarker(TimeStamp aVsyncTimestamp)
 {
 #ifdef MOZ_ENABLE_PROFILER_SPS
-  MOZ_ASSERT(CompositorBridgeParent::IsInCompositorThread());
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   VsyncPayload* payload = new VsyncPayload(aVsyncTimestamp);
   PROFILER_MARKER_PAYLOAD("VsyncTimestamp", payload);
 #endif
 }
 
 /*static */ void
 CompositorBridgeParent::PostInsertVsyncProfilerMarker(TimeStamp aVsyncTimestamp)
 {
   // Called in the vsync thread
-  if (profiler_is_active() && sCompositorThreadHolder) {
+  if (profiler_is_active() && CompositorThreadHolder::IsActive()) {
     CompositorLoop()->PostTask(
       NewRunnableFunction(InsertVsyncProfilerMarker, aVsyncTimestamp));
   }
 }
 
 /* static */ void
 CompositorBridgeParent::RequestNotifyLayerTreeReady(uint64_t aLayersId, CompositorUpdateObserver* aObserver)
 {
@@ -2067,17 +1960,19 @@ public:
 
   virtual bool AllocUnsafeShmem(size_t aSize,
                                 mozilla::ipc::SharedMemory::SharedMemoryType aType,
                                 mozilla::ipc::Shmem* aShmem) override;
 
   virtual void DeallocShmem(mozilla::ipc::Shmem& aShmem) override;
 
 protected:
-  void OnChannelConnected(int32_t pid) override { mCompositorThreadHolder = sCompositorThreadHolder; }
+  void OnChannelConnected(int32_t pid) override {
+    mCompositorThreadHolder = CompositorThreadHolder::GetSingleton();
+  }
 private:
   // Private destructor, to discourage deletion outside of Release():
   virtual ~CrossProcessCompositorBridgeParent();
 
   void DeferredDestroy();
 
   // There can be many CPCPs, and IPDL-generated code doesn't hold a
   // reference to top-level actors.  So we hold a reference to
--- a/gfx/layers/ipc/CompositorBridgeParent.h
+++ b/gfx/layers/ipc/CompositorBridgeParent.h
@@ -12,19 +12,16 @@
 // its responsiveness objectives:
 //    1) Compose a frame within 15ms of receiving a ScheduleCompositeCall
 //    2) Unless a frame was composited within the throttle threshold in
 //       which the deadline will be 15ms + throttle threshold
 //#define COMPOSITOR_PERFORMANCE_WARNING
 
 #include <stdint.h>                     // for uint64_t
 #include "Layers.h"                     // for Layer
-#include "base/basictypes.h"            // for DISALLOW_EVIL_CONSTRUCTORS
-#include "base/platform_thread.h"       // for PlatformThreadId
-#include "base/thread.h"                // for Thread
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT_HELPER2
 #include "mozilla/Attributes.h"         // for override
 #include "mozilla/Maybe.h"
 #include "mozilla/Monitor.h"            // for Monitor
 #include "mozilla/RefPtr.h"             // for RefPtr
 #include "mozilla/TimeStamp.h"          // for TimeStamp
 #include "mozilla/dom/ipc/IdType.h"
 #include "mozilla/gfx/Point.h"          // for IntSize
@@ -63,51 +60,30 @@ namespace layers {
 class APZCTreeManager;
 class AsyncCompositionManager;
 class Compositor;
 class CompositorBridgeParent;
 class LayerManagerComposite;
 class LayerTransactionParent;
 class PAPZParent;
 class CrossProcessCompositorBridgeParent;
+class CompositorThreadHolder;
 
 struct ScopedLayerTreeRegistration
 {
   ScopedLayerTreeRegistration(APZCTreeManager* aApzctm,
                               uint64_t aLayersId,
                               Layer* aRoot,
                               GeckoContentController* aController);
   ~ScopedLayerTreeRegistration();
 
 private:
   uint64_t mLayersId;
 };
 
-class CompositorThreadHolder final
-{
-  NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_MAIN_THREAD_DESTRUCTION(CompositorThreadHolder)
-
-public:
-  CompositorThreadHolder();
-
-  base::Thread* GetCompositorThread() const {
-    return mCompositorThread;
-  }
-
-private:
-  ~CompositorThreadHolder();
-
-  base::Thread* const mCompositorThread;
-
-  static base::Thread* CreateCompositorThread();
-  static void DestroyCompositorThread(base::Thread* aCompositorThread);
-
-  friend class CompositorBridgeParent;
-};
-
 /**
  * Manages the vsync (de)registration and tracking on behalf of the
  * compositor when it need to paint.
  * Turns vsync notifications into scheduled composites.
  **/
 class CompositorVsyncScheduler
 {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CompositorVsyncScheduler)
@@ -226,16 +202,17 @@ protected:
 };
 
 class CompositorBridgeParent final : public PCompositorBridgeParent,
                                      public ShadowLayersManager,
                                      public ISurfaceAllocator,
                                      public ShmemAllocator
 {
   friend class CompositorVsyncScheduler;
+  friend class CompositorThreadHolder;
 
 public:
   explicit CompositorBridgeParent(widget::CompositorWidgetProxy* aWidget,
                                   CSSToLayoutDeviceScale aScale,
                                   bool aUseAPZ,
                                   bool aUseExternalSurfaceSize = false,
                                   int aSurfaceWidth = -1, int aSurfaceHeight = -1);
 
@@ -391,37 +368,16 @@ public:
   void InvalidateRemoteLayers();
 
   /**
    * Returns a pointer to the compositor corresponding to the given ID.
    */
   static CompositorBridgeParent* GetCompositor(uint64_t id);
 
   /**
-   * Returns the compositor thread's message loop.
-   *
-   * This message loop is used by CompositorBridgeParent, ImageBridgeParent,
-   * and VRManagerParent
-   */
-  static MessageLoop* CompositorLoop();
-
-  /**
-   * Creates the compositor thread and the global compositor map.
-   */
-  static void StartUp();
-
-  /**
-   * Waits for all [CrossProcess]CompositorBridgeParent's to be gone,
-   * and destroys the compositor thread and global compositor map.
-   *
-   * Does not return until all of that has completed.
-   */
-  static void ShutDown();
-
-  /**
    * Allocate an ID that can be used to refer to a layer tree and
    * associated resources that live only on the compositor thread.
    *
    * Must run on the content main thread.
    */
   static uint64_t AllocateLayerTreeId();
   /**
    * Release compositor-thread resources referred to by |aID|.
@@ -514,21 +470,16 @@ public:
   static void PostInsertVsyncProfilerMarker(mozilla::TimeStamp aVsyncTimestamp);
 
   static void RequestNotifyLayerTreeReady(uint64_t aLayersId, CompositorUpdateObserver* aObserver);
   static void RequestNotifyLayerTreeCleared(uint64_t aLayersId, CompositorUpdateObserver* aObserver);
   static void SwapLayerTreeObservers(uint64_t aLayer, uint64_t aOtherLayer);
 
   float ComputeRenderIntegrity();
 
-  /**
-   * Returns true if the calling thread is the compositor thread.
-   */
-  static bool IsInCompositorThread();
-
   widget::CompositorWidgetProxy* GetWidgetProxy() { return mWidgetProxy; }
 
   void ForceComposeToTarget(gfx::DrawTarget* aTarget, const gfx::IntRect* aRect = nullptr);
 
   /**
    * Creates a new RemoteContentController for aTabId. Should only be called on
    * the main thread.
    *
@@ -579,17 +530,32 @@ protected:
    * Add a compositor to the global compositor map.
    */
   static void AddCompositor(CompositorBridgeParent* compositor, uint64_t* id);
   /**
    * Remove a compositor from the global compositor map.
    */
   static CompositorBridgeParent* RemoveCompositor(uint64_t id);
 
-   /**
+  /**
+   * Creates the global compositor map.
+   */
+  static void Initialize();
+
+  /**
+   * Destroys the compositor thread and global compositor map.
+   */
+  static void Shutdown();
+
+  /**
+   * Finish the shutdown operation on the compositor thread.
+   */
+  static void FinishShutdown();
+
+  /**
    * Return true if current state allows compositing, that is
    * finishing a layers transaction.
    */
   bool CanComposite();
 
   void DidComposite(TimeStamp& aCompositeStart, TimeStamp& aCompositeEnd);
 
   // The indirect layer tree lock must be held before calling this function.
new file mode 100644
--- /dev/null
+++ b/gfx/layers/ipc/CompositorThread.cpp
@@ -0,0 +1,153 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 sts=2 ts=8 et tw=99 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "CompositorThread.h"
+#include "MainThreadUtils.h"
+#include "nsThreadUtils.h"
+#include "CompositorBridgeParent.h"
+#include "mozilla/media/MediaSystemResourceService.h"
+
+namespace mozilla {
+
+namespace gfx {
+// See VRManagerChild.cpp
+void ReleaseVRManagerParentSingleton();
+} // namespace gfx
+
+namespace layers {
+
+static StaticRefPtr<CompositorThreadHolder> sCompositorThreadHolder;
+static bool sFinishedCompositorShutDown = false;
+
+// See ImageBridgeChild.cpp
+void ReleaseImageBridgeParentSingleton();
+
+CompositorThreadHolder* GetCompositorThreadHolder()
+{
+  return sCompositorThreadHolder;
+}
+
+base::Thread*
+CompositorThread()
+{
+  return sCompositorThreadHolder
+         ? sCompositorThreadHolder->GetCompositorThread()
+         : nullptr;
+}
+
+/* static */ MessageLoop*
+CompositorThreadHolder::Loop()
+{
+  return CompositorThread() ? CompositorThread()->message_loop() : nullptr;
+}
+
+CompositorThreadHolder*
+CompositorThreadHolder::GetSingleton()
+{
+  return sCompositorThreadHolder;
+}
+
+CompositorThreadHolder::CompositorThreadHolder()
+  : mCompositorThread(CreateCompositorThread())
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_COUNT_CTOR(CompositorThreadHolder);
+}
+
+CompositorThreadHolder::~CompositorThreadHolder()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  MOZ_COUNT_DTOR(CompositorThreadHolder);
+
+  DestroyCompositorThread(mCompositorThread);
+}
+
+/* static */ void
+CompositorThreadHolder::DestroyCompositorThread(base::Thread* aCompositorThread)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  MOZ_ASSERT(!sCompositorThreadHolder, "We shouldn't be destroying the compositor thread yet.");
+
+  CompositorBridgeParent::Shutdown();
+  delete aCompositorThread;
+  sFinishedCompositorShutDown = true;
+}
+
+/* static */ base::Thread*
+CompositorThreadHolder::CreateCompositorThread()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  MOZ_ASSERT(!sCompositorThreadHolder, "The compositor thread has already been started!");
+
+  base::Thread* compositorThread = new base::Thread("Compositor");
+
+  base::Thread::Options options;
+  /* Timeout values are powers-of-two to enable us get better data.
+     128ms is chosen for transient hangs because 8Hz should be the minimally
+     acceptable goal for Compositor responsiveness (normal goal is 60Hz). */
+  options.transient_hang_timeout = 128; // milliseconds
+  /* 2048ms is chosen for permanent hangs because it's longer than most
+   * Compositor hangs seen in the wild, but is short enough to not miss getting
+   * native hang stacks. */
+  options.permanent_hang_timeout = 2048; // milliseconds
+#if defined(_WIN32)
+  /* With d3d9 the compositor thread creates native ui, see DeviceManagerD3D9. As
+   * such the thread is a gui thread, and must process a windows message queue or
+   * risk deadlocks. Chromium message loop TYPE_UI does exactly what we need. */
+  options.message_loop_type = MessageLoop::TYPE_UI;
+#endif
+
+  if (!compositorThread->StartWithOptions(options)) {
+    delete compositorThread;
+    return nullptr;
+  }
+
+  CompositorBridgeParent::Initialize();
+
+  return compositorThread;
+}
+
+void
+CompositorThreadHolder::Start()
+{
+  MOZ_ASSERT(NS_IsMainThread(), "Should be on the main Thread!");
+  MOZ_ASSERT(!sCompositorThreadHolder, "The compositor thread has already been started!");
+
+  sCompositorThreadHolder = new CompositorThreadHolder();
+}
+
+void
+CompositorThreadHolder::Shutdown()
+{
+  MOZ_ASSERT(NS_IsMainThread(), "Should be on the main Thread!");
+  MOZ_ASSERT(sCompositorThreadHolder, "The compositor thread has already been shut down!");
+
+  ReleaseImageBridgeParentSingleton();
+  gfx::ReleaseVRManagerParentSingleton();
+  MediaSystemResourceService::Shutdown();
+
+  sCompositorThreadHolder = nullptr;
+
+  // No locking is needed around sFinishedCompositorShutDown because it is only
+  // ever accessed on the main thread.
+  while (!sFinishedCompositorShutDown) {
+    NS_ProcessNextEvent(nullptr, true);
+  }
+
+  CompositorBridgeParent::FinishShutdown();
+}
+
+/* static */ bool
+CompositorThreadHolder::IsInCompositorThread()
+{
+  return CompositorThread() &&
+         CompositorThread()->thread_id() == PlatformThread::CurrentId();
+}
+
+} // namespace mozilla
+} // namespace layers
new file mode 100644
--- /dev/null
+++ b/gfx/layers/ipc/CompositorThread.h
@@ -0,0 +1,68 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 sts=2 ts=8 et tw=99 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef mozilla_layers_CompositorThread_h
+#define mozilla_layers_CompositorThread_h
+
+#include "base/basictypes.h"            // for DISALLOW_EVIL_CONSTRUCTORS
+#include "base/platform_thread.h"       // for PlatformThreadId
+#include "base/thread.h"                // for Thread
+#include "base/message_loop.h"
+#include "nsISupportsImpl.h"
+#include "ThreadSafeRefcountingWithMainThreadDestruction.h"
+
+namespace mozilla {
+namespace layers {
+
+class CompositorThreadHolder final
+{
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_MAIN_THREAD_DESTRUCTION(CompositorThreadHolder)
+
+public:
+  CompositorThreadHolder();
+
+  base::Thread* GetCompositorThread() const {
+    return mCompositorThread;
+  }
+
+  static CompositorThreadHolder* GetSingleton();
+
+  static bool IsActive() {
+    return !!GetSingleton();
+  }
+
+  /**
+   * Creates the compositor thread and the global compositor map.
+   */
+  static void Start();
+
+  /*
+   * Waits for all [CrossProcess]CompositorBridgeParents to shutdown and
+   * releases compositor-thread owned resources.
+   */
+  static void Shutdown();
+
+  static MessageLoop* Loop();
+
+  // Returns true if the calling thread is the compositor thread.
+  static bool IsInCompositorThread();
+
+private:
+  ~CompositorThreadHolder();
+
+  base::Thread* const mCompositorThread;
+
+  static base::Thread* CreateCompositorThread();
+  static void DestroyCompositorThread(base::Thread* aCompositorThread);
+
+  friend class CompositorBridgeParent;
+};
+
+base::Thread* CompositorThread();
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_CompositorThread_h
--- a/gfx/layers/ipc/ImageBridgeChild.cpp
+++ b/gfx/layers/ipc/ImageBridgeChild.cpp
@@ -19,17 +19,17 @@
 #include "mozilla/ReentrantMonitor.h"   // for ReentrantMonitor, etc
 #include "mozilla/ipc/MessageChannel.h" // for MessageChannel, etc
 #include "mozilla/ipc/Transport.h"      // for Transport
 #include "mozilla/gfx/Point.h"          // for IntSize
 #include "mozilla/layers/AsyncCanvasRenderer.h"
 #include "mozilla/media/MediaSystemResourceManager.h" // for MediaSystemResourceManager
 #include "mozilla/media/MediaSystemResourceManagerChild.h" // for MediaSystemResourceManagerChild
 #include "mozilla/layers/CompositableClient.h"  // for CompositableChild, etc
-#include "mozilla/layers/CompositorBridgeParent.h" // for CompositorBridgeParent
+#include "mozilla/layers/CompositorThread.h"
 #include "mozilla/layers/ISurfaceAllocator.h"  // for ISurfaceAllocator
 #include "mozilla/layers/ImageClient.h"  // for ImageClient
 #include "mozilla/layers/LayersMessages.h"  // for CompositableOperation
 #include "mozilla/layers/PCompositableChild.h"  // for PCompositableChild
 #include "mozilla/layers/PImageContainerChild.h"
 #include "mozilla/layers/TextureClient.h"  // for TextureClient
 #include "mozilla/mozalloc.h"           // for operator new, etc
 #include "nsAutoPtr.h"                  // for nsRefPtr
@@ -839,17 +839,17 @@ bool ImageBridgeChild::StartUpOnThread(T
   MOZ_ASSERT(aThread, "ImageBridge needs a thread.");
   if (sImageBridgeChildSingleton == nullptr) {
     sImageBridgeChildThread = aThread;
     if (!aThread->IsRunning()) {
       aThread->Start();
     }
     sImageBridgeChildSingleton = new ImageBridgeChild();
     sImageBridgeParentSingleton = new ImageBridgeParent(
-      CompositorBridgeParent::CompositorLoop(), nullptr, base::GetCurrentProcId());
+      CompositorThreadHolder::Loop(), nullptr, base::GetCurrentProcId());
     sImageBridgeChildSingleton->ConnectAsync(sImageBridgeParentSingleton);
     sImageBridgeChildSingleton->GetMessageLoop()->PostTask(
       NewRunnableFunction(CallSendImageBridgeThreadId,
                           sImageBridgeChildSingleton.get()));
     return true;
   } else {
     return false;
   }
--- a/gfx/layers/ipc/ImageBridgeParent.cpp
+++ b/gfx/layers/ipc/ImageBridgeParent.cpp
@@ -15,16 +15,17 @@
 #include "mozilla/HalTypes.h"           // for hal::THREAD_PRIORITY_COMPOSITOR
 #include "mozilla/ipc/MessageChannel.h" // for MessageChannel, etc
 #include "mozilla/ipc/ProtocolUtils.h"
 #include "mozilla/ipc/Transport.h"      // for Transport
 #include "mozilla/ipc/GeckoChildProcessHost.h"
 #include "mozilla/media/MediaSystemResourceManagerParent.h" // for MediaSystemResourceManagerParent
 #include "mozilla/layers/CompositableTransactionParent.h"
 #include "mozilla/layers/CompositorBridgeParent.h"  // for CompositorBridgeParent
+#include "mozilla/layers/CompositorThread.h"
 #include "mozilla/layers/LayerManagerComposite.h"
 #include "mozilla/layers/LayersMessages.h"  // for EditReply
 #include "mozilla/layers/LayersSurfaces.h"  // for PGrallocBufferParent
 #include "mozilla/layers/PCompositableParent.h"
 #include "mozilla/layers/PImageBridgeParent.h"
 #include "mozilla/layers/TextureHostOGL.h"  // for TextureHostOGL
 #include "mozilla/layers/Compositor.h"
 #include "mozilla/mozalloc.h"           // for operator new, etc
@@ -195,17 +196,17 @@ ConnectImageBridgeInParentProcess(ImageB
                                   base::ProcessId aOtherPid)
 {
   aBridge->Open(aTransport, aOtherPid, XRE_GetIOMessageLoop(), ipc::ParentSide);
 }
 
 /*static*/ PImageBridgeParent*
 ImageBridgeParent::Create(Transport* aTransport, ProcessId aChildProcessId, GeckoChildProcessHost* aProcessHost)
 {
-  MessageLoop* loop = CompositorBridgeParent::CompositorLoop();
+  MessageLoop* loop = CompositorThreadHolder::Loop();
   RefPtr<ImageBridgeParent> bridge = new ImageBridgeParent(loop, aTransport, aChildProcessId);
 
   if (aProcessHost) {
     bridge->mSubprocess = aProcessHost;
     aProcessHost->AssociateActor();
   }
 
   loop->PostTask(NewRunnableFunction(ConnectImageBridgeInParentProcess,
--- a/gfx/layers/moz.build
+++ b/gfx/layers/moz.build
@@ -153,16 +153,17 @@ EXPORTS.mozilla.layers += [
     'ImageDataSerializer.h',
     'ipc/APZChild.h',
     'ipc/AsyncTransactionTracker.h',
     'ipc/CompositableForwarder.h',
     'ipc/CompositableTransactionParent.h',
     'ipc/CompositorBridgeChild.h',
     'ipc/CompositorBridgeParent.h',
     'ipc/CompositorLRU.h',
+    'ipc/CompositorThread.h',
     'ipc/FenceUtils.h',
     'ipc/GonkNativeHandle.h',
     'ipc/GonkNativeHandleUtils.h',
     'ipc/ImageBridgeChild.h',
     'ipc/ImageBridgeParent.h',
     'ipc/ImageContainerParent.h',
     'ipc/ISurfaceAllocator.h',
     'ipc/LayerAnimationUtils.h',
@@ -338,16 +339,17 @@ UNIFIED_SOURCES += [
     'ImageLayers.cpp',
     'ipc/APZChild.cpp',
     'ipc/AsyncTransactionTracker.cpp',
     'ipc/CompositableTransactionParent.cpp',
     'ipc/CompositorBench.cpp',
     'ipc/CompositorBridgeChild.cpp',
     'ipc/CompositorBridgeParent.cpp',
     'ipc/CompositorLRU.cpp',
+    'ipc/CompositorThread.cpp',
     'ipc/FenceUtils.cpp',
     'ipc/ImageBridgeChild.cpp',
     'ipc/ImageBridgeParent.cpp',
     'ipc/ImageContainerParent.cpp',
     'ipc/ISurfaceAllocator.cpp',
     'ipc/LayerAnimationUtils.cpp',
     'ipc/LayerTransactionChild.cpp',
     'ipc/LayerTransactionParent.cpp',
--- a/gfx/layers/opengl/CompositorOGL.cpp
+++ b/gfx/layers/opengl/CompositorOGL.cpp
@@ -1242,33 +1242,22 @@ CompositorOGL::DrawQuad(const Rect& aRec
   case EffectTypes::RGB: {
       TexturedEffect* texturedEffect =
           static_cast<TexturedEffect*>(aEffectChain.mPrimaryEffect.get());
       TextureSource *source = texturedEffect->mTexture;
 
       didSetBlendMode = SetBlendMode(gl(), blendMode, texturedEffect->mPremultiplied);
 
       gfx::Filter filter = texturedEffect->mFilter;
-      Matrix4x4 textureTransform = source->AsSourceOGL()->GetTextureTransform();
 
-#ifdef MOZ_WIDGET_ANDROID
-      gfx::Matrix textureTransform2D;
-      if (filter != gfx::Filter::POINT &&
-          aTransform.Is2DIntegerTranslation() &&
-          textureTransform.Is2D(&textureTransform2D) &&
-          textureTransform2D.HasOnlyIntegerTranslation()) {
-        // On Android we encounter small resampling errors in what should be
-        // pixel-aligned compositing operations. This works around them. This
-        // code should not be needed!
-        filter = gfx::Filter::POINT;
-      }
-#endif
       source->AsSourceOGL()->BindTexture(LOCAL_GL_TEXTURE0, filter);
 
       program->SetTextureUnit(0);
+
+      Matrix4x4 textureTransform = source->AsSourceOGL()->GetTextureTransform();
       program->SetTextureTransform(textureTransform);
 
       if (maskType != MaskType::MaskNone) {
         BindMaskForProgram(program, sourceMask, LOCAL_GL_TEXTURE1, maskQuadTransform);
       }
       if (mixBlendBackdrop) {
         BindBackdrop(program, mixBlendBackdrop, LOCAL_GL_TEXTURE2);
       }
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -1,16 +1,16 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/layers/AsyncTransactionTracker.h" // for AsyncTransactionTracker
 #include "mozilla/layers/CompositorBridgeChild.h"
-#include "mozilla/layers/CompositorBridgeParent.h"
+#include "mozilla/layers/CompositorThread.h"
 #include "mozilla/layers/ImageBridgeChild.h"
 #include "mozilla/layers/SharedBufferManagerChild.h"
 #include "mozilla/layers/ISurfaceAllocator.h"     // for GfxMemoryImageReporter
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/TimeStamp.h"
 
 #include "mozilla/Logging.h"
@@ -863,17 +863,17 @@ gfxPlatform::InitLayersIPC()
       return;
     }
     sLayersIPCIsUp = true;
 
     AsyncTransactionTrackersHolder::Initialize();
 
     if (XRE_IsParentProcess())
     {
-        mozilla::layers::CompositorBridgeParent::StartUp();
+        layers::CompositorThreadHolder::Start();
 #ifdef MOZ_WIDGET_GONK
         SharedBufferManagerChild::StartUp();
 #endif
         mozilla::layers::ImageBridgeChild::StartUp();
         gfx::VRManagerChild::StartUpSameProcess();
     }
 }
 
@@ -900,17 +900,17 @@ gfxPlatform::ShutdownLayersIPC()
         layers::CompositorBridgeChild::ShutDown();
         layers::ImageBridgeChild::ShutDown();
 
 #ifdef MOZ_WIDGET_GONK
         layers::SharedBufferManagerChild::ShutDown();
 #endif
 
         // This has to happen after shutting down the child protocols.
-        layers::CompositorBridgeParent::ShutDown();
+        layers::CompositorThreadHolder::Shutdown();
     } else {
       // TODO: There are other kind of processes and we should make sure gfx
       // stuff is either not created there or shut down properly.
     }
 }
 
 gfxPlatform::~gfxPlatform()
 {
--- a/gfx/thebes/gfxPlatform.h
+++ b/gfx/thebes/gfxPlatform.h
@@ -131,17 +131,18 @@ enum class DeviceResetReason
   INVALID_CALL,
   OUT_OF_MEMORY,
   FORCED_RESET,
   UNKNOWN
 };
 
 enum class ForcedDeviceResetReason
 {
-  OPENSHAREDHANDLE = 0
+  OPENSHAREDHANDLE = 0,
+  COMPOSITOR_UPDATED,
 };
 
 class gfxPlatform {
     friend class SRGBOverrideObserver;
 
 public:
     typedef mozilla::gfx::Color Color;
     typedef mozilla::gfx::DataSourceSurface DataSourceSurface;
@@ -628,16 +629,18 @@ public:
      */
     static bool PerfWarnings();
 
     void NotifyCompositorCreated(mozilla::layers::LayersBackend aBackend);
     mozilla::layers::LayersBackend GetCompositorBackend() const {
       return mCompositorBackend;
     }
 
+    virtual void CompositorUpdated() {}
+
     // Return information on how child processes should initialize graphics
     // devices. Currently this is only used on Windows.
     virtual void GetDeviceInitData(mozilla::gfx::DeviceInitData* aOut);
 
     // Plugin async drawing support.
     virtual bool SupportsPluginDirectBitmapDrawing() {
       return false;
     }
--- a/gfx/thebes/gfxUserFontSet.cpp
+++ b/gfx/thebes/gfxUserFontSet.cpp
@@ -1326,25 +1326,27 @@ gfxUserFontSet::UserFontCache::Entry::Re
                 spec.Truncate(252);
                 spec.Append("...");
             }
             path.AppendPrintf(", url=%s", spec.get());
         }
         if (mPrincipal) {
             nsCOMPtr<nsIURI> uri;
             mPrincipal->GetURI(getter_AddRefs(uri));
-            nsCString spec;
-            uri->GetSpec(spec);
-            if (!spec.IsEmpty()) {
-                // Include a clue as to who loaded this resource. (Note that
-                // because of font entry sharing, other pages may now be using
-                // this resource, and the original page may not even be loaded
-                // any longer.)
-                spec.ReplaceChar('/', '\\');
-                path.AppendPrintf(", principal=%s", spec.get());
+            if (uri) {
+                nsCString spec;
+                uri->GetSpec(spec);
+                if (!spec.IsEmpty()) {
+                    // Include a clue as to who loaded this resource. (Note
+                    // that because of font entry sharing, other pages may now
+                    // be using this resource, and the original page may not
+                    // even be loaded any longer.)
+                    spec.ReplaceChar('/', '\\');
+                    path.AppendPrintf(", principal=%s", spec.get());
+                }
             }
         }
     }
     path.Append(')');
 
     return aCb->
         Callback(EmptyCString(), path,
                  nsIMemoryReporter::KIND_HEAP, nsIMemoryReporter::UNITS_BYTES,
--- a/gfx/thebes/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -32,17 +32,17 @@
 #include "nsIGfxInfo.h"
 #include "GfxDriverInfo.h"
 
 #include "gfxCrashReporterUtils.h"
 
 #include "gfxGDIFontList.h"
 #include "gfxGDIFont.h"
 
-#include "mozilla/layers/CompositorBridgeParent.h"   // for CompositorBridgeParent::IsInCompositorThread
+#include "mozilla/layers/CompositorThread.h"
 #include "DeviceManagerD3D9.h"
 #include "mozilla/layers/ReadbackManagerD3D11.h"
 
 #include "WinUtils.h"
 
 #include "gfxDWriteFontList.h"
 #include "gfxDWriteFonts.h"
 #include "gfxDWriteCommon.h"
@@ -62,20 +62,20 @@
 #include "nsMemory.h"
 
 #include <d3d11.h>
 
 #include "nsIMemoryReporter.h"
 #include <winternl.h>
 #include "d3dkmtQueryStatistics.h"
 
+#include "base/thread.h"
 #include "SurfaceCache.h"
 #include "gfxPrefs.h"
 #include "gfxConfig.h"
-
 #include "VsyncSource.h"
 #include "DriverCrashGuard.h"
 #include "mozilla/dom/ContentParent.h"
 
 using namespace mozilla;
 using namespace mozilla::gfx;
 using namespace mozilla::layers;
 using namespace mozilla::widget;
@@ -942,16 +942,23 @@ static DeviceResetReason HResultToResetR
   case E_OUTOFMEMORY:
     return DeviceResetReason::OUT_OF_MEMORY;
   default:
     MOZ_ASSERT(false);
   }
   return DeviceResetReason::UNKNOWN;
 }
 
+void
+gfxWindowsPlatform::CompositorUpdated()
+{
+  ForceDeviceReset(ForcedDeviceResetReason::COMPOSITOR_UPDATED);
+  UpdateRenderMode();
+}
+
 bool
 gfxWindowsPlatform::IsDeviceReset(HRESULT hr, DeviceResetReason* aResetReason)
 {
   if (hr != S_OK) {
     mDeviceResetReason = HResultToResetReason(hr);
     mHasDeviceReset = true;
     if (aResetReason) {
       *aResetReason = mDeviceResetReason;
@@ -1405,17 +1412,17 @@ gfxWindowsPlatform::D3D9DeviceReset() {
 already_AddRefed<DeviceManagerD3D9>
 gfxWindowsPlatform::GetD3D9DeviceManager()
 {
   // We should only create the d3d9 device on the compositor thread
   // or we don't have a compositor thread.
   RefPtr<DeviceManagerD3D9> result;
   if (!mDeviceManager &&
       (!gfxPlatform::UsesOffMainThreadCompositing() ||
-       CompositorBridgeParent::IsInCompositorThread())) {
+       CompositorThreadHolder::IsInCompositorThread())) {
     mDeviceManager = new DeviceManagerD3D9();
     if (!mDeviceManager->Init()) {
       gfxCriticalError() << "[D3D9] Could not Initialize the DeviceManagerD3D9";
       mDeviceManager = nullptr;
     }
   }
 
   MutexAutoLock lock(mDeviceLock);
--- a/gfx/thebes/gfxWindowsPlatform.h
+++ b/gfx/thebes/gfxWindowsPlatform.h
@@ -173,16 +173,18 @@ public:
 
     virtual bool CanUseHardwareVideoDecoding() override;
 
     /**
      * Check whether format is supported on a platform or not (if unclear, returns true)
      */
     virtual bool IsFontFormatSupported(nsIURI *aFontURI, uint32_t aFormatFlags) override;
 
+    virtual void CompositorUpdated() override;
+
     bool DidRenderingDeviceReset(DeviceResetReason* aResetReason = nullptr) override;
     void SchedulePaintIfDeviceReset() override;
     void UpdateRenderModeIfDeviceReset() override;
 
     mozilla::gfx::BackendType GetContentBackendFor(mozilla::layers::LayersBackend aLayers) override;
 
     // ClearType is not always enabled even when available (e.g. Windows XP)
     // if either of these prefs are enabled and apply, use ClearType rendering
--- a/gfx/vr/ipc/VRManagerChild.cpp
+++ b/gfx/vr/ipc/VRManagerChild.cpp
@@ -5,17 +5,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "VRManagerChild.h"
 #include "VRManagerParent.h"
 #include "VRDeviceProxy.h"
 #include "VRDeviceProxyOrientationFallBack.h"
 #include "mozilla/StaticPtr.h"
-#include "mozilla/layers/CompositorBridgeParent.h" // for CompositorBridgeParent
+#include "mozilla/layers/CompositorThread.h" // for CompositorThread
 #include "mozilla/dom/Navigator.h"
 
 namespace mozilla {
 namespace gfx {
 
 static StaticRefPtr<VRManagerChild> sVRManagerChildSingleton;
 static StaticRefPtr<VRManagerParent> sVRManagerParentSingleton;
 
@@ -72,17 +72,17 @@ VRManagerChild::StartUpInChildProcess(Tr
 /*static*/ void
 VRManagerChild::StartUpSameProcess()
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on the main Thread!");
   if (sVRManagerChildSingleton == nullptr) {
     sVRManagerChildSingleton = new VRManagerChild();
     sVRManagerParentSingleton = VRManagerParent::CreateSameProcess();
     sVRManagerChildSingleton->Open(sVRManagerParentSingleton->GetIPCChannel(),
-                                   mozilla::layers::CompositorBridgeParent::CompositorLoop(),
+                                   mozilla::layers::CompositorThreadHolder::Loop(),
                                    mozilla::ipc::ChildSide);
   }
 }
 
 /*static*/ void
 VRManagerChild::ShutDown()
 {
   MOZ_ASSERT(NS_IsMainThread());
--- a/gfx/vr/ipc/VRManagerParent.cpp
+++ b/gfx/vr/ipc/VRManagerParent.cpp
@@ -5,28 +5,21 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "VRManagerParent.h"
 #include "mozilla/gfx/PVRManagerParent.h"
 #include "mozilla/ipc/ProtocolTypes.h"
 #include "mozilla/ipc/ProtocolUtils.h"       // for IToplevelProtocol
 #include "mozilla/TimeStamp.h"               // for TimeStamp
-#include "mozilla/layers/CompositorBridgeParent.h"
+#include "mozilla/layers/CompositorThread.h"
 #include "mozilla/unused.h"
 #include "VRManager.h"
 
 namespace mozilla {
-namespace layers {
-
-// defined in CompositorBridgeParent.cpp
-CompositorThreadHolder* GetCompositorThreadHolder();
-
-} // namespace layers
-
 namespace gfx {
 
 VRManagerParent::VRManagerParent(MessageLoop* aLoop,
                                  Transport* aTransport,
                                  ProcessId aChildProcessId)
 {
   MOZ_COUNT_CTOR(VRManagerParent);
   MOZ_ASSERT(NS_IsMainThread());
@@ -71,36 +64,36 @@ VRManagerParent::ConnectVRManagerInParen
 {
   aVRManager->Open(aTransport, aOtherPid, XRE_GetIOMessageLoop(), ipc::ParentSide);
   aVRManager->RegisterWithManager();
 }
 
 /*static*/ VRManagerParent*
 VRManagerParent::CreateCrossProcess(Transport* aTransport, ProcessId aChildProcessId)
 {
-  MessageLoop* loop = mozilla::layers::CompositorBridgeParent::CompositorLoop();
+  MessageLoop* loop = mozilla::layers::CompositorThreadHolder::Loop();
   RefPtr<VRManagerParent> vmp = new VRManagerParent(loop, aTransport, aChildProcessId);
   vmp->mSelfRef = vmp;
   loop->PostTask(NewRunnableFunction(ConnectVRManagerInParentProcess,
                                      vmp.get(), aTransport, aChildProcessId));
   return vmp.get();
 }
 
 /*static*/ void
 VRManagerParent::RegisterVRManagerInCompositorThread(VRManagerParent* aVRManager)
 {
   aVRManager->RegisterWithManager();
 }
 
 /*static*/ VRManagerParent*
 VRManagerParent::CreateSameProcess()
 {
-  MessageLoop* loop = mozilla::layers::CompositorBridgeParent::CompositorLoop();
+  MessageLoop* loop = mozilla::layers::CompositorThreadHolder::Loop();
   RefPtr<VRManagerParent> vmp = new VRManagerParent(loop, nullptr, base::GetCurrentProcId());
-  vmp->mCompositorThreadHolder = layers::GetCompositorThreadHolder();
+  vmp->mCompositorThreadHolder = layers::CompositorThreadHolder::GetSingleton();
   vmp->mSelfRef = vmp;
   loop->PostTask(NewRunnableFunction(RegisterVRManagerInCompositorThread, vmp.get()));
   return vmp.get();
 }
 
 void
 VRManagerParent::DeferredDestroy()
 {
@@ -134,17 +127,17 @@ VRManagerParent::CloneToplevel(const Inf
     }
   }
   return nullptr;
 }
 
 void
 VRManagerParent::OnChannelConnected(int32_t aPid)
 {
-  mCompositorThreadHolder = layers::GetCompositorThreadHolder();
+  mCompositorThreadHolder = layers::CompositorThreadHolder::GetSingleton();
 }
 
 bool
 VRManagerParent::RecvRefreshDevices()
 {
   VRManager* vm = VRManager::Get();
   vm->RefreshVRDevices();
 
--- a/gfx/vr/ipc/VRManagerParent.h
+++ b/gfx/vr/ipc/VRManagerParent.h
@@ -3,17 +3,17 @@
  */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MOZILLA_GFX_VR_VRMANAGERPARENT_H
 #define MOZILLA_GFX_VR_VRMANAGERPARENT_H
 
-#include "mozilla/layers/CompositorBridgeParent.h" // for CompositorThreadHolder
+#include "mozilla/layers/CompositorThread.h" // for CompositorThreadHolder
 #include "mozilla/gfx/PVRManagerParent.h" // for PVRManagerParent
 #include "mozilla/ipc/ProtocolUtils.h"    // for IToplevelProtocol
 #include "mozilla/TimeStamp.h"            // for TimeStamp
 #include "gfxVR.h"                        // for VRFieldOfView
 
 namespace mozilla {
 namespace gfx {
 
--- a/js/public/GCAPI.h
+++ b/js/public/GCAPI.h
@@ -581,17 +581,18 @@ class JS_PUBLIC_API(AutoCheckCannotGC) :
 {
   public:
     AutoCheckCannotGC() : AutoAssertOnGC() {}
     explicit AutoCheckCannotGC(JSRuntime* rt) : AutoAssertOnGC(rt) {}
 } JS_HAZ_GC_INVALIDATED;
 
 /**
  * Unsets the gray bit for anything reachable from |thing|. |kind| should not be
- * JS::TraceKind::Shape. |thing| should be non-null.
+ * JS::TraceKind::Shape. |thing| should be non-null. The return value indicates
+ * if anything was unmarked.
  */
 extern JS_FRIEND_API(bool)
 UnmarkGrayGCThingRecursively(GCCellPtr thing);
 
 } /* namespace JS */
 
 namespace js {
 namespace gc {
--- a/js/public/TracingAPI.h
+++ b/js/public/TracingAPI.h
@@ -66,29 +66,47 @@ class JS_PUBLIC_API(JSTracer)
         Tenuring,
         Callback
     };
     bool isMarkingTracer() const { return tag_ == TracerKindTag::Marking || tag_ == TracerKindTag::WeakMarking; }
     bool isWeakMarkingTracer() const { return tag_ == TracerKindTag::WeakMarking; }
     bool isTenuringTracer() const { return tag_ == TracerKindTag::Tenuring; }
     bool isCallbackTracer() const { return tag_ == TracerKindTag::Callback; }
     inline JS::CallbackTracer* asCallbackTracer();
+#ifdef DEBUG
+    bool checkEdges() { return checkEdges_; }
+#endif
 
   protected:
     JSTracer(JSRuntime* rt, TracerKindTag tag,
              WeakMapTraceKind weakTraceKind = TraceWeakMapValues)
-      : runtime_(rt), weakMapAction_(weakTraceKind), tag_(tag)
+      : runtime_(rt)
+      , weakMapAction_(weakTraceKind)
+#ifdef DEBUG
+      , checkEdges_(true)
+#endif
+      , tag_(tag)
     {}
 
+#ifdef DEBUG
+    // Set whether to check edges are valid in debug builds.
+    void setCheckEdges(bool check) {
+        checkEdges_ = check;
+    }
+#endif
+
   private:
-    JSRuntime*          runtime_;
-    WeakMapTraceKind    weakMapAction_;
+    JSRuntime* runtime_;
+    WeakMapTraceKind weakMapAction_;
+#ifdef DEBUG
+    bool checkEdges_;
+#endif
 
   protected:
-    TracerKindTag       tag_;
+    TracerKindTag tag_;
 };
 
 namespace JS {
 
 class AutoTracingName;
 class AutoTracingIndex;
 class AutoTracingCallback;
 
--- a/js/src/gc/GCInternals.h
+++ b/js/src/gc/GCInternals.h
@@ -80,17 +80,22 @@ class MOZ_RAII AutoStopVerifyingBarriers
 {
     GCRuntime* gc;
     bool restartPreVerifier;
 
   public:
     AutoStopVerifyingBarriers(JSRuntime* rt, bool isShutdown)
       : gc(&rt->gc)
     {
-        restartPreVerifier = gc->endVerifyPreBarriers() && !isShutdown;
+        if (gc->isVerifyPreBarriersEnabled()) {
+            gc->endVerifyPreBarriers();
+            restartPreVerifier = !isShutdown;
+        } else {
+            restartPreVerifier = false;
+        }
     }
 
     ~AutoStopVerifyingBarriers() {
         // Nasty special case: verification runs a minor GC, which *may* nest
         // inside of an outer minor GC. This is not allowed by the
         // gc::Statistics phase tree. So we pause the "real" GC, if in fact one
         // is in progress.
         gcstats::Phase outer = gc->stats.currentPhase();
@@ -110,18 +115,18 @@ class MOZ_RAII AutoStopVerifyingBarriers
 #else
 struct MOZ_RAII AutoStopVerifyingBarriers
 {
     AutoStopVerifyingBarriers(JSRuntime*, bool) {}
 };
 #endif /* JS_GC_ZEAL */
 
 #ifdef JSGC_HASH_TABLE_CHECKS
-void
-CheckHashTablesAfterMovingGC(JSRuntime* rt);
+void CheckHashTablesAfterMovingGC(JSRuntime* rt);
+void CheckHeapAfterMovingGC(JSRuntime* rt);
 #endif
 
 struct MovingTracer : JS::CallbackTracer
 {
     explicit MovingTracer(JSRuntime* rt) : CallbackTracer(rt, TraceWeakMapKeysValues) {}
 
     void onObjectEdge(JSObject** objp) override;
     void onShapeEdge(Shape** shapep) override;
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -189,17 +189,17 @@ class GCSchedulingTunables
     double highFrequencyHeapGrowthMax() const { return highFrequencyHeapGrowthMax_; }
     double highFrequencyHeapGrowthMin() const { return highFrequencyHeapGrowthMin_; }
     double lowFrequencyHeapGrowth() const { return lowFrequencyHeapGrowth_; }
     bool isDynamicMarkSliceEnabled() const { return dynamicMarkSliceEnabled_; }
     bool areRefreshFrameSlicesEnabled() const { return refreshFrameSlicesEnabled_; }
     unsigned minEmptyChunkCount(const AutoLockGC&) const { return minEmptyChunkCount_; }
     unsigned maxEmptyChunkCount() const { return maxEmptyChunkCount_; }
 
-    bool setParameter(JSGCParamKey key, uint32_t value, const AutoLockGC& lock);
+    MOZ_MUST_USE bool setParameter(JSGCParamKey key, uint32_t value, const AutoLockGC& lock);
 };
 
 /*
  * GC Scheduling Overview
  * ======================
  *
  * Scheduling GC's in SpiderMonkey/Firefox is tremendously complicated because
  * of the large number of subtle, cross-cutting, and widely dispersed factors
@@ -579,59 +579,61 @@ class ChainedIter
 typedef HashMap<Value*, const char*, DefaultHasher<Value*>, SystemAllocPolicy> RootedValueMap;
 
 using AllocKinds = mozilla::EnumSet<AllocKind>;
 
 class GCRuntime
 {
   public:
     explicit GCRuntime(JSRuntime* rt);
-    bool init(uint32_t maxbytes, uint32_t maxNurseryBytes);
+    MOZ_MUST_USE bool init(uint32_t maxbytes, uint32_t maxNurseryBytes);
     void finishRoots();
     void finish();
 
     inline bool hasZealMode(ZealMode mode);
     inline void clearZealMode(ZealMode mode);
     inline bool upcomingZealousGC();
     inline bool needZealousGC();
 
-    bool addRoot(Value* vp, const char* name);
+    MOZ_MUST_USE bool addRoot(Value* vp, const char* name);
     void removeRoot(Value* vp);
     void setMarkStackLimit(size_t limit, AutoLockGC& lock);
 
-    bool setParameter(JSGCParamKey key, uint32_t value, AutoLockGC& lock);
+    MOZ_MUST_USE bool setParameter(JSGCParamKey key, uint32_t value, AutoLockGC& lock);
     uint32_t getParameter(JSGCParamKey key, const AutoLockGC& lock);
 
-    bool triggerGC(JS::gcreason::Reason reason);
+    MOZ_MUST_USE bool triggerGC(JS::gcreason::Reason reason);
     void maybeAllocTriggerZoneGC(Zone* zone, const AutoLockGC& lock);
+    // The return value indicates if we were able to do the GC.
     bool triggerZoneGC(Zone* zone, JS::gcreason::Reason reason);
-    bool maybeGC(Zone* zone);
+    MOZ_MUST_USE bool maybeGC(Zone* zone);
     void maybePeriodicFullGC();
     void minorGC(JS::gcreason::Reason reason) {
         gcstats::AutoPhase ap(stats, gcstats::PHASE_MINOR_GC);
         minorGCImpl(reason, nullptr);
     }
     void minorGC(JSContext* cx, JS::gcreason::Reason reason);
     void evictNursery(JS::gcreason::Reason reason = JS::gcreason::EVICT_NURSERY) {
         gcstats::AutoPhase ap(stats, gcstats::PHASE_EVICT_NURSERY);
         minorGCImpl(reason, nullptr);
     }
+    // The return value indicates whether a major GC was performed.
     bool gcIfRequested(JSContext* cx = nullptr);
     void gc(JSGCInvocationKind gckind, JS::gcreason::Reason reason);
     void startGC(JSGCInvocationKind gckind, JS::gcreason::Reason reason, int64_t millis = 0);
     void gcSlice(JS::gcreason::Reason reason, int64_t millis = 0);
     void finishGC(JS::gcreason::Reason reason);
     void abortGC();
     void startDebugGC(JSGCInvocationKind gckind, SliceBudget& budget);
     void debugGCSlice(SliceBudget& budget);
 
     void triggerFullGCForAtoms() {
         MOZ_ASSERT(fullGCForAtomsRequested_);
         fullGCForAtomsRequested_ = false;
-        triggerGC(JS::gcreason::ALLOC_TRIGGER);
+        MOZ_RELEASE_ASSERT(triggerGC(JS::gcreason::ALLOC_TRIGGER));
     }
 
     void runDebugGC();
     inline void poke();
 
     enum TraceOrMarkRuntime {
         TraceRuntime,
         MarkRuntime
@@ -750,36 +752,38 @@ class GCRuntime
     void disableGenerationalGC();
     void enableGenerationalGC();
 
     void disableCompactingGC();
     void enableCompactingGC();
     bool isCompactingGCEnabled() const;
 
     void setGrayRootsTracer(JSTraceDataOp traceOp, void* data);
-    bool addBlackRootsTracer(JSTraceDataOp traceOp, void* data);
+    MOZ_MUST_USE bool addBlackRootsTracer(JSTraceDataOp traceOp, void* data);
     void removeBlackRootsTracer(JSTraceDataOp traceOp, void* data);
 
     void setMaxMallocBytes(size_t value);
     int32_t getMallocBytes() const { return mallocBytesUntilGC; }
     void resetMallocBytes();
     bool isTooMuchMalloc() const { return mallocBytesUntilGC <= 0; }
     void updateMallocCounter(JS::Zone* zone, size_t nbytes);
     void onTooMuchMalloc();
 
     void setGCCallback(JSGCCallback callback, void* data);
     void callGCCallback(JSGCStatus status) const;
     void setObjectsTenuredCallback(JSObjectsTenuredCallback callback,
                                    void* data);
     void callObjectsTenuredCallback();
-    bool addFinalizeCallback(JSFinalizeCallback callback, void* data);
+    MOZ_MUST_USE bool addFinalizeCallback(JSFinalizeCallback callback, void* data);
     void removeFinalizeCallback(JSFinalizeCallback func);
-    bool addWeakPointerZoneGroupCallback(JSWeakPointerZoneGroupCallback callback, void* data);
+    MOZ_MUST_USE bool addWeakPointerZoneGroupCallback(JSWeakPointerZoneGroupCallback callback,
+                                                      void* data);
     void removeWeakPointerZoneGroupCallback(JSWeakPointerZoneGroupCallback callback);
-    bool addWeakPointerCompartmentCallback(JSWeakPointerCompartmentCallback callback, void* data);
+    MOZ_MUST_USE bool addWeakPointerCompartmentCallback(JSWeakPointerCompartmentCallback callback,
+                                                        void* data);
     void removeWeakPointerCompartmentCallback(JSWeakPointerCompartmentCallback callback);
     JS::GCSliceCallback setSliceCallback(JS::GCSliceCallback callback);
     JS::GCNurseryCollectionCallback setNurseryCollectionCallback(
         JS::GCNurseryCollectionCallback callback);
 
     void setFullCompartmentChecks(bool enable);
 
     bool isManipulatingDeadZones() { return manipulatingDeadZones; }
@@ -836,17 +840,17 @@ class GCRuntime
     const ChunkPool& emptyChunks(const AutoLockGC& lock) const { return emptyChunks_; }
     typedef ChainedIter<Chunk*, ChunkPool::Iter, ChunkPool::Iter> NonEmptyChunksIter;
     NonEmptyChunksIter allNonEmptyChunks() {
         return NonEmptyChunksIter(ChunkPool::Iter(availableChunks_), ChunkPool::Iter(fullChunks_));
     }
 
 #ifdef JS_GC_ZEAL
     void startVerifyPreBarriers();
-    bool endVerifyPreBarriers();
+    void endVerifyPreBarriers();
     void finishVerifier();
     bool isVerifyPreBarriersEnabled() const { return !!verifyPreData; }
 #else
     bool isVerifyPreBarriersEnabled() const { return false; }
 #endif
 
     // Free certain LifoAlloc blocks from the background sweep thread.
     void freeUnusedLifoBlocksAfterSweeping(LifoAlloc* lifo);
@@ -855,17 +859,17 @@ class GCRuntime
     // Public here for ReleaseArenaLists and FinalizeTypedArenas.
     void releaseArena(Arena* arena, const AutoLockGC& lock);
 
     void releaseHeldRelocatedArenas();
     void releaseHeldRelocatedArenasWithoutUnlocking(const AutoLockGC& lock);
 
     // Allocator
     template <AllowGC allowGC>
-    bool checkAllocatorState(JSContext* cx, AllocKind kind);
+    MOZ_MUST_USE bool checkAllocatorState(JSContext* cx, AllocKind kind);
     template <AllowGC allowGC>
     JSObject* tryNewNurseryObject(JSContext* cx, size_t thingSize, size_t nDynamicSlots,
                                   const Class* clasp);
     template <AllowGC allowGC>
     static JSObject* tryNewTenuredObject(ExclusiveContext* cx, AllocKind kind, size_t thingSize,
                                          size_t nDynamicSlots);
     template <typename T, AllowGC allowGC>
     static T* tryNewTenuredThing(ExclusiveContext* cx, AllocKind kind, size_t thingSize);
@@ -883,17 +887,17 @@ class GCRuntime
     // For ArenaLists::allocateFromArena()
     friend class ArenaLists;
     Chunk* pickChunk(const AutoLockGC& lock,
                      AutoMaybeStartBackgroundAllocation& maybeStartBGAlloc);
     Arena* allocateArena(Chunk* chunk, Zone* zone, AllocKind kind, const AutoLockGC& lock);
     void arenaAllocatedDuringGC(JS::Zone* zone, Arena* arena);
 
     // Allocator internals
-    bool gcIfNeededPerAllocation(JSContext* cx);
+    MOZ_MUST_USE bool gcIfNeededPerAllocation(JSContext* cx);
     template <typename T>
     static void checkIncrementalZoneState(ExclusiveContext* cx, T* t);
     static void* refillFreeListFromAnyThread(ExclusiveContext* cx, AllocKind thingKind,
                                              size_t thingSize);
     static void* refillFreeListFromMainThread(JSContext* cx, AllocKind thingKind,
                                               size_t thingSize);
     static void* refillFreeListOffMainThread(ExclusiveContext* cx, AllocKind thingKind);
 
@@ -916,42 +920,43 @@ class GCRuntime
     void resetIncrementalGC(const char* reason);
 
     // Assert if the system state is such that we should never
     // receive a request to do GC work.
     void checkCanCallAPI();
 
     // Check if the system state is such that GC has been supressed
     // or otherwise delayed.
-    bool checkIfGCAllowedInCurrentState(JS::gcreason::Reason reason);
+    MOZ_MUST_USE bool checkIfGCAllowedInCurrentState(JS::gcreason::Reason reason);
 
     gcstats::ZoneGCStats scanZonesBeforeGC();
     void collect(bool nonincrementalByAPI, SliceBudget budget, JS::gcreason::Reason reason) JS_HAZ_GC_CALL;
-    bool gcCycle(bool nonincrementalByAPI, SliceBudget& budget, JS::gcreason::Reason reason);
+    MOZ_MUST_USE bool gcCycle(bool nonincrementalByAPI, SliceBudget& budget,
+                              JS::gcreason::Reason reason);
     void incrementalCollectSlice(SliceBudget& budget, JS::gcreason::Reason reason);
 
     void pushZealSelectedObjects();
     void purgeRuntime();
-    bool beginMarkPhase(JS::gcreason::Reason reason);
+    MOZ_MUST_USE bool beginMarkPhase(JS::gcreason::Reason reason);
     bool shouldPreserveJITCode(JSCompartment* comp, int64_t currentTime,
                                JS::gcreason::Reason reason);
     void bufferGrayRoots();
     void markCompartments();
     IncrementalProgress drainMarkStack(SliceBudget& sliceBudget, gcstats::Phase phase);
     template <class CompartmentIterT> void markWeakReferences(gcstats::Phase phase);
     void markWeakReferencesInCurrentGroup(gcstats::Phase phase);
     template <class ZoneIterT, class CompartmentIterT> void markGrayReferences(gcstats::Phase phase);
     void markBufferedGrayRoots(JS::Zone* zone);
     void markGrayReferencesInCurrentGroup(gcstats::Phase phase);
     void markAllWeakReferences(gcstats::Phase phase);
     void markAllGrayReferences(gcstats::Phase phase);
 
     void beginSweepPhase(bool lastGC);
     void findZoneGroups();
-    bool findZoneEdgesForWeakMaps();
+    MOZ_MUST_USE bool findZoneEdgesForWeakMaps();
     void getNextZoneGroup();
     void endMarkingZoneGroup();
     void beginSweepingZoneGroup();
     bool shouldReleaseObservedTypes();
     void endSweepingZoneGroup();
     IncrementalProgress sweepPhase(SliceBudget& sliceBudget);
     void endSweepPhase(bool lastGC);
     void sweepZones(FreeOp* fop, bool lastGC);
@@ -962,18 +967,18 @@ class GCRuntime
     void sweepBackgroundThings(ZoneList& zones, LifoAlloc& freeBlocks, ThreadType threadType);
     void assertBackgroundSweepingFinished();
     bool shouldCompact();
     void beginCompactPhase();
     IncrementalProgress compactPhase(JS::gcreason::Reason reason, SliceBudget& sliceBudget);
     void endCompactPhase(JS::gcreason::Reason reason);
     void sweepTypesAfterCompacting(Zone* zone);
     void sweepZoneAfterCompacting(Zone* zone);
-    bool relocateArenas(Zone* zone, JS::gcreason::Reason reason, Arena*& relocatedListOut,
-                        SliceBudget& sliceBudget);
+    MOZ_MUST_USE bool relocateArenas(Zone* zone, JS::gcreason::Reason reason,
+                                     Arena*& relocatedListOut, SliceBudget& sliceBudget);
     void updateTypeDescrObjects(MovingTracer* trc, Zone* zone);
     void updateCellPointers(MovingTracer* trc, Zone* zone, AllocKinds kinds, size_t bgTaskCount);
     void updateAllCellPointers(MovingTracer* trc, Zone* zone);
     void updatePointersToRelocatedCells(Zone* zone);
     void protectAndHoldArenas(Arena* arenaList);
     void unprotectHeldRelocatedArenas();
     void releaseRelocatedArenas(Arena* arenaList);
     void releaseRelocatedArenasWithoutUnlocking(Arena* arenaList, const AutoLockGC& lock);
@@ -1042,16 +1047,18 @@ class GCRuntime
     // An incrementing id used to assign unique ids to cells that require one.
     mozilla::Atomic<uint64_t, mozilla::ReleaseAcquire> nextCellUniqueId_;
 
     /*
      * Number of the committed arenas in all GC chunks including empty chunks.
      */
     mozilla::Atomic<uint32_t, mozilla::ReleaseAcquire> numArenasFreeCommitted;
     VerifyPreTracer* verifyPreData;
+
+  private:
     bool chunkAllocationSinceLastGC;
     int64_t nextFullGCTime;
     int64_t lastGCTime;
 
     JSGCMode mode;
 
     mozilla::Atomic<size_t, mozilla::ReleaseAcquire> numActiveZoneIters;
 
--- a/js/src/gc/GCTrace.h
+++ b/js/src/gc/GCTrace.h
@@ -12,33 +12,33 @@
 namespace js {
 
 class ObjectGroup;
 
 namespace gc {
 
 #ifdef JS_GC_TRACE
 
-extern bool InitTrace(GCRuntime& gc);
+extern MOZ_MUST_USE bool InitTrace(GCRuntime& gc);
 extern void FinishTrace();
 extern bool TraceEnabled();
 extern void TraceNurseryAlloc(Cell* thing, size_t size);
 extern void TraceTenuredAlloc(Cell* thing, AllocKind kind);
 extern void TraceCreateObject(JSObject* object);
 extern void TraceMinorGCStart();
 extern void TracePromoteToTenured(Cell* src, Cell* dst);
 extern void TraceMinorGCEnd();
 extern void TraceMajorGCStart();
 extern void TraceTenuredFinalize(Cell* thing);
 extern void TraceMajorGCEnd();
 extern void TraceTypeNewScript(js::ObjectGroup* group);
 
 #else
 
-inline bool InitTrace(GCRuntime& gc) { return true; }
+inline MOZ_MUST_USE bool InitTrace(GCRuntime& gc) { return true; }
 inline void FinishTrace() {}
 inline bool TraceEnabled() { return false; }
 inline void TraceNurseryAlloc(Cell* thing, size_t size) {}
 inline void TraceTenuredAlloc(Cell* thing, AllocKind kind) {}
 inline void TraceCreateObject(JSObject* object) {}
 inline void TraceMinorGCStart() {}
 inline void TracePromoteToTenured(Cell* src, Cell* dst) {}
 inline void TraceMinorGCEnd() {}
--- a/js/src/gc/Heap.h
+++ b/js/src/gc/Heap.h
@@ -48,16 +48,17 @@ extern bool
 RuntimeFromMainThreadIsHeapMajorCollecting(JS::shadow::Zone* shadowZone);
 
 // Barriers can't be triggered during backend Ion compilation, which may run on
 // a helper thread.
 extern bool
 CurrentThreadIsIonCompiling();
 #endif
 
+// The return value indicates if anything was unmarked.
 extern bool
 UnmarkGrayCellRecursively(gc::Cell* cell, JS::TraceKind kind);
 
 extern void
 TraceManuallyBarrieredGenericPointerEdge(JSTracer* trc, gc::Cell** thingp, const char* name);
 
 namespace gc {
 
@@ -290,16 +291,17 @@ class TenuredCell : public Cell
 {
   public:
     // Construct a TenuredCell from a void*, making various sanity assertions.
     static MOZ_ALWAYS_INLINE TenuredCell* fromPointer(void* ptr);
     static MOZ_ALWAYS_INLINE const TenuredCell* fromPointer(const void* ptr);
 
     // Mark bit management.
     MOZ_ALWAYS_INLINE bool isMarked(uint32_t color = BLACK) const;
+    // The return value indicates if the cell went from unmarked to marked.
     MOZ_ALWAYS_INLINE bool markIfUnmarked(uint32_t color = BLACK) const;
     MOZ_ALWAYS_INLINE void unmark(uint32_t color) const;
     MOZ_ALWAYS_INLINE void copyMarkBitsFrom(const TenuredCell* src);
 
     // Note: this is in TenuredCell because JSObject subclasses are sometimes
     // used tagged.
     static MOZ_ALWAYS_INLINE bool isNullLike(const Cell* thing) { return !thing; }
 
@@ -872,16 +874,17 @@ struct ChunkBitmap
     }
 
     MOZ_ALWAYS_INLINE MOZ_TSAN_BLACKLIST bool isMarked(const Cell* cell, uint32_t color) {
         uintptr_t* word, mask;
         getMarkWordAndMask(cell, color, &word, &mask);
         return *word & mask;
     }
 
+    // The return value indicates if the cell went from unmarked to marked.
     MOZ_ALWAYS_INLINE bool markIfUnmarked(const Cell* cell, uint32_t color) {
         uintptr_t* word, mask;
         getMarkWordAndMask(cell, BLACK, &word, &mask);
         if (*word & mask)
             return false;
         *word |= mask;
         if (color != BLACK) {
             /*
@@ -990,17 +993,17 @@ struct Chunk
         return info.trailer.storeBuffer;
     }
 
     Arena* allocateArena(JSRuntime* rt, JS::Zone* zone, AllocKind kind, const AutoLockGC& lock);
 
     void releaseArena(JSRuntime* rt, Arena* arena, const AutoLockGC& lock);
     void recycleArena(Arena* arena, SortedArenaList& dest, size_t thingsPerArena);
 
-    bool decommitOneFreeArena(JSRuntime* rt, AutoLockGC& lock);
+    MOZ_MUST_USE bool decommitOneFreeArena(JSRuntime* rt, AutoLockGC& lock);
     void decommitAllArenasWithoutUnlocking(const AutoLockGC& lock);
 
     static Chunk* allocate(JSRuntime* rt);
 
   private:
     inline void init(JSRuntime* rt);
 
     void decommitAllArenas(JSRuntime* rt);
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -176,16 +176,19 @@ template <> bool ThingIsPermanentAtomOrW
 template<typename T>
 void
 js::CheckTracedThing(JSTracer* trc, T* thing)
 {
 #ifdef DEBUG
     MOZ_ASSERT(trc);
     MOZ_ASSERT(thing);
 
+    if (!trc->checkEdges())
+        return;
+
     thing = MaybeForwarded(thing);
 
     /* This function uses data that's not available in the nursery. */
     if (IsInsideNursery(thing))
         return;
 
     MOZ_ASSERT_IF(!IsMovingTracer(trc) && !trc->isTenuringTracer(), !IsForwarded(thing));
 
--- a/js/src/gc/Marking.h
+++ b/js/src/gc/Marking.h
@@ -82,33 +82,33 @@ class MarkStack
     ptrdiff_t position() const { return tos_ - stack_; }
 
     void setStack(uintptr_t* stack, size_t tosIndex, size_t capacity) {
         stack_ = stack;
         tos_ = stack + tosIndex;
         end_ = stack + capacity;
     }
 
-    bool init(JSGCMode gcMode);
+    MOZ_MUST_USE bool init(JSGCMode gcMode);
 
     void setBaseCapacity(JSGCMode mode);
     size_t maxCapacity() const { return maxCapacity_; }
     void setMaxCapacity(size_t maxCapacity);
 
-    bool push(uintptr_t item) {
+    MOZ_MUST_USE bool push(uintptr_t item) {
         if (tos_ == end_) {
             if (!enlarge(1))
                 return false;
         }
         MOZ_ASSERT(tos_ < end_);
         *tos_++ = item;
         return true;
     }
 
-    bool push(uintptr_t item1, uintptr_t item2, uintptr_t item3) {
+    MOZ_MUST_USE bool push(uintptr_t item1, uintptr_t item2, uintptr_t item3) {
         uintptr_t* nextTos = tos_ + 3;
         if (nextTos > end_) {
             if (!enlarge(3))
                 return false;
             nextTos = tos_ + 3;
         }
         MOZ_ASSERT(nextTos <= end_);
         tos_[0] = item1;
@@ -125,17 +125,17 @@ class MarkStack
     uintptr_t pop() {
         MOZ_ASSERT(!isEmpty());
         return *--tos_;
     }
 
     void reset();
 
     /* Grow the stack, ensuring there is space for at least count elements. */
-    bool enlarge(unsigned count);
+    MOZ_MUST_USE bool enlarge(unsigned count);
 
     void setGCMode(JSGCMode gcMode);
 
     size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
 };
 
 namespace gc {
 
@@ -163,17 +163,17 @@ using WeakKeyTable = OrderedHashMap<JS::
                                     js::SystemAllocPolicy>;
 
 } /* namespace gc */
 
 class GCMarker : public JSTracer
 {
   public:
     explicit GCMarker(JSRuntime* rt);
-    bool init(JSGCMode gcMode);
+    MOZ_MUST_USE bool init(JSGCMode gcMode);
 
     void setMaxCapacity(size_t maxCap) { stack.setMaxCapacity(maxCap); }
     size_t maxCapacity() const { return stack.maxCapacity(); }
 
     void start();
     void stop();
     void reset();
 
@@ -211,26 +211,26 @@ class GCMarker : public JSTracer
     void abortLinearWeakMarking() {
         leaveWeakMarkingMode();
         linearWeakMarkingDisabled_ = true;
     }
 
     void delayMarkingArena(gc::Arena* arena);
     void delayMarkingChildren(const void* thing);
     void markDelayedChildren(gc::Arena* arena);
-    bool markDelayedChildren(SliceBudget& budget);
+    MOZ_MUST_USE bool markDelayedChildren(SliceBudget& budget);
     bool hasDelayedChildren() const {
         return !!unmarkedArenaStackTop;
     }
 
     bool isDrained() {
         return isMarkStackEmpty() && !unmarkedArenaStackTop;
     }
 
-    bool drainMarkStack(SliceBudget& budget);
+    MOZ_MUST_USE bool drainMarkStack(SliceBudget& budget);
 
     void setGCMode(JSGCMode mode) { stack.setGCMode(mode); }
 
     size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
 
 #ifdef DEBUG
     bool shouldCheckCompartments() { return strictCompartmentChecking; }
 #endif
@@ -284,17 +284,17 @@ class GCMarker : public JSTracer
 
     // We may not have concrete types yet, so this has to be outside the header.
     template <typename T>
     void dispatchToTraceChildren(T* thing);
 
     // Mark the given GC thing, but do not trace its children. Return true
     // if the thing became marked.
     template <typename T>
-    bool mark(T* thing);
+    MOZ_MUST_USE bool mark(T* thing);
 
     void pushTaggedPtr(StackTag tag, void* ptr) {
         checkZone(ptr);
         uintptr_t addr = reinterpret_cast<uintptr_t>(ptr);
         MOZ_ASSERT(!(addr & StackTagMask));
         if (!stack.push(addr | uintptr_t(tag)))
             delayMarkingChildren(ptr);
     }
@@ -314,17 +314,17 @@ class GCMarker : public JSTracer
         if (!stack.push(endAddr, startAddr, tagged))
             delayMarkingChildren(obj);
     }
 
     bool isMarkStackEmpty() {
         return stack.isEmpty();
     }
 
-    bool restoreValueArray(JSObject* obj, void** vpp, void** endp);
+    MOZ_MUST_USE bool restoreValueArray(JSObject* obj, void** vpp, void** endp);
     void saveValueRanges();
     inline void processMarkStackTop(SliceBudget& budget);
 
     /* The mark stack. Pointers in this stack are "gray" in the GC sense. */
     MarkStack stack;
 
     /* The color is only applied to objects and functions. */
     uint32_t color;
@@ -446,16 +446,17 @@ struct RewrapTaggedPointer<Value, T>
 {
     static Value wrap(typename IsPrivateGCThingInValue<T>::Type* thing) {
         return JS::PrivateGCThingValue(thing);
     }
 };
 
 } /* namespace gc */
 
+// The return value indicates if anything was unmarked.
 bool
 UnmarkGrayShapeRecursively(Shape* shape);
 
 template<typename T>
 void
 CheckTracedThing(JSTracer* trc, T* thing);
 
 template<typename T>
--- a/js/src/gc/Memory.cpp
+++ b/js/src/gc/Memory.cpp
@@ -257,24 +257,23 @@ MarkPagesUnused(void* p, size_t size)
     if (!DecommitEnabled())
         return true;
 
     MOZ_ASSERT(OffsetFromAligned(p, pageSize) == 0);
     LPVOID p2 = MapMemoryAt(p, size, MEM_RESET);
     return p2 == p;
 }
 
-bool
+void
 MarkPagesInUse(void* p, size_t size)
 {
     if (!DecommitEnabled())
-        return true;
+        return;
 
     MOZ_ASSERT(OffsetFromAligned(p, pageSize) == 0);
-    return true;
 }
 
 size_t
 GetPageFaultCount()
 {
     PROCESS_MEMORY_COUNTERS pmc;
     if (!GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc)))
         return 0;
@@ -315,17 +314,16 @@ MarkPagesUnused(void* p, size_t size)
     MOZ_ASSERT(OffsetFromAligned(p, pageSize) == 0);
     return true;
 }
 
 bool
 MarkPagesInUse(void* p, size_t size)
 {
     MOZ_ASSERT(OffsetFromAligned(p, pageSize) == 0);
-    return true;
 }
 
 size_t
 GetPageFaultCount()
 {
     // GetProcessMemoryInfo is unavailable.
     return 0;
 }
@@ -394,20 +392,19 @@ MarkPagesUnused(void* p, size_t size)
     MOZ_ASSERT(OffsetFromAligned(p, pageSize) == 0);
     return true;
 }
 
 bool
 MarkPagesInUse(void* p, size_t size)
 {
     if (!DecommitEnabled())
-        return true;
+        return;
 
     MOZ_ASSERT(OffsetFromAligned(p, pageSize) == 0);
-    return true;
 }
 
 size_t
 GetPageFaultCount()
 {
     return 0;
 }
 
@@ -662,24 +659,23 @@ MarkPagesUnused(void* p, size_t size)
     if (!DecommitEnabled())
         return false;
 
     MOZ_ASSERT(OffsetFromAligned(p, pageSize) == 0);
     int result = madvise(p, size, MADV_DONTNEED);
     return result != -1;
 }
 
-bool
+void
 MarkPagesInUse(void* p, size_t size)
 {
     if (!DecommitEnabled())
-        return true;
+        return;
 
     MOZ_ASSERT(OffsetFromAligned(p, pageSize) == 0);
-    return true;
 }
 
 size_t
 GetPageFaultCount()
 {
     struct rusage usage;
     int err = getrusage(RUSAGE_SELF, &usage);
     if (err)
--- a/js/src/gc/Memory.h
+++ b/js/src/gc/Memory.h
@@ -24,17 +24,17 @@ void UnmapPages(void* p, size_t size);
 
 // Tell the OS that the given pages are not in use, so they should not be
 // written to a paging file. This may be a no-op on some platforms.
 bool MarkPagesUnused(void* p, size_t size);
 
 // Undo |MarkPagesUnused|: tell the OS that the given pages are of interest
 // and should be paged in and out normally. This may be a no-op on some
 // platforms.
-bool MarkPagesInUse(void* p, size_t size);
+void MarkPagesInUse(void* p, size_t size);
 
 // Returns #(hard faults) + #(soft faults)
 size_t GetPageFaultCount();
 
 // Allocate memory mapped content.
 // The offset must be aligned according to alignment requirement.
 void* AllocateMappedContent(int fd, size_t offset, size_t length, size_t alignment);
 
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -497,16 +497,23 @@ js::Nursery::collect(JSRuntime* rt, JS::
     // Make sure hashtables have been updated after the collection.
     TIME_START(checkHashTables);
 #ifdef JS_GC_ZEAL
     if (rt->hasZealMode(ZealMode::CheckHashTablesOnMinorGC))
         CheckHashTablesAfterMovingGC(rt);
 #endif
     TIME_END(checkHashTables);
 
+    TIME_START(checkHeap);
+#ifdef JS_GC_ZEAL
+    if (rt->hasZealMode(ZealMode::CheckHeapOnMovingGC))
+        CheckHeapAfterMovingGC(rt);
+#endif
+    TIME_END(checkHeap);
+
     // Resize the nursery.
     TIME_START(resize);
     double promotionRate = mover.tenuredSize / double(allocationEnd() - start());
     if (promotionRate > 0.05)
         growAllocableSpace();
     else if (promotionRate < 0.01)
         shrinkAllocableSpace();
     TIME_END(resize);
@@ -547,16 +554,17 @@ js::Nursery::collect(JSRuntime* rt, JS::
         } PrintList[] = {
             {"canIon", TIME_TOTAL(cancelIonCompilations)},
             {"mkVals", TIME_TOTAL(traceValues)},
             {"mkClls", TIME_TOTAL(traceCells)},
             {"mkSlts", TIME_TOTAL(traceSlots)},
             {"mcWCll", TIME_TOTAL(traceWholeCells)},
             {"mkGnrc", TIME_TOTAL(traceGenericEntries)},
             {"ckTbls", TIME_TOTAL(checkHashTables)},
+            {"ckHeap", TIME_TOTAL(checkHeap)},
             {"mkRntm", TIME_TOTAL(markRuntime)},
             {"mkDbgr", TIME_TOTAL(markDebugger)},
             {"clrNOC", TIME_TOTAL(clearNewObjectCache)},
             {"collct", TIME_TOTAL(collectToFP)},
             {" tenCB", TIME_TOTAL(objectsTenuredCallback)},
             {"swpABO", TIME_TOTAL(sweepArrayBufferViewList)},
             {"updtIn", TIME_TOTAL(updateJitActivations)},
             {"frSlts", TIME_TOTAL(freeMallocedBuffers)},
--- a/js/src/gc/Nursery.h
+++ b/js/src/gc/Nursery.h
@@ -105,17 +105,17 @@ class Nursery
         numActiveChunks_(0),
         numNurseryChunks_(0),
         profileThreshold_(0),
         enableProfiling_(false),
         freeMallocedBuffersTask(nullptr)
     {}
     ~Nursery();
 
-    bool init(uint32_t maxNurseryBytes);
+    MOZ_MUST_USE bool init(uint32_t maxNurseryBytes);
 
     bool exists() const { return numNurseryChunks_ != 0; }
     size_t numChunks() const { return numNurseryChunks_; }
     size_t nurserySize() const { return numNurseryChunks_ << ChunkShift; }
 
     void enable();
     void disable();
     bool isEnabled() const { return numActiveChunks_ != 0; }
@@ -166,34 +166,34 @@ class Nursery
      */
     void collect(JSRuntime* rt, JS::gcreason::Reason reason, ObjectGroupList* pretenureGroups);
 
     /*
      * Check if the thing at |*ref| in the Nursery has been forwarded. If so,
      * sets |*ref| to the new location of the object and returns true. Otherwise
      * returns false and leaves |*ref| unset.
      */
-    MOZ_ALWAYS_INLINE bool getForwardedPointer(JSObject** ref) const;
+    MOZ_ALWAYS_INLINE MOZ_MUST_USE bool getForwardedPointer(JSObject** ref) const;
 
     /* Forward a slots/elements pointer stored in an Ion frame. */
     void forwardBufferPointer(HeapSlot** pSlotsElems);
 
     void maybeSetForwardingPointer(JSTracer* trc, void* oldData, void* newData, bool direct) {
         if (trc->isTenuringTracer() && isInside(oldData))
             setForwardingPointer(oldData, newData, direct);
     }
 
     /* Mark a malloced buffer as no longer needing to be freed. */
     void removeMallocedBuffer(void* buffer) {
         mallocedBuffers.remove(buffer);
     }
 
     void waitBackgroundFreeEnd();
 
-    bool addedUniqueIdToCell(gc::Cell* cell) {
+    MOZ_MUST_USE bool addedUniqueIdToCell(gc::Cell* cell) {
         if (!IsInsideNursery(cell) || !isEnabled())
             return true;
         MOZ_ASSERT(cellsWithUid_.initialized());
         MOZ_ASSERT(!cellsWithUid_.has(cell));
         return cellsWithUid_.put(cell);
     }
 
     size_t sizeOfHeapCommitted() const {
--- a/js/src/gc/Statistics.h
+++ b/js/src/gc/Statistics.h
@@ -155,32 +155,32 @@ struct Statistics
      * DAGs, this decision should be reconsidered.
      */
     static const size_t MaxMultiparentPhases = 6;
     static const size_t NumTimingArrays = MaxMultiparentPhases + 1;
 
     /* Create a convenient type for referring to tables of phase times. */
     using PhaseTimeTable = int64_t[NumTimingArrays][PHASE_LIMIT];
 
-    static bool initialize();
+    static MOZ_MUST_USE bool initialize();
 
     explicit Statistics(JSRuntime* rt);
     ~Statistics();
 
     void beginPhase(Phase phase);
     void endPhase(Phase phase);
     void endParallelPhase(Phase phase, const GCParallelTask* task);
 
     void beginSlice(const ZoneGCStats& zoneStats, JSGCInvocationKind gckind,
                     SliceBudget budget, JS::gcreason::Reason reason);
     void endSlice();
     void setSliceCycleCount(unsigned cycleCount);
 
-    bool startTimingMutator();
-    bool stopTimingMutator(double& mutator_ms, double& gc_ms);
+    MOZ_MUST_USE bool startTimingMutator();
+    MOZ_MUST_USE bool stopTimingMutator(double& mutator_ms, double& gc_ms);
 
     void reset(const char* reason) {
         if (!aborted)
             slices.back().resetReason = reason;
     }
 
     void nonincremental(const char* reason) { nonincrementalReason_ = reason; }
 
--- a/js/src/gc/StoreBuffer.cpp
+++ b/js/src/gc/StoreBuffer.cpp
@@ -61,32 +61,30 @@ StoreBuffer::disable()
     if (!enabled_)
         return;
 
     aboutToOverflow_ = false;
 
     enabled_ = false;
 }
 
-bool
+void
 StoreBuffer::clear()
 {
     if (!enabled_)
-        return true;
+        return;
 
     aboutToOverflow_ = false;
     cancelIonCompilations_ = false;
 
     bufferVal.clear();
     bufferCell.clear();
     bufferSlot.clear();
     bufferWholeCell.clear();
     bufferGeneric.clear();
-
-    return true;
 }
 
 void
 StoreBuffer::setAboutToOverflow()
 {
     if (!aboutToOverflow_) {
         aboutToOverflow_ = true;
         runtime_->gc.stats.count(gcstats::STAT_STOREBUFFER_OVERFLOW);
--- a/js/src/gc/StoreBuffer.h
+++ b/js/src/gc/StoreBuffer.h
@@ -69,17 +69,17 @@ class StoreBuffer
         T last_;
 
         /* Maximum number of entries before we request a minor GC. */
         const static size_t MaxEntries = 48 * 1024 / sizeof(T);
 
         explicit MonoTypeBuffer() : last_(T()) {}
         ~MonoTypeBuffer() { stores_.finish(); }
 
-        bool init() {
+        MOZ_MUST_USE bool init() {
             if (!stores_.initialized() && !stores_.init())
                 return false;
             clear();
             return true;
         }
 
         void clear() {
             last_ = T();
@@ -136,17 +136,17 @@ class StoreBuffer
 
     struct GenericBuffer
     {
         LifoAlloc* storage_;
 
         explicit GenericBuffer() : storage_(nullptr) {}
         ~GenericBuffer() { js_delete(storage_); }
 
-        bool init() {
+        MOZ_MUST_USE bool init() {
             if (!storage_)
                 storage_ = js_new<LifoAlloc>(LifoAllocBlockSize);
             clear();
             return bool(storage_);
         }
 
         void clear() {
             if (!storage_)
@@ -405,17 +405,17 @@ class StoreBuffer
 #endif
     {
     }
 
     bool enable();
     void disable();
     bool isEnabled() const { return enabled_; }
 
-    bool clear();
+    void clear();
 
     /* Get the overflowed status. */
     bool isAboutToOverflow() const { return aboutToOverflow_; }
 
     bool cancelIonCompilations() const { return cancelIonCompilations_; }
 
     /* Insert a single edge into the buffer/remembered set. */
     void putValue(JS::Value* vp) { put(bufferVal, ValueEdge(vp)); }
--- a/js/src/gc/Verifier.cpp
+++ b/js/src/gc/Verifier.cpp
@@ -3,16 +3,18 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifdef MOZ_VALGRIND
 # include <valgrind/memcheck.h>
 #endif
 
+#include "mozilla/IntegerPrintfMacros.h"
+
 #include "jscntxt.h"
 #include "jsgc.h"
 #include "jsprf.h"
 
 #include "gc/GCInternals.h"
 #include "gc/Zone.h"
 #include "js/GCAPI.h"
 #include "js/HashTable.h"
@@ -297,23 +299,23 @@ AssertMarkedOrAllocated(const EdgeValue&
         return;
 
     char msgbuf[1024];
     JS_snprintf(msgbuf, sizeof(msgbuf), "[barrier verifier] Unmarked edge: %s", edge.label);
     MOZ_ReportAssertionFailure(msgbuf, __FILE__, __LINE__);
     MOZ_CRASH();
 }
 
-bool
+void
 gc::GCRuntime::endVerifyPreBarriers()
 {
     VerifyPreTracer* trc = verifyPreData;
 
     if (!trc)
-        return false;
+        return;
 
     MOZ_ASSERT(!JS::IsGenerationalGCEnabled(rt));
 
     AutoPrepareForTracing prep(rt, SkipAtoms);
 
     bool compartmentCreated = false;
 
     /* We need to disable barriers before tracing, which may invoke barriers. */
@@ -352,17 +354,16 @@ gc::GCRuntime::endVerifyPreBarriers()
             node = NextNode(node);
         }
     }
 
     marker.reset();
     marker.stop();
 
     js_delete(trc);
-    return true;
 }
 
 /*** Barrier Verifier Scheduling ***/
 
 void
 gc::GCRuntime::verifyPreBarriers()
 {
     if (verifyPreData)
@@ -409,8 +410,128 @@ js::gc::GCRuntime::finishVerifier()
 {
     if (verifyPreData) {
         js_delete(verifyPreData);
         verifyPreData = nullptr;
     }
 }
 
 #endif /* JS_GC_ZEAL */
+
+#ifdef JSGC_HASH_TABLE_CHECKS
+
+class CheckHeapTracer : public JS::CallbackTracer
+{
+  public:
+    explicit CheckHeapTracer(JSRuntime* rt);
+    bool init();
+    bool check();
+
+  private:
+    void onChild(const JS::GCCellPtr& thing) override;
+
+    struct WorkItem {
+        WorkItem(JS::GCCellPtr thing, const char* name, int parentIndex)
+          : thing(thing), name(name), parentIndex(parentIndex), processed(false)
+        {}
+
+        JS::GCCellPtr thing;
+        const char* name;
+        int parentIndex;
+        bool processed;
+    };
+
+    JSRuntime* rt;
+    bool oom;
+    size_t failures;
+    HashSet<Cell*, DefaultHasher<Cell*>, SystemAllocPolicy> visited;
+    Vector<WorkItem, 0, SystemAllocPolicy> stack;
+    int parentIndex;
+};
+
+CheckHeapTracer::CheckHeapTracer(JSRuntime* rt)
+  : CallbackTracer(rt, TraceWeakMapKeysValues),
+    rt(rt),
+    oom(false),
+    failures(0),
+    parentIndex(-1)
+{
+    setCheckEdges(false);
+}
+
+bool
+CheckHeapTracer::init()
+{
+    return visited.init();
+}
+
+void
+CheckHeapTracer::onChild(const JS::GCCellPtr& thing)
+{
+    Cell* cell = thing.asCell();
+    if (visited.lookup(cell))
+        return;
+
+    if (!visited.put(cell)) {
+        oom = true;
+        return;
+    }
+
+    if (!IsGCThingValidAfterMovingGC(cell)) {
+        failures++;
+        fprintf(stderr, "Stale pointer %p\n", cell);
+        const char* name = contextName();
+        for (int index = parentIndex; index != -1; index = stack[index].parentIndex) {
+            const WorkItem& parent = stack[index];
+            cell = parent.thing.asCell();
+            fprintf(stderr, "  from %s %p %s edge\n",
+                    GCTraceKindToAscii(cell->getTraceKind()), cell, name);
+            name = parent.name;
+        }
+        fprintf(stderr, "  from root %s\n", name);
+        return;
+    }
+
+    WorkItem item(thing, contextName(), parentIndex);
+    if (!stack.append(item))
+        oom = true;
+}
+
+bool
+CheckHeapTracer::check()
+{
+    // The analysis thinks that markRuntime might GC by calling a GC callback.
+    JS::AutoSuppressGCAnalysis nogc(rt);
+    rt->gc.markRuntime(this, GCRuntime::TraceRuntime);
+
+    while (!stack.empty()) {
+        WorkItem item = stack.back();
+        if (item.processed) {
+            stack.popBack();
+        } else {
+            parentIndex = stack.length() - 1;
+            TraceChildren(this, item.thing);
+            stack.back().processed = true;
+        }
+    }
+
+    if (oom)
+        return false;
+
+    if (failures) {
+        fprintf(stderr, "Heap check: %zu failure(s) out of %" PRIu32 " pointers checked\n",
+                failures, visited.count());
+    }
+    MOZ_RELEASE_ASSERT(failures == 0);
+
+    return true;
+}
+
+void
+js::gc::CheckHeapAfterMovingGC(JSRuntime* rt)
+{
+    MOZ_ASSERT(rt->isHeapCollecting());
+    CheckHeapTracer tracer(rt);
+    if (!tracer.init() || !tracer.check())
+        fprintf(stderr, "OOM checking heap\n");
+}
+
+#endif /* JSGC_HASH_TABLE_CHECKS */
--- a/js/src/gc/Zone.h
+++ b/js/src/gc/Zone.h
@@ -667,16 +667,16 @@ class ZoneAllocPolicy
     template <typename T>
     T* pod_realloc(T* p, size_t oldSize, size_t newSize) {
         return zone->pod_realloc<T>(p, oldSize, newSize);
     }
 
     void free_(void* p) { js_free(p); }
     void reportAllocOverflow() const {}
 
-    bool checkSimulatedOOM() const {
+    MOZ_MUST_USE bool checkSimulatedOOM() const {
         return !js::oom::ShouldFailWithOOM();
     }
 };
 
 } // namespace js
 
 #endif // gc_Zone_h
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -1209,17 +1209,18 @@ const char* gc::ZealModeHelpText =
     "    6: (StackRooting) Verify stack rooting\n"
     "    7: (GenerationalGC) Collect the nursery every N nursery allocations\n"
     "    8: (IncrementalRootsThenFinish) Incremental GC in two slices: 1) mark roots 2) finish collection\n"
     "    9: (IncrementalMarkAllThenFinish) Incremental GC in two slices: 1) mark all 2) new marking and finish\n"
     "   10: (IncrementalMultipleSlices) Incremental GC in multiple slices\n"
     "   11: (IncrementalMarkingValidator) Verify incremental marking\n"
     "   12: (ElementsBarrier) Always use the individual element post-write barrier, regardless of elements size\n"
     "   13: (CheckHashTablesOnMinorGC) Check internal hashtables on minor GC\n"
-    "   14: (Compact) Perform a shrinking collection every N allocations\n";
+    "   14: (Compact) Perform a shrinking collection every N allocations\n"
+    "   15: (CheckHeapOnMovingGC) Walk the heap to check all pointers have been updated\n";
 
 void
 GCRuntime::setZeal(uint8_t zeal, uint32_t frequency)
 {
     MOZ_ASSERT(zeal <= unsigned(ZealMode::Limit));
 
     if (verifyPreData)
         VerifyBarriers(rt, PreBarrierVerifier);
@@ -3377,30 +3378,30 @@ GCRuntime::triggerZoneGC(Zone* zone, JS:
     }
 
     /* GC is already running. */
     if (rt->isHeapCollecting())
         return false;
 
 #ifdef JS_GC_ZEAL
     if (hasZealMode(ZealMode::Alloc)) {
-        triggerGC(reason);
+        MOZ_RELEASE_ASSERT(triggerGC(reason));
         return true;
     }
 #endif
 
     if (zone->isAtomsZone()) {
         /* We can't do a zone GC of the atoms compartment. */
         if (rt->keepAtoms()) {
             /* Skip GC and retrigger later, since atoms zone won't be collected
              * if keepAtoms is true. */
             fullGCForAtomsRequested_ = true;
             return false;
         }
-        triggerGC(reason);
+        MOZ_RELEASE_ASSERT(triggerGC(reason));
         return true;
     }
 
     PrepareZoneForGC(zone);
     requestMajorGC(reason);
     return true;
 }
 
@@ -4355,18 +4356,18 @@ GCRuntime::markWeakReferences(gcstats::P
 {
     MOZ_ASSERT(marker.isDrained());
 
     gcstats::AutoPhase ap1(stats, phase);
 
     marker.enterWeakMarkingMode();
 
     // TODO bug 1167452: Make weak marking incremental
-    SliceBudget budget = SliceBudget::unlimited();
-    marker.drainMarkStack(budget);
+    auto unlimited = SliceBudget::unlimited();
+    MOZ_RELEASE_ASSERT(marker.drainMarkStack(unlimited));
 
     for (;;) {
         bool markedAny = false;
         if (!marker.isWeakMarkingTracer()) {
             for (ZoneIterT zone(rt); !zone.done(); zone.next())
                 markedAny |= WeakMapBase::markZoneIteratively(zone, &marker);
         }
         for (CompartmentsIterT<ZoneIterT> c(rt); !c.done(); c.next()) {
@@ -4375,17 +4376,17 @@ GCRuntime::markWeakReferences(gcstats::P
         }
         markedAny |= Debugger::markAllIteratively(&marker);
         markedAny |= jit::JitRuntime::MarkJitcodeGlobalTableIteratively(&marker);
 
         if (!markedAny)
             break;
 
         auto unlimited = SliceBudget::unlimited();
-        marker.drainMarkStack(unlimited);
+        MOZ_RELEASE_ASSERT(marker.drainMarkStack(unlimited));
     }
     MOZ_ASSERT(marker.isDrained());
 
     marker.leaveWeakMarkingMode();
 }
 
 void
 GCRuntime::markWeakReferencesInCurrentGroup(gcstats::Phase phase)
@@ -4402,17 +4403,17 @@ GCRuntime::markGrayReferences(gcstats::P
         for (ZoneIterT zone(rt); !zone.done(); zone.next())
             markBufferedGrayRoots(zone);
     } else {
         MOZ_ASSERT(!isIncremental);
         if (JSTraceDataOp op = grayRootTracer.op)
             (*op)(&marker, grayRootTracer.data);
     }
     auto unlimited = SliceBudget::unlimited();
-    marker.drainMarkStack(unlimited);
+    MOZ_RELEASE_ASSERT(marker.drainMarkStack(unlimited));
 }
 
 void
 GCRuntime::markGrayReferencesInCurrentGroup(gcstats::Phase phase)
 {
     markGrayReferences<GCZoneGroupIter, GCCompartmentGroupIter>(phase);
 }
 
@@ -4563,19 +4564,19 @@ js::gc::MarkingValidator::nonIncremental
             gcmarker->reset();
 
             for (auto chunk = gc->allNonEmptyChunks(); !chunk.done(); chunk.next())
                 chunk->bitmap.clear();
         }
 
         gc->markRuntime(gcmarker, GCRuntime::MarkRuntime);
 
+        gc->incrementalState = MARK;
         auto unlimited = SliceBudget::unlimited();
-        gc->incrementalState = MARK;
-        gc->marker.drainMarkStack(unlimited);
+        MOZ_RELEASE_ASSERT(gc->marker.drainMarkStack(unlimited));
     }
 
     gc->incrementalState = SWEEP;
     {
         gcstats::AutoPhase ap1(gc->stats, gcstats::PHASE_SWEEP);
         gcstats::AutoPhase ap2(gc->stats, gcstats::PHASE_SWEEP_MARK);
 
         gc->markAllWeakReferences(gcstats::PHASE_SWEEP_MARK_WEAK);
@@ -5026,17 +5027,17 @@ MarkIncomingCrossCompartmentPointers(JSR
             }
         }
 
         if (unlinkList)
             c->gcIncomingGrayPointers = nullptr;
     }
 
     auto unlimited = SliceBudget::unlimited();
-    rt->gc.marker.drainMarkStack(unlimited);
+    MOZ_RELEASE_ASSERT(rt->gc.marker.drainMarkStack(unlimited));
 }
 
 static bool
 RemoveFromGrayList(JSObject* wrapper)
 {
     if (!IsGrayListObject(wrapper))
         return false;
 
@@ -5888,16 +5889,20 @@ GCRuntime::compactPhase(JS::gcreason::Re
 
     // Clear runtime caches that can contain cell pointers.
     rt->newObjectCache.purge();
     rt->nativeIterCache.purge();
 
 #ifdef DEBUG
     CheckHashTablesAfterMovingGC(rt);
 #endif
+#ifdef JS_GC_ZEAL
+    if (rt->hasZealMode(ZealMode::CheckHeapOnMovingGC))
+        CheckHeapAfterMovingGC(rt);
+#endif
 
     return zonesToMaybeCompact.isEmpty() ? Finished : NotFinished;
 }
 
 void
 GCRuntime::endCompactPhase(JS::gcreason::Reason reason)
 {
     startedCompacting = false;
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -1181,23 +1181,28 @@ MaybeForwarded(T t)
         t = Forwarded(t);
     MakeAccessibleAfterMovingGC(t);
     return t;
 }
 
 #ifdef JSGC_HASH_TABLE_CHECKS
 
 template <typename T>
+inline bool
+IsGCThingValidAfterMovingGC(T* t)
+{
+    return !IsInsideNursery(t) && !RelocationOverlay::isCellForwarded(t);
+}
+
+template <typename T>
 inline void
 CheckGCThingAfterMovingGC(T* t)
 {
-    if (t) {
-        MOZ_RELEASE_ASSERT(!IsInsideNursery(t));
-        MOZ_RELEASE_ASSERT(!RelocationOverlay::isCellForwarded(t));
-    }
+    if (t)
+        MOZ_RELEASE_ASSERT(IsGCThingValidAfterMovingGC(t));
 }
 
 template <typename T>
 inline void
 CheckGCThingAfterMovingGC(const ReadBarriered<T*>& t)
 {
     CheckGCThingAfterMovingGC(t.unbarrieredGet());
 }
@@ -1223,23 +1228,24 @@ CheckValueAfterMovingGC(const JS::Value&
             D(StackRooting, 6)                 \
             D(GenerationalGC, 7)               \
             D(IncrementalRootsThenFinish, 8)   \
             D(IncrementalMarkAllThenFinish, 9) \
             D(IncrementalMultipleSlices, 10)   \
             D(IncrementalMarkingValidator, 11) \
             D(ElementsBarrier, 12)             \
             D(CheckHashTablesOnMinorGC, 13)    \
-            D(Compact, 14)
+            D(Compact, 14)                     \
+            D(CheckHeapOnMovingGC, 15)
 
 enum class ZealMode {
 #define ZEAL_MODE(name, value) name = value,
     JS_FOR_EACH_ZEAL_MODE(ZEAL_MODE)
 #undef ZEAL_MODE
-    Limit = 14
+    Limit = 15
 };
 
 enum VerifierType {
     PreBarrierVerifier
 };
 
 #ifdef JS_GC_ZEAL
 
--- a/layout/base/RestyleManager.cpp
+++ b/layout/base/RestyleManager.cpp
@@ -1150,16 +1150,25 @@ RestyleManager::AnimationsWithDestroyedF
   for (nsIContent* content : aArray) {
     if (content->GetPrimaryFrame()) {
       continue;
     }
     dom::Element* element = content->AsElement();
 
     animationManager->StopAnimationsForElement(element, aPseudoType);
     transitionManager->StopTransitionsForElement(element, aPseudoType);
+
+    // All other animations should keep running but not running on the
+    // *compositor* at this point.
+    EffectSet* effectSet = EffectSet::GetEffectSet(element, aPseudoType);
+    if (effectSet) {
+      for (KeyframeEffectReadOnly* effect : *effectSet) {
+        effect->ResetIsRunningOnCompositor();
+      }
+    }
   }
 }
 
 static inline dom::Element*
 ElementForStyleContext(nsIContent* aParentContent,
                        nsIFrame* aFrame,
                        CSSPseudoElementType aPseudoType);
 
--- a/layout/base/nsDocumentViewer.cpp
+++ b/layout/base/nsDocumentViewer.cpp
@@ -96,25 +96,19 @@
 #ifdef NS_PRINTING
 
 #include "nsIWebBrowserPrint.h"
 
 #include "nsPrintEngine.h"
 
 // Print Options
 #include "nsIPrintSettings.h"
-#include "nsIPrintOptions.h"
+#include "nsIPrintSettingsService.h"
 #include "nsISimpleEnumerator.h"
 
-#ifdef DEBUG
-// PrintOptions is now implemented by PrintSettingsService
-static const char sPrintOptionsContractID[] =
-  "@mozilla.org/gfx/printsettings-service;1";
-#endif // DEBUG
-
 #include "nsIPluginDocument.h"
 
 #endif // NS_PRINTING
 
 //focus
 #include "nsIDOMEventTarget.h"
 #include "nsIDOMEventListener.h"
 #include "nsISelectionController.h"
@@ -2720,21 +2714,22 @@ nsDocumentViewer::Print(bool            
 
 #ifdef DEBUG
   nsresult rv = NS_ERROR_FAILURE;
 
   mDebugFile = aDebugFile;
   // if they don't pass in a PrintSettings, then make one
   // it will have all the default values
   printSettings = aPrintSettings;
-  nsCOMPtr<nsIPrintOptions> printOptions = do_GetService(sPrintOptionsContractID, &rv);
+  nsCOMPtr<nsIPrintSettingsService> printSettingsSvc
+    = do_GetService("@mozilla.org/gfx/printsettings-service;1", &rv);
   if (NS_SUCCEEDED(rv)) {
     // if they don't pass in a PrintSettings, then make one
     if (printSettings == nullptr) {
-      printOptions->CreatePrintSettings(getter_AddRefs(printSettings));
+      printSettingsSvc->GetNewPrintSettings(getter_AddRefs(printSettings));
     }
     NS_ASSERTION(printSettings, "You can't PrintPreview without a PrintSettings!");
   }
   if (printSettings) printSettings->SetPrintSilent(aSilent);
   if (printSettings) printSettings->SetShowPrintProgress(false);
 #endif
 
 
--- a/layout/base/nsPresContext.cpp
+++ b/layout/base/nsPresContext.cpp
@@ -2729,31 +2729,16 @@ nsPresContext::IsRootContentDocument() c
   if (!view) {
     return true;
   }
 
   nsIFrame* f = view->GetFrame();
   return (f && f->PresContext()->IsChrome());
 }
 
-bool
-nsPresContext::IsCrossProcessRootContentDocument()
-{
-  if (!IsRootContentDocument()) {
-    return false;
-  }
-
-  if (XRE_IsParentProcess()) {
-    return true;
-  }
-
-  TabChild* tabChild = TabChild::GetFrom(mShell);
-  return (tabChild && tabChild->IsRootContentDocument());
-}
-
 bool nsPresContext::GetPaintFlashing() const
 {
   if (!mPaintFlashingInitialized) {
     bool pref = Preferences::GetBool("nglayout.debug.paint_flashing");
     if (!pref && IsChrome()) {
       pref = Preferences::GetBool("nglayout.debug.paint_flashing_chrome");
     }
     mPaintFlashing = pref;
--- a/layout/base/nsPresContext.h
+++ b/layout/base/nsPresContext.h
@@ -1034,17 +1034,16 @@ public:
   }
 
   virtual size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
   virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const {
     return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
   }
 
   bool IsRootContentDocument() const;
-  bool IsCrossProcessRootContentDocument();
 
   bool IsGlyph() const {
     return mIsGlyph;
   }
 
   void SetIsGlyph(bool aValue) {
     mIsGlyph = aValue;
   }
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -5368,18 +5368,31 @@ static bool IsTransparentContainerElemen
   if (!docShell) {
     return false;
   }
 
   nsCOMPtr<nsPIDOMWindowOuter> pwin = docShell->GetWindow();
   if (!pwin)
     return false;
   nsCOMPtr<Element> containerElement = pwin->GetFrameElementInternal();
-  return containerElement &&
-         containerElement->HasAttr(kNameSpaceID_None, nsGkAtoms::transparent);
+
+  TabChild* tab = TabChild::GetFrom(docShell);
+  if (tab) {
+    // Check if presShell is the top PresShell. Only the top can
+    // influence the canvas background color.
+    nsCOMPtr<nsIPresShell> presShell = aPresContext->GetPresShell();
+    nsCOMPtr<nsIPresShell> topPresShell = tab->GetPresShell();
+    if (presShell != topPresShell) {
+      tab = nullptr;
+    }
+  }
+
+  return (containerElement &&
+          containerElement->HasAttr(kNameSpaceID_None, nsGkAtoms::transparent))
+    || (tab && tab->IsTransparent());
 }
 
 nscolor PresShell::GetDefaultBackgroundColorToDraw()
 {
   if (!mPresContext || !mPresContext->GetBackgroundColorDraw()) {
     return NS_RGB(255,255,255);
   }
   return mPresContext->DefaultBackgroundColor();
@@ -5401,17 +5414,17 @@ void PresShell::UpdateCanvasBackground()
     bool drawBackgroundImage;
     bool drawBackgroundColor;
     mCanvasBackgroundColor =
       nsCSSRendering::DetermineBackgroundColor(mPresContext, bgStyle,
                                                rootStyleFrame,
                                                drawBackgroundImage,
                                                drawBackgroundColor);
     mHasCSSBackgroundColor = drawBackgroundColor;
-    if (GetPresContext()->IsCrossProcessRootContentDocument() &&
+    if (mPresContext->IsRootContentDocument() &&
         !IsTransparentContainerElement(mPresContext)) {
       mCanvasBackgroundColor =
         NS_ComposeColors(GetDefaultBackgroundColorToDraw(), mCanvasBackgroundColor);
     }
   }
 
   // If the root element of the document (ie html) has style 'display: none'
   // then the document's background color does not get drawn; cache the
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -87,16 +87,17 @@
 #include "StickyScrollContainer.h"
 #include "nsFontInflationData.h"
 #include "gfxASurface.h"
 #include "nsRegion.h"
 #include "nsIFrameInlines.h"
 
 #include "mozilla/AsyncEventDispatcher.h"
 #include "mozilla/EffectCompositor.h"
+#include "mozilla/EffectSet.h"
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/EventStateManager.h"
 #include "mozilla/EventStates.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/LookAndFeel.h"
 #include "mozilla/MouseEvents.h"
 #include "mozilla/css/ImageLoader.h"
 #include "mozilla/gfx/Tools.h"
@@ -695,17 +696,17 @@ nsFrame::DestroyFrom(nsIFrame* aDestruct
       RestyleManager::ReframingStyleContexts* rsc =
         presContext->RestyleManager()->AsGecko()->GetReframingStyleContexts();
       if (rsc) {
         rsc->Put(mContent, mStyleContext);
       }
     }
   }
 
-  if (HasCSSAnimations() || HasCSSTransitions()) {
+  if (EffectSet::GetEffectSet(this)) {
     // If no new frame for this element is created by the end of the
     // restyling process, stop animations and transitions for this frame
     if (presContext->RestyleManager()->IsGecko()) {
       RestyleManager::AnimationsWithDestroyedFrame* adf =
         presContext->RestyleManager()->AsGecko()->GetAnimationsWithDestroyedFrame();
       // AnimationsWithDestroyedFrame only lives during the restyling process.
       if (adf) {
         adf->Put(mContent, mStyleContext);
--- a/layout/printing/ipc/PRemotePrintJob.ipdl
+++ b/layout/printing/ipc/PRemotePrintJob.ipdl
@@ -26,16 +26,28 @@ parent:
   // print device.
   // This will always deallocate the shared memory.
   async ProcessPage(Shmem aStoredPage);
 
   // This informs the real print device that we've finished, so it can trigger
   // the actual print.
   async FinalizePrint();
 
+  // Report a state change to listeners in the parent process.
+  async StateChange(long aStateFlags,
+                    nsresult aStatus);
+
+  // Report a progress change to listeners in the parent process.
+  async ProgressChange(long aCurSelfProgress,
+                       long aMaxSelfProgress,
+                       long aCurTotalProgress,
+                       long aMaxTotalProgress);
+
+  // Report a status change to listeners in the parent process.
+  async StatusChange(nsresult aStatus);
 
 child:
   // Inform the child that the print has been initialized in the parent or has
   // failed with result aRv.
   async PrintInitializationResult(nsresult aRv);
 
   // Inform the child that the latest page has been processed remotely.
   async PageProcessed();
--- a/layout/printing/ipc/RemotePrintJobChild.cpp
+++ b/layout/printing/ipc/RemotePrintJobChild.cpp
@@ -8,16 +8,19 @@
 
 #include "mozilla/unused.h"
 #include "nsPagePrintTimer.h"
 #include "nsPrintEngine.h"
 
 namespace mozilla {
 namespace layout {
 
+NS_IMPL_ISUPPORTS(RemotePrintJobChild,
+                  nsIWebProgressListener)
+
 RemotePrintJobChild::RemotePrintJobChild()
 {
   MOZ_COUNT_CTOR(RemotePrintJobChild);
 }
 
 nsresult
 RemotePrintJobChild::InitializePrint(const nsString& aDocumentTitle,
                                      const nsString& aPrintToFile,
@@ -81,16 +84,66 @@ RemotePrintJobChild::SetPagePrintTimer(n
 void
 RemotePrintJobChild::SetPrintEngine(nsPrintEngine* aPrintEngine)
 {
   MOZ_ASSERT(aPrintEngine);
 
   mPrintEngine = aPrintEngine;
 }
 
+// nsIWebProgressListener
+
+NS_IMETHODIMP
+RemotePrintJobChild::OnStateChange(nsIWebProgress* aProgress,
+                                   nsIRequest* aRequest, uint32_t aStateFlags,
+                                   nsresult aStatus)
+{
+  Unused << SendStateChange(aStateFlags, aStatus);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+RemotePrintJobChild::OnProgressChange(nsIWebProgress * aProgress,
+                                      nsIRequest * aRequest,
+                                      int32_t aCurSelfProgress,
+                                      int32_t aMaxSelfProgress,
+                                      int32_t aCurTotalProgress,
+                                      int32_t aMaxTotalProgress)
+{
+  Unused << SendProgressChange(aCurSelfProgress, aMaxSelfProgress,
+                               aCurTotalProgress, aMaxTotalProgress);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+RemotePrintJobChild::OnLocationChange(nsIWebProgress* aProgress,
+                                      nsIRequest* aRequest, nsIURI* aURI,
+                                      uint32_t aFlags)
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+RemotePrintJobChild::OnStatusChange(nsIWebProgress* aProgress,
+                                    nsIRequest* aRequest, nsresult aStatus,
+                                    const char16_t* aMessage)
+{
+  Unused << SendStatusChange(aStatus);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+RemotePrintJobChild::OnSecurityChange(nsIWebProgress* aProgress,
+                                      nsIRequest* aRequest, uint32_t aState)
+{
+  return NS_OK;
+}
+
+// End of nsIWebProgressListener
+
 RemotePrintJobChild::~RemotePrintJobChild()
 {
   MOZ_COUNT_DTOR(RemotePrintJobChild);
 }
 
 void
 RemotePrintJobChild::ActorDestroy(ActorDestroyReason aWhy)
 {
--- a/layout/printing/ipc/RemotePrintJobChild.h
+++ b/layout/printing/ipc/RemotePrintJobChild.h
@@ -5,27 +5,30 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_layout_RemotePrintJobChild_h
 #define mozilla_layout_RemotePrintJobChild_h
 
 #include "mozilla/layout/PRemotePrintJobChild.h"
 
 #include "mozilla/RefPtr.h"
+#include "nsIWebProgressListener.h"
 
 class nsPagePrintTimer;
 class nsPrintEngine;
 
 namespace mozilla {
 namespace layout {
 
 class RemotePrintJobChild final : public PRemotePrintJobChild
+                                , public nsIWebProgressListener
 {
 public:
-  NS_INLINE_DECL_REFCOUNTING(RemotePrintJobChild)
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIWEBPROGRESSLISTENER
 
   RemotePrintJobChild();
 
   void ActorDestroy(ActorDestroyReason aWhy) final;
 
   nsresult InitializePrint(const nsString& aDocumentTitle,
                            const nsString& aPrintToFile,
                            const int32_t& aStartPage,
--- a/layout/printing/ipc/RemotePrintJobParent.cpp
+++ b/layout/printing/ipc/RemotePrintJobParent.cpp
@@ -10,16 +10,17 @@
 
 #include "gfxContext.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/unused.h"
 #include "nsComponentManagerUtils.h"
 #include "nsDeviceContext.h"
 #include "nsIDeviceContextSpec.h"
 #include "nsIPrintSettings.h"
+#include "nsIWebProgressListener.h"
 #include "PrintTranslator.h"
 
 namespace mozilla {
 namespace layout {
 
 RemotePrintJobParent::RemotePrintJobParent(nsIPrintSettings* aPrintSettings)
   : mPrintSettings(aPrintSettings)
 {
@@ -144,16 +145,66 @@ RemotePrintJobParent::RecvAbortPrint(con
   if (mPrintDeviceContext) {
     Unused << mPrintDeviceContext->AbortDocument();
   }
 
   Unused << Send__delete__(this);
   return true;
 }
 
+bool
+RemotePrintJobParent::RecvStateChange(const long& aStateFlags,
+                                      const nsresult& aStatus)
+{
+  uint32_t numberOfListeners = mPrintProgressListeners.Length();
+  for (uint32_t i = 0; i < numberOfListeners; ++i) {
+    nsIWebProgressListener* listener = mPrintProgressListeners.SafeElementAt(i);
+    listener->OnStateChange(nullptr, nullptr, aStateFlags, aStatus);
+  }
+
+  return true;
+}
+
+bool
+RemotePrintJobParent::RecvProgressChange(const long& aCurSelfProgress,
+                                         const long& aMaxSelfProgress,
+                                         const long& aCurTotalProgress,
+                                         const long& aMaxTotalProgress)
+{
+  uint32_t numberOfListeners = mPrintProgressListeners.Length();
+  for (uint32_t i = 0; i < numberOfListeners; ++i) {
+    nsIWebProgressListener* listener = mPrintProgressListeners.SafeElementAt(i);
+    listener->OnProgressChange(nullptr, nullptr,
+                               aCurSelfProgress, aMaxSelfProgress,
+                               aCurTotalProgress, aMaxTotalProgress);
+  }
+
+  return true;
+}
+
+bool
+RemotePrintJobParent::RecvStatusChange(const nsresult& aStatus)
+{
+  uint32_t numberOfListeners = mPrintProgressListeners.Length();
+  for (uint32_t i = 0; i < numberOfListeners; ++i) {
+    nsIWebProgressListener* listener = mPrintProgressListeners.SafeElementAt(i);
+    listener->OnStatusChange(nullptr, nullptr, aStatus, nullptr);
+  }
+
+  return true;
+}
+
+void
+RemotePrintJobParent::RegisterListener(nsIWebProgressListener* aListener)
+{
+  MOZ_ASSERT(aListener);
+
+  mPrintProgressListeners.AppendElement(aListener);
+}
+
 RemotePrintJobParent::~RemotePrintJobParent()
 {
   MOZ_COUNT_DTOR(RemotePrintJobParent);
 }
 
 void
 RemotePrintJobParent::ActorDestroy(ActorDestroyReason aWhy)
 {
--- a/layout/printing/ipc/RemotePrintJobParent.h
+++ b/layout/printing/ipc/RemotePrintJobParent.h
@@ -4,22 +4,24 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_layout_RemotePrintJobParent_h
 #define mozilla_layout_RemotePrintJobParent_h
 
 #include "mozilla/layout/PRemotePrintJobParent.h"
 
+#include "nsCOMArray.h"
 #include "nsCOMPtr.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/UniquePtr.h"
 
 class nsDeviceContext;
 class nsIPrintSettings;
+class nsIWebProgressListener;
 class PrintTranslator;
 
 namespace mozilla {
 namespace layout {
 
 class RemotePrintJobParent final : public PRemotePrintJobParent
 {
 public:
@@ -33,27 +35,45 @@ public:
                            const int32_t& aEndPage) final;
 
   bool RecvProcessPage(Shmem&& aStoredPage) final;
 
   bool RecvFinalizePrint() final;
 
   bool RecvAbortPrint(const nsresult& aRv) final;
 
+  bool RecvStateChange(const long& aStateFlags,
+                       const nsresult& aStatus) final;
+
+  bool RecvProgressChange(const long& aCurSelfProgress,
+                          const long& aMaxSelfProgress,
+                          const long& aCurTotalProgress,
+                          const long& aMaxTotalProgress) final;
+
+  bool RecvStatusChange(const nsresult& aStatus) final;
+
+  /**
+    * Register a progress listener to receive print progress updates.
+    *
+    * @param aListener the progress listener to register. Must not be null.
+    */
+  void RegisterListener(nsIWebProgressListener* aListener);
+
 private:
   ~RemotePrintJobParent() final;
 
   nsresult InitializePrintDevice(const nsString& aDocumentTitle,
                                  const nsString& aPrintToFile,
                                  const int32_t& aStartPage,
                                  const int32_t& aEndPage);
 
   nsresult PrintPage(const Shmem& aStoredPage);
 
   nsCOMPtr<nsIPrintSettings> mPrintSettings;
   RefPtr<nsDeviceContext> mPrintDeviceContext;
   UniquePtr<PrintTranslator> mPrintTranslator;
+  nsCOMArray<nsIWebProgressListener> mPrintProgressListeners;
 };
 
 } // namespace layout
 } // namespace mozilla
 
 #endif // mozilla_layout_RemotePrintJobParent_h
--- a/layout/printing/nsPrintData.cpp
+++ b/layout/printing/nsPrintData.cpp
@@ -115,8 +115,20 @@ nsPrintData::DoOnProgressChange(int32_t 
     nsIWebProgressListener* wpl = mPrintProgressListeners.ObjectAt(i);
     wpl->OnProgressChange(nullptr, nullptr, aProgress, aMaxProgress, aProgress, aMaxProgress);
     if (aDoStartStop) {
       wpl->OnStateChange(nullptr, nullptr, aFlag, NS_OK);
     }
   }
 }
 
+void
+nsPrintData::DoOnStatusChange(nsresult aStatus)
+{
+  uint32_t numberOfListeners = mPrintProgressListeners.Length();
+  for (uint32_t i = 0; i < numberOfListeners; ++i) {
+    nsIWebProgressListener* listener = mPrintProgressListeners.SafeElementAt(i);
+    if (listener) {
+      listener->OnStatusChange(nullptr, nullptr, aStatus, nullptr);
+    }
+  }
+}
+
--- a/layout/printing/nsPrintData.h
+++ b/layout/printing/nsPrintData.h
@@ -46,16 +46,18 @@ public:
   // Listener Helper Methods
   void OnEndPrinting();
   void OnStartPrinting();
   void DoOnProgressChange(int32_t      aProgress,
                           int32_t      aMaxProgress,
                           bool         aDoStartStop,
                           int32_t      aFlag);
 
+  void DoOnStatusChange(nsresult aStatus);
+
 
   ePrintDataType               mType;            // the type of data this is (Printing or Print Preview)
   RefPtr<nsDeviceContext>   mPrintDC;
   FILE                        *mDebugFilePtr;    // a file where information can go to when printing
 
   nsPrintObject *                mPrintObject;
   nsPrintObject *                mSelectedPO;
 
--- a/layout/printing/nsPrintEngine.cpp
+++ b/layout/printing/nsPrintEngine.cpp
@@ -470,24 +470,39 @@ nsPrintEngine::DoCommonPrint(bool       
     if (viewer) {
       viewer->SetTextZoom(1.0f);
       viewer->SetFullZoom(1.0f);
       viewer->SetMinFontSize(0);
     }
   }
 
   // Create a print session and let the print settings know about it.
+  // Don't overwrite an existing print session.
   // The print settings hold an nsWeakPtr to the session so it does not
   // need to be cleared from the settings at the end of the job.
   // XXX What lifetime does the printSession need to have?
   nsCOMPtr<nsIPrintSession> printSession;
+  bool remotePrintJobListening = false;
   if (!aIsPrintPreview) {
-    printSession = do_CreateInstance("@mozilla.org/gfx/printsession;1", &rv);
-    NS_ENSURE_SUCCESS(rv, rv);
-    mPrt->mPrintSettings->SetPrintSession(printSession);
+    rv = mPrt->mPrintSettings->GetPrintSession(getter_AddRefs(printSession));
+    if (NS_FAILED(rv) || !printSession) {
+      printSession = do_CreateInstance("@mozilla.org/gfx/printsession;1", &rv);
+      NS_ENSURE_SUCCESS(rv, rv);
+      mPrt->mPrintSettings->SetPrintSession(printSession);
+    } else {
+      RefPtr<mozilla::layout::RemotePrintJobChild> remotePrintJob;
+      printSession->GetRemotePrintJob(getter_AddRefs(remotePrintJob));
+      if (NS_SUCCEEDED(rv) && remotePrintJob) {
+        // If we have a RemotePrintJob add it to the print progress listeners,
+        // so it can forward to the parent.
+        mPrt->mPrintProgressListeners.AppendElement(remotePrintJob);
+        remotePrintJobListening = true;
+      }
+    }
+
   }
 
   if (aWebProgressListener != nullptr) {
     mPrt->mPrintProgressListeners.AppendObject(aWebProgressListener);
   }
 
   // Get the currently focused window and cache it
   // because the Print Dialog will "steal" focus and later when you try
@@ -607,16 +622,27 @@ nsPrintEngine::DoCommonPrint(bool       
         if (NS_SUCCEEDED(rv)) {
           // since we got the dialog and it worked then make sure we 
           // are telling GFX we want to print silent
           printSilently = true;
 
           if (mPrt->mPrintSettings) {
             // The user might have changed shrink-to-fit in the print dialog, so update our copy of its state
             mPrt->mPrintSettings->GetShrinkToFit(&mPrt->mShrinkToFit);
+
+            // If we haven't already added the RemotePrintJob as a listener,
+            // add it now if there is one.
+            if (!remotePrintJobListening) {
+              RefPtr<mozilla::layout::RemotePrintJobChild> remotePrintJob;
+              printSession->GetRemotePrintJob(getter_AddRefs(remotePrintJob));
+              if (NS_SUCCEEDED(rv) && remotePrintJob) {
+                mPrt->mPrintProgressListeners.AppendElement(remotePrintJob);
+                remotePrintJobListening = true;
+              }
+            }
           }
         } else if (rv == NS_ERROR_NOT_IMPLEMENTED) {
           // This means the Dialog service was there,
           // but they choose not to implement this dialog and
           // are looking for default behavior from the toolkit
           rv = NS_OK;
         }
       } else {
@@ -1526,16 +1552,19 @@ nsPrintEngine::FirePrintingErrorEvent(ns
   event->InitCustomEvent(NS_LITERAL_STRING("PrintingError"), false, false,
                          resultVariant);
   event->SetTrusted(true);
 
   RefPtr<AsyncEventDispatcher> asyncDispatcher =
     new AsyncEventDispatcher(doc, event);
   asyncDispatcher->mOnlyChromeDispatch = true;
   asyncDispatcher->RunDOMEventWhenSafe();
+
+  // Inform any progress listeners of the Error.
+  mPrt->DoOnStatusChange(aPrintError);
 }
 
 //-----------------------------------------------------------------
 //-- Section: Reflow Methods
 //-----------------------------------------------------------------
 
 nsresult
 nsPrintEngine::ReconstructAndReflow(bool doSetPixelScale)
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -356,17 +356,17 @@ pref("media.apple.mp4.enabled", true);
 // media.gmp.storage.version.observed, and if the versions don't match,
 // we clear storage and set media.gmp.storage.version.observed=expected.
 // This provides a mechanism to clear GMP storage when non-compatible
 // changes are made.
 pref("media.gmp.storage.version.expected", 1);
 
 // Filter what triggers user notifications.
 // See DecoderDoctorDocumentWatcher::ReportAnalysis for details.
-pref("media.decoder-doctor.notifications-allowed", "MediaWidevineNoWMFNoSilverlight");
+pref("media.decoder-doctor.notifications-allowed", "MediaWMFNeeded,MediaWidevineNoWMFNoSilverlight");
 // Whether we report partial failures.
 pref("media.decoder-doctor.verbose", false);
 
 // Whether to suspend decoding of videos in background tabs.
 pref("media.suspend-bkgnd-video.enabled", true);
 
 #ifdef MOZ_WEBRTC
 pref("media.navigator.enabled", true);
--- a/python/mozbuild/mozbuild/artifacts.py
+++ b/python/mozbuild/mozbuild/artifacts.py
@@ -819,21 +819,20 @@ class Artifacts(object):
                           'pushid': pushid,
                           'num': NUM_PUSHHEADS_TO_QUERY_PER_PARENT},
                          'Retrieving the last {num} pushheads starting with id {pushid} on {tree}')
                 candidate_pushheads.extend(pushhead_cache.pushid_range(tree, start, end))
 
         return candidate_pushheads
 
     def _get_hg_revisions_from_git(self):
-
-        # First commit is HEAD, next is HEAD~1, etc.
         rev_list = subprocess.check_output([
             self._git, 'rev-list', '--topo-order',
-            'HEAD~{num}..HEAD'.format(num=NUM_REVISIONS_TO_QUERY),
+            '--max-count={num}'.format(num=NUM_REVISIONS_TO_QUERY),
+            'HEAD',
         ])
 
         hg_hash_list = subprocess.check_output([
             self._git, 'cinnabar', 'git2hg'
         ] + rev_list.splitlines())
 
         zeroes = "0" * 40
 
--- a/testing/mozharness/configs/builds/branch_specifics.py
+++ b/testing/mozharness/configs/builds/branch_specifics.py
@@ -179,16 +179,91 @@ config = {
             'win32-mulet': {
                 'update_channel': 'default',
             },
             'win64-debug': {
                 'update_channel': 'default',
             },
         },
     },
+    'mozilla-esr45': {
+        'enable_release_promotion': True,
+        'repo_path': 'releases/mozilla-esr45',
+        'update_channel': 'esr',
+        'branch_uses_per_checkin_strategy': True,
+        'use_branch_in_symbols_extra_buildid': False,
+        'stage_server': 'upload.ffxbld.productdelivery.prod.mozaws.net',
+        'platform_overrides': {
+            'linux': {
+                'src_mozconfig': 'browser/config/mozconfigs/linux32/release',
+                'force_clobber': True,
+            },
+            'linux64': {
+                'src_mozconfig': 'browser/config/mozconfigs/linux64/release',
+                'force_clobber': True,
+            },
+            'macosx64': {
+                'src_mozconfig': 'browser/config/mozconfigs/macosx-universal/release',
+                'force_clobber': True,
+            },
+            'win32': {
+                'src_mozconfig': 'browser/config/mozconfigs/win32/release',
+                'force_clobber': True,
+            },
+            'win64': {
+                'src_mozconfig': 'browser/config/mozconfigs/win64/release',
+                'force_clobber': True,
+            },
+            'linux-debug': {
+                'update_channel': 'default',
+            },
+            'linux64-debug': {
+                'update_channel': 'default',
+            },
+            'linux64-asan-debug': {
+                'update_channel': 'default',
+            },
+            'linux64-asan': {
+                'update_channel': 'default',
+            },
+            'linux64-cc': {
+                'update_channel': 'default',
+            },
+            'linux64-st-an-debug': {
+                'update_channel': 'default',
+            },
+            'linux64-st-an': {
+                'update_channel': 'default',
+            },
+            'linux64-tsan': {
+                'update_channel': 'default',
+            },
+            'macosx64-debug': {
+                'update_channel': 'default',
+            },
+            'macosx64-st-an': {
+                'update_channel': 'default',
+            },
+            'macosx64-mulet': {
+                'update_channel': 'default',
+            },
+            'macosx64-st-an-debug': {
+                'update_channel': 'default',
+            },
+            'win32-debug': {
+                'update_channel': 'default',
+            },
+            'win32-mulet': {
+                'update_channel': 'default',
+            },
+            'win64-debug': {
+                'update_channel': 'default',
+            },
+        },
+    },
     'mozilla-aurora': {
         'repo_path': 'releases/mozilla-aurora',
         'update_channel': 'aurora',
         'branch_uses_per_checkin_strategy': True,
         'stage_server': 'upload.ffxbld.productdelivery.prod.mozaws.net',
     },
     'try': {
         'repo_path': 'try',
new file mode 100644
--- /dev/null
+++ b/testing/mozharness/configs/releases/postrelease_firefox_esr45.py
@@ -0,0 +1,18 @@
+config = {
+    "log_name": "bump_esr45",
+    "version_files": [
+        {"file": "browser/config/version.txt"},
+        {"file": "browser/config/version_display.txt"},
+        {"file": "config/milestone.txt"},
+    ],
+    "repo": {
+        "repo": "https://hg.mozilla.org/releases/mozilla-esr45",
+        "revision": "default",
+        "dest": "mozilla-esr45",
+        "vcs": "hg",
+    },
+    "push_dest": "ssh://hg.mozilla.org/releases/mozilla-esr45",
+    "ignore_no_changes": True,
+    "ssh_user": "ffxbld",
+    "ssh_key": "~/.ssh/ffxbld_rsa",
+}
new file mode 100644
--- /dev/null
+++ b/testing/mozharness/configs/releases/updates_firefox_esr45.py
@@ -0,0 +1,34 @@
+
+config = {
+    "log_name": "updates_esr45",
+    "repo": {
+        "repo": "https://hg.mozilla.org/build/tools",
+        "revision": "default",
+        "dest": "tools",
+        "vcs": "hg",
+    },
+    "push_dest": "ssh://hg.mozilla.org/build/tools",
+    "shipped-locales-url": "https://hg.mozilla.org/releases/mozilla-esr45/raw-file/{revision}/browser/locales/shipped-locales",
+    "ignore_no_changes": True,
+    "ssh_user": "ffxbld",
+    "ssh_key": "~/.ssh/ffxbld_rsa",
+    "archive_domain": "archive.mozilla.org",
+    "archive_prefix": "https://archive.mozilla.org/pub",
+    "previous_archive_prefix": "https://archive.mozilla.org/pub",
+    "download_domain": "download.mozilla.org",
+    "balrog_url": "https://aus5.mozilla.org",
+    "balrog_username": "ffxbld",
+    "update_channels": {
+        "esr": {
+            "version_regex": r".*",
+            "requires_mirrors": True,
+            "patcher_config": "mozEsr45-branch-patcher2.cfg",
+            "update_verify_channel": "esr-localtest",
+            "mar_channel_ids": [],
+            "channel_names": ["esr", "esr-localtest", "esr-cdntest"],
+            "rules_to_update": ["esr45-cdntest", "esr45-localtest"],
+            "publish_rules": ["esr"],
+        },
+    },
+    "balrog_use_dummy_suffix": False,
+}
new file mode 100644
--- /dev/null
+++ b/testing/mozharness/configs/single_locale/mozilla-esr45.py
@@ -0,0 +1,40 @@
+config = {
+    "nightly_build": True,
+    "branch": "mozilla-esr45",
+    "en_us_binary_url": "http://ftp.mozilla.org/pub/mozilla.org/firefox/nightly/latest-mozilla-esr45/",
+    "update_channel": "esr",
+    "latest_mar_dir": '/pub/mozilla.org/firefox/nightly/latest-mozilla-esr45-l10n',
+
+    # l10n
+    "hg_l10n_base": "https://hg.mozilla.org/releases/l10n/mozilla-release",
+
+    # repositories
+    "mozilla_dir": "mozilla-esr45",
+    "repos": [{
+        "vcs": "hg",
+        "repo": "https://hg.mozilla.org/build/tools",
+        "revision": "default",
+        "dest": "tools",
+    }, {
+        "vcs": "hgtool",
+        "repo": "https://hg.mozilla.org/releases/mozilla-esr45",
+        "revision": "default",
+        "dest": "mozilla-esr45",
+    }, {
+        "vcs": "hgtool",
+        "repo": "https://hg.mozilla.org/build/compare-locales",
+        "revision": "RELEASE_AUTOMATION"
+    }],
+    # purge options
+    'purge_minsize': 12,
+    'is_automation': True,
+    'default_actions': [
+        "clobber",
+        "pull",
+        "list-locales",
+        "setup",
+        "repack",
+        "taskcluster-upload",
+        "summary",
+    ],
+}
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -331,16 +331,36 @@
   "FORGET_SKIPPABLE_MAX": {
     "alert_emails": ["dev-telemetry-gc-alerts@mozilla.org"],
     "expires_in_version": "never",
     "kind": "exponential",
     "high": 10000,
     "n_buckets": 50,
     "description": "Max time spent on one forget skippable (ms)"
   },
+  "FULLSCREEN_TRANSITION_BLACK_MS": {
+    "alert_emails": ["xquan@mozilla.com"],
+    "expires_in_version": "never",
+    "kind": "exponential",
+    "low": 100,
+    "high": 5000,
+    "n_buckets": 50,
+    "bug_numbers": [1271160],
+    "description": "The time spent in the fully-black screen in fullscreen transition"
+  },
+  "FULLSCREEN_CHANGE_MS": {
+    "alert_emails": ["xquan@mozilla.com"],
+    "expires_in_version": "never",
+    "kind": "exponential",
+    "low": 100,
+    "high": 5000,
+    "n_buckets": 50,
+    "bug_numbers": [1271160],
+    "description": "The time content uses to enter/exit fullscreen regardless of fullscreen transition timeout"
+  },
   "GC_REASON_2": {
     "alert_emails": ["dev-telemetry-gc-alerts@mozilla.org"],
     "expires_in_version": "never",
     "kind": "enumerated",
     "n_values": 100,
     "description": "Reason (enum value) for initiating a GC"
   },
   "GC_IS_COMPARTMENTAL": {
new file mode 100644
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/unit/test_listmanager.js
@@ -0,0 +1,245 @@
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
+                                  "resource://gre/modules/NetUtil.jsm");
+
+// These tables share the same updateURL.
+const TEST_TABLE_DATA_LIST = [
+  // 0:
+  {
+    tableName: "test-listmanager0-digest256",
+    providerName: "google",
+    updateUrl: "http://localhost:4444/safebrowsing/update",
+    gethashUrl: "http://localhost:4444/safebrowsing/gethash0",
+  },
+
+  // 1:
+  {
+    tableName: "test-listmanager1-digest256",
+    providerName: "google",
+    updateUrl: "http://localhost:4444/safebrowsing/update",
+    gethashUrl: "http://localhost:4444/safebrowsing/gethash1",
+  },
+
+  // 2.
+  {
+    tableName: "test-listmanager2-digest256",
+    providerName: "google",
+    updateUrl: "http://localhost:4444/safebrowsing/update",
+    gethashUrl: "http://localhost:4444/safebrowsing/gethash2",
+  }
+];
+
+// This table has a different update URL.
+const TEST_TABLE_DATA_ANOTHER = {
+  tableName: "test-listmanageranother-digest256",
+  providerName: "google",
+  updateUrl: "http://localhost:5555/safebrowsing/update",
+  gethashUrl: "http://localhost:5555/safebrowsing/gethash-another",
+};
+
+const PREF_NEXTUPDATETIME = "browser.safebrowsing.provider.google.nextupdatetime";
+
+let gListManager = Cc["@mozilla.org/url-classifier/listmanager;1"]
+                     .getService(Ci.nsIUrlListManager);
+
+// Global test server for serving safebrowsing updates.
+let gHttpServ = null;
+let gUpdateResponse = "";
+let gExpectedUpdateRequest = "";
+
+// Handles request for TEST_TABLE_DATA_ANOTHER.
+let gHttpServAnother = null;
+
+// These two variables are used to synchronize the last two racing updates
+// (in terms of "update URL") in test_update_all_tables().
+let gUpdatedCntForTableData = 0; // For TEST_TABLE_DATA_LIST.
+let gIsAnotherUpdated = false;   // For TEST_TABLE_DATA_ANOTHER.
+
+prefBranch.setBoolPref("browser.safebrowsing.debug", true);
+
+// Register tables.
+TEST_TABLE_DATA_LIST.forEach(function(t) {
+  gListManager.registerTable(t.tableName,
+                             t.providerName,
+                             t.updateUrl,
+                             t.gethashUrl);
+});
+gListManager.registerTable(TEST_TABLE_DATA_ANOTHER.tableName,
+                           TEST_TABLE_DATA_ANOTHER.providerName,
+                           TEST_TABLE_DATA_ANOTHER.updateUrl,
+                           TEST_TABLE_DATA_ANOTHER.gethashUrl);
+
+const SERVER_INVOLVED_TEST_CASE_LIST = [
+  // - Do table0 update.
+  // - Server would respond "a:5:32:32\n[DATA]".
+  function test_update_table0() {
+    disableAllUpdates();
+
+    gListManager.enableUpdate(TEST_TABLE_DATA_LIST[0].tableName);
+    gExpectedUpdateRequest = TEST_TABLE_DATA_LIST[0].tableName + ";\n";
+
+    gUpdateResponse = "n:1000\ni:" + TEST_TABLE_DATA_LIST[0].tableName + "\n";
+    gUpdateResponse += readFileToString("data/digest2.chunk");
+
+    forceTableUpdate();
+  },
+
+  // - Do table0 update again. Since chunk 5 was added to table0 in the last
+  //   update, the expected request contains "a:5".
+  // - Server would respond "s;2-12\n[DATA]".
+  function test_update_table0_with_existing_chunks() {
+    disableAllUpdates();
+
+    gListManager.enableUpdate(TEST_TABLE_DATA_LIST[0].tableName);
+    gExpectedUpdateRequest = TEST_TABLE_DATA_LIST[0].tableName + ";a:5\n";
+
+    gUpdateResponse = "n:1000\ni:" + TEST_TABLE_DATA_LIST[0].tableName + "\n";
+    gUpdateResponse += readFileToString("data/digest1.chunk");
+
+    forceTableUpdate();
+  },
+
+  // - Do all-table update.
+  // - Server would respond no chunk control.
+  //
+  // Note that this test MUST be the last one in the array since we rely on
+  // the number of sever-involved test case to synchronize the racing last
+  // two udpates for different URL.
+  function test_update_all_tables() {
+    disableAllUpdates();
+
+    // Enable all tables including TEST_TABLE_DATA_ANOTHER!
+    TEST_TABLE_DATA_LIST.forEach(function(t) {
+      gListManager.enableUpdate(t.tableName);
+    });
+    gListManager.enableUpdate(TEST_TABLE_DATA_ANOTHER.tableName);
+
+    gExpectedUpdateRequest = TEST_TABLE_DATA_LIST[0].tableName + ";a:5:s:2-12\n" +
+                             TEST_TABLE_DATA_LIST[1].tableName + ";\n" +
+                             TEST_TABLE_DATA_LIST[2].tableName + ";\n";
+    gUpdateResponse = "n:1000\n";
+
+    forceTableUpdate();
+  },
+
+];
+
+SERVER_INVOLVED_TEST_CASE_LIST.forEach(t => add_test(t));
+
+// Tests nsIUrlListManager.getGethashUrl.
+add_test(function test_getGethashUrl() {
+  TEST_TABLE_DATA_LIST.forEach(function (t) {
+    equal(gListManager.getGethashUrl(t.tableName), t.gethashUrl);
+  });
+  equal(gListManager.getGethashUrl(TEST_TABLE_DATA_ANOTHER.tableName),
+        TEST_TABLE_DATA_ANOTHER.gethashUrl);
+  run_next_test();
+});
+
+function run_test() {
+  // Setup primary testing server.
+  gHttpServ = new HttpServer();
+  gHttpServ.registerDirectory("/", do_get_cwd());
+
+  gHttpServ.registerPathHandler("/safebrowsing/update", function(request, response) {
+    let body = NetUtil.readInputStreamToString(request.bodyInputStream,
+                                               request.bodyInputStream.available());
+
+    // Verify if the request is as expected.
+    equal(body, gExpectedUpdateRequest);
+
+    // Respond the update which is controlled by the test case.
+    response.setHeader("Content-Type",
+                       "application/vnd.google.safebrowsing-update", false);
+    response.setStatusLine(request.httpVersion, 200, "OK");
+    response.bodyOutputStream.write(gUpdateResponse, gUpdateResponse.length);
+
+    gUpdatedCntForTableData++;
+
+    if (gUpdatedCntForTableData !== SERVER_INVOLVED_TEST_CASE_LIST.length) {
+      // This is not the last test case so run the next once upon the
+      // the update success.
+      waitForUpdateSuccess(run_next_test);
+      return;
+    }
+
+    if (gIsAnotherUpdated) {
+      run_next_test();  // All tests are done. Just finish.
+      return;
+    }
+
+    do_print("Waiting for TEST_TABLE_DATA_ANOTHER to be tested ...");
+  });
+
+  gHttpServ.start(4444);
+
+  // Setup another testing server for the different update URL.
+  gHttpServAnother = new HttpServer();
+  gHttpServAnother.registerDirectory("/", do_get_cwd());
+
+  gHttpServAnother.registerPathHandler("/safebrowsing/update", function(request, response) {
+    let body = NetUtil.readInputStreamToString(request.bodyInputStream,
+                                               request.bodyInputStream.available());
+
+    // Verify if the request is as expected.
+    equal(body, TEST_TABLE_DATA_ANOTHER.tableName + ";\n");
+
+    // Respond with no chunk control.
+    response.setHeader("Content-Type",
+                       "application/vnd.google.safebrowsing-update", false);
+    response.setStatusLine(request.httpVersion, 200, "OK");
+
+    let content = "n:1000\n";
+    response.bodyOutputStream.write(content, content.length);
+
+    gIsAnotherUpdated = true;
+
+    if (gUpdatedCntForTableData === SERVER_INVOLVED_TEST_CASE_LIST.length) {
+      // All tests are done!
+      run_next_test();
+      return;
+    }
+
+    do_print("Wait for all sever-involved tests to be done ...");
+  });
+
+  gHttpServAnother.start(5555);
+
+  run_next_test();
+}
+
+// A trick to force updating tables. However, before calling this, we have to
+// call disableAllUpdates() first to clean up the updateCheckers in listmanager.
+function forceTableUpdate() {
+  prefBranch.setCharPref(PREF_NEXTUPDATETIME, "1");
+  gListManager.maybeToggleUpdateChecking();
+}
+
+function disableAllUpdates() {
+  TEST_TABLE_DATA_LIST.forEach(t => gListManager.disableUpdate(t.tableName));
+  gListManager.disableUpdate(TEST_TABLE_DATA_ANOTHER.tableName);
+}
+
+// Since there's no public interface on listmanager to know the update success,
+// we could only rely on the refresh of "nextupdatetime".
+function waitForUpdateSuccess(callback) {
+  let nextupdatetime = parseInt(prefBranch.getCharPref(PREF_NEXTUPDATETIME));
+  do_print("nextupdatetime: " + nextupdatetime);
+  if (nextupdatetime !== 1) {
+    callback();
+    return;
+  }
+  do_timeout(1000, waitForUpdateSuccess.bind(null, callback));
+}
+
+// Construct an update from a file.
+function readFileToString(aFilename) {
+  let f = do_get_file(aFilename);
+  let stream = Cc["@mozilla.org/network/file-input-stream;1"]
+    .createInstance(Ci.nsIFileInputStream);
+  stream.init(f, -1, 0, 0);
+  let buf = NetUtil.readInputStreamToString(stream, stream.available());
+  return buf;
+}
--- a/toolkit/components/url-classifier/tests/unit/xpcshell.ini
+++ b/toolkit/components/url-classifier/tests/unit/xpcshell.ini
@@ -12,8 +12,9 @@ support-files =
 [test_hashcompleter.js]
 # Bug 752243: Profile cleanup frequently fails
 #skip-if = os == "mac" || os == "linux"
 [test_partial.js]
 [test_prefixset.js]
 [test_provider_url.js]
 [test_streamupdater.js]
 [test_digest256.js]
+[test_listmanager.js]
--- a/toolkit/content/widgets/browser.xml
+++ b/toolkit/content/widgets/browser.xml
@@ -1321,16 +1321,32 @@
           if (!this.docShell || !this.docShell.contentViewer) {
             return true;
           }
           return {permitUnload: this.docShell.contentViewer.permitUnload(), timedOut: false};
         ]]>
         </body>
       </method>
 
+      <method name="print">
+        <parameter name="aPrintSettings"/>
+        <parameter name="aPrintProgressListener"/>
+        <body>
+          <![CDATA[
+            var owner = this.QueryInterface(Components.interfaces.nsIFrameLoaderOwner);
+            if (!owner.frameLoader) {
+              throw Components.Exception("No frame loader.",
+                                         Components.results.NS_ERROR_FAILURE);
+            }
+
+            owner.frameLoader.print(aPrintSettings, aPrintProgressListener);
+          ]]>
+        </body>
+      </method>
+
       <!-- This will go away if the binding has been removed for some reason. -->
       <field name="_alive">true</field>
     </implementation>
 
     <handlers>
       <handler event="keypress" keycode="VK_F7" group="system">
         <![CDATA[
           if (event.defaultPrevented || !event.isTrusted)
--- a/widget/android/nsPrintOptionsAndroid.cpp
+++ b/widget/android/nsPrintOptionsAndroid.cpp
@@ -20,17 +20,18 @@ public:
 nsPrintOptionsAndroid::nsPrintOptionsAndroid()
 {
 }
 
 nsPrintOptionsAndroid::~nsPrintOptionsAndroid()
 {
 }
 
-NS_IMETHODIMP nsPrintOptionsAndroid::CreatePrintSettings(nsIPrintSettings **_retval)
+nsresult
+nsPrintOptionsAndroid::_CreatePrintSettings(nsIPrintSettings** _retval)
 {
   nsPrintSettings * printSettings = new nsPrintSettingsAndroid();
   NS_ENSURE_TRUE(printSettings, NS_ERROR_OUT_OF_MEMORY);
   NS_ADDREF(*_retval = printSettings);
   (void)InitPrintSettingsFromPrefs(*_retval, false,
                                    nsIPrintSettings::kInitSaveAll);
   return NS_OK;
 }
--- a/widget/android/nsPrintOptionsAndroid.h
+++ b/widget/android/nsPrintOptionsAndroid.h
@@ -12,12 +12,12 @@
 //***    nsPrintOptions
 //*****************************************************************************
 class nsPrintOptionsAndroid : public nsPrintOptions
 {
 public:
   nsPrintOptionsAndroid();
   virtual ~nsPrintOptionsAndroid();
 
-  NS_IMETHOD CreatePrintSettings(nsIPrintSettings **_retval);
+  nsresult _CreatePrintSettings(nsIPrintSettings** _retval) override;
 };
 
 #endif /* nsPrintOptionsAndroid_h__ */
--- a/widget/gtk/nsDeviceContextSpecG.cpp
+++ b/widget/gtk/nsDeviceContextSpecG.cpp
@@ -426,21 +426,16 @@ NS_IMETHODIMP nsPrinterEnumeratorGTK::In
   DO_PR_DEBUG_LOG(("Setting default filename to '%s'\n", filename.get()));
   aPrintSettings->SetToFileName(NS_ConvertUTF8toUTF16(filename).get());
 
   aPrintSettings->SetIsInitializedFromPrinter(true);
 
   return NS_OK;    
 }
 
-NS_IMETHODIMP nsPrinterEnumeratorGTK::DisplayPropertiesDlg(const char16_t *aPrinter, nsIPrintSettings *aPrintSettings)
-{
-  return NS_OK;
-}
-
 //----------------------------------------------------------------------
 nsresult GlobalPrinters::InitializeGlobalPrinters ()
 {
   if (PrintersAreAllocated()) {
     return NS_OK;
   }
 
   mGlobalPrinterList = new nsTArray<nsString>();
--- a/widget/nsIPrintOptions.idl
+++ b/widget/nsIPrintOptions.idl
@@ -1,109 +1,33 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 #include "nsIPrintSettings.idl"
 
-%{ C++
-struct nsFont;
-
-namespace mozilla {
-namespace embedding {
-  class PrintData;
-}
-}
-%}
-
 interface nsIStringEnumerator;
-interface nsIWebBrowserPrint;
-
-/**
- * Native types
- */
-[ref] native nsNativeFontRef(nsFont);
-[ref] native PrintDataRef(const mozilla::embedding::PrintData);
-[ptr] native PrintDataPtr(mozilla::embedding::PrintData);
 
 /**
  * Print options interface
  *
  * Do not attempt to freeze this API - it still needs lots of work. Consult
  * John Keiser <jkeiser@netscape.com> and Roland Mainz
  * <roland.mainz@informatik.med.uni-giessen.de> for futher details.
  */
 [scriptable, uuid(2ac74034-700e-40fd-8059-81d33223af58)]
 
 interface nsIPrintOptions : nsISupports
 {
   /**
    * Show Native Print Options dialog, this may not be supported on all platforms
    */
   void ShowPrintSetupDialog(in nsIPrintSettings aThePrintSettings);
-
-  /**
-   * Creates a new PrintSettnigs Object
-   * and initializes it from prefs
-   */
-  nsIPrintSettings CreatePrintSettings();
-
-  /**
-   * Get a prefixed integer pref 
-   */
-  int32_t getPrinterPrefInt(in nsIPrintSettings aPrintSettings, in wstring
-                            aPrefName);
-
-  /**
-   * display Printer Job Properties dialog
-   */
-  void displayJobProperties (in wstring aPrinter, in nsIPrintSettings
-                             aPrintSettings, out boolean aDisplayed);
-
-  /**
-   * Native data constants
-   */
-  const short kNativeDataPrintRecord        = 0;
-
-  [noscript] voidPtr GetNativeData(in short aDataType);
-
-  /**
-   * Given some nsIPrintSettings and (optionally) an nsIWebBrowserPrint, populates
-   * a PrintData representing them which can be sent over IPC. Values are only
-   * ever read from aSettings and aWBP.
-   *
-   * @param aSettings
-   *        An nsIPrintSettings for a print job.
-   * @param aWBP (optional)
-   *        The nsIWebBrowserPrint for the print job.
-   * @param data
-   *        Pointer to a pre-existing PrintData to populate.
-   *
-   * @return nsresult
-   */
-  [noscript] void SerializeToPrintData(in nsIPrintSettings aPrintSettings,
-                                       in nsIWebBrowserPrint aWebBrowserPrint,
-                                       in PrintDataPtr data);
-
-  /**
-   * This function is the opposite of SerializeToPrintData, in that it takes
-   * a PrintData, and populates a pre-existing nsIPrintSettings with the data
-   * from PrintData.
-   *
-   * @param PrintData
-   *        Printing information sent through IPC.
-   * @param settings
-   *        A pre-existing nsIPrintSettings to populate with the PrintData.
-   *
-   * @return nsresult
-   */
-  [noscript] void DeserializeToPrintSettings(in PrintDataRef data,
-                                             in nsIPrintSettings aPrintSettings);
 };
 
 [scriptable, uuid(5e738fff-404c-4c94-9189-e8f2cce93e94)]
 
 interface nsIPrinterEnumerator : nsISupports
 {
   /**
    * The name of the system default printer. This name should also be
@@ -121,16 +45,10 @@ interface nsIPrinterEnumerator : nsISupp
    *   Number of Copies
    */
   void initPrintSettingsFromPrinter(in wstring aPrinterName, in nsIPrintSettings aPrintSettings);
 
   /**
    * The list of printer names
    */
   readonly attribute nsIStringEnumerator printerNameList;
-
-  /*  takes printer selected and will display job properties dlg for that printer
-   *  returns true if dialog displays
-   */
-  void displayPropertiesDlg(in wstring aPrinter, in nsIPrintSettings aPrintSettings);
-
 };
 
--- a/widget/nsIPrintSettingsService.idl
+++ b/widget/nsIPrintSettingsService.idl
@@ -5,16 +5,31 @@
 
 /* Interface to the Service for gwetting the Global PrintSettings object
    or a unique PrintSettings object
 */
 
 #include "nsISupports.idl"
 
 interface nsIPrintSettings;
+interface nsIWebBrowserPrint;
+
+%{ C++
+namespace mozilla {
+namespace embedding {
+  class PrintData;
+}
+}
+%}
+
+/**
+ * Native types
+ */
+[ref] native PrintDataRef(const mozilla::embedding::PrintData);
+[ptr] native PrintDataPtr(mozilla::embedding::PrintData);
 
 [scriptable, uuid(841387C8-72E6-484b-9296-BF6EEA80D58A)]
 interface nsIPrintSettingsService : nsISupports
 {
   /**
    * Returns a "global" PrintSettings object 
    * Creates a new the first time, if one doesn't exist.
    *
@@ -29,17 +44,18 @@ interface nsIPrintSettingsService : nsIS
    *
    * For example, if each browser was to have its own unique
    * PrintSettings, then each browser window would call this to
    * create its own unique PrintSettings object.
    *
    * If each browse window was to use the same PrintSettings object
    * then it should use "globalPrintSettings"
    *
-   * Initializes the newPrintSettings from the default printer
+   * Initializes the newPrintSettings from the unprefixed printer
+   * (Note: this may not happen if there is an OS specific implementation.)
    *
    */
   readonly attribute nsIPrintSettings newPrintSettings;
 
   /**
    * The name of the last printer used, or else the system default printer.
    */
   readonly attribute wstring defaultPrinterName;
@@ -92,15 +108,50 @@ interface nsIPrintSettingsService : nsIS
    * Items not written:
    *   startPageRange, endPageRange, scaling, printRange, title
    *   docURL, howToEnableFrameUI, isCancelled, printFrameTypeUsage
    *   printFrameType, printSilent, shrinkToFit, numCopies
    *
    */
   void savePrintSettingsToPrefs(in nsIPrintSettings aPrintSettings, in boolean aUsePrinterNamePrefix, in unsigned long aFlags);
 
+  /**
+   * Given some nsIPrintSettings and (optionally) an nsIWebBrowserPrint,
+   * populates a PrintData representing them which can be sent over IPC. Values
+   * are only ever read from aSettings and aWBP.
+   *
+   * @param aSettings
+   *        An nsIPrintSettings for a print job.
+   * @param aWBP (optional)
+   *        The nsIWebBrowserPrint for the print job.
+   * @param data
+   *        Pointer to a pre-existing PrintData to populate.
+   *
+   * @return nsresult
+   */
+  [noscript]
+  void SerializeToPrintData(in nsIPrintSettings aPrintSettings,
+                            in nsIWebBrowserPrint aWebBrowserPrint,
+                            in PrintDataPtr data);
+
+  /**
+   * This function is the opposite of SerializeToPrintData, in that it takes
+   * a PrintData, and populates a pre-existing nsIPrintSettings with the data
+   * from PrintData.
+   *
+   * @param PrintData
+   *        Printing information sent through IPC.
+   * @param settings
+   *        A pre-existing nsIPrintSettings to populate with the PrintData.
+   *
+   * @return nsresult
+   */
+  [noscript]
+  void DeserializeToPrintSettings(in PrintDataRef data,
+                                  in nsIPrintSettings aPrintSettings);
+
 };
 
 %{C++
 // {841387C8-72E6-484b-9296-BF6EEA80D58A}
 #define NS_PRINTSETTINGSSERVICE_IID \
  {0x841387c8, 0x72e6, 0x484b, { 0x92, 0x96, 0xbf, 0x6e, 0xea, 0x80, 0xd5, 0x8a}}
 %}
--- a/widget/nsPrintOptionsImpl.cpp
+++ b/widget/nsPrintOptionsImpl.cpp
@@ -7,19 +7,19 @@
 
 #include "mozilla/embedding/PPrinting.h"
 #include "mozilla/layout/RemotePrintJobChild.h"
 #include "mozilla/RefPtr.h"
 #include "nsPrintingProxy.h"
 #include "nsReadableUtils.h"
 #include "nsPrintSettingsImpl.h"
 #include "nsIPrintSession.h"
+#include "nsServiceManagerUtils.h"
 
 #include "nsIDOMWindow.h"
-#include "nsIServiceManager.h"
 #include "nsIDialogParamBlock.h"
 #include "nsXPCOM.h"
 #include "nsISupportsPrimitives.h"
 #include "nsIWindowWatcher.h"
 #include "nsISupportsArray.h"
 #include "prprf.h"
 
 #include "nsIStringEnumerator.h"
@@ -100,16 +100,26 @@ nsPrintOptions::Init()
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsPrintOptions::SerializeToPrintData(nsIPrintSettings* aSettings,
                                      nsIWebBrowserPrint* aWBP,
                                      PrintData* data)
 {
+  nsCOMPtr<nsIPrintSession> session;
+  nsresult rv = aSettings->GetPrintSession(getter_AddRefs(session));
+  if (NS_SUCCEEDED(rv) && session) {
+    RefPtr<RemotePrintJobChild> remotePrintJob;
+    rv = session->GetRemotePrintJob(getter_AddRefs(remotePrintJob));
+    if (NS_SUCCEEDED(rv)) {
+      data->remotePrintJobChild() = remotePrintJob;
+    }
+  }
+
   aSettings->GetStartPageRange(&data->startPageRange());
   aSettings->GetEndPageRange(&data->endPageRange());
 
   aSettings->GetEdgeTop(&data->edgeTop());
   aSettings->GetEdgeLeft(&data->edgeLeft());
   aSettings->GetEdgeBottom(&data->edgeBottom());
   aSettings->GetEdgeRight(&data->edgeRight());
 
@@ -960,43 +970,16 @@ nsPrintOptions::WritePrefs(nsIPrintSetti
   }
 
   // Not Writing Out:
   //   Number of Copies
 
   return NS_OK;
 }
 
-NS_IMETHODIMP
-nsPrintOptions::DisplayJobProperties(const char16_t *aPrinter,
-                                     nsIPrintSettings* aPrintSettings,
-                                     bool *aDisplayed)
-{
-  NS_ENSURE_ARG_POINTER(aPrinter);
-  *aDisplayed = false;
-
-  nsresult rv;
-  nsCOMPtr<nsIPrinterEnumerator> propDlg =
-           do_CreateInstance(NS_PRINTER_ENUMERATOR_CONTRACTID, &rv);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  NS_ENSURE_ARG_POINTER(aPrintSettings);
-  rv = propDlg->DisplayPropertiesDlg(aPrinter, aPrintSettings);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  *aDisplayed = true;
-
-  return rv;
-}
-
-NS_IMETHODIMP nsPrintOptions::GetNativeData(int16_t aDataType, void * *_retval)
-{
-  return NS_ERROR_NOT_IMPLEMENTED;
-}
-
 nsresult nsPrintOptions::_CreatePrintSettings(nsIPrintSettings **_retval)
 {
   // does not initially ref count
   nsPrintSettings * printSettings = new nsPrintSettings();
   NS_ENSURE_TRUE(printSettings, NS_ERROR_OUT_OF_MEMORY);
 
   NS_ADDREF(*_retval = printSettings); // ref count
 
@@ -1006,38 +989,33 @@ nsresult nsPrintOptions::_CreatePrintSet
   (*_retval)->SetPrinterName(printerName.get());
 
   (void)InitPrintSettingsFromPrefs(*_retval, false,
                                    nsIPrintSettings::kInitSaveAll);
 
   return NS_OK;
 }
 
-NS_IMETHODIMP nsPrintOptions::CreatePrintSettings(nsIPrintSettings **_retval)
-{
-  return _CreatePrintSettings(_retval);
-}
-
 NS_IMETHODIMP
 nsPrintOptions::GetGlobalPrintSettings(nsIPrintSettings **aGlobalPrintSettings)
 {
   nsresult rv;
 
-  rv = CreatePrintSettings(getter_AddRefs(mGlobalPrintSettings));
+  rv = _CreatePrintSettings(getter_AddRefs(mGlobalPrintSettings));
   NS_ENSURE_SUCCESS(rv, rv);
 
   NS_ADDREF(*aGlobalPrintSettings = mGlobalPrintSettings.get());
 
   return rv;
 }
 
 NS_IMETHODIMP
 nsPrintOptions::GetNewPrintSettings(nsIPrintSettings * *aNewPrintSettings)
 {
-  return CreatePrintSettings(aNewPrintSettings);
+  return _CreatePrintSettings(aNewPrintSettings);
 }
 
 NS_IMETHODIMP
 nsPrintOptions::GetDefaultPrinterName(char16_t * *aDefaultPrinterName)
 {
   nsresult rv;
   nsCOMPtr<nsIPrinterEnumerator> prtEnum =
            do_GetService(NS_PRINTER_ENUMERATOR_CONTRACTID, &rv);
@@ -1099,16 +1077,17 @@ nsPrintOptions::InitPrintSettingsFromPri
 
   rv = prtEnum->InitPrintSettingsFromPrinter(aPrinterName, aPrintSettings);
   NS_ENSURE_SUCCESS(rv, rv);
 
   aPrintSettings->SetIsInitializedFromPrinter(true);
   return rv;
 }
 
+#ifndef MOZ_X11
 /** ---------------------------------------------------
  *  Helper function - Returns either the name or sets the length to zero
  */
 static nsresult 
 GetAdjustedPrinterName(nsIPrintSettings* aPS, bool aUsePNP,
                        nsAString& aPrinterName)
 {
   NS_ENSURE_ARG_POINTER(aPS);
@@ -1138,41 +1117,17 @@ GetAdjustedPrinterName(nsIPrintSettings*
     int32_t i = 0;
     while ((i = aPrinterName.FindChar(uChar, i)) != kNotFound) {
       aPrinterName.Replace(i, 1, replSubstr);
       i++;
     }
   }
   return NS_OK;
 }
-
-NS_IMETHODIMP
-nsPrintOptions::GetPrinterPrefInt(nsIPrintSettings *aPrintSettings,
-                                  const char16_t *aPrefName, int32_t *_retval)
-{
-  NS_ENSURE_ARG_POINTER(aPrintSettings);
-  NS_ENSURE_ARG_POINTER(aPrefName);
-
-  nsAutoString prtName;
-  // Get the Printer Name from the PrintSettings
-  // to use as a prefix for Pref Names
-  GetAdjustedPrinterName(aPrintSettings, true, prtName);
-
-  const char* prefName =
-    GetPrefName(NS_LossyConvertUTF16toASCII(aPrefName).get(), prtName);
-
-  NS_ENSURE_TRUE(prefName, NS_ERROR_FAILURE);
-
-  int32_t iVal;
-  nsresult rv = Preferences::GetInt(prefName, &iVal);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  *_retval = iVal;
-  return rv;
-}
+#endif
 
 NS_IMETHODIMP 
 nsPrintOptions::InitPrintSettingsFromPrefs(nsIPrintSettings* aPS,
                                            bool aUsePNP, uint32_t aFlags)
 {
   NS_ENSURE_ARG_POINTER(aPS);
 
   bool isInitialized;
--- a/widget/qt/nsDeviceContextSpecQt.cpp
+++ b/widget/qt/nsDeviceContextSpecQt.cpp
@@ -251,16 +251,8 @@ NS_IMETHODIMP nsPrinterEnumeratorQt::Ini
         const char16_t* aPrinterName,
         nsIPrintSettings* aPrintSettings)
 {
     DO_PR_DEBUG_LOG(("nsPrinterEnumeratorQt::InitPrintSettingsFromPrinter()"));
     // XXX Leave NS_OK for now
     // Probably should use NS_ERROR_NOT_IMPLEMENTED
     return NS_OK;
 }
-
-NS_IMETHODIMP nsPrinterEnumeratorQt::DisplayPropertiesDlg(
-        const char16_t* aPrinter,
-        nsIPrintSettings* aPrintSettings)
-{
-    return NS_ERROR_NOT_IMPLEMENTED;
-}
-
--- a/widget/windows/nsDeviceContextSpecWin.cpp
+++ b/widget/windows/nsDeviceContextSpecWin.cpp
@@ -509,24 +509,16 @@ nsPrinterEnumeratorWin::GetPrinterNameLi
     LPWSTR name = GlobalPrinters::GetInstance()->GetItemFromList(printerInx);
     names[printerInx].Assign(name);
   }
 
   return NS_NewAdoptingStringEnumerator(aPrinterNameList, printers);
 }
 
 //----------------------------------------------------------------------------------
-// Display the AdvancedDocumentProperties for the selected Printer
-NS_IMETHODIMP nsPrinterEnumeratorWin::DisplayPropertiesDlg(const char16_t *aPrinterName, nsIPrintSettings* aPrintSettings)
-{
-  // Implementation removed because it is unused
-  return NS_OK;
-}
-
-//----------------------------------------------------------------------------------
 //-- Global Printers
 //----------------------------------------------------------------------------------
 
 //----------------------------------------------------------------------------------
 // THe array hold the name and port for each printer
 void 
 GlobalPrinters::ReallocatePrinters()
 {