Bug 694706 - Get rid of checkerboard for remote viewport. r=cjones
authorOleg Romashin <romaxa@gmail.com>
Thu, 20 Oct 2011 15:17:09 -0700
changeset 79733 36d12bbf310a006adb8529850317e3c0cb9b1ea8
parent 79732 15e15fead382a9fbfaa9b12ea049b24089b3555f
child 79734 73512caf4647f79b7c0d019ca11e390bac61b424
push id506
push userclegnitto@mozilla.com
push dateWed, 09 Nov 2011 02:03:18 +0000
treeherdermozilla-aurora@63587fc7bb93 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscjones
bugs694706
milestone10.0a1
Bug 694706 - Get rid of checkerboard for remote viewport. r=cjones
dom/ipc/PBrowser.ipdl
dom/ipc/TabChild.cpp
dom/ipc/TabChild.h
dom/ipc/TabParent.cpp
dom/ipc/TabParent.h
layout/base/nsPresShell.cpp
layout/ipc/RenderFrameParent.cpp
layout/ipc/RenderFrameParent.h
layout/reftests/reftest-sanity/reftest.list
layout/reftests/reftest-sanity/test-displayport-bg.html
layout/tools/reftest/reftest-content.js
layout/tools/reftest/reftest.js
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -58,16 +58,17 @@ using nsQueryContentEvent;
 using nsRect;
 using nsSelectionEvent;
 using nsTextEvent;
 using nsMouseEvent;
 using nsMouseScrollEvent;
 using nsKeyEvent;
 using RemoteDOMEvent;
 using mozilla::WindowsHandle;
