Bug 1068190 - Part 1: Add foundation for compositor unit tests. r=mstange
authorBenoit Girard <b56girard@gmail.com>
Wed, 14 Jan 2015 17:24:09 -0500
changeset 251036 5680121a4909bf31e487b2fc0ad212cb9c0c2140
parent 251035 00781c4524274dc4a923c8fea2dafa71e86d1bb8
child 251037 4a6767d4b59ba0e1a586d2cb28632c46c3c85307
push id4610
push userjlund@mozilla.com
push dateMon, 30 Mar 2015 18:32:55 +0000
treeherdermozilla-beta@4df54044d9ef [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmstange
bugs1068190
milestone38.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1068190 - Part 1: Add foundation for compositor unit tests. r=mstange
gfx/2d/MacIOSurface.cpp
gfx/layers/Compositor.cpp
gfx/layers/LayerScope.cpp
gfx/layers/composite/LayerManagerComposite.cpp
gfx/layers/ipc/CompositorParent.cpp
gfx/layers/ipc/CompositorParent.h
gfx/layers/opengl/CompositorOGL.cpp
gfx/tests/gtest/TestCompositor.cpp
gfx/tests/gtest/TestLayers.cpp
gfx/tests/gtest/moz.build
toolkit/xre/moz.build
toolkit/xre/nsAppRunner.cpp
toolkit/xre/nsAppRunner.h
widget/nsIWidget.h
--- a/gfx/2d/MacIOSurface.cpp
+++ b/gfx/2d/MacIOSurface.cpp
@@ -384,20 +384,24 @@ size_t MacIOSurface::GetHeight() {
   return GetDevicePixelHeight() / intScaleFactor;
 }
 
 size_t MacIOSurface::GetPlaneCount() {
   return MacIOSurfaceLib::IOSurfaceGetPlaneCount(mIOSurfacePtr);
 }
 
 /*static*/ size_t MacIOSurface::GetMaxWidth() {
+  if (!MacIOSurfaceLib::isInit())
+    return -1;
   return MacIOSurfaceLib::IOSurfaceGetPropertyMaximum(MacIOSurfaceLib::kPropWidth);
 }
 
 /*static*/ size_t MacIOSurface::GetMaxHeight() {
+  if (!MacIOSurfaceLib::isInit())
+    return -1;
   return MacIOSurfaceLib::IOSurfaceGetPropertyMaximum(MacIOSurfaceLib::kPropHeight);
 }
 
 size_t MacIOSurface::GetDevicePixelWidth() {
   return MacIOSurfaceLib::IOSurfaceGetWidth(mIOSurfacePtr);
 }
 
 size_t MacIOSurface::GetDevicePixelHeight() {
--- a/gfx/layers/Compositor.cpp
+++ b/gfx/layers/Compositor.cpp
@@ -4,16 +4,17 @@
  * 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/CompositorParent.h"  // for CompositorParent
 #include "mozilla/layers/Effects.h"     // for Effect, EffectChain, etc
 #include "mozilla/mozalloc.h"           // for operator delete, etc
 #include "gfx2DGlue.h"
+#include "nsAppRunner.h"
 
 namespace mozilla {
 namespace gfx {
 class Matrix4x4;
 }
 
 namespace layers {
 
@@ -23,17 +24,17 @@ Compositor::GetBackend()
 {
   AssertOnCompositorThread();
   return sBackend;
 }
 
 /* static */ void
 Compositor::SetBackend(LayersBackend backend)
 {
-  if (sBackend != LayersBackend::LAYERS_NONE && sBackend != backend) {
+  if (!gIsGtest && sBackend != LayersBackend::LAYERS_NONE && sBackend != backend) {
     // Assert this once we figure out bug 972891.
     //MOZ_CRASH("Trying to use more than one OMTC compositor.");
 
 #ifdef XP_MACOSX
     printf("ERROR: Changing compositor from %u to %u.\n",
            unsigned(sBackend), unsigned(backend));
 #endif
   }
--- a/gfx/layers/LayerScope.cpp
+++ b/gfx/layers/LayerScope.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 20; 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/. */
 
 /* This must occur *after* layers/PLayers.h to avoid typedefs conflicts. */
 #include "LayerScope.h"
 
+#include "nsAppRunner.h"
 #include "Composer2D.h"
 #include "Effects.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Endian.h"
 #include "TexturePoolOGL.h"
 #include "mozilla/layers/CompositorOGL.h"
 #include "mozilla/layers/CompositorParent.h"
@@ -976,17 +977,17 @@ LayerScope::SendLayerDump(UniquePtr<Pack
     WebSocketHelper::GetSocketManager()->AppendDebugData(
         new DebugGLLayersData(Move(aPacket)));
 }
 
 bool
 LayerScope::CheckSendable()
 {
     // Only compositor threads check LayerScope status
-    MOZ_ASSERT(CompositorParent::IsInCompositorThread());
+    MOZ_ASSERT(CompositorParent::IsInCompositorThread() || gIsGtest);
 
     if (!gfxPrefs::LayerScopeEnabled()) {
         return false;
     }
     if (!WebSocketHelper::GetSocketManager()) {
         Init();
         return false;
     }
--- a/gfx/layers/composite/LayerManagerComposite.cpp
+++ b/gfx/layers/composite/LayerManagerComposite.cpp
@@ -38,16 +38,17 @@
 #include "mozilla/layers/Compositor.h"  // for Compositor
 #include "mozilla/layers/CompositorTypes.h"
 #include "mozilla/layers/Effects.h"     // for Effect, EffectChain, etc
 #include "mozilla/layers/LayerMetricsWrapper.h" // for LayerMetricsWrapper
 #include "mozilla/layers/LayersTypes.h"  // for etc
 #include "ipc/CompositorBench.h"        // for CompositorBench
 #include "ipc/ShadowLayerUtils.h"
 #include "mozilla/mozalloc.h"           // for operator new, etc
+#include "nsAppRunner.h"
 #include "nsAutoPtr.h"                  // for nsRefPtr
 #include "nsCOMPtr.h"                   // for already_AddRefed
 #include "nsDebug.h"                    // for NS_WARNING, NS_RUNTIMEABORT, etc
 #include "nsISupportsImpl.h"            // for Layer::AddRef, etc
 #include "nsIWidget.h"                  // for nsIWidget
 #include "nsPoint.h"                    // for nsIntPoint
 #include "nsRect.h"                     // for nsIntRect
 #include "nsRegion.h"                   // for nsIntRegion, etc
@@ -327,39 +328,45 @@ LayerManagerComposite::CreateOptimalMask
 {
   NS_RUNTIMEABORT("Should only be called on the drawing side");
   return nullptr;
 }
 
 already_AddRefed<PaintedLayer>
 LayerManagerComposite::CreatePaintedLayer()
 {
-  NS_RUNTIMEABORT("Should only be called on the drawing side");
-  return nullptr;
+  MOZ_ASSERT(gIsGtest, "Unless you're testing the compositor using GTest,"
+                       "this should only be called on the drawing side");
+  nsRefPtr<PaintedLayer> layer = new PaintedLayerComposite(this);
+  return layer.forget();
 }
 
 already_AddRefed<ContainerLayer>
 LayerManagerComposite::CreateContainerLayer()
 {
-  NS_RUNTIMEABORT("Should only be called on the drawing side");
-  return nullptr;
+  MOZ_ASSERT(gIsGtest, "Unless you're testing the compositor using GTest,"
+                       "this should only be called on the drawing side");
+  nsRefPtr<ContainerLayer> layer = new ContainerLayerComposite(this);
+  return layer.forget();
 }
 
 already_AddRefed<ImageLayer>
 LayerManagerComposite::CreateImageLayer()
 {
   NS_RUNTIMEABORT("Should only be called on the drawing side");
   return nullptr;
 }
 
 already_AddRefed<ColorLayer>
 LayerManagerComposite::CreateColorLayer()
 {
-  NS_RUNTIMEABORT("Should only be called on the drawing side");
-  return nullptr;
+  MOZ_ASSERT(gIsGtest, "Unless you're testing the compositor using GTest,"
+                       "this should only be called on the drawing side");
+  nsRefPtr<ColorLayer> layer = new ColorLayerComposite(this);
+  return layer.forget();
 }
 
 already_AddRefed<CanvasLayer>
 LayerManagerComposite::CreateCanvasLayer()
 {
   NS_RUNTIMEABORT("Should only be called on the drawing side");
   return nullptr;
 }
--- a/gfx/layers/ipc/CompositorParent.cpp
+++ b/gfx/layers/ipc/CompositorParent.cpp
@@ -849,18 +849,18 @@ CompositorParent::CompositeCallback(Time
   }
 
   mCurrentCompositeTask = nullptr;
   CompositeToTarget(nullptr);
 }
 
 // Go down the composite layer tree, setting properties to match their
 // content-side counterparts.
-static void
-SetShadowProperties(Layer* aLayer)
+/* static */ void
+CompositorParent::SetShadowProperties(Layer* aLayer)
 {
   // FIXME: Bug 717688 -- Do these updates in LayerTransactionParent::RecvUpdate.
   LayerComposite* layerComposite = aLayer->AsLayerComposite();
   // Set the layerComposite's base transform to the layer's base transform.
   layerComposite->SetShadowTransform(aLayer->GetBaseTransform());
   layerComposite->SetShadowTransformSetByAnimation(false);
   layerComposite->SetShadowVisibleRegion(aLayer->GetVisibleRegion());
   layerComposite->SetShadowClipRect(aLayer->GetClipRect());
@@ -1667,17 +1667,17 @@ CrossProcessCompositorParent::ShadowLaye
   if (!state) {
     return;
   }
   MOZ_ASSERT(state->mParent);
   state->mParent->ScheduleRotationOnCompositorThread(aTargetConfig, aIsFirstPaint);
 
   Layer* shadowRoot = aLayerTree->GetRoot();
   if (shadowRoot) {
-    SetShadowProperties(shadowRoot);
+    CompositorParent::SetShadowProperties(shadowRoot);
   }
   UpdateIndirectTree(id, shadowRoot, aTargetConfig);
 
   state->mParent->NotifyShadowTreeTransaction(id, aIsFirstPaint, aScheduleComposite,
       aPaintSequenceNumber, aIsRepeatTransaction);
 
   // Send the 'remote paint ready' message to the content thread if it has already asked.
   if(mNotifyAfterRemotePaint)  {
--- a/gfx/layers/ipc/CompositorParent.h
+++ b/gfx/layers/ipc/CompositorParent.h
@@ -185,16 +185,18 @@ public:
    * be called by the widget code when it loses its viewport information
    * (or for whatever reason wants to refresh the viewport information).
    * The information refresh happens because the compositor will call
    * SetFirstPaintViewport on the next frame of composition.
    */
   void ForceIsFirstPaint();
   void Destroy();
 
+  static void SetShadowProperties(Layer* aLayer);
+
   void NotifyChildCreated(const uint64_t& aChild);
 
   void AsyncRender();
 
   // Can be called from any thread
   void ScheduleRenderOnCompositorThread();
   void SchedulePauseOnCompositorThread();
   /**
--- a/gfx/layers/opengl/CompositorOGL.cpp
+++ b/gfx/layers/opengl/CompositorOGL.cpp
@@ -24,16 +24,17 @@
 #include "mozilla/gfx/BasePoint.h"      // for BasePoint
 #include "mozilla/gfx/Matrix.h"         // for Matrix4x4, Matrix
 #include "mozilla/layers/LayerManagerComposite.h"  // for LayerComposite, etc
 #include "mozilla/layers/CompositingRenderTargetOGL.h"
 #include "mozilla/layers/Effects.h"     // for EffectChain, TexturedEffect, etc
 #include "mozilla/layers/TextureHost.h"  // for TextureSource, etc
 #include "mozilla/layers/TextureHostOGL.h"  // for TextureSourceOGL, etc
 #include "mozilla/mozalloc.h"           // for operator delete, etc
+#include "nsAppRunner.h"
 #include "nsAString.h"
 #include "nsIConsoleService.h"          // for nsIConsoleService, etc
 #include "nsIWidget.h"                  // for nsIWidget
 #include "nsLiteralString.h"            // for NS_LITERAL_STRING
 #include "nsMathUtils.h"                // for NS_roundf
 #include "nsRect.h"                     // for nsIntRect
 #include "nsServiceManagerUtils.h"      // for do_GetService
 #include "nsString.h"                   // for nsString, nsAutoCString, etc
@@ -99,16 +100,23 @@ CompositorOGL::~CompositorOGL()
   Destroy();
 }
 
 already_AddRefed<mozilla::gl::GLContext>
 CompositorOGL::CreateContext()
 {
   nsRefPtr<GLContext> context;
 
+  // Used by mock widget to create an offscreen context
+  void* widgetOpenGLContext = mWidget->GetNativeData(NS_NATIVE_OPENGL_CONTEXT);
+  if (widgetOpenGLContext) {
+    GLContext* alreadyRefed = reinterpret_cast<GLContext*>(widgetOpenGLContext);
+    return already_AddRefed<GLContext>(alreadyRefed);
+  }
+
 #ifdef XP_WIN
   if (PR_GetEnv("MOZ_LAYERS_PREFER_EGL")) {
     printf_stderr("Trying GL layers...\n");
     context = gl::GLContextProviderEGL::CreateForWindow(mWidget);
   }
 #endif
 
   // Allow to create offscreen GL context for main Layer Manager
@@ -430,17 +438,17 @@ CompositorOGL::PrepareViewport(const gfx
   // drawing directly into the window's back buffer, so this keeps things
   // looking correct.
   // XXX: We keep track of whether the window size changed, so we could skip
   // this update if it hadn't changed since the last call.
 
   // Matrix to transform (0, 0, aWidth, aHeight) to viewport space (-1.0, 1.0,
   // 2, 2) and flip the contents.
   Matrix viewMatrix;
-  if (mGLContext->IsOffscreen()) {
+  if (mGLContext->IsOffscreen() && !gIsGtest) {
     // In case of rendering via GL Offscreen context, disable Y-Flipping
     viewMatrix.PreTranslate(-1.0, -1.0);
     viewMatrix.PreScale(2.0f / float(aSize.width), 2.0f / float(aSize.height));
   } else {
     viewMatrix.PreTranslate(-1.0, 1.0);
     viewMatrix.PreScale(2.0f / float(aSize.width), 2.0f / float(aSize.height));
     viewMatrix.PreScale(1.0f, -1.0f);
   }
new file mode 100644
--- /dev/null
+++ b/gfx/tests/gtest/TestCompositor.cpp
@@ -0,0 +1,267 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+#include "gfxUtils.h"
+#include "gtest/gtest.h"
+#include "TestLayers.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/layers/BasicCompositor.h"  // for BasicCompositor
+#include "mozilla/layers/Compositor.h"  // for Compositor
+#include "mozilla/layers/CompositorOGL.h"  // for CompositorOGL
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/layers/LayerManagerComposite.h"
+#include "nsBaseWidget.h"
+#include "GLContext.h"
+#include "GLContextProvider.h"
+#include <vector>
+
+const int gCompWidth = 256;
+const int gCompHeight = 256;
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+using namespace mozilla::layers;
+using namespace mozilla::gl;
+
+class MockWidget : public nsBaseWidget
+{
+public:
+  MockWidget() {}
+
+  NS_DECL_ISUPPORTS_INHERITED
+
+  NS_IMETHOD              GetClientBounds(nsIntRect &aRect) {
+    aRect = nsIntRect(0, 0, gCompWidth, gCompHeight);
+    return NS_OK;
+  }
+  NS_IMETHOD              GetBounds(nsIntRect &aRect) { return GetClientBounds(aRect); }
+
+  void* GetNativeData(uint32_t aDataType) MOZ_OVERRIDE {
+    if (aDataType == NS_NATIVE_OPENGL_CONTEXT) {
+      mozilla::gl::SurfaceCaps caps = mozilla::gl::SurfaceCaps::ForRGB();
+      caps.preserve = false;
+      caps.bpp16 = false;
+      nsRefPtr<GLContext> context = GLContextProvider::CreateOffscreen(
+        gfxIntSize(gCompWidth, gCompHeight), caps);
+      return context.forget().take();
+    }
+    return nullptr;
+  }
+
+  NS_IMETHOD              Create(nsIWidget *aParent,
+                                 nsNativeWidget aNativeParent,
+                                 const nsIntRect &aRect,
+                                 nsDeviceContext *aContext,
+                                 nsWidgetInitData *aInitData = nullptr) { return NS_OK; }
+  NS_IMETHOD              Show(bool aState) { return NS_OK; }
+  virtual bool            IsVisible() const { return true; }
+  NS_IMETHOD              ConstrainPosition(bool aAllowSlop,
+                                            int32_t *aX, int32_t *aY) { return NS_OK; }
+  NS_IMETHOD              Move(double aX, double aY) { return NS_OK; }
+  NS_IMETHOD              Resize(double aWidth, double aHeight, bool aRepaint) { return NS_OK; }
+  NS_IMETHOD              Resize(double aX, double aY,
+                                 double aWidth, double aHeight, bool aRepaint) { return NS_OK; }
+
+  NS_IMETHOD              Enable(bool aState) { return NS_OK; }
+  virtual bool            IsEnabled() const { return true; }
+  NS_IMETHOD              SetFocus(bool aRaise) { return NS_OK; }
+  virtual nsresult        ConfigureChildren(const nsTArray<Configuration>& aConfigurations) { return NS_OK; }
+  NS_IMETHOD              Invalidate(const nsIntRect &aRect) { return NS_OK; }
+  NS_IMETHOD              SetTitle(const nsAString& title) { return NS_OK; }
+  virtual nsIntPoint      WidgetToScreenOffset() { return nsIntPoint(0, 0); }
+  NS_IMETHOD              DispatchEvent(mozilla::WidgetGUIEvent* aEvent,
+                                        nsEventStatus& aStatus) { return NS_OK; }
+  NS_IMETHOD              CaptureRollupEvents(nsIRollupListener * aListener, bool aDoCapture) { return NS_OK; }
+  NS_IMETHOD_(void)       SetInputContext(const InputContext& aContext,
+                                          const InputContextAction& aAction) {}
+  NS_IMETHOD_(InputContext) GetInputContext() { abort(); }
+  NS_IMETHOD              ReparentNativeWidget(nsIWidget* aNewParent) { return NS_OK; }
+private:
+  ~MockWidget() {}
+};
+
+NS_IMPL_ISUPPORTS_INHERITED0(MockWidget, nsBaseWidget)
+
+struct LayerManagerData {
+  RefPtr<MockWidget> mWidget;
+  RefPtr<Compositor> mCompositor;
+  nsRefPtr<LayerManagerComposite> mLayerManager;
+
+  LayerManagerData(Compositor* compositor, MockWidget* widget, LayerManagerComposite* layerManager)
+    : mWidget(widget)
+    , mCompositor(compositor)
+    , mLayerManager(layerManager)
+  {}
+};
+
+static TemporaryRef<Compositor> CreateTestCompositor(LayersBackend backend, MockWidget* widget)
+{
+  gfxPrefs::GetSingleton();
+
+  RefPtr<Compositor> compositor;
+
+  if (backend == LayersBackend::LAYERS_OPENGL) {
+    compositor = new CompositorOGL(widget,
+                                   gCompWidth,
+                                   gCompHeight,
+                                   true);
+    compositor->SetDestinationSurfaceSize(IntSize(gCompWidth, gCompHeight));
+  } else if (backend == LayersBackend::LAYERS_BASIC) {
+    compositor = new BasicCompositor(widget);
+#ifdef XP_WIN
+  } else if (backend == LayersBackend::LAYERS_D3D11) {
+    //compositor = new CompositorD3D11();
+    MOZ_CRASH(); // No support yet
+  } else if (backend == LayersBackend::LAYERS_D3D9) {
+    //compositor = new CompositorD3D9(this, mWidget);
+    MOZ_CRASH(); // No support yet
+#endif
+  }
+
+  if (!compositor) {
+    printf_stderr("Failed to construct layer manager for the requested backend\n");
+    abort();
+  }
+
+  return compositor;
+}
+
+/**
+ * Get a list of layers managers for the platform to run the test on.
+ */
+static std::vector<LayerManagerData> GetLayerManagers(std::vector<LayersBackend> aBackends)
+{
+  std::vector<LayerManagerData> managers;
+
+  for (size_t i = 0; i < aBackends.size(); i++) {
+    auto backend = aBackends[i];
+
+    RefPtr<MockWidget> widget = new MockWidget();
+    RefPtr<Compositor> compositor = CreateTestCompositor(backend, widget);
+
+    nsRefPtr<LayerManagerComposite> layerManager = new LayerManagerComposite(compositor);
+
+    layerManager->Initialize();
+
+    managers.push_back(LayerManagerData(compositor, widget, layerManager));
+  }
+
+  return managers;
+}
+
+/**
+ * This will return the default list of backends that
+ * units test should run against.
+ */
+static std::vector<LayersBackend> GetPlatformBackends()
+{
+  std::vector<LayersBackend> backends;
+
+  // For now we only support Basic for gtest
+  backends.push_back(LayersBackend::LAYERS_BASIC);
+
+#ifdef XP_MACOSX
+  backends.push_back(LayersBackend::LAYERS_OPENGL);
+#endif
+
+  // TODO Support OGL/D3D backends with unit test
+  return backends;
+}
+
+static TemporaryRef<DrawTarget> CreateDT()
+{
+  RefPtr<DrawTarget> dt = gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
+    IntSize(gCompWidth, gCompHeight), SurfaceFormat::B8G8R8A8);
+
+  return dt;
+}
+
+static bool CompositeAndCompare(nsRefPtr<LayerManagerComposite> layerManager, DrawTarget* refDT)
+{
+  RefPtr<DrawTarget> drawTarget = CreateDT();
+
+  layerManager->BeginTransactionWithDrawTarget(drawTarget, nsIntRect(0, 0, gCompWidth, gCompHeight));
+  layerManager->EndEmptyTransaction();
+
+  RefPtr<SourceSurface> ss = drawTarget->Snapshot();
+  RefPtr<DataSourceSurface> dss = ss->GetDataSurface();
+  uint8_t* bitmap = dss->GetData();
+
+  RefPtr<SourceSurface> ssRef = refDT->Snapshot();
+  RefPtr<DataSourceSurface> dssRef = ssRef->GetDataSurface();
+  uint8_t* bitmapRef = dssRef->GetData();
+
+  for (int y = 0; y < gCompHeight; y++) {
+    for (int x = 0; x < gCompWidth; x++) {
+      for (size_t channel = 0; channel < 4; channel++) {
+        uint8_t bit = bitmap[y * dss->Stride() + x * 4 + channel];
+        uint8_t bitRef = bitmapRef[y * dss->Stride() + x * 4 + channel];
+        if (bit != bitRef) {
+          printf("Layer Tree:\n");
+          layerManager->Dump();
+          printf("Original:\n");
+          gfxUtils::DumpAsDataURI(drawTarget);
+          printf("\n\n");
+
+          printf("Reference:\n");
+          gfxUtils::DumpAsDataURI(refDT);
+          printf("\n\n");
+
+          return false;
+        }
+      }
+    }
+  }
+
+  return true;
+}
+
+TEST(Gfx, CompositorConstruct)
+{
+  auto layerManagers = GetLayerManagers(GetPlatformBackends());
+}
+
+TEST(Gfx, CompositorSimpleTree)
+{
+  auto layerManagers = GetLayerManagers(GetPlatformBackends());
+  for (size_t i = 0; i < layerManagers.size(); i++) {
+    nsRefPtr<LayerManagerComposite> layerManager = layerManagers[i].mLayerManager;
+    nsRefPtr<LayerManager> lmBase = layerManager.get();
+    nsTArray<nsRefPtr<Layer>> layers;
+    nsIntRegion layerVisibleRegion[] = {
+      nsIntRegion(nsIntRect(0, 0, gCompWidth, gCompHeight)),
+      nsIntRegion(nsIntRect(0, 0, gCompWidth, gCompHeight)),
+      nsIntRegion(nsIntRect(0, 0, 100, 100)),
+      nsIntRegion(nsIntRect(0, 50, 100, 100)),
+    };
+    nsRefPtr<Layer> root = CreateLayerTree("c(ooo)", layerVisibleRegion, nullptr, lmBase, layers);
+
+    { // background
+      ColorLayer* colorLayer = layers[1]->AsColorLayer();
+      colorLayer->SetColor(gfxRGBA(1.f, 0.f, 1.f, 1.f));
+      colorLayer->SetBounds(colorLayer->GetVisibleRegion().GetBounds());
+    }
+
+    {
+      ColorLayer* colorLayer = layers[2]->AsColorLayer();
+      colorLayer->SetColor(gfxRGBA(1.f, 0.f, 0.f, 1.f));
+      colorLayer->SetBounds(colorLayer->GetVisibleRegion().GetBounds());
+    }
+
+    {
+      ColorLayer* colorLayer = layers[3]->AsColorLayer();
+      colorLayer->SetColor(gfxRGBA(0.f, 0.f, 1.f, 1.f));
+      colorLayer->SetBounds(colorLayer->GetVisibleRegion().GetBounds());
+    }
+
+    RefPtr<DrawTarget> refDT = CreateDT();
+    refDT->FillRect(Rect(0, 0, gCompWidth, gCompHeight), ColorPattern(Color(1.f, 0.f, 1.f, 1.f)));
+    refDT->FillRect(Rect(0, 0, 100, 100), ColorPattern(Color(1.f, 0.f, 0.f, 1.f)));
+    refDT->FillRect(Rect(0, 50, 100, 100), ColorPattern(Color(0.f, 0.f, 1.f, 1.f)));
+    EXPECT_TRUE(CompositeAndCompare(layerManager, refDT));
+  }
+}
+
--- a/gfx/tests/gtest/TestLayers.cpp
+++ b/gfx/tests/gtest/TestLayers.cpp
@@ -2,44 +2,22 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 #include "TestLayers.h"
 #include "gtest/gtest.h"
 #include "gmock/gmock.h"
 #include "mozilla/layers/LayerMetricsWrapper.h"
+#include "mozilla/layers/CompositorParent.h"
 
 using namespace mozilla;
 using namespace mozilla::gfx;
 using namespace mozilla::layers;
 
-class TestLayerManager: public LayerManager {
-public:
-  TestLayerManager()
-    : LayerManager()
-  {}
-
-  virtual bool EndEmptyTransaction(EndTransactionFlags aFlags = END_DEFAULT) { return false; }
-  virtual already_AddRefed<ContainerLayer> CreateContainerLayer() { return nullptr; }
-  virtual void GetBackendName(nsAString& aName) {}
-  virtual LayersBackend GetBackendType() { return LayersBackend::LAYERS_BASIC; }
-  virtual void BeginTransaction() {}
-  virtual already_AddRefed<ImageLayer> CreateImageLayer() { return nullptr; }
-  virtual void SetRoot(Layer* aLayer) {}
-  virtual already_AddRefed<ColorLayer> CreateColorLayer() { return nullptr; }
-  virtual void BeginTransactionWithTarget(gfxContext* aTarget) {}
-  virtual already_AddRefed<CanvasLayer> CreateCanvasLayer() { return nullptr; }
-  virtual void EndTransaction(DrawPaintedLayerCallback aCallback,
-                              void* aCallbackData,
-                              EndTransactionFlags aFlags = END_DEFAULT) {}
-  virtual int32_t GetMaxTextureSize() const { return 0; }
-  virtual already_AddRefed<PaintedLayer> CreatePaintedLayer() { return nullptr; }
-};
-
 class TestContainerLayer: public ContainerLayer {
 public:
   explicit TestContainerLayer(LayerManager* aManager)
     : ContainerLayer(aManager, nullptr)
   {}
 
   virtual const char* Name() const {
     return "TestContainerLayer";
@@ -68,16 +46,54 @@ public:
     return TYPE_PAINTED;
   }
 
   virtual void InvalidateRegion(const nsIntRegion& aRegion) {
     MOZ_CRASH();
   }
 };
 
+class TestLayerManager: public LayerManager {
+public:
+  TestLayerManager()
+    : LayerManager()
+  {}
+
+  virtual bool EndEmptyTransaction(EndTransactionFlags aFlags = END_DEFAULT) { return false; }
+  virtual already_AddRefed<ContainerLayer> CreateContainerLayer() {
+    nsRefPtr<ContainerLayer> layer = new TestContainerLayer(this);
+    return layer.forget();
+  }
+  virtual void GetBackendName(nsAString& aName) {}
+  virtual LayersBackend GetBackendType() { return LayersBackend::LAYERS_BASIC; }
+  virtual void BeginTransaction() {}
+  virtual already_AddRefed<ImageLayer> CreateImageLayer() {
+    NS_RUNTIMEABORT("Not implemented.");
+    return nullptr;
+  }
+  virtual already_AddRefed<PaintedLayer> CreatePaintedLayer() {
+    nsRefPtr<PaintedLayer> layer = new TestPaintedLayer(this);
+    return layer.forget();
+  }
+  virtual already_AddRefed<ColorLayer> CreateColorLayer() {
+    NS_RUNTIMEABORT("Not implemented.");
+    return nullptr;
+  }
+  virtual void SetRoot(Layer* aLayer) {}
+  virtual void BeginTransactionWithTarget(gfxContext* aTarget) {}
+  virtual already_AddRefed<CanvasLayer> CreateCanvasLayer() {
+    NS_RUNTIMEABORT("Not implemented.");
+    return nullptr;
+  }
+  virtual void EndTransaction(DrawPaintedLayerCallback aCallback,
+                              void* aCallbackData,
+                              EndTransactionFlags aFlags = END_DEFAULT) {}
+  virtual int32_t GetMaxTextureSize() const { return 0; }
+};
+
 class TestUserData: public LayerUserData {
 public:
   MOCK_METHOD0(Die, void());
   virtual ~TestUserData() { Die(); }
 };
 
 
 TEST(Layers, LayerConstructor) {
@@ -148,33 +164,37 @@ TEST(Layers, UserData) {
   delete layerPtr;
 
 }
 
 static
 already_AddRefed<Layer> CreateLayer(char aLayerType, LayerManager* aManager) {
   nsRefPtr<Layer> layer = nullptr;
   if (aLayerType == 'c') {
-    layer = new TestContainerLayer(aManager);
+    layer = aManager->CreateContainerLayer();
   } else if (aLayerType == 't') {
-    layer = new TestPaintedLayer(aManager);
+    layer = aManager->CreatePaintedLayer();
+  } else if (aLayerType == 'o') {
+    layer = aManager->CreateColorLayer();
   }
   return layer.forget();
 }
 
 already_AddRefed<Layer> CreateLayerTree(
     const char* aLayerTreeDescription,
     nsIntRegion* aVisibleRegions,
     const Matrix4x4* aTransforms,
     nsRefPtr<LayerManager>& manager,
     nsTArray<nsRefPtr<Layer> >& aLayersOut) {
 
   aLayersOut.Clear();
 
-  manager = new TestLayerManager();
+  if (!manager) {
+    manager = new TestLayerManager();
+  }
 
   nsRefPtr<Layer> rootLayer = nullptr;
   nsRefPtr<ContainerLayer> parentContainerLayer = nullptr;
   nsRefPtr<Layer> lastLayer = nullptr;
   int layerNumber = 0;
   for (size_t i = 0; i < strlen(aLayerTreeDescription); i++) {
     if (aLayerTreeDescription[i] == '(') {
       if (!lastLayer) {
@@ -210,16 +230,21 @@ already_AddRefed<Layer> CreateLayerTree(
         parentContainerLayer->InsertAfter(layer, parentContainerLayer->GetLastChild());
         layer->SetParent(parentContainerLayer);
       }
       lastLayer = layer;
     }
   }
   if (rootLayer) {
     rootLayer->ComputeEffectiveTransforms(Matrix4x4());
+    manager->SetRoot(rootLayer);
+    if (rootLayer->AsLayerComposite()) {
+      // Only perform this for LayerManagerComposite
+      CompositorParent::SetShadowProperties(rootLayer);
+    }
   }
   return rootLayer.forget();
 }
 
 TEST(Layers, LayerTree) {
   const char* layerTreeSyntax = "c(c(tt))";
   nsIntRegion layerVisibleRegion[] = {
     nsIntRegion(nsIntRect(0,0,100,100)),
--- a/gfx/tests/gtest/moz.build
+++ b/gfx/tests/gtest/moz.build
@@ -6,16 +6,17 @@
 
 UNIFIED_SOURCES += [
     'gfxSurfaceRefCountTest.cpp',
     # Disabled on suspicion of causing bug 904227
     #'gfxWordCacheTest.cpp',
     'TestAsyncPanZoomController.cpp',
     'TestBufferRotation.cpp',
     'TestColorNames.cpp',
+    'TestCompositor.cpp',
     'TestGfxPrefs.cpp',
     'TestLayers.cpp',
     'TestRegion.cpp',
     'TestSkipChars.cpp',
     # Hangs on linux in ApplyGdkScreenFontOptions
     #'gfxFontSelectionTest.cpp',
     'TestTextures.cpp',
     # Test works but it doesn't assert anything
--- a/toolkit/xre/moz.build
+++ b/toolkit/xre/moz.build
@@ -15,16 +15,18 @@ XPIDL_SOURCES += [
 
 if CONFIG['OS_ARCH'] == 'WINNT':
     XPIDL_SOURCES += [
         'nsIWinAppHelper.idl',
     ]
 
 XPIDL_MODULE = 'xulapp'
 
+EXPORTS += ['nsAppRunner.h']
+
 if CONFIG['MOZ_INSTRUMENT_EVENT_LOOP']:
     EXPORTS += ['EventTracer.h']
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
     UNIFIED_SOURCES += [
         'nsNativeAppSupportWin.cpp',
     ]
 elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -209,16 +209,18 @@ char **gArgv;
 static const char gToolkitVersion[] = NS_STRINGIFY(GRE_MILESTONE);
 static const char gToolkitBuildID[] = NS_STRINGIFY(GRE_BUILDID);
 
 static nsIProfileLock* gProfileLock;
 
 int    gRestartArgc;
 char **gRestartArgv;
 
+bool gIsGtest = false;
+
 #ifdef MOZ_WIDGET_QT
 static int    gQtOnlyArgc;
 static char **gQtOnlyArgv;
 #endif
 
 #if defined(MOZ_WIDGET_GTK)
 #if defined(DEBUG) || defined(NS_BUILD_REFCNT_LOGGING)
 #define CLEANUP_MEMORY 1
@@ -3554,17 +3556,19 @@ XREMain::XRE_mainStartup(bool* aExitFlag
 
   if (PR_GetEnv("MOZ_RUN_GTEST")) {
     int result;
 #ifdef XP_WIN
     UseParentConsole();
 #endif
     // RunGTest will only be set if we're in xul-unit
     if (mozilla::RunGTest) {
+      gIsGtest = true;
       result = mozilla::RunGTest();
+      gIsGtest = false;
     } else {
       result = 1;
       printf("TEST-UNEXPECTED-FAIL | gtest | Not compiled with enable-tests\n");
     }
     *aExitFlag = true;
     return result;
   }
 
--- a/toolkit/xre/nsAppRunner.h
+++ b/toolkit/xre/nsAppRunner.h
@@ -53,16 +53,18 @@ extern const nsXREAppData* gAppData;
 extern bool gSafeMode;
 
 extern int    gArgc;
 extern char **gArgv;
 extern int    gRestartArgc;
 extern char **gRestartArgv;
 extern bool gLogConsoleErrors;
 
+extern bool gIsGtest;
+
 /**
  * Create the nativeappsupport implementation.
  *
  * @note XPCOMInit has not happened yet.
  */
 nsresult NS_CreateNativeAppSupport(nsINativeAppSupport* *aResult);
 
 nsresult
--- a/widget/nsIWidget.h
+++ b/widget/nsIWidget.h
@@ -86,16 +86,17 @@ typedef void* nsNativeWidget;
 #define NS_NATIVE_OFFSETY     7
 #define NS_NATIVE_PLUGIN_PORT 8
 #define NS_NATIVE_SCREEN      9
 // The toplevel GtkWidget containing this nsIWidget:
 #define NS_NATIVE_SHELLWIDGET 10
 // Has to match to NPNVnetscapeWindow, and shareable across processes
 // HWND on Windows and XID on X11
 #define NS_NATIVE_SHAREABLE_WINDOW 11
+#define NS_NATIVE_OPENGL_CONTEXT   12
 #ifdef XP_MACOSX
 #define NS_NATIVE_PLUGIN_PORT_QD    100
 #define NS_NATIVE_PLUGIN_PORT_CG    101
 #endif
 #ifdef XP_WIN
 #define NS_NATIVE_TSF_THREAD_MGR       100
 #define NS_NATIVE_TSF_CATEGORY_MGR     101
 #define NS_NATIVE_TSF_DISPLAY_ATTR_MGR 102