+using nscolor;
 
 namespace mozilla {
 namespace dom {
 
 rpc protocol PBrowser
 {
     manager PContent;
 
@@ -185,16 +186,17 @@ parent:
     sync GetDPI() returns (float value);
 
     /**
      * Return native data of root widget
      */
     sync GetWidgetNativeData() returns (WindowsHandle value);
 
     SetCursor(PRUint32 value);
+    SetBackgroundColor(nscolor color);
 
     PContentPermissionRequest(nsCString aType, URI uri);
 
     PContentDialog(PRUint32 aType, nsCString aName, nsCString aFeatures,
                    PRInt32[] aIntParams, nsString[] aStringParams);
 
     /**
      * Create a layout frame (encapsulating a remote layer tree) for
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -118,16 +118,17 @@ public:
 };
 
 
 TabChild::TabChild(PRUint32 aChromeFlags)
   : mRemoteFrame(nsnull)
   , mTabChildGlobal(nsnull)
   , mChromeFlags(aChromeFlags)
   , mOuterRect(0, 0, 0, 0)
+  , mLastBackgroundColor(NS_RGB(255, 255, 255))
 {
     printf("creating %d!\n", NS_IsMainThread());
 }
 
 nsresult
 TabChild::Init()
 {
   nsCOMPtr<nsIWebBrowser> webBrowser = do_CreateInstance(NS_WEBBROWSER_CONTRACTID);
@@ -995,16 +996,25 @@ TabChild::InitWidget(const nsIntSize& si
     NS_ABORT_IF_FALSE(lf && lf->HasShadowManager(),
                       "PuppetWidget should have shadow manager");
     lf->SetParentBackendType(be);
 
     mRemoteFrame = remoteFrame;
     return true;
 }
 
+void
+TabChild::SetBackgroundColor(const nscolor& aColor)
+{
+  if (mLastBackgroundColor != aColor) {
+    mLastBackgroundColor = aColor;
+    SendSetBackgroundColor(mLastBackgroundColor);
+  }
+}
+
 static bool
 SendSyncMessageToParent(void* aCallbackData,
                         const nsAString& aMessage,
                         const nsAString& aJSON,
                         InfallibleTArray<nsString>* aJSONRetVal)
 {
   return static_cast<TabChild*>(aCallbackData)->
     SendSyncMessage(nsString(aMessage), nsString(aJSON),
@@ -1116,9 +1126,8 @@ TabChildGlobal::GetJSContextForEventHand
 
 nsIPrincipal* 
 TabChildGlobal::GetPrincipal()
 {
   if (!mTabChild)
     return nsnull;
   return mTabChild->GetPrincipal();
 }
-
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -250,16 +250,17 @@ public:
     virtual bool DeallocPOfflineCacheUpdate(POfflineCacheUpdateChild* offlineCacheUpdate);
 
     nsIWebNavigation* WebNavigation() { return mWebNav; }
 
     JSContext* GetJSContext() { return mCx; }
 
     nsIPrincipal* GetPrincipal() { return mPrincipal; }
 
+    void SetBackgroundColor(const nscolor& aColor);
 protected:
     NS_OVERRIDE
     virtual PRenderFrameChild* AllocPRenderFrame();
     NS_OVERRIDE
     virtual bool DeallocPRenderFrame(PRenderFrameChild* aFrame);
     NS_OVERRIDE
     virtual bool RecvDestroy();
 
@@ -273,16 +274,17 @@ private:
     void DestroyWindow();
 
     nsCOMPtr<nsIWebNavigation> mWebNav;
     nsCOMPtr<nsIWidget> mWidget;
     RenderFrameChild* mRemoteFrame;
     nsRefPtr<TabChildGlobal> mTabChildGlobal;
     PRUint32 mChromeFlags;
     nsIntRect mOuterRect;
+    nscolor mLastBackgroundColor;
 
     DISALLOW_EVIL_CONSTRUCTORS(TabChild);
 };
 
 inline TabChild*
 GetTabChildFrom(nsIDocShell* aDocShell)
 {
     nsCOMPtr<nsITabChild> tc = do_GetInterface(aDocShell);
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -337,16 +337,27 @@ TabParent::RecvSetCursor(const PRUint32&
   nsCOMPtr<nsIWidget> widget = GetWidget();
   if (widget) {
     widget->SetCursor((nsCursor) aCursor);
   }
   return true;
 }
 
 bool
+TabParent::RecvSetBackgroundColor(const nscolor& aColor)
+{
+  if (nsRefPtr<nsFrameLoader> frameLoader = GetFrameLoader()) {
+    if (RenderFrameParent* frame = frameLoader->GetCurrentRemoteFrame()) {
+      frame->SetBackgroundColor(aColor);
+    }
+  }
+  return true;
+}
+
+bool
 TabParent::RecvNotifyIMEFocus(const bool& aFocus,
                               nsIMEUpdatePreference* aPreference,
                               PRUint32* aSeqno)
 {
   nsCOMPtr<nsIWidget> widget = GetWidget();
   if (!widget)
     return true;
 
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -103,16 +103,17 @@ public:
     virtual bool RecvNotifyIMETextHint(const nsString& aText);
     virtual bool RecvEndIMEComposition(const bool& aCancel,
                                        nsString* aComposition);
     virtual bool RecvGetIMEEnabled(PRUint32* aValue);
     virtual bool RecvSetInputMode(const PRUint32& aValue, const nsString& aType, const nsString& aAction, const PRUint32& aReason);
     virtual bool RecvGetIMEOpenState(bool* aValue);
     virtual bool RecvSetIMEOpenState(const bool& aValue);
     virtual bool RecvSetCursor(const PRUint32& aValue);
+    virtual bool RecvSetBackgroundColor(const nscolor& aValue);
     virtual bool RecvGetDPI(float* aValue);
     virtual bool RecvGetWidgetNativeData(WindowsHandle* aValue);
     virtual PContentDialogParent* AllocPContentDialog(const PRUint32& aType,
                                                       const nsCString& aName,
                                                       const nsCString& aFeatures,
                                                       const InfallibleTArray<int>& aIntParams,
                                                       const InfallibleTArray<nsString>& aStringParams);
     virtual bool DeallocPContentDialog(PContentDialogParent* aDialog)
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -51,16 +51,18 @@
  * identified per MPL Section 3.3
  *
  * Date         Modified by     Description of modification
  * 05/03/2000   IBM Corp.       Observer events for reflow states
  */
 
 /* a presentation of a document, part 2 */
 
+#include "mozilla/dom/PBrowserChild.h"
+#include "mozilla/dom/TabChild.h"
 #include "mozilla/Util.h"
 
 #include "nsPresShell.h"
 #include "nsPresContext.h"
 #include "nsIContent.h"
 #include "mozilla/dom/Element.h"
 #include "nsIDocument.h"
 #include "nsIDOMXULDocument.h"
@@ -5087,16 +5089,21 @@ void PresShell::UpdateCanvasBackground()
   }
 
   // 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
   // color we actually draw.
   if (!FrameConstructor()->GetRootElementFrame()) {
     mCanvasBackgroundColor = GetDefaultBackgroundColorToDraw();
   }
+  if (XRE_GetProcessType() == GeckoProcessType_Content) {
+    if (TabChild* tabChild = GetTabChildFrom(this)) {
+      tabChild->SetBackgroundColor(mCanvasBackgroundColor);
+    }
+  }
 }
 
 nscolor PresShell::ComputeBackstopColor(nsIView* aDisplayRoot)
 {
   nsIWidget* widget = aDisplayRoot->GetWidget();
   if (widget && widget->GetTransparencyMode() != eTransparencyOpaque) {
     // Within a transparent widget, so the backstop color must be
     // totally transparent.
--- a/layout/ipc/RenderFrameParent.cpp
+++ b/layout/ipc/RenderFrameParent.cpp
@@ -57,38 +57,16 @@ typedef nsContentView::ViewConfig ViewCo
 using namespace mozilla::layers;
 
 namespace mozilla {
 namespace layout {
 
 typedef FrameMetrics::ViewID ViewID;
 typedef RenderFrameParent::ViewMap ViewMap;
 
-nsRefPtr<ImageContainer> sCheckerboard = nsnull;
-
-class CheckerBoardPatternDeleter : public nsIObserver
-{
-public:
-  NS_DECL_NSIOBSERVER
-  NS_DECL_ISUPPORTS
-};
-
-NS_IMPL_ISUPPORTS1(CheckerBoardPatternDeleter, nsIObserver)
-
-NS_IMETHODIMP
-CheckerBoardPatternDeleter::Observe(nsISupports* aSubject,
-                                    const char* aTopic,
-                                    const PRUnichar* aData)
-{
-  if (!strcmp(aTopic, "xpcom-shutdown")) {
-    sCheckerboard = nsnull;
-  }
-  return NS_OK;
-}
-
 // Represents (affine) transforms that are calculated from a content view.
 struct ViewTransform {
   ViewTransform(nsIntPoint aTranslation = nsIntPoint(0, 0), float aXScale = 1, float aYScale = 1)
     : mTranslation(aTranslation)
     , mXScale(aXScale)
     , mYScale(aYScale)
   {}
 
@@ -419,71 +397,24 @@ BuildViewMap(ViewMap& oldContentViews, V
 
   for (Layer* child = aLayer->GetFirstChild();
        child; child = child->GetNextSibling()) {
     BuildViewMap(oldContentViews, newContentViews, aFrameLoader, child,
                  aXScale, aYScale);
   }
 }
 
-#define BOARDSIZE 32
-#define CHECKERSIZE 16
-already_AddRefed<gfxASurface>
-GetBackgroundImage()
-{
-  static unsigned int data[BOARDSIZE * BOARDSIZE];
-  static bool initialized = false;
-  if (!initialized) {
-    initialized = true;
-    for (unsigned int y = 0; y < BOARDSIZE; y++) {
-      for (unsigned int x = 0; x < BOARDSIZE; x++) {
-        bool col_odd = (x / CHECKERSIZE) & 1;
-        bool row_odd = (y / CHECKERSIZE) & 1;
-        if (col_odd ^ row_odd) { // xor
-          data[y * BOARDSIZE + x] = 0xFFFFFFFF;
-        }
-        else {
-          data[y * BOARDSIZE + x] = 0xFFDDDDDD;
-        }
-      }
-    }
-  }
-
-  nsRefPtr<gfxASurface> s =
-    new gfxImageSurface((unsigned char*) data,
-                        gfxIntSize(BOARDSIZE, BOARDSIZE),
-                        BOARDSIZE * sizeof(unsigned int),
-                        gfxASurface::ImageFormatARGB32);
-  return s.forget();
-}
-
 static void
 BuildBackgroundPatternFor(ContainerLayer* aContainer,
                           ContainerLayer* aShadowRoot,
-                          const FrameMetrics& aMetrics,
                           const ViewConfig& aConfig,
+                          const gfxRGBA& aColor,
                           LayerManager* aManager,
-                          nsIFrame* aFrame,
-                          nsDisplayListBuilder* aBuilder)
+                          nsIFrame* aFrame)
 {
-  // We tile a visible region that is the frame's area \setminus the
-  // rect in our frame onto which valid pixels from remote content
-  // will be drawn.  It's just a waste of CPU cycles to draw a
-  // checkerboard behind that content.
-  //
-  // We want to give the background the illusion of moving while the
-  // user pans, so we nudge the tiling area a bit based on the
-  // "desired" scroll offset.
-  //
-  // The background-image layer is added to the layer tree "behind"
-  // the shadow tree.  It doesn't matter in theory which is behind/in
-  // front, except that having the background in front of content
-  // means we have to be more careful about snapping boundaries,
-  // whereas having it behind allows us to trade off simplicity for
-  // "wasted" drawing of a few extra pixels.
   ShadowLayer* shadowRoot = aShadowRoot->AsShadowLayer();
   gfxMatrix t;
   if (!shadowRoot->GetShadowTransform().Is2D(&t)) {
     return;
   }
 
   // Get the rect bounding the shadow content, transformed into the
   // same space as |aFrame|
@@ -500,75 +431,33 @@ BuildBackgroundPatternFor(ContainerLayer
   nscoord auPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
   nsIntRect frameRect = aFrame->GetRect().ToOutsidePixels(auPerDevPixel);
 
   // If the shadow tree covers the frame rect, don't bother building
   // the background, it wouldn't be visible
   if (localIntContentVis.Contains(frameRect)) {
     return;
   }
-
-  nsRefPtr<gfxASurface> bgImage = GetBackgroundImage();
-  gfxIntSize bgImageSize = bgImage->GetSize();
-
-  // Set up goop needed to get a cairo image into its own layer
-  if (!sCheckerboard) {
-    sCheckerboard = aManager->CreateImageContainer().get();
-    const Image::Format fmts[] = { Image::CAIRO_SURFACE };
-    nsRefPtr<Image> img = sCheckerboard->CreateImage(fmts, 1);
-    CairoImage::Data data = { bgImage.get(), bgImageSize };
-    static_cast<CairoImage*>(img.get())->SetData(data);
-    sCheckerboard->SetCurrentImage(img);
-    nsCOMPtr<nsIObserverService> observerService =
-      mozilla::services::GetObserverService();
-    if (!observerService) {
-      return;
-    }
-    nsresult rv = observerService->AddObserver(new CheckerBoardPatternDeleter, "xpcom-shutdown", false);
-    if (NS_FAILED(rv)) {
-      return;
-    }
-  }
-
-  nsRefPtr<ImageLayer> layer = aManager->CreateImageLayer();
-  layer->SetContainer(sCheckerboard);
-
-  // The tile source is the entire background image
-  nsIntRect tileSource(0, 0, bgImageSize.width, bgImageSize.height);
-  layer->SetTileSourceRect(&tileSource);
-
-  // The origin of the tiling plane, top-left of the tile source rect,
-  // is at layer-space point <0,0>.  Set up a translation from that
-  // origin to the frame top-left, with the little nudge included.
-  nsIntPoint translation = frameRect.TopLeft();
-  nsIntPoint panNudge = aConfig.mScrollOffset.ToNearestPixels(auPerDevPixel);
-  // This offset must be positive to ensure that the tiling rect
-  // contains the frame's visible rect.  The "desired" scroll offset
-  // is allowed to be negative, however, so we fix that up here.
-  panNudge.x = (panNudge.x % bgImageSize.width);
-  if (panNudge.x < 0) panNudge.x += bgImageSize.width;
-  panNudge.y = (panNudge.y % bgImageSize.height);
-  if (panNudge.y < 0) panNudge.y += bgImageSize.height;
-
-  translation -= panNudge;
-  layer->SetTransform(gfx3DMatrix::Translation(translation.x, translation.y, 0));
+  nsRefPtr<ColorLayer> layer = aManager->CreateColorLayer();
+  layer->SetColor(aColor);
 
   // The visible area of the background is the frame's area minus the
   // content area
   nsIntRegion bgRgn(frameRect);
   bgRgn.Sub(bgRgn, localIntContentVis);
-  bgRgn.MoveBy(-translation);
+  bgRgn.MoveBy(-frameRect.TopLeft());
   layer->SetVisibleRegion(bgRgn);
 
   aContainer->InsertAfter(layer, nsnull);
 }
 
 RenderFrameParent::RenderFrameParent(nsFrameLoader* aFrameLoader)
   : mFrameLoader(aFrameLoader)
   , mFrameLoaderDestroyed(false)
+  , mBackgroundColor(gfxRGBA(1, 1, 1))
 {
   if (aFrameLoader) {
     mContentViews[FrameMetrics::ROOT_SCROLL_ID] =
       new nsContentView(aFrameLoader, FrameMetrics::ROOT_SCROLL_ID);
   }
 }
 
 RenderFrameParent::~RenderFrameParent()
@@ -686,19 +575,19 @@ RenderFrameParent::BuildLayer(nsDisplayL
   ViewTransform transform;
   TransformShadowTree(aBuilder, mFrameLoader, aFrame, shadowRoot, transform);
   mContainer->SetClipRect(nsnull);
 
   if (mFrameLoader->AsyncScrollEnabled()) {
     const nsContentView* view = GetContentView(FrameMetrics::ROOT_SCROLL_ID);
     BuildBackgroundPatternFor(mContainer,
                               shadowRoot,
-                              shadowRoot->GetFrameMetrics(),
                               view->GetViewConfig(),
-                              aManager, aFrame, aBuilder);
+                              mBackgroundColor,
+                              aManager, aFrame);
   }
   mContainer->SetVisibleRegion(aVisibleRect);
 
   return nsRefPtr<Layer>(mContainer).forget();
 }
 
 void
 RenderFrameParent::OwnerContentChanged(nsIContent* aContent)
--- a/layout/ipc/RenderFrameParent.h
+++ b/layout/ipc/RenderFrameParent.h
@@ -93,16 +93,18 @@ public:
 
   already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
                                      nsIFrame* aFrame,
                                      LayerManager* aManager,
                                      const nsIntRect& aVisibleRect);
 
   void OwnerContentChanged(nsIContent* aContent);
 
+  void SetBackgroundColor(nscolor aColor) { mBackgroundColor = gfxRGBA(aColor); };
+
 protected:
   NS_OVERRIDE void ActorDestroy(ActorDestroyReason why);
 
   NS_OVERRIDE virtual PLayersParent* AllocPLayers(LayerManager::LayersBackend* aBackendType);
   NS_OVERRIDE virtual bool DeallocPLayers(PLayersParent* aLayers);
 
 private:
   void BuildViewMap();
@@ -128,16 +130,18 @@ private:
   //
   // Prefer the extra bit of state to null'ing out mFrameLoader in
   // Destroy() so that less code needs to be special-cased for after
   // Destroy().
   // 
   // It's possible for mFrameLoader==null and
   // mFrameLoaderDestroyed==false.
   bool mFrameLoaderDestroyed;
+  // this is gfxRGBA because that's what ColorLayer wants.
+  gfxRGBA mBackgroundColor;
 };
 
 } // namespace layout
 } // namespace mozilla
 
 /**
  * A DisplayRemote exists solely to graft a child process's shadow
  * layer tree (for a given RenderFrameParent) into its parent
--- a/layout/reftests/reftest-sanity/reftest.list
+++ b/layout/reftests/reftest-sanity/reftest.list
@@ -86,16 +86,17 @@ needs-focus load needs-focus.html
 fails == data:text/plain,HELLO about:blank
 needs-focus == data:text/plain, about:blank
 
 # Sanity check of viewport+displayport overrides
 fails-if(!browserIsRemote) == test-displayport.html test-displayport-ref.html # bug 593168
 skip-if(!browserIsRemote) != test-displayport-2.html test-displayport-ref.html # bug 593168
 skip-if(!browserIsRemote) == 647192-1.html 647192-1-ref.html
 skip-if(!browserIsRemote) == 656041-1.html 656041-1-ref.html
+skip-if(!browserIsRemote) == test-displayport-bg.html test-displayport-ref.html # bug 694706
 
 # IPC Position-fixed frames/layers test
 # Fixed layers are temporarily disabled (bug 656167).
 #== test-pos-fixed.html test-pos-fixed-ref.html
 == test-bg-attachment-fixed.html test-bg-attachment-fixed-ref.html
 
 # reftest syntax: require-or
 require-or(unrecognizedCondition,skip) script scripttest-fail.html
new file mode 100644
--- /dev/null
+++ b/layout/reftests/reftest-sanity/test-displayport-bg.html
@@ -0,0 +1,7 @@
+<!DOCTYPE HTML>
+<html reftest-viewport-w="100" reftest-viewport-h="100"
+      reftest-displayport-w="400" reftest-displayport-h="500"
+      reftest-async-scroll=true>
+<body bgcolor=green style="position: absolute; left:0px; top:0px;width:800px; height:1000px;"></body>
+</html>
+
--- a/layout/tools/reftest/reftest-content.js
+++ b/layout/tools/reftest/reftest-content.js
@@ -252,16 +252,20 @@ function setupDisplayport(contentRootEle
     var dpw = attrOrDefault("reftest-displayport-w", 0);
     var dph = attrOrDefault("reftest-displayport-h", 0);
     var dpx = attrOrDefault("reftest-displayport-x", 0);
     var dpy = attrOrDefault("reftest-displayport-y", 0);
     if (dpw !== 0 || dph !== 0) {
         LogInfo("Setting displayport to <x="+ dpx +", y="+ dpy +", w="+ dpw +", h="+ dph +">");
         windowUtils().setDisplayPortForElement(dpx, dpy, dpw, dph, content.document.documentElement);
     }
+    var asyncScroll = attrOrDefault("reftest-async-scroll", false);
+    if (asyncScroll) {
+      SendEnableAsyncScroll();
+    }
 
     // XXX support resolution when needed
 
     // XXX support viewconfig when needed
 }
 
 function resetDisplayport() {
     // XXX currently the displayport configuration lives on the
@@ -746,16 +750,21 @@ function SendException(what)
     sendAsyncMessage("reftest:Exception", { what: what });
 }
 
 function SendFailedLoad(why)
 {
     sendAsyncMessage("reftest:FailedLoad", { why: why });
 }
 
+function SendEnableAsyncScroll()
+{
+    sendAsyncMessage("reftest:EnableAsyncScroll");
+}
+
 // Return true if a snapshot was taken.
 function SendInitCanvasWithSnapshot()
 {
     // If we're in the same process as the top-level XUL window, then
     // drawing that window will also update our layers, so no
     // synchronization is needed.
     //
     // NB: this is a test-harness optimization only, it must not
--- a/layout/tools/reftest/reftest.js
+++ b/layout/tools/reftest/reftest.js
@@ -1341,16 +1341,17 @@ function CleanUpCrashDumpFiles()
 
 function FinishTestItem()
 {
     // Replace document with BLANK_URL_FOR_CLEARING in case there are
     // assertions when unloading.
     gDumpLog("REFTEST INFO | Loading a blank page\n");
     // After clearing, content will notify us of the assertion count
     // and tests will continue.
+    SetAsyncScroll(false);
     SendClear();
 }
 
 function DoAssertionCheck(numAsserts)
 {
     if (gDebug.isDebugBuild) {
         if (gBrowserIsRemote) {
             // Count chrome-process asserts too when content is out of
@@ -1437,20 +1438,31 @@ function RegisterMessageListenersAndLoad
     gBrowserMessageManager.addMessageListener(
         "reftest:UpdateCanvasForInvalidation",
         function (m) { RecvUpdateCanvasForInvalidation(m.json.rects); }
     );
     gBrowserMessageManager.addMessageListener(
         "reftest:ExpectProcessCrash",
         function (m) { RecvExpectProcessCrash(); }
     );
+    gBrowserMessageManager.addMessageListener(
+        "reftest:EnableAsyncScroll",
+        function (m) { SetAsyncScroll(true); }
+    );
 
     gBrowserMessageManager.loadFrameScript("chrome://reftest/content/reftest-content.js", true);
 }
 
+function SetAsyncScroll(enabled)
+{
+    gBrowser.QueryInterface(CI.nsIFrameLoaderOwner).frameLoader.renderMode =
+        enabled ? CI.nsIFrameLoader.RENDER_MODE_ASYNC_SCROLL :
+                  CI.nsIFrameLoader.RENDER_MODE_DEFAULT;
+}
+
 function RecvAssertionCount(count)
 {
     DoAssertionCheck(count);
 }
 
 function RecvContentReady()
 {
     InitAndStartRefTests();