Merge m-c to autoland, a=merge
authorWes Kocher <wkocher@mozilla.com>
Fri, 10 Feb 2017 15:40:01 -0800
changeset 342342 779d10ed78f56643b57e58bf2e60600abf915b13
parent 342341 bb88d5bc2548767c7a964f77bd006d55155c5720 (current diff)
parent 342301 855e6b2f6199189f37cea093cbdd1735e297e8aa (diff)
child 342343 a621ec4ab74b532b50bc2942af6fea8d9afa55c1
push id31349
push userphilringnalda@gmail.com
push dateSun, 12 Feb 2017 01:41:07 +0000
treeherdermozilla-central@43e17d5a1492 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone54.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to autoland, a=merge MozReview-Commit-ID: 4z9MXOzghI7
dom/media/webaudio/test/browser.ini
dom/media/webaudio/test/browser_bug1181073.js
js/src/jit/CacheIR.cpp
taskcluster/ci/test/test-platforms.yml
taskcluster/ci/test/test-sets.yml
taskcluster/taskgraph/target_tasks.py
testing/web-platform/meta/XMLHttpRequest/open-url-bogus.htm.ini
--- a/accessible/ipc/DocAccessibleParent.cpp
+++ b/accessible/ipc/DocAccessibleParent.cpp
@@ -540,16 +540,17 @@ DocAccessibleParent::SetCOMProxy(const R
   if (outerDoc) {
     outerDoc->GetNativeInterface((void**) &rawNative);
     MOZ_ASSERT(rawNative);
   }
 
   IAccessibleHolder::COMPtrType ptr(rawNative);
   IAccessibleHolder holder(Move(ptr));
 
+  IAccessibleHolder hWndAccHolder;
   if (nsWinUtils::IsWindowEmulationStarted()) {
     RootAccessible* rootDocument = outerDoc->RootAccessible();
     MOZ_ASSERT(rootDocument);
 
     bool isActive = true;
     nsIntRect rect(CW_USEDEFAULT, CW_USEDEFAULT, 0, 0);
     if (Compatibility::IsDolphin()) {
       rect = Bounds();
@@ -563,20 +564,26 @@ DocAccessibleParent::SetCOMProxy(const R
     HWND hWnd = nsWinUtils::CreateNativeWindow(kClassNameTabContent,
                                                parentWnd, rect.x, rect.y,
                                                rect.width, rect.height,
                                                isActive);
     if (hWnd) {
       // Attach accessible document to the emulated native window
       ::SetPropW(hWnd, kPropNameDocAccParent, (HANDLE)this);
       SetEmulatedWindowHandle(hWnd);
+      IAccessible* rawHWNDAcc = nullptr;
+      if (SUCCEEDED(::AccessibleObjectFromWindow(hWnd, OBJID_WINDOW,
+                                                 IID_IAccessible,
+                                                 (void**)&rawHWNDAcc))) {
+        hWndAccHolder.Set(IAccessibleHolder::COMPtrType(rawHWNDAcc));
+      }
     }
   }
   Unused << SendParentCOMProxy(holder, reinterpret_cast<uintptr_t>(
-                               mEmulatedWindowHandle));
+                               mEmulatedWindowHandle), hWndAccHolder);
 }
 
 void
 DocAccessibleParent::SetEmulatedWindowHandle(HWND aWindowHandle)
 {
   if (!aWindowHandle && mEmulatedWindowHandle && IsTopLevel()) {
     ::DestroyWindow(mEmulatedWindowHandle);
   }
--- a/accessible/ipc/win/DocAccessibleChild.cpp
+++ b/accessible/ipc/win/DocAccessibleChild.cpp
@@ -42,22 +42,28 @@ DocAccessibleChild::Shutdown()
   }
 
   PushDeferredEvent(MakeUnique<SerializedShutdown>(this));
   DetachDocument();
 }
 
 ipc::IPCResult
 DocAccessibleChild::RecvParentCOMProxy(const IAccessibleHolder& aParentCOMProxy,
-                                       const WindowsHandle& aEmulatedWindowHandle)
+                                       const WindowsHandle& aEmulatedWindowHandle,
+                                       const IAccessibleHolder& aEmulatedWindowCOMProxy)
 {
   MOZ_ASSERT(!mParentProxy && !aParentCOMProxy.IsNull());
   mParentProxy.reset(const_cast<IAccessibleHolder&>(aParentCOMProxy).Release());
   SetConstructedInParentProcess();
   mEmulatedWindowHandle = reinterpret_cast<HWND>(aEmulatedWindowHandle);
+  if (!aEmulatedWindowCOMProxy.IsNull()) {
+    MOZ_ASSERT(!mEmulatedWindowProxy);
+    mEmulatedWindowProxy.reset(
+      const_cast<IAccessibleHolder&>(aEmulatedWindowCOMProxy).Release());
+  }
 
   for (uint32_t i = 0, l = mDeferredEvents.Length(); i < l; ++i) {
     mDeferredEvents[i]->Dispatch();
   }
 
   mDeferredEvents.Clear();
 
   return IPC_OK();
--- a/accessible/ipc/win/DocAccessibleChild.h
+++ b/accessible/ipc/win/DocAccessibleChild.h
@@ -24,19 +24,21 @@ class DocAccessibleChild : public DocAcc
 public:
   explicit DocAccessibleChild(DocAccessible* aDoc);
   ~DocAccessibleChild();
 
   virtual void Shutdown() override;
 
   virtual ipc::IPCResult
   RecvParentCOMProxy(const IAccessibleHolder& aParentCOMProxy,
-                     const WindowsHandle& aEmulatedWindowHandle) override;
+                     const WindowsHandle& aEmulatedWindowHandle,
+                     const IAccessibleHolder& aEmulatedWindowCOMProxy) override;
 
   HWND GetEmulatedWindowHandle() const { return mEmulatedWindowHandle; }
+  IAccessible* GetEmulatedWindowIAccessible() const { return mEmulatedWindowProxy.get(); }
 
   IAccessible* GetParentIAccessible() const { return mParentProxy.get(); }
 
   bool SendEvent(const uint64_t& aID, const uint32_t& type);
   bool SendHideEvent(const uint64_t& aRootID, const bool& aFromUser);
   bool SendStateChangeEvent(const uint64_t& aID, const uint64_t& aState,
                             const bool& aEnabled);
   bool SendCaretMoveEvent(const uint64_t& aID, const int32_t& aOffset);
@@ -307,16 +309,17 @@ private:
     void Dispatch(DocAccessibleChild* aIPCDoc) override
     {
       aIPCDoc->Shutdown();
     }
   };
 
   bool mIsRemoteConstructed;
   mscom::ProxyUniquePtr<IAccessible> mParentProxy;
+  mscom::ProxyUniquePtr<IAccessible> mEmulatedWindowProxy;
   nsTArray<UniquePtr<DeferredEvent>> mDeferredEvents;
   HWND mEmulatedWindowHandle;
 };
 
 } // namespace a11y
 } // namespace mozilla
 
 #endif // mozilla_a11y_DocAccessibleChild_h
--- a/accessible/ipc/win/PDocAccessible.ipdl
+++ b/accessible/ipc/win/PDocAccessible.ipdl
@@ -62,15 +62,16 @@ parent:
    */
   async BindChildDoc(PDocAccessible aChildDoc, uint64_t aID);
 
   sync GetWindowedPluginIAccessible(WindowsHandle aHwnd)
     returns (IAccessibleHolder aPluginCOMProxy);
 
 child:
   async ParentCOMProxy(IAccessibleHolder aParentCOMProxy,
-                       WindowsHandle aEmulatedWindowHandle);
+                       WindowsHandle aEmulatedWindowHandle,
+                       IAccessibleHolder aEmulatedWindowCOMProxy);
 
   async __delete__();
 };
 
 }
 }
--- a/accessible/windows/msaa/DocAccessibleWrap.cpp
+++ b/accessible/windows/msaa/DocAccessibleWrap.cpp
@@ -48,17 +48,24 @@ STDMETHODIMP
 DocAccessibleWrap::get_accParent(
       /* [retval][out] */ IDispatch __RPC_FAR *__RPC_FAR *ppdispParent)
 {
   // We might be a top-level document in a content process.
   DocAccessibleChild* ipcDoc = IPCDoc();
   if (!ipcDoc) {
     return DocAccessible::get_accParent(ppdispParent);
   }
-  IAccessible* dispParent = ipcDoc->GetParentIAccessible();
+
+  // Emulated window proxy is only set for the top level content document when
+  // emulation is enabled.
+  IAccessible* dispParent = ipcDoc->GetEmulatedWindowIAccessible();
+  if (!dispParent) {
+    dispParent = ipcDoc->GetParentIAccessible();
+  }
+
   if (!dispParent) {
     return S_FALSE;
   }
 
   dispParent->AddRef();
   *ppdispParent = static_cast<IDispatch*>(dispParent);
   return S_OK;
 }
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -146,19 +146,26 @@ XPCOMUtils.defineLazyGetter(this, "PageM
   Cu.import("resource://gre/modules/PageMenu.jsm", tmp);
   return new tmp.PageMenuParent();
 });
 
 XPCOMUtils.defineLazyGetter(this, "PopupNotifications", function() {
   let tmp = {};
   Cu.import("resource://gre/modules/PopupNotifications.jsm", tmp);
   try {
+    // Hide all notifications while the URL is being edited and the address bar
+    // has focus, including the virtual focus in the results popup.
+    let shouldSuppress = () => {
+      return gURLBar.getAttribute("pageproxystate") != "valid" &&
+             gURLBar.focused;
+    };
     return new tmp.PopupNotifications(gBrowser,
                                       document.getElementById("notification-popup"),
-                                      document.getElementById("notification-popup-box"));
+                                      document.getElementById("notification-popup-box"),
+                                      { shouldSuppress });
   } catch (ex) {
     Cu.reportError(ex);
     return null;
   }
 });
 
 XPCOMUtils.defineLazyGetter(this, "Win7Features", function() {
   if (AppConstants.platform != "win")
@@ -2436,27 +2443,18 @@ function BrowserPageInfo(documentURL, in
                     "chrome,toolbar,dialog=no,resizable", args);
 }
 
 /**
  * Sets the URI to display in the location bar.
  *
  * @param aURI [optional]
  *        nsIURI to set. If this is unspecified, the current URI will be used.
- * @param aOptions [optional]
- *        An object with the following properties:
- *        {
- *          isForLocationChange:
- *            Set to true to indicate that the function was invoked to respond
- *            to a location change event, rather than to reset the current URI
- *            value. This is useful to avoid calling PopupNotifications.jsm
- *            multiple times.
- *        }
  */
-function URLBarSetURI(aURI, aOptions = {}) {
+function URLBarSetURI(aURI) {
   var value = gBrowser.userTypedValue;
   var valid = false;
 
   if (value == null) {
     let uri = aURI || gBrowser.currentURI;
     // Strip off "wyciwyg://" and passwords for the location bar
     try {
       uri = Services.uriFixup.createExposableURI(uri);
@@ -2479,17 +2477,17 @@ function URLBarSetURI(aURI, aOptions = {
       }
     }
 
     valid = !isBlankPageURL(uri.spec);
   }
 
   gURLBar.value = value;
   gURLBar.valueIsTyped = !valid;
-  SetPageProxyState(valid ? "valid" : "invalid", aOptions);
+  SetPageProxyState(valid ? "valid" : "invalid");
 }
 
 function losslessDecodeURI(aURI) {
   let scheme = aURI.scheme;
   if (scheme == "moz-action")
     throw new Error("losslessDecodeURI should never get a moz-action URI");
 
   var value = aURI.spec;
@@ -2586,48 +2584,56 @@ function UpdatePageProxyState() {
  * different than the loaded page, because it's being edited or because a search
  * result is currently selected and is displayed in the location bar.
  *
  * @param aState
  *        The string "valid" indicates that the security indicators and other
  *        related user interface elments should be shown because the URI in the
  *        location bar matches the loaded page. The string "invalid" indicates
  *        that the URI in the location bar is different than the loaded page.
- * @param aOptions [optional]
- *        An object with the following properties:
- *        {
- *          isForLocationChange:
- *            Set to true to indicate that the function was invoked to respond
- *            to a location change event. This is useful to avoid calling
- *            PopupNotifications.jsm multiple times.
- *        }
  */
-function SetPageProxyState(aState, aOptions = {}) {
+function SetPageProxyState(aState) {
   if (!gURLBar)
     return;
 
+  let oldPageProxyState = gURLBar.getAttribute("pageproxystate");
+  // The "browser_urlbar_stop_pending.js" test uses a MutationObserver to do
+  // some verifications at this point, and it breaks if we don't write the
+  // attribute, even if it hasn't changed (bug 1338115).
   gURLBar.setAttribute("pageproxystate", aState);
 
   // the page proxy state is set to valid via OnLocationChange, which
   // gets called when we switch tabs.
   if (aState == "valid") {
     gLastValidURLStr = gURLBar.value;
     gURLBar.addEventListener("input", UpdatePageProxyState);
   } else if (aState == "invalid") {
     gURLBar.removeEventListener("input", UpdatePageProxyState);
   }
 
-  // Only need to call anchorVisibilityChange if the PopupNotifications object
-  // for this window has already been initialized (i.e. its getter no
-  // longer exists). If this is the result of a locations change, then we will
-  // already invoke PopupNotifications.locationChange separately.
-  if (!Object.getOwnPropertyDescriptor(window, "PopupNotifications").get &&
-      !aOptions.isForLocationChange) {
-    PopupNotifications.anchorVisibilityChange();
-  }
+  // After we've ensured that we've applied the listeners and updated the value
+  // of gLastValidURLStr, return early if the actual state hasn't changed.
+  if (oldPageProxyState == aState) {
+    return;
+  }
+
+  UpdatePopupNotificationsVisibility();
+}
+
+function UpdatePopupNotificationsVisibility() {
+  // Only need to do something if the PopupNotifications object for this window
+  // has already been initialized (i.e. its getter no longer exists).
+  if (Object.getOwnPropertyDescriptor(window, "PopupNotifications").get) {
+    return;
+  }
+
+  // Notify PopupNotifications that the visible anchors may have changed. This
+  // also checks the suppression state according to the "shouldSuppress"
+  // function defined earlier in this file.
+  PopupNotifications.anchorVisibilityChange();
 }
 
 function PageProxyClickHandler(aEvent) {
   if (aEvent.button == 1 && gPrefService.getBoolPref("middlemouse.paste"))
     middleMousePaste(aEvent);
 }
 
 var gMenuButtonBadgeManager = {
@@ -4536,17 +4542,17 @@ var XULBrowserWindow = {
       if ((location == "about:blank" && checkEmptyPageOrigin()) ||
           location == "") {  // Second condition is for new tabs, otherwise
                              // reload function is enabled until tab is refreshed.
         this.reloadCommand.setAttribute("disabled", "true");
       } else {
         this.reloadCommand.removeAttribute("disabled");
       }
 
-      URLBarSetURI(aLocationURI, { isForLocationChange: true });
+      URLBarSetURI(aLocationURI);
 
       BookmarkingUI.onLocationChange();
 
       gIdentityHandler.onLocationChange();
 
       SocialUI.updateState();
 
       UITour.onLocationChange(location);
--- a/browser/base/content/test/popupNotifications/browser_popupNotification_no_anchors.js
+++ b/browser/base/content/test/popupNotifications/browser_popupNotification_no_anchors.js
@@ -97,57 +97,134 @@ var tests = [
       dismissNotification(popup);
     },
     onHidden(popup) {
       this.notification.remove();
       gBrowser.removeTab(gBrowser.selectedTab);
       gBrowser.selectedTab = this.oldSelectedTab;
     }
   },
-  // Test that popupnotifications are anchored to the identity icon while
-  // editing the URL in the location bar, and restored to their anchors when the
-  // URL is reverted.
+  // Test that popupnotifications are hidden while editing the URL in the
+  // location bar, anchored to the identity icon when the focus is moved away
+  // from the location bar, and restored when the URL is reverted.
   { id: "Test#4",
     *run() {
-      this.oldSelectedTab = gBrowser.selectedTab;
-      yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.com/");
+      for (let persistent of [false, true]) {
+        let shown = waitForNotificationPanel();
+        this.notifyObj = new BasicNotification(this.id);
+        this.notifyObj.anchorID = "geo-notification-icon";
+        this.notifyObj.addOptions({ persistent });
+        this.notification = showNotification(this.notifyObj);
+        yield shown;
+
+        checkPopup(PopupNotifications.panel, this.notifyObj);
+
+        // Typing in the location bar should hide the notification.
+        let hidden = waitForNotificationPanelHidden();
+        gURLBar.select();
+        EventUtils.synthesizeKey("*", {});
+        yield hidden;
+
+        is(document.getElementById("geo-notification-icon").boxObject.width, 0,
+           "geo anchor shouldn't be visible");
+
+        // Moving focus to the next control should show the notifications again,
+        // anchored to the identity icon. We clear the URL bar before moving the
+        // focus so that the awesomebar popup doesn't get in the way.
+        shown = waitForNotificationPanel();
+        EventUtils.synthesizeKey("VK_BACK_SPACE", {});
+        EventUtils.synthesizeKey("VK_TAB", {});
+        yield shown;
+
+        is(PopupNotifications.panel.anchorNode.id, "identity-icon",
+           "notification anchored to identity icon");
+
+        // Moving focus to the location bar should hide the notification again.
+        hidden = waitForNotificationPanelHidden();
+        EventUtils.synthesizeKey("VK_TAB", { shiftKey: true });
+        yield hidden;
+
+        // Reverting the URL should show the notification again.
+        shown = waitForNotificationPanel();
+        EventUtils.synthesizeKey("VK_ESCAPE", {});
+        yield shown;
+
+        checkPopup(PopupNotifications.panel, this.notifyObj);
 
-      let shownInitially = waitForNotificationPanel();
+        hidden = waitForNotificationPanelHidden();
+        this.notification.remove();
+        yield hidden;
+      }
+      goNext();
+    }
+  },
+  // Test that popupnotifications triggered while editing the URL in the
+  // location bar are only shown later when the URL is reverted.
+  { id: "Test#5",
+    *run() {
+      for (let persistent of [false, true]) {
+        // Start editing the URL, ensuring that the awesomebar popup is hidden.
+        gURLBar.select();
+        EventUtils.synthesizeKey("*", {});
+        EventUtils.synthesizeKey("VK_BACK_SPACE", {});
+
+        // Trying to show a notification should display nothing.
+        let notShowing = promiseTopicObserved("PopupNotifications-updateNotShowing");
+        this.notifyObj = new BasicNotification(this.id);
+        this.notifyObj.anchorID = "geo-notification-icon";
+        this.notifyObj.addOptions({ persistent });
+        this.notification = showNotification(this.notifyObj);
+        yield notShowing;
+
+        // Reverting the URL should show the notification.
+        let shown = waitForNotificationPanel();
+        EventUtils.synthesizeKey("VK_ESCAPE", {});
+        yield shown;
+
+        checkPopup(PopupNotifications.panel, this.notifyObj);
+
+        let hidden = waitForNotificationPanelHidden();
+        this.notification.remove();
+        yield hidden;
+      }
+
+      goNext();
+    }
+  },
+  // Test that persistent panels are still open after switching to another tab
+  // and back, even while editing the URL in the new tab.
+  { id: "Test#6",
+    *run() {
+      let shown = waitForNotificationPanel();
       this.notifyObj = new BasicNotification(this.id);
       this.notifyObj.anchorID = "geo-notification-icon";
       this.notifyObj.addOptions({
         persistent: true,
       });
       this.notification = showNotification(this.notifyObj);
-      yield shownInitially;
+      yield shown;
+
+      // Switching to a new tab should hide the notification.
+      let hidden = waitForNotificationPanelHidden();
+      this.oldSelectedTab = gBrowser.selectedTab;
+      yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.com/");
+      yield hidden;
+
+      // Start editing the URL.
+      gURLBar.select();
+      EventUtils.synthesizeKey("*", {});
+
+      // Switching to the old tab should show the notification again.
+      shown = waitForNotificationPanel();
+      gBrowser.removeTab(gBrowser.selectedTab);
+      gBrowser.selectedTab = this.oldSelectedTab;
+      yield shown;
 
       checkPopup(PopupNotifications.panel, this.notifyObj);
 
-      let shownAgain = waitForNotificationPanel();
-      // This will cause the popup to hide and show again.
-      gURLBar.select();
-      EventUtils.synthesizeKey("*", {});
-      // Keep the URL bar empty, so we don't show the awesomebar.
-      EventUtils.synthesizeKey("VK_BACK_SPACE", {});
-      yield shownAgain;
-
-      is(document.getElementById("geo-notification-icon").boxObject.width, 0,
-         "geo anchor shouldn't be visible");
-      is(PopupNotifications.panel.anchorNode.id, "identity-icon",
-         "notification anchored to identity icon");
-
-      let shownLastTime = waitForNotificationPanel();
-      // This will cause the popup to hide and show again.
-      EventUtils.synthesizeKey("VK_ESCAPE", {});
-      yield shownLastTime;
-
-      checkPopup(PopupNotifications.panel, this.notifyObj);
-
-      let hidden = new Promise(resolve => onPopupEvent("popuphidden", resolve));
+      hidden = waitForNotificationPanelHidden();
       this.notification.remove();
-      gBrowser.removeTab(gBrowser.selectedTab);
-      gBrowser.selectedTab = this.oldSelectedTab;
       yield hidden;
 
       goNext();
     }
   },
 ];
--- a/browser/base/content/test/popupNotifications/head.js
+++ b/browser/base/content/test/popupNotifications/head.js
@@ -259,16 +259,24 @@ function onPopupEvent(eventName, callbac
 function waitForNotificationPanel() {
   return new Promise(resolve => {
     onPopupEvent("popupshown", function() {
       resolve(this);
     });
   });
 }
 
+function waitForNotificationPanelHidden() {
+  return new Promise(resolve => {
+    onPopupEvent("popuphidden", function() {
+      resolve(this);
+    });
+  });
+}
+
 function triggerMainCommand(popup) {
   let notifications = popup.childNodes;
   ok(notifications.length > 0, "at least one notification displayed");
   let notification = notifications[0];
   info("Triggering main command for notification " + notification.id);
   EventUtils.synthesizeMouseAtCenter(notification.button, {});
 }
 
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -1222,23 +1222,29 @@ file, You can obtain one at http://mozil
             this._clearNoActions();
         }
       ]]></handler>
 
       <handler event="focus"><![CDATA[
         if (event.originalTarget == this.inputField) {
           this._hideURLTooltip();
           this.formatValue();
+          if (this.getAttribute("pageproxystate") != "valid") {
+            UpdatePopupNotificationsVisibility();
+          }
         }
       ]]></handler>
 
       <handler event="blur"><![CDATA[
         if (event.originalTarget == this.inputField) {
           this._clearNoActions();
           this.formatValue();
+          if (this.getAttribute("pageproxystate") != "valid") {
+            UpdatePopupNotificationsVisibility();
+          }
         }
         if (ExtensionSearchHandler.hasActiveInputSession()) {
           ExtensionSearchHandler.handleInputCancelled();
         }
       ]]></handler>
 
       <handler event="dragstart" phase="capturing"><![CDATA[
         // Drag only if the gesture starts from the input field.
--- a/browser/components/places/content/controller.js
+++ b/browser/components/places/content/controller.js
@@ -1483,39 +1483,41 @@ var PlacesControllerDragHelper = {
       // expected cases, which are either unsupported flavors, or items which
       // cannot be dropped in the current insertionpoint. The last case will
       // likely force us to use unwrapNodes for the private data types of
       // places.
       if (flavor == TAB_DROP_TYPE)
         continue;
 
       let data = dt.mozGetDataAt(flavor, i);
-      let dragged;
+      let nodes;
       try {
-        dragged = PlacesUtils.unwrapNodes(data, flavor)[0];
+        nodes = PlacesUtils.unwrapNodes(data, flavor);
       } catch (e) {
         return false;
       }
 
-      // Only bookmarks and urls can be dropped into tag containers.
-      if (ip.isTag &&
-          dragged.type != PlacesUtils.TYPE_X_MOZ_URL &&
-          (dragged.type != PlacesUtils.TYPE_X_MOZ_PLACE ||
-           (dragged.uri && dragged.uri.startsWith("place:")) ))
-        return false;
+      for (let dragged of nodes) {
+        // Only bookmarks and urls can be dropped into tag containers.
+        if (ip.isTag &&
+            dragged.type != PlacesUtils.TYPE_X_MOZ_URL &&
+            (dragged.type != PlacesUtils.TYPE_X_MOZ_PLACE ||
+             (dragged.uri && dragged.uri.startsWith("place:")) ))
+          return false;
 
-      // The following loop disallows the dropping of a folder on itself or
-      // on any of its descendants.
-      if (dragged.type == PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER ||
-          (dragged.uri && dragged.uri.startsWith("place:")) ) {
-        let parentId = ip.itemId;
-        while (parentId != PlacesUtils.placesRootId) {
-          if (dragged.concreteId == parentId || dragged.id == parentId)
-            return false;
-          parentId = PlacesUtils.bookmarks.getFolderIdForItem(parentId);
+        // The following loop disallows the dropping of a folder on itself or
+        // on any of its descendants.
+        if (dragged.type == PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER ||
+            (dragged.uri && dragged.uri.startsWith("place:")) ) {
+          let parentId = ip.itemId;
+          while (parentId != PlacesUtils.placesRootId) {
+            if (dragged.concreteId == parentId || dragged.id == parentId)
+              return false;
+            parentId = PlacesUtils.bookmarks.getFolderIdForItem(parentId);
+          }
         }
       }
     }
     return true;
   },
 
   /**
    * Determines if an unwrapped node can be moved.
@@ -1571,73 +1573,87 @@ var PlacesControllerDragHelper = {
     let doCopy = ["copy", "link"].includes(dt.dropEffect);
 
     let transactions = [];
     let dropCount = dt.mozItemCount;
     let movedCount = 0;
     let parentGuid = PlacesUIUtils.useAsyncTransactions ?
                        (yield insertionPoint.promiseGuid()) : null;
     let tagName = insertionPoint.tagName;
+
+    // Following flavors may contain duplicated data.
+    let duplicable = new Map();
+    duplicable.set(PlacesUtils.TYPE_UNICODE, new Set());
+    duplicable.set(PlacesUtils.TYPE_X_MOZ_URL, new Set());
+
     for (let i = 0; i < dropCount; ++i) {
       let flavor = this.getFirstValidFlavor(dt.mozTypesAt(i));
       if (!flavor)
         return;
 
       let data = dt.mozGetDataAt(flavor, i);
-      let unwrapped;
+      if (duplicable.has(flavor)) {
+        let handled = duplicable.get(flavor);
+        if (handled.has(data))
+          continue;
+        handled.add(data);
+      }
+
+      let nodes;
       if (flavor != TAB_DROP_TYPE) {
-        // There's only ever one in the D&D case.
-        unwrapped = PlacesUtils.unwrapNodes(data, flavor)[0];
+        nodes = PlacesUtils.unwrapNodes(data, flavor);
       } else if (data instanceof XULElement && data.localName == "tab" &&
                data.ownerGlobal instanceof ChromeWindow) {
         let uri = data.linkedBrowser.currentURI;
         let spec = uri ? uri.spec : "about:blank";
-        unwrapped = { uri: spec,
-                      title: data.label,
-                      type: PlacesUtils.TYPE_X_MOZ_URL};
+        nodes = [{ uri: spec,
+                   title: data.label,
+                   type: PlacesUtils.TYPE_X_MOZ_URL}];
       } else
         throw new Error("bogus data was passed as a tab");
 
-      let index = insertionPoint.index;
+      for (let unwrapped of nodes) {
+        let index = insertionPoint.index;
 
-      // Adjust insertion index to prevent reversal of dragged items. When you
-      // drag multiple elts upward: need to increment index or each successive
-      // elt will be inserted at the same index, each above the previous.
-      let dragginUp = insertionPoint.itemId == unwrapped.parent &&
-                      index < PlacesUtils.bookmarks.getItemIndex(unwrapped.id);
-      if (index != -1 && dragginUp)
-        index += movedCount++;
+        // Adjust insertion index to prevent reversal of dragged items. When you
+        // drag multiple elts upward: need to increment index or each successive
+        // elt will be inserted at the same index, each above the previous.
+        let dragginUp = insertionPoint.itemId == unwrapped.parent &&
+                        index < PlacesUtils.bookmarks.getItemIndex(unwrapped.id);
+        if (index != -1 && dragginUp)
+          index += movedCount++;
 
-      // If dragging over a tag container we should tag the item.
-      if (insertionPoint.isTag) {
-        let uri = NetUtil.newURI(unwrapped.uri);
-        let tagItemId = insertionPoint.itemId;
-        if (PlacesUIUtils.useAsyncTransactions)
-          transactions.push(PlacesTransactions.Tag({ uri, tag: tagName }));
-        else
-          transactions.push(new PlacesTagURITransaction(uri, [tagItemId]));
-      } else {
-        // If this is not a copy, check for safety that we can move the source,
-        // otherwise report an error and fallback to a copy.
-        if (!doCopy && !PlacesControllerDragHelper.canMoveUnwrappedNode(unwrapped)) {
-          Components.utils.reportError("Tried to move an unmovable Places " +
-                                       "node, reverting to a copy operation.");
-          doCopy = true;
-        }
-        if (PlacesUIUtils.useAsyncTransactions) {
-          transactions.push(
-            PlacesUIUtils.getTransactionForData(unwrapped,
-                                                flavor,
-                                                parentGuid,
-                                                index,
-                                                doCopy));
+        // If dragging over a tag container we should tag the item.
+        if (insertionPoint.isTag) {
+          let uri = NetUtil.newURI(unwrapped.uri);
+          let tagItemId = insertionPoint.itemId;
+          if (PlacesUIUtils.useAsyncTransactions)
+            transactions.push(PlacesTransactions.Tag({ uri, tag: tagName }));
+          else
+            transactions.push(new PlacesTagURITransaction(uri, [tagItemId]));
         } else {
-          transactions.push(PlacesUIUtils.makeTransaction(unwrapped,
-                              flavor, insertionPoint.itemId,
-                              index, doCopy));
+          // If this is not a copy, check for safety that we can move the
+          // source, otherwise report an error and fallback to a copy.
+          if (!doCopy && !PlacesControllerDragHelper.canMoveUnwrappedNode(unwrapped)) {
+            Components.utils.reportError("Tried to move an unmovable Places " +
+                                         "node, reverting to a copy operation.");
+            doCopy = true;
+          }
+          if (PlacesUIUtils.useAsyncTransactions) {
+            transactions.push(
+              PlacesUIUtils.getTransactionForData(unwrapped,
+                                                  flavor,
+                                                  parentGuid,
+                                                  index,
+                                                  doCopy));
+          } else {
+            transactions.push(PlacesUIUtils.makeTransaction(unwrapped,
+                                flavor, insertionPoint.itemId,
+                                index, doCopy));
+          }
         }
       }
     }
 
     if (PlacesUIUtils.useAsyncTransactions) {
       yield PlacesTransactions.batch(transactions);
     } else {
       let txn = new PlacesAggregatedTransaction("DropItems", transactions);
--- a/browser/components/places/tests/browser/browser_475045.js
+++ b/browser/components/places/tests/browser/browser_475045.js
@@ -47,19 +47,59 @@ add_task(function* test() {
     let bookmarkIds = PlacesUtils.bookmarks
                       .getBookmarkIdsForURI(uri);
     ok(bookmarkIds.length == 1, "There should be exactly one bookmark");
 
     PlacesUtils.bookmarks.removeItem(bookmarkIds[0]);
 
     // Verify that we removed the bookmark successfully.
     ok(!PlacesUtils.bookmarks.isBookmarked(uri), "URI should be removed");
-  }
+  };
+
+  /**
+   * Simulates a drop of multiple URIs onto the bookmarks bar.
+   *
+   * @param aEffect
+   *        The effect to use for the drop operation: move, copy, or link.
+   * @param aMimeType
+   *        The mime type to use for the drop operation.
+   */
+  let simulateDragDropMultiple = function(aEffect, aMimeType) {
+    const uriSpecs = [
+      "http://www.mozilla.org/C54263C6-A484-46CF-8E2B-FE131586348A",
+      "http://www.mozilla.org/71381257-61E6-4376-AF7C-BF3C5FD8870D",
+      "http://www.mozilla.org/091A88BD-5743-4C16-A005-3D2EA3A3B71E"
+    ];
+    let uris = uriSpecs.map(spec => makeURI(spec));
+    let data;
+    if (aMimeType == "text/x-moz-url")
+      data = uriSpecs.map(spec => spec + "\n" + spec).join("\n");
+    else
+      data = uriSpecs.join("\n");
+    EventUtils.synthesizeDrop(placesItems.childNodes[0],
+                              placesItems,
+                              [[{type: aMimeType,
+                                 data}]],
+                              aEffect, window);
+
+    // Verify that the drop produces exactly one bookmark per each URL.
+    for (let uri of uris) {
+      let bookmarkIds = PlacesUtils.bookmarks
+        .getBookmarkIdsForURI(uri);
+      ok(bookmarkIds.length == 1, "There should be exactly one bookmark");
+
+      PlacesUtils.bookmarks.removeItem(bookmarkIds[0]);
+
+      // Verify that we removed the bookmark successfully.
+      ok(!PlacesUtils.bookmarks.isBookmarked(uri), "URI should be removed");
+    }
+  };
 
   // Simulate a bookmark drop for all of the mime types and effects.
   let mimeTypes = ["text/plain", "text/unicode", "text/x-moz-url"];
   let effects = ["move", "copy", "link"];
   effects.forEach(function(effect) {
     mimeTypes.forEach(function(mimeType) {
       simulateDragDrop(effect, mimeType);
+      simulateDragDropMultiple(effect, mimeType);
     });
   });
 });
--- a/browser/components/sessionstore/test/browser_undoCloseById.js
+++ b/browser/components/sessionstore/test/browser_undoCloseById.js
@@ -1,35 +1,35 @@
 "use strict";
 
 /**
  * This test is for the undoCloseById function.
  */
 
 Cu.import("resource:///modules/sessionstore/SessionStore.jsm");
 
-function openAndCloseTab(window, url) {
+async function openAndCloseTab(window, url) {
   let tab = window.gBrowser.addTab(url);
-  yield promiseBrowserLoaded(tab.linkedBrowser, true, url);
-  yield TabStateFlusher.flush(tab.linkedBrowser);
-  yield promiseRemoveTab(tab);
+  await promiseBrowserLoaded(tab.linkedBrowser, true, url);
+  await TabStateFlusher.flush(tab.linkedBrowser);
+  await promiseRemoveTab(tab);
 }
 
-function* openWindow(url) {
-  let win = yield promiseNewWindowLoaded();
+async function openWindow(url) {
+  let win = await promiseNewWindowLoaded();
   let flags = Ci.nsIWebNavigation.LOAD_FLAGS_REPLACE_HISTORY;
   win.gBrowser.selectedBrowser.loadURIWithFlags(url, flags);
-  yield promiseBrowserLoaded(win.gBrowser.selectedBrowser, true, url);
+  await promiseBrowserLoaded(win.gBrowser.selectedBrowser, true, url);
   return win;
 }
 
-function closeWindow(win) {
-  yield BrowserTestUtils.closeWindow(win);
+async function closeWindow(win) {
+  await BrowserTestUtils.closeWindow(win);
   // Wait 20 ms to allow SessionStorage a chance to register the closed window.
-  yield new Promise(resolve => setTimeout(resolve, 20));
+  await new Promise(resolve => setTimeout(resolve, 20));
 }
 
 add_task(function* test_undoCloseById() {
   // Clear the lists of closed windows and tabs.
   forgetClosedWindows();
   while (SessionStore.getClosedTabCount(window)) {
     SessionStore.forgetClosedTab(window, 0);
   }
--- a/browser/extensions/mortar/moz.build
+++ b/browser/extensions/mortar/moz.build
@@ -1,11 +1,13 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
+XPCSHELL_TESTS_MANIFESTS += ['test/unit/xpcshell.ini']
+
 SOURCES += [
     'host/rpc.cc',
 ]
 
 SharedLibrary('rpc')
new file mode 100644
--- /dev/null
+++ b/browser/extensions/mortar/test/unit/head.js
@@ -0,0 +1,59 @@
+"use strict";
+
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+Components.utils.import("resource://gre/modules/Services.jsm");
+// For the following 5 lines of codes, we redirect the
+// path of the "ppapi.js" in addon to the exact file path.
+Components.utils.import("resource://gre/modules/NetUtil.jsm");
+let resHandler = Services.io.getProtocolHandler("resource")
+                         .QueryInterface(Components.interfaces.nsISubstitutingProtocolHandler);
+let dataURI = NetUtil.newURI(do_get_file("."));
+resHandler.setSubstitution("ppapi.js", dataURI);
+
+// Load the script
+load("ppapi-runtime.jsm");
+
+let instanceId = 1;
+let url = "http://example.com";
+let info = {
+    documentURL: "chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai",
+    url,
+    setupJSInstanceObject: false,
+    isFullFrame: false, //pluginElement.ownerDocument.mozSyntheticDocument,
+    arguments: {
+      keys: ["src", "full-frame", "top-level-url"],
+      values: [url, "", url],
+    },
+};
+
+// Head.js is a shared file for all the test_ppb***.js.
+// Right now, window, process and MessageManager are base classes here.
+// Fill in the classes when you need the functions of them
+// and add more mocked classses if you need them in
+// ppapi-runtime.jsm for your tests.
+class Mock_Window {}
+class Mock_Process {}
+class Mock_MessageManager {
+  addMessageListener () {
+  }
+}
+
+// Here the new PPAPIRuntime, Call_PpbFunc and new PPAPIInstance are the
+// core part to invoke codes in ppapi-runtime.jsm.
+let rt = new PPAPIRuntime(new Mock_Process());
+
+function Call_PpbFunc(obj) {
+  if (!obj || !obj.__interface || !obj.__version || !obj.__method) {
+    ok(false, 'invalid JSON');
+  }
+  let fn = obj.__interface + "_" + obj.__method;
+  return rt.table[fn](obj);
+}
+
+// PPAPIInstance constructor(id, rt, info, window, eventHandler, containerWindow, mm)
+let instance = new PPAPIInstance(instanceId, rt, info, new Mock_Window(), null /*docShell.chromeEventHandler*/, null, new Mock_MessageManager());
+
+do_register_cleanup(function () {
+  resHandler.setSubstitution("ppapi.js", null);
+})
+
new file mode 100644
--- /dev/null
+++ b/browser/extensions/mortar/test/unit/test_ppbkeyboard.js
@@ -0,0 +1,49 @@
+"use strict";
+
+const PPB_TESTCASES = [
+  {"__interface":"PPB_KeyboardInputEvent","__version":"1.2","__method":"IsKeyboardInputEvent","resource":0},
+  {"__interface":"PPB_KeyboardInputEvent","__version":"1.2","__method":"GetKeyCode","key_event":0},
+  {"__interface":"PPB_KeyboardInputEvent","__version":"1.2","__method":"GetCharacterText","character_event":0}
+];
+
+
+class Mock_DomEvent {
+  constructor(eventType) {
+    this.type = eventType;
+    this.timeStamp = 0;
+  }
+}
+
+class Mock_KeyboardInputEvent extends Mock_DomEvent {
+  constructor(eventType, keyCode, charCode) {
+    super(eventType);
+    this.keyCode = keyCode;
+    this.charCode = charCode;
+  }
+}
+
+function run_test() {
+  // We mock a "keydown" event to test "PPB_KeyboardInputEvent".
+  let event = new Mock_KeyboardInputEvent("keydown", 65, 0);
+  // To test PPB_KeyboardInputEvent we need to invoke event resource constructor
+  // in ppapi-runtime.jsm to get a resource id.
+  let eventType = EventTypes.get(event.type);
+  let resource = new eventType.resourceCtor(instance, event);
+  let PP_ResourceID = resource.toJSON();
+
+  PPB_TESTCASES[0].resource = PP_ResourceID;
+  PPB_TESTCASES[1].key_event = PP_ResourceID;
+  Assert.equal(Call_PpbFunc(PPB_TESTCASES[0]), PP_Bool.PP_TRUE);
+  Assert.equal(Call_PpbFunc(PPB_TESTCASES[1]), 65); // 65 is the keyCode when you press 'A'.
+
+  // We mock a "keypress" event to test "PPB_KeyboardInputEvent".
+  event = new Mock_KeyboardInputEvent("keypress", 0, 65);
+  eventType = EventTypes.get(event.type);
+  resource = new eventType.resourceCtor(instance, event);
+  PP_ResourceID = resource.toJSON();
+
+  PPB_TESTCASES[0].resource = PP_ResourceID;
+  PPB_TESTCASES[2].character_event = PP_ResourceID;
+  Assert.equal(Call_PpbFunc(PPB_TESTCASES[0]), PP_Bool.PP_TRUE);
+  Assert.equal(Call_PpbFunc(PPB_TESTCASES[2]).type, PP_VarType.PP_VARTYPE_STRING);
+}
new file mode 100644
--- /dev/null
+++ b/browser/extensions/mortar/test/unit/xpcshell.ini
@@ -0,0 +1,8 @@
+[DEFAULT]
+head = head.js
+tail =
+support-files =
+  ../../host/common/opengles2-utils.jsm
+  ../../host/common/ppapi-runtime.jsm
+
+[test_ppbkeyboard.js]
--- a/devtools/client/performance/modules/marker-dom-utils.js
+++ b/devtools/client/performance/modules/marker-dom-utils.js
@@ -220,17 +220,17 @@ exports.MarkerDOMUtils = {
   buildCustom: function (doc, marker, options) {
     let elements = [];
 
     if (options.allocations && shouldShowAllocationsTrigger(marker)) {
       let hbox = doc.createElement("hbox");
       hbox.className = "marker-details-customcontainer";
 
       let label = doc.createElement("label");
-      label.className = "custom-button devtools-button";
+      label.className = "custom-button";
       label.setAttribute("value", "Show allocation triggers");
       label.setAttribute("type", "show-allocations");
       label.setAttribute("data-action", JSON.stringify({
         endTime: marker.start,
         action: "show-allocations"
       }));
 
       hbox.appendChild(label);
--- a/devtools/client/themes/performance.css
+++ b/devtools/client/themes/performance.css
@@ -690,16 +690,22 @@
 
 .marker-details-duration {
   font-weight: bold;
 }
 
 .marker-details-customcontainer .custom-button {
   padding: 2px 5px;
   border-width: 1px;
+  color: var(--theme-highlight-blue);
+  text-decoration: none;
+}
+
+.marker-details-customcontainer .custom-button:hover {
+  text-decoration: underline;
 }
 
 /**
  * Marker colors
  */
 
 menuitem.marker-color-graphs-full-red .menu-iconic-left::after,
 .marker-color-graphs-full-red {
--- a/docshell/test/test_bug385434.html
+++ b/docshell/test/test_bug385434.html
@@ -92,17 +92,17 @@ function eventExpected(msg) {
  *
  * We can't always just wait for page load in order to observe that a
  * hashchange didn't happen.  In these cases, we call longWait() and yield
  * until either a hashchange occurs or longWait's callback is scheduled.  This
  * is something of a hack; it's entirely possible that longWait won't wait long
  * enough, and we won't observe what should have been a failure of the test.
  * But it shouldn't happen that good code will randomly *fail* this test.
  */
-function run_test() {
+function* run_test() {
   /*
    * TEST 1 tests that:
    *    <body onhashchange = ... >  works,
    *    the event is (not) fired at the correct times
    */
   var frame = document.getElementById("frame");
   var frameCw = frame.contentWindow;
 
--- a/dom/audiochannel/AudioChannelService.cpp
+++ b/dom/audiochannel/AudioChannelService.cpp
@@ -215,16 +215,27 @@ AudioChannelService::GetOrCreate()
     return nullptr;
   }
 
   CreateServiceIfNeeded();
   RefPtr<AudioChannelService> service = gAudioChannelService.get();
   return service.forget();
 }
 
+/* static */ already_AddRefed<AudioChannelService>
+AudioChannelService::Get()
+{
+  if (sXPCOMShuttingDown) {
+    return nullptr;
+  }
+
+  RefPtr<AudioChannelService> service = gAudioChannelService.get();
+  return service.forget();
+}
+
 /* static */ PRLogModuleInfo*
 AudioChannelService::GetAudioChannelLog()
 {
   static PRLogModuleInfo *gAudioChannelLog;
   if (!gAudioChannelLog) {
     gAudioChannelLog = PR_NewLogModule("AudioChannel");
   }
   return gAudioChannelLog;
@@ -928,16 +939,27 @@ AudioChannelService::IsAudioChannelActiv
                                           bool* aActive)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   auto* window = nsPIDOMWindowOuter::From(aWindow)->GetScriptableTop();
   *aActive = IsAudioChannelActive(window, (AudioChannel)aAudioChannel);
   return NS_OK;
 }
+
+bool
+AudioChannelService::IsWindowActive(nsPIDOMWindowOuter* aWindow)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  auto* window = nsPIDOMWindowOuter::From(aWindow)->GetScriptableTop();
+  AudioChannelWindow* winData = GetOrCreateWindowData(window);
+  return !winData->mAudibleAgents.IsEmpty();
+}
+
 void
 AudioChannelService::SetDefaultVolumeControlChannel(int32_t aChannel,
                                                     bool aVisible)
 {
   SetDefaultVolumeControlChannelInternal(aChannel, aVisible,
                                          CONTENT_PROCESS_ID_MAIN);
 }
 
--- a/dom/audiochannel/AudioChannelService.h
+++ b/dom/audiochannel/AudioChannelService.h
@@ -84,21 +84,27 @@ public:
   enum AudibleChangedReasons : uint32_t {
     eVolumeChanged = 0,
     eDataAudibleChanged = 1,
     ePauseStateChanged = 2
   };
 
   /**
    * Returns the AudioChannelServce singleton.
-   * If AudioChannelServce is not exist, create and return new one.
+   * If AudioChannelService doesn't exist, create and return new one.
    * Only to be called from main thread.
    */
   static already_AddRefed<AudioChannelService> GetOrCreate();
 
+  /**
+   * Returns the AudioChannelService singleton if one exists.
+   * If AudioChannelService doesn't exist, returns null.
+   */
+  static already_AddRefed<AudioChannelService> Get();
+
   static bool IsAudioChannelMutedByDefault();
 
   static PRLogModuleInfo* GetAudioChannelLog();
 
   static bool IsEnableAudioCompeting();
 
   static bool IsServiceStarted();
 
@@ -145,16 +151,18 @@ public:
 
   bool GetAudioChannelMuted(nsPIDOMWindowOuter* aWindow, AudioChannel aChannel);
 
   void SetAudioChannelMuted(nsPIDOMWindowOuter* aWindow, AudioChannel aChannel,
                             bool aMuted);
 
   bool IsAudioChannelActive(nsPIDOMWindowOuter* aWindow, AudioChannel aChannel);
 
+  bool IsWindowActive(nsPIDOMWindowOuter* aWindow);
+
   /**
    * Return true if there is a telephony channel active in this process
    * or one of its subprocesses.
    */
   bool TelephonyChannelIsActive();
 
   /**
    * Return true if a normal or content channel is active for the given
--- a/dom/base/DOMParser.h
+++ b/dom/base/DOMParser.h
@@ -77,17 +77,20 @@ public:
   }
 
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override
   {
     return mozilla::dom::DOMParserBinding::Wrap(aCx, this, aGivenProto);
   }
 
 private:
-  explicit DOMParser(nsISupports* aOwner) : mOwner(aOwner), mAttemptedInit(false)
+  explicit DOMParser(nsISupports* aOwner)
+    : mOwner(aOwner)
+    , mAttemptedInit(false)
+    , mOriginalPrincipalWasSystem(false)
   {
     MOZ_ASSERT(aOwner);
   }
 
   nsresult InitInternal(nsISupports* aOwner, nsIPrincipal* prin,
                         nsIURI* documentURI, nsIURI* baseURI);
 
   nsresult SetUpDocument(DocumentFlavor aFlavor, nsIDOMDocument** aResult);
--- a/dom/base/TimeoutManager.cpp
+++ b/dom/base/TimeoutManager.cpp
@@ -29,19 +29,20 @@ static int32_t gMinTimeoutValue = 0;
 static int32_t gMinBackgroundTimeoutValue = 0;
 static int32_t gMinTrackingTimeoutValue = 0;
 static int32_t gMinTrackingBackgroundTimeoutValue = 0;
 static bool    gAnnotateTrackingChannels = false;
 int32_t
 TimeoutManager::DOMMinTimeoutValue(bool aIsTracking) const {
   // First apply any back pressure delay that might be in effect.
   int32_t value = std::max(mBackPressureDelayMS, 0);
-  // Don't use the background timeout value when there are audio contexts
-  // present, so that background audio can keep running smoothly. (bug 1181073)
-  bool isBackground = !mWindow.AsInner()->HasAudioContexts() &&
+  // Don't use the background timeout value when the tab is playing audio.
+  // Until bug 1336484 we only used to do this for pages that use Web Audio.
+  // The original behavior was implemented in bug 11811073.
+  bool isBackground = !mWindow.AsInner()->IsPlayingAudio() &&
     mWindow.IsBackgroundInternal();
   auto minValue = aIsTracking ? (isBackground ? gMinTrackingBackgroundTimeoutValue
                                               : gMinTrackingTimeoutValue)
                               : (isBackground ? gMinBackgroundTimeoutValue
                                               : gMinTimeoutValue);
   return std::max(minValue, value);
 }
 
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -2325,17 +2325,17 @@ nsDOMWindowUtils::GetLayerManagerRemote(
   nsCOMPtr<nsIWidget> widget = GetWidget();
   if (!widget)
     return NS_ERROR_FAILURE;
 
   LayerManager *mgr = widget->GetLayerManager();
   if (!mgr)
     return NS_ERROR_FAILURE;
 
-  *retval = !!mgr->AsShadowForwarder();
+  *retval = !!mgr->AsKnowsCompositor();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMWindowUtils::GetSupportsHardwareH264Decoding(JS::MutableHandle<JS::Value> aPromise)
 {
   nsCOMPtr<nsPIDOMWindowOuter> window = do_QueryReferent(mWindow);
   NS_ENSURE_STATE(window);
@@ -2343,17 +2343,17 @@ nsDOMWindowUtils::GetSupportsHardwareH26
     do_QueryInterface(window->GetCurrentInnerWindow());
   NS_ENSURE_STATE(parentObject);
 #ifdef MOZ_FMP4
   nsCOMPtr<nsIWidget> widget = GetWidget();
   NS_ENSURE_STATE(widget);
   LayerManager *mgr = widget->GetLayerManager();
   NS_ENSURE_STATE(mgr);
   RefPtr<Promise> promise =
-    MP4Decoder::IsVideoAccelerated(mgr->AsShadowForwarder(), parentObject);
+    MP4Decoder::IsVideoAccelerated(mgr->AsKnowsCompositor(), parentObject);
   NS_ENSURE_STATE(promise);
   aPromise.setObject(*promise->PromiseObj());
 #else
   ErrorResult rv;
   RefPtr<Promise> promise = Promise::Create(parentObject, rv);
   if (rv.Failed()) {
     return rv.StealNSResult();
   }
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -4147,19 +4147,23 @@ nsPIDOMWindowInner::Thaw()
 
 void
 nsPIDOMWindowInner::SyncStateFromParentWindow()
 {
   nsGlobalWindow::Cast(this)->SyncStateFromParentWindow();
 }
 
 bool
-nsPIDOMWindowInner::HasAudioContexts() const
-{
-  return !mAudioContexts.IsEmpty();
+nsPIDOMWindowInner::IsPlayingAudio()
+{
+  RefPtr<AudioChannelService> acs = AudioChannelService::Get();
+  if (!acs) {
+    return false;
+  }
+  return acs->IsWindowActive(GetOuterWindow());
 }
 
 mozilla::dom::TimeoutManager&
 nsPIDOMWindowInner::TimeoutManager()
 {
   return *mTimeoutManager;
 }
 
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -873,17 +873,17 @@ public:
   // calls.
   void Freeze();
   void Thaw();
 
   // Apply the parent window's suspend, freeze, and modal state to the current
   // window.
   void SyncStateFromParentWindow();
 
-  bool HasAudioContexts() const;
+  bool IsPlayingAudio();
 
   mozilla::dom::TimeoutManager& TimeoutManager();
 
   bool IsRunningTimeout();
 
 protected:
   void CreatePerformanceObjectIfNeeded();
 };
--- a/dom/base/test/browser.ini
+++ b/dom/base/test/browser.ini
@@ -1,29 +1,36 @@
 [DEFAULT]
 support-files =
+  audio.ogg
   empty.html
+  file_audioLoop.html
+  file_audioLoopInIframe.html
   file_bug1011748_redirect.sjs
   file_bug1011748_OK.sjs
   file_messagemanager_unload.html
+  file_pluginAudio.html
   file_use_counter_outer.html
   file_use_counter_svg_getElementById.svg
   file_use_counter_svg_currentScale.svg
   file_use_counter_svg_fill_pattern_definition.svg
   file_use_counter_svg_fill_pattern.svg
   file_use_counter_svg_fill_pattern_internal.svg
   file_use_counter_svg_fill_pattern_data.svg
+  file_webaudioLoop.html
+  plugin.js
 
 [browser_bug593387.js]
 [browser_bug902350.js]
 tags = mcb
 [browser_bug1011748.js]
 [browser_bug1058164.js]
 [browser_messagemanager_loadprocessscript.js]
 skip-if = e10s # Bug 1315042
 [browser_messagemanager_targetframeloader.js]
 [browser_messagemanager_unload.js]
 [browser_pagehide_on_tab_close.js]
 skip-if = e10s # this tests non-e10s behavior. it's not expected to work in e10s.
 [browser_state_notifications.js]
 skip-if = true # Bug 1271028
 [browser_use_counters.js]
 [browser_bug1307747.js]
+[browser_timeout_throttling_with_audio_playback.js]
new file mode 100644
--- /dev/null
+++ b/dom/base/test/browser_timeout_throttling_with_audio_playback.js
@@ -0,0 +1,61 @@
+const kBaseURI = "http://mochi.test:8888/browser/dom/base/test/empty.html";
+const kPluginJS = "chrome://mochitests/content/browser/dom/base/test/plugin.js";
+var testURLs = [
+  "http://mochi.test:8888/browser/dom/base/test/file_audioLoop.html",
+  "http://mochi.test:8888/browser/dom/base/test/file_audioLoopInIframe.html",
+  "http://mochi.test:8888/browser/dom/base/test/file_pluginAudio.html",
+  "http://mochi.test:8888/browser/dom/base/test/file_webaudioLoop.html",
+];
+
+// We want to ensure that while audio is being played back, a background tab is
+// treated the same as a foreground tab as far as timeout throttling is concerned.
+// So we use a 10ms minimum timeout value for foreground tabs and a 100,000 second
+// minimum timeout value for background tabs.  This means that in case the test
+// fails, it will time out in practice, but just for sanity the test condition
+// ensures that the observed timeout delay falls in this range.
+const kMinTimeoutForeground = 10;
+const kMinTimeoutBackground = 100 * 1000 * 1000;
+
+Services.scriptloader.loadSubScript(kPluginJS, this);
+
+function* runTest(url) {
+  let currentTab = gBrowser.selectedTab;
+  let newTab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, kBaseURI);
+  let newBrowser = gBrowser.getBrowserForTab(newTab);
+
+  // Wait for the UI to indicate that audio is being played back.
+  let promise = BrowserTestUtils.waitForAttribute("soundplaying", newTab, "true");
+  newBrowser.loadURI(url);
+  yield promise;
+
+  // Put the tab in the background.
+  yield BrowserTestUtils.switchTab(gBrowser, currentTab);
+
+  let timeout = yield ContentTask.spawn(newBrowser, {}, function() {
+    return new Promise(resolve => {
+      let before = new Date();
+      content.window.setTimeout(function() {
+        let after = new Date();
+        resolve(after - before);
+      }, 0);
+    });
+  });
+  ok(timeout >= kMinTimeoutForeground &&
+     timeout <= kMinTimeoutBackground, `Got the correct timeout (${timeout})`);
+
+  // All done.
+  yield BrowserTestUtils.removeTab(newTab);
+}
+
+add_task(function* setup() {
+  yield SpecialPowers.pushPrefEnv({"set": [
+    ["dom.min_timeout_value", kMinTimeoutForeground],
+    ["dom.min_background_timeout_value", kMinTimeoutBackground],
+  ]});
+});
+
+add_task(function* test() {
+  for (var url of testURLs) {
+    yield runTest(url);
+  }
+});
new file mode 100644
--- /dev/null
+++ b/dom/base/test/file_audioLoopInIframe.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<iframe src="file_audioLoop.html"></iframe>
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -184,16 +184,17 @@ support-files =
   file_youtube_flash_embed.html
   forRemoval.resource
   forRemoval.resource^headers^
   formReset.html
   invalid_accesscontrol.resource
   invalid_accesscontrol.resource^headers^
   mutationobserver_dialog.html
   orientationcommon.js
+  plugin.js
   script-1_bug597345.sjs
   script-2_bug597345.js
   script_bug602838.sjs
   send_gzip_content.sjs
   somedatas.resource
   somedatas.resource^headers^
   variable_style_sheet.sjs
   viewport_helpers.js
new file mode 100644
--- /dev/null
+++ b/dom/base/test/plugin.js
@@ -0,0 +1,32 @@
+// Copied from /dom/plugins/test/mochitest/utils.js
+function getTestPlugin(pluginName) {
+  var ph = SpecialPowers.Cc["@mozilla.org/plugin/host;1"]
+                                 .getService(SpecialPowers.Ci.nsIPluginHost);
+  var tags = ph.getPluginTags();
+  var name = pluginName || "Test Plug-in";
+  for (var tag of tags) {
+    if (tag.name == name) {
+      return tag;
+    }
+  }
+
+  ok(false, "Could not find plugin tag with plugin name '" + name + "'");
+  return null;
+}
+// Copied from /dom/plugins/test/mochitest/utils.js
+function setTestPluginEnabledState(newEnabledState, pluginName) {
+  var oldEnabledState = SpecialPowers.setTestPluginEnabledState(newEnabledState, pluginName);
+  if (!oldEnabledState) {
+    return;
+  }
+  var plugin = getTestPlugin(pluginName);
+  while (plugin.enabledState != newEnabledState) {
+    // Run a nested event loop to wait for the preference change to
+    // propagate to the child. Yuck!
+    SpecialPowers.Services.tm.currentThread.processNextEvent(true);
+  }
+  SimpleTest.registerCleanupFunction(function() {
+    SpecialPowers.setTestPluginEnabledState(oldEnabledState, pluginName);
+  });
+}
+setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
--- a/dom/base/test/test_pluginAudioNotification.html
+++ b/dom/base/test/test_pluginAudioNotification.html
@@ -1,55 +1,23 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <title>Test for audio controller in windows</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="plugin.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
 <pre id="test">
 </pre>
 <iframe></iframe>
 
 <script type="application/javascript">
 
-// Copied from /dom/plugins/test/mochitest/utils.js
-function getTestPlugin(pluginName) {
-  var ph = SpecialPowers.Cc["@mozilla.org/plugin/host;1"]
-                                 .getService(SpecialPowers.Ci.nsIPluginHost);
-  var tags = ph.getPluginTags();
-  var name = pluginName || "Test Plug-in";
-  for (var tag of tags) {
-    if (tag.name == name) {
-      return tag;
-    }
-  }
-
-  ok(false, "Could not find plugin tag with plugin name '" + name + "'");
-  return null;
-}
-// Copied from /dom/plugins/test/mochitest/utils.js
-function setTestPluginEnabledState(newEnabledState, pluginName) {
-  var oldEnabledState = SpecialPowers.setTestPluginEnabledState(newEnabledState, pluginName);
-  if (!oldEnabledState) {
-    return;
-  }
-  var plugin = getTestPlugin(pluginName);
-  while (plugin.enabledState != newEnabledState) {
-    // Run a nested event loop to wait for the preference change to
-    // propagate to the child. Yuck!
-    SpecialPowers.Services.tm.currentThread.processNextEvent(true);
-  }
-  SimpleTest.registerCleanupFunction(function() {
-    SpecialPowers.setTestPluginEnabledState(oldEnabledState, pluginName);
-  });
-}
-setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
-
 SimpleTest.waitForExplicitFinish();
 
 var expectedNotification = null;
 var iframe = null;
 
 var observer = {
   observe: function(subject, topic, data) {
     is(topic, "audio-playback", "audio-playback received");
--- a/dom/canvas/crashtests/crashtests.list
+++ b/dom/canvas/crashtests/crashtests.list
@@ -6,18 +6,18 @@ load 553938-1.html
 load 647480.html
 load 727547.html
 load 729116.html
 asserts-if(stylo,1) load 745699-1.html # bug 1324700
 load 746813-1.html
 load 743499-negative-size.html
 skip-if(Android) load 745818-large-source.html # Bug XXX - Crashes Android mid-run w/o a stack
 load 767337-1.html
-asserts-if(stylo,1) skip-if(Android) load 780392-1.html # bug 1324700
-skip-if(Android) skip-if(gtkWidget&&isDebugBuild) load 789933-1.html # bug 1155252 for linux
+asserts-if(stylo,1) skip-if(Android) skip-if(webrender) load 780392-1.html # bug 1324700. bug 1322816 for webrender
+skip-if(Android) skip-if(gtkWidget&&isDebugBuild) skip-if(webrender) load 789933-1.html # bug 1155252 for linux, bug 1322816 for webrender
 load 794463-1.html
 load 802926-1.html
 load 896047-1.html
 load 916128-1.html
 load 934939-1.html
 asserts-if(stylo,1) load 1099143-1.html # bug 1324700
 load 1161277-1.html
 load 1183363.html
--- a/dom/canvas/test/reftest/reftest.list
+++ b/dom/canvas/test/reftest/reftest.list
@@ -1,20 +1,20 @@
 # WebGL Reftests!
 default-preferences pref(webgl.force-enabled,true) pref(media.useAudioChannelAPI,true) pref(dom.audiochannel.mutedByDefault,false)
 
 # Check that disabling works:
 skip-if(Android)                           == webgl-disable-test.html?nogl wrapper.html?green.png
-pref(webgl.disabled,true) skip-if(Android)  == webgl-disable-test.html      wrapper.html?green.png
+pref(webgl.disabled,true) skip-if(Android) == webgl-disable-test.html      wrapper.html?green.png
 
 # Basic WebGL tests:
 # Do we get pixels to the screen at all?
 # Neither of these should ever break.
 skip-if(Android)                                        == webgl-clear-test.html wrapper.html?green.png
-pref(webgl.force-layers-readback,true) skip-if(Android)  == webgl-clear-test.html?readback wrapper.html?green.png
+pref(webgl.force-layers-readback,true) skip-if(Android) == webgl-clear-test.html?readback wrapper.html?green.png
 
 # Make sure that our choice of attribs doesn't break rendering.
 skip-if(Android) == webgl-clear-test.html?depth wrapper.html?green.png
 skip-if(Android) == webgl-clear-test.html?stencil wrapper.html?green.png
 skip-if(Android) == webgl-clear-test.html?depth&stencil wrapper.html?green.png
 
 # Check that resize works:
 skip-if(Android) == webgl-resize-test.html  wrapper.html?green.png
@@ -48,71 +48,71 @@ skip-if(Android)                        
 skip-if(Android)                                                                                                        == webgl-color-test.html?frame=1&__&________&premult&_____ wrapper.html?colors-no-alpha.png
 skip-if(Android)                                                                                                        == webgl-color-test.html?frame=1&aa&________&premult&_____ wrapper.html?colors-no-alpha.png
 skip-if(Android)                                                                                                        == webgl-color-test.html?frame=1&__&preserve&premult&_____ wrapper.html?colors-no-alpha.png
 skip-if(Android)                                                                                                        == webgl-color-test.html?frame=1&aa&preserve&premult&_____ wrapper.html?colors-no-alpha.png
 fuzzy(1,30000) fails-if(winWidget&&layersGPUAccelerated&&!d3d11) skip-if(Android)                                       == webgl-color-test.html?frame=1&__&________&_______&alpha wrapper.html?colors-non-premult.png
 fuzzy(1,30000) fails-if(winWidget&&layersGPUAccelerated&&!d3d11) skip-if(Android)                                       == webgl-color-test.html?frame=1&aa&________&_______&alpha wrapper.html?colors-non-premult.png
 fuzzy(1,30000) fails-if(winWidget&&layersGPUAccelerated&&!d3d11) skip-if(Android)                                       == webgl-color-test.html?frame=1&__&preserve&_______&alpha wrapper.html?colors-non-premult.png
 fuzzy(1,30000) fails-if(winWidget&&layersGPUAccelerated&&!d3d11) skip-if(Android)                                       == webgl-color-test.html?frame=1&aa&preserve&_______&alpha wrapper.html?colors-non-premult.png
-skip-if(Android)                                                                                                        == webgl-color-test.html?frame=1&__&________&premult&alpha wrapper.html?colors-premult.png
-skip-if(Android)                                                                                                        == webgl-color-test.html?frame=1&aa&________&premult&alpha wrapper.html?colors-premult.png
-skip-if(Android)                                                                                                        == webgl-color-test.html?frame=1&__&preserve&premult&alpha wrapper.html?colors-premult.png
-skip-if(Android)                                                                                                        == webgl-color-test.html?frame=1&aa&preserve&premult&alpha wrapper.html?colors-premult.png
+skip-if(Android)                                                                                                        fails-if(webrender) == webgl-color-test.html?frame=1&__&________&premult&alpha wrapper.html?colors-premult.png
+skip-if(Android)                                                                                                        fails-if(webrender) == webgl-color-test.html?frame=1&aa&________&premult&alpha wrapper.html?colors-premult.png
+skip-if(Android)                                                                                                        fails-if(webrender) == webgl-color-test.html?frame=1&__&preserve&premult&alpha wrapper.html?colors-premult.png
+skip-if(Android)                                                                                                        fails-if(webrender) == webgl-color-test.html?frame=1&aa&preserve&premult&alpha wrapper.html?colors-premult.png
 
 skip-if(Android)                                                                                                        == webgl-color-test.html?frame=6&__&________&_______&_____ wrapper.html?colors-no-alpha.png
 skip-if(Android)                                                                                                        == webgl-color-test.html?frame=6&aa&________&_______&_____ wrapper.html?colors-no-alpha.png
 skip-if(Android)                                                                                                        == webgl-color-test.html?frame=6&__&preserve&_______&_____ wrapper.html?colors-no-alpha.png
 skip-if(Android)                                                                                                        == webgl-color-test.html?frame=6&aa&preserve&_______&_____ wrapper.html?colors-no-alpha.png
 skip-if(Android)                                                                                                        == webgl-color-test.html?frame=6&__&________&premult&_____ wrapper.html?colors-no-alpha.png
 skip-if(Android)                                                                                                        == webgl-color-test.html?frame=6&aa&________&premult&_____ wrapper.html?colors-no-alpha.png
 skip-if(Android)                                                                                                        == webgl-color-test.html?frame=6&__&preserve&premult&_____ wrapper.html?colors-no-alpha.png
 skip-if(Android)                                                                                                        == webgl-color-test.html?frame=6&aa&preserve&premult&_____ wrapper.html?colors-no-alpha.png
 fuzzy(1,30000) fails-if(winWidget&&layersGPUAccelerated&&!d3d11) skip-if(Android)                                       == webgl-color-test.html?frame=6&__&________&_______&alpha wrapper.html?colors-non-premult.png
 fuzzy(1,30000) fails-if(winWidget&&layersGPUAccelerated&&!d3d11) skip-if(Android)                                       == webgl-color-test.html?frame=6&aa&________&_______&alpha wrapper.html?colors-non-premult.png
 fuzzy(1,30000) fails-if(winWidget&&layersGPUAccelerated&&!d3d11) skip-if(Android)                                       == webgl-color-test.html?frame=6&__&preserve&_______&alpha wrapper.html?colors-non-premult.png
 fuzzy(1,30000) fails-if(winWidget&&layersGPUAccelerated&&!d3d11) skip-if(Android)                                       == webgl-color-test.html?frame=6&aa&preserve&_______&alpha wrapper.html?colors-non-premult.png
-skip-if(Android)                                                                                                        == webgl-color-test.html?frame=6&__&________&premult&alpha wrapper.html?colors-premult.png
-skip-if(Android)                                                                                                        == webgl-color-test.html?frame=6&aa&________&premult&alpha wrapper.html?colors-premult.png
-skip-if(Android)                                                                                                        == webgl-color-test.html?frame=6&__&preserve&premult&alpha wrapper.html?colors-premult.png
-skip-if(Android)                                                                                                        == webgl-color-test.html?frame=6&aa&preserve&premult&alpha wrapper.html?colors-premult.png
+skip-if(Android)                                                                                                        fails-if(webrender) == webgl-color-test.html?frame=6&__&________&premult&alpha wrapper.html?colors-premult.png
+skip-if(Android)                                                                                                        fails-if(webrender) == webgl-color-test.html?frame=6&aa&________&premult&alpha wrapper.html?colors-premult.png
+skip-if(Android)                                                                                                        fails-if(webrender) == webgl-color-test.html?frame=6&__&preserve&premult&alpha wrapper.html?colors-premult.png
+skip-if(Android)                                                                                                        fails-if(webrender) == webgl-color-test.html?frame=6&aa&preserve&premult&alpha wrapper.html?colors-premult.png
 
 skip-if(Android)                                                                 pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=1&readback&__&________&_______&_____ wrapper.html?colors-no-alpha.png
 skip-if(Android)                                                                 pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=1&readback&aa&________&_______&_____ wrapper.html?colors-no-alpha.png
 skip-if(Android)                                                                 pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=1&readback&__&preserve&_______&_____ wrapper.html?colors-no-alpha.png
 skip-if(Android)                                                                 pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=1&readback&aa&preserve&_______&_____ wrapper.html?colors-no-alpha.png
 skip-if(Android)                                                                 pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=1&readback&__&________&premult&_____ wrapper.html?colors-no-alpha.png
 skip-if(Android)                                                                 pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=1&readback&aa&________&premult&_____ wrapper.html?colors-no-alpha.png
 skip-if(Android)                                                                 pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=1&readback&__&preserve&premult&_____ wrapper.html?colors-no-alpha.png
 skip-if(Android)                                                                 pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=1&readback&aa&preserve&premult&_____ wrapper.html?colors-no-alpha.png
 fuzzy(1,30000) fails-if(winWidget&&layersGPUAccelerated&&!d3d11) skip-if(Android) pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=1&readback&__&________&_______&alpha wrapper.html?colors-non-premult.png
 fuzzy(1,30000) fails-if(winWidget&&layersGPUAccelerated&&!d3d11) skip-if(Android) pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=1&readback&aa&________&_______&alpha wrapper.html?colors-non-premult.png
 fuzzy(1,30000) fails-if(winWidget&&layersGPUAccelerated&&!d3d11) skip-if(Android) pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=1&readback&__&preserve&_______&alpha wrapper.html?colors-non-premult.png
 fuzzy(1,30000) fails-if(winWidget&&layersGPUAccelerated&&!d3d11) skip-if(Android) pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=1&readback&aa&preserve&_______&alpha wrapper.html?colors-non-premult.png
-skip-if(Android)                                                                 pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=1&readback&__&________&premult&alpha wrapper.html?colors-premult.png
-skip-if(Android)                                                                 pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=1&readback&aa&________&premult&alpha wrapper.html?colors-premult.png
-skip-if(Android)                                                                 pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=1&readback&__&preserve&premult&alpha wrapper.html?colors-premult.png
-skip-if(Android)                                                                 pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=1&readback&aa&preserve&premult&alpha wrapper.html?colors-premult.png
+skip-if(Android)                                                                 pref(webgl.force-layers-readback,true) fails-if(webrender) == webgl-color-test.html?frame=1&readback&__&________&premult&alpha wrapper.html?colors-premult.png
+skip-if(Android)                                                                 pref(webgl.force-layers-readback,true) fails-if(webrender) == webgl-color-test.html?frame=1&readback&aa&________&premult&alpha wrapper.html?colors-premult.png
+skip-if(Android)                                                                 pref(webgl.force-layers-readback,true) fails-if(webrender) == webgl-color-test.html?frame=1&readback&__&preserve&premult&alpha wrapper.html?colors-premult.png
+skip-if(Android)                                                                 pref(webgl.force-layers-readback,true) fails-if(webrender) == webgl-color-test.html?frame=1&readback&aa&preserve&premult&alpha wrapper.html?colors-premult.png
 
 skip-if(Android)                                                                 pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&__&________&_______&_____ wrapper.html?colors-no-alpha.png
 skip-if(Android)                                                                 pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&aa&________&_______&_____ wrapper.html?colors-no-alpha.png
 skip-if(Android)                                                                 pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&__&preserve&_______&_____ wrapper.html?colors-no-alpha.png
 skip-if(Android)                                                                 pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&aa&preserve&_______&_____ wrapper.html?colors-no-alpha.png
 skip-if(Android)                                                                 pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&__&________&premult&_____ wrapper.html?colors-no-alpha.png
 skip-if(Android)                                                                 pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&aa&________&premult&_____ wrapper.html?colors-no-alpha.png
 skip-if(Android)                                                                 pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&__&preserve&premult&_____ wrapper.html?colors-no-alpha.png
 skip-if(Android)                                                                 pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&aa&preserve&premult&_____ wrapper.html?colors-no-alpha.png
 fuzzy(1,30000) fails-if(winWidget&&layersGPUAccelerated&&!d3d11) skip-if(Android) pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&__&________&_______&alpha wrapper.html?colors-non-premult.png
 fuzzy(1,30000) fails-if(winWidget&&layersGPUAccelerated&&!d3d11) skip-if(Android) pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&aa&________&_______&alpha wrapper.html?colors-non-premult.png
 fuzzy(1,30000) fails-if(winWidget&&layersGPUAccelerated&&!d3d11) skip-if(Android) pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&__&preserve&_______&alpha wrapper.html?colors-non-premult.png
 fuzzy(1,30000) fails-if(winWidget&&layersGPUAccelerated&&!d3d11) skip-if(Android) pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&aa&preserve&_______&alpha wrapper.html?colors-non-premult.png
-skip-if(Android)                                                                 pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&__&________&premult&alpha wrapper.html?colors-premult.png
-skip-if(Android)                                                                 pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&aa&________&premult&alpha wrapper.html?colors-premult.png
-skip-if(Android)                                                                 pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&__&preserve&premult&alpha wrapper.html?colors-premult.png
-skip-if(Android)                                                                 pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&aa&preserve&premult&alpha wrapper.html?colors-premult.png
+skip-if(Android)                                                                 pref(webgl.force-layers-readback,true) fails-if(webrender) == webgl-color-test.html?frame=6&readback&__&________&premult&alpha wrapper.html?colors-premult.png
+skip-if(Android)                                                                 pref(webgl.force-layers-readback,true) fails-if(webrender) == webgl-color-test.html?frame=6&readback&aa&________&premult&alpha wrapper.html?colors-premult.png
+skip-if(Android)                                                                 pref(webgl.force-layers-readback,true) fails-if(webrender) == webgl-color-test.html?frame=6&readback&__&preserve&premult&alpha wrapper.html?colors-premult.png
+skip-if(Android)                                                                 pref(webgl.force-layers-readback,true) fails-if(webrender) == webgl-color-test.html?frame=6&readback&aa&preserve&premult&alpha wrapper.html?colors-premult.png
 
 # Check for hanging bindings/state settings:
 skip-if(Android) == webgl-hanging-fb-test.html?__&________  wrapper.html?green.png
 skip-if(Android) == webgl-hanging-fb-test.html?aa&________  wrapper.html?green.png
 skip-if(Android) == webgl-hanging-fb-test.html?__&preserve  wrapper.html?green.png
 skip-if(Android) == webgl-hanging-fb-test.html?aa&preserve  wrapper.html?green.png
 skip-if(Android) pref(webgl.force-layers-readback,true)  == webgl-hanging-fb-test.html?readback&__&________  wrapper.html?green.png
 skip-if(Android) pref(webgl.force-layers-readback,true)  == webgl-hanging-fb-test.html?readback&aa&________  wrapper.html?green.png
@@ -152,22 +152,22 @@ skip-if(!winWidget) pref(webgl.disable-a
 # Bug 1255062
 == clip-multiple-move-1.html clip-multiple-move-1-ref.html
 == clip-multiple-move-2.html clip-multiple-move-2-ref.html
 
 # Bug 815648
 == stroketext-shadow.html stroketext-shadow-ref.html
 
 # focus rings
-pref(canvas.focusring.enabled,true) skip-if(cocoaWidget) skip-if(winWidget) needs-focus == drawFocusIfNeeded.html drawFocusIfNeeded-ref.html
+pref(canvas.focusring.enabled,true) skip-if(cocoaWidget) skip-if(winWidget) needs-focus fails-if(webrender) == drawFocusIfNeeded.html drawFocusIfNeeded-ref.html
 pref(canvas.customfocusring.enabled,true) skip-if(Android||cocoaWidget||winWidget) fuzzy-if(gtkWidget,64,410) needs-focus == drawCustomFocusRing.html drawCustomFocusRing-ref.html
 
 # Check that captureStream() displays in a local video element
 == capturestream.html wrapper.html?green.png
 
-fuzzy-if(azureSkia,16,2) fuzzy-if(Android,3,40) fuzzy-if(/^Windows\x20NT\x2010\.0/.test(http.oscpu),1,1) == 1177726-text-stroke-bounds.html 1177726-text-stroke-bounds-ref.html
+fuzzy-if(azureSkia,16,2) fuzzy-if(Android,3,40) fuzzy-if(/^Windows\x20NT\x2010\.0/.test(http.oscpu),1,1) fails-if(webrender) == 1177726-text-stroke-bounds.html 1177726-text-stroke-bounds-ref.html
 
 # Canvas Filter Reftests
 include filters/reftest.list
 
 # Bug 1305963
 == mozCurrentTransform.html mozCurrentTransform-ref.html
 == mozCurrentTransformInverse.html mozCurrentTransform-ref.html
--- a/dom/html/reftests/reftest.list
+++ b/dom/html/reftests/reftest.list
@@ -57,9 +57,9 @@ fuzzy(3,640) == bug917595-exif-rotated.j
 == table-border-2.html table-border-2-ref.html
 != table-border-2.html table-border-2-notref.html
 
 # Test imageset is using permissions.default.image
 pref(permissions.default.image,1) HTTP == bug1196784-with-srcset.html bug1196784-no-srcset.html
 pref(permissions.default.image,2) HTTP == bug1196784-with-srcset.html bug1196784-no-srcset.html
 
 # Test video with rotation information can be rotated.
-== bug1228601-video-rotation-90.html bug1228601-video-rotated-ref.html
+fails-if(webrender) == bug1228601-video-rotation-90.html bug1228601-video-rotated-ref.html
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -31,16 +31,17 @@
 #include "mozilla/layers/APZEventState.h"
 #include "mozilla/layers/ContentProcessController.h"
 #include "mozilla/layers/CompositorBridgeChild.h"
 #include "mozilla/layers/DoubleTapToZoom.h"
 #include "mozilla/layers/IAPZCTreeManager.h"
 #include "mozilla/layers/ImageBridgeChild.h"
 #include "mozilla/layers/InputAPZContext.h"
 #include "mozilla/layers/ShadowLayers.h"
+#include "mozilla/layers/WebRenderLayerManager.h"
 #include "mozilla/layout/RenderFrameChild.h"
 #include "mozilla/layout/RenderFrameParent.h"
 #include "mozilla/LookAndFeel.h"
 #include "mozilla/MouseEvents.h"
 #include "mozilla/Move.h"
 #include "mozilla/PresShell.h"
 #include "mozilla/ProcessHangMonitor.h"
 #include "mozilla/ScopeExit.h"
@@ -421,16 +422,24 @@ TabChild::TabChild(nsIContentChild* aMan
     }
   }
 
   for (uint32_t idx = 0; idx < NUMBER_OF_AUDIO_CHANNELS; idx++) {
     mAudioChannelsActive.AppendElement(false);
   }
 }
 
+const CompositorOptions&
+TabChild::GetCompositorOptions() const
+{
+  // If you're calling this before mCompositorOptions is set, well.. don't.
+  MOZ_ASSERT(mCompositorOptions);
+  return mCompositorOptions.ref();
+}
+
 bool
 TabChild::AsyncPanZoomEnabled() const
 {
   // By the time anybody calls this, we must have had InitRenderingState called
   // already, and so mCompositorOptions should be populated.
   MOZ_RELEASE_ASSERT(mCompositorOptions);
   return mCompositorOptions->UseAPZ();
 }
@@ -2379,37 +2388,41 @@ TabChild::RecvSetDocShellIsActive(const 
   // channel and the PContent channel, we have an ordering problem. This code
   // ensures that we respect the order in which the requests were made and
   // ignore stale requests.
   if (mLayerObserverEpoch >= aLayerObserverEpoch) {
     return IPC_OK();
   }
   mLayerObserverEpoch = aLayerObserverEpoch;
 
-  MOZ_ASSERT(mPuppetWidget);
-  MOZ_ASSERT(mPuppetWidget->GetLayerManager());
-  MOZ_ASSERT(mPuppetWidget->GetLayerManager()->GetBackendType() ==
-             LayersBackend::LAYERS_CLIENT);
-
   auto clearForcePaint = MakeScopeExit([&] {
     // We might force a paint, or we might already have painted and this is a
     // no-op. In either case, once we exit this scope, we need to alert the
     // ProcessHangMonitor that we've finished responding to what might have
     // been a request to force paint. This is so that the BackgroundHangMonitor
     // for force painting can be made to wait again.
     if (aIsActive) {
       ProcessHangMonitor::ClearForcePaint();
     }
   });
 
-  // We send the current layer observer epoch to the compositor so that
-  // TabParent knows whether a layer update notification corresponds to the
-  // latest SetDocShellIsActive request that was made.
-  if (ClientLayerManager* clm = mPuppetWidget->GetLayerManager()->AsClientLayerManager()) {
-    clm->SetLayerObserverEpoch(aLayerObserverEpoch);
+  if (mCompositorOptions) {
+    // Note that |GetLayerManager()| has side-effects in that it creates a layer
+    // manager if one doesn't exist already. Calling it inside a debug-only
+    // assertion is generally bad but in this case we call it unconditionally
+    // just below so it's ok.
+    MOZ_ASSERT(mPuppetWidget);
+    MOZ_ASSERT(mPuppetWidget->GetLayerManager());
+    MOZ_ASSERT(mPuppetWidget->GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_CLIENT
+            || mPuppetWidget->GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_WR);
+
+    // We send the current layer observer epoch to the compositor so that
+    // TabParent knows whether a layer update notification corresponds to the
+    // latest SetDocShellIsActive request that was made.
+    mPuppetWidget->GetLayerManager()->SetLayerObserverEpoch(aLayerObserverEpoch);
   }
 
   // docshell is consider prerendered only if not active yet
   mIsPrerendered &= !aIsActive;
   nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation());
   if (docShell) {
     bool wasActive;
     docShell->GetIsActive(&wasActive);
@@ -2592,19 +2605,26 @@ TabChild::InitRenderingState(const Textu
       sTabChildren->Put(aLayersId, this);
       mLayersId = aLayersId;
     }
 
     ShadowLayerForwarder* lf =
         mPuppetWidget->GetLayerManager(
             nullptr, mTextureFactoryIdentifier.mParentBackend)
                 ->AsShadowForwarder();
-    // As long as we are creating a ClientLayerManager for the puppet widget,
-    // lf must be non-null here.
-    MOZ_ASSERT(lf);
+
+    LayerManager* lm = mPuppetWidget->GetLayerManager();
+    if (lm->AsWebRenderLayerManager()) {
+      lm->AsWebRenderLayerManager()->Initialize(compositorChild,
+                                                aLayersId,
+                                                &mTextureFactoryIdentifier);
+      ImageBridgeChild::IdentifyCompositorTextureHost(mTextureFactoryIdentifier);
+      gfx::VRManagerChild::IdentifyTextureHost(mTextureFactoryIdentifier);
+      InitAPZState();
+    }
 
     if (lf) {
       nsTArray<LayersBackend> backends;
       backends.AppendElement(mTextureFactoryIdentifier.mParentBackend);
       bool success;
       PLayerTransactionChild* shadowManager =
           compositorChild->SendPLayerTransactionConstructor(backends,
                                                             aLayersId, &mTextureFactoryIdentifier, &success);
@@ -2897,22 +2917,20 @@ TabChild::GetFrom(uint64_t aLayersId)
 
 void
 TabChild::DidComposite(uint64_t aTransactionId,
                        const TimeStamp& aCompositeStart,
                        const TimeStamp& aCompositeEnd)
 {
   MOZ_ASSERT(mPuppetWidget);
   MOZ_ASSERT(mPuppetWidget->GetLayerManager());
-  MOZ_ASSERT(mPuppetWidget->GetLayerManager()->GetBackendType() ==
-               LayersBackend::LAYERS_CLIENT);
-
-  RefPtr<ClientLayerManager> manager = mPuppetWidget->GetLayerManager()->AsClientLayerManager();
-
-  manager->DidComposite(aTransactionId, aCompositeStart, aCompositeEnd);
+  MOZ_ASSERT(mPuppetWidget->GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_CLIENT
+             || mPuppetWidget->GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_WR);
+
+  mPuppetWidget->GetLayerManager()->DidComposite(aTransactionId, aCompositeStart, aCompositeEnd);
 }
 
 void
 TabChild::DidRequestComposite(const TimeStamp& aCompositeReqStart,
                               const TimeStamp& aCompositeReqEnd)
 {
   nsCOMPtr<nsIDocShell> docShellComPtr = do_GetInterface(WebNavigation());
   if (!docShellComPtr) {
@@ -2936,30 +2954,29 @@ TabChild::DidRequestComposite(const Time
   }
 }
 
 void
 TabChild::ClearCachedResources()
 {
   MOZ_ASSERT(mPuppetWidget);
   MOZ_ASSERT(mPuppetWidget->GetLayerManager());
-  MOZ_ASSERT(mPuppetWidget->GetLayerManager()->GetBackendType() ==
-               LayersBackend::LAYERS_CLIENT);
-
-  ClientLayerManager *manager = mPuppetWidget->GetLayerManager()->AsClientLayerManager();
-  manager->ClearCachedResources();
+  MOZ_ASSERT(mPuppetWidget->GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_CLIENT
+             || mPuppetWidget->GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_WR);
+
+  mPuppetWidget->GetLayerManager()->ClearCachedResources();
 }
 
 void
 TabChild::InvalidateLayers()
 {
   MOZ_ASSERT(mPuppetWidget);
   MOZ_ASSERT(mPuppetWidget->GetLayerManager());
-  MOZ_ASSERT(mPuppetWidget->GetLayerManager()->GetBackendType() ==
-               LayersBackend::LAYERS_CLIENT);
+  MOZ_ASSERT(mPuppetWidget->GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_CLIENT
+             || mPuppetWidget->GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_WR);
 
   RefPtr<LayerManager> lm = mPuppetWidget->GetLayerManager();
   FrameLayerBuilder::InvalidateAllLayers(lm);
 }
 
 void
 TabChild::ReinitRendering()
 {
@@ -3008,23 +3025,24 @@ TabChild::ReinitRendering()
   nsCOMPtr<nsIDocument> doc(GetDocument());
   doc->NotifyLayerManagerRecreated();
 }
 
 void
 TabChild::CompositorUpdated(const TextureFactoryIdentifier& aNewIdentifier,
                             uint64_t aDeviceResetSeqNo)
 {
+  MOZ_ASSERT(mPuppetWidget->GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_CLIENT
+             || mPuppetWidget->GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_WR);
+
   RefPtr<LayerManager> lm = mPuppetWidget->GetLayerManager();
-  ClientLayerManager* clm = lm->AsClientLayerManager();
-  MOZ_ASSERT(clm);
 
   mTextureFactoryIdentifier = aNewIdentifier;
-  clm->UpdateTextureFactoryIdentifier(aNewIdentifier, aDeviceResetSeqNo);
-  FrameLayerBuilder::InvalidateAllLayers(clm);
+  lm->UpdateTextureFactoryIdentifier(aNewIdentifier, aDeviceResetSeqNo);
+  FrameLayerBuilder::InvalidateAllLayers(lm);
 }
 
 NS_IMETHODIMP
 TabChild::OnShowTooltip(int32_t aXCoords, int32_t aYCoords, const char16_t *aTipText,
                         const char16_t *aTipDir)
 {
     nsString str(aTipText);
     nsString dir(aTipDir);
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -616,16 +616,17 @@ public:
 
   bool IPCOpen() const { return mIPCOpen; }
 
   bool ParentIsActive() const
   {
     return mParentIsActive;
   }
 
+  const mozilla::layers::CompositorOptions& GetCompositorOptions() const;
   bool AsyncPanZoomEnabled() const;
 
   virtual ScreenIntSize GetInnerSize() override;
 
   // Call RecvShow(nsIntSize(0, 0)) and block future calls to RecvShow().
   void DoFakeShow(const TextureFactoryIdentifier& aTextureFactoryIdentifier,
                   const uint64_t& aLayersId,
                   PRenderFrameChild* aRenderFrame,
--- a/dom/media/MP3Demuxer.cpp
+++ b/dom/media/MP3Demuxer.cpp
@@ -254,17 +254,19 @@ MP3TrackDemuxer::ScanUntil(const TimeUni
          aTime.ToMicroseconds(), AverageFrameLength(), mNumParsedFrames,
          mFrameIndex, mOffset);
 
   if (!aTime.ToMicroseconds()) {
     return FastSeek(aTime);
   }
 
   if (Duration(mFrameIndex) > aTime) {
-    FastSeek(aTime);
+    // We've seeked past the target time, rewind back a little to correct it.
+    const int64_t rewind = aTime.ToMicroseconds() / 100;
+    FastSeek(aTime - TimeUnit::FromMicroseconds(rewind));
   }
 
   if (Duration(mFrameIndex + 1) > aTime) {
     return SeekPosition();
   }
 
   MediaByteRange nextRange = FindNextFrame();
   while (SkipNextFrame(nextRange) && Duration(mFrameIndex + 1) < aTime) {
--- a/dom/media/test/crashtests/crashtests.list
+++ b/dom/media/test/crashtests/crashtests.list
@@ -5,17 +5,17 @@ load 466945-1.html
 load 468763-1.html
 load 474744-1.html
 asserts-if(stylo,8) HTTP load 481136-1.html # bug 1324671 # needs to be HTTP to recognize the ogg as an audio file?
 load 492286-1.xhtml
 load 493915-1.html
 load 495794-1.html
 load 576612-1.html
 load 752784-1.html
-load 789075-1.html
+skip-if(webrender) load 789075-1.html # bug 1322816 for webrender
 HTTP load 795892-1.html
 load 844563.html
 load 846612.html
 load 852838.html
 load 865537-1.html
 load 868504.html
 load 874869.html
 load 874915.html
--- a/dom/media/webaudio/moz.build
+++ b/dom/media/webaudio/moz.build
@@ -11,20 +11,16 @@ DIRS += ['blink']
 
 TEST_DIRS += ['gtest']
 
 MOCHITEST_MANIFESTS += [
     'test/blink/mochitest.ini',
     'test/mochitest.ini',
 ]
 
-BROWSER_CHROME_MANIFESTS += [
-    'test/browser.ini',
-]
-
 TEST_HARNESS_FILES.testing.mochitest.tests.dom.media.webaudio.test.blink += [
     'test/blink/audio-testing.js',
     'test/blink/convolution-testing.js',
     'test/blink/panner-model-testing.js',
 ]
 
 EXPORTS += [
     'AlignedTArray.h',
deleted file mode 100644
--- a/dom/media/webaudio/test/browser.ini
+++ /dev/null
@@ -1,1 +0,0 @@
-[browser_bug1181073.js]
\ No newline at end of file
deleted file mode 100644
--- a/dom/media/webaudio/test/browser_bug1181073.js
+++ /dev/null
@@ -1,40 +0,0 @@
-add_task(function*() {
-  // Make the min_background_timeout_value very high to avoid problems on slow machines
-  yield SpecialPowers.pushPrefEnv({
-    'set': [['dom.min_background_timeout_value', 3000]]
-  });
-
-  // Make a new tab, and put it in the background
-  yield BrowserTestUtils.withNewTab("about:blank", function*(browser) {
-    yield BrowserTestUtils.withNewTab("about:blank", function*() {
-      let time = yield ContentTask.spawn(browser, null, function () {
-        return new Promise(resolve => {
-          let start = content.performance.now();
-          let id = content.window.setInterval(function() {
-            let end = content.performance.now();
-            content.window.clearInterval(id);
-            resolve(end - start);
-          }, 0);
-        });
-      });
-
-      ok(time > 2000, "Interval is throttled with no webaudio (" + time + " ms)");
-
-      time = yield ContentTask.spawn(browser, null, function () {
-        return new Promise(resolve => {
-          // Create an audio context, and save it on the window so it doesn't get GCed
-          content.window._audioCtx = new content.window.AudioContext();
-
-          let start = content.performance.now();
-          let id = content.window.setInterval(function() {
-            let end = content.performance.now();
-            content.window.clearInterval(id);
-            resolve(end - start);
-          }, 0);
-        });
-      });
-
-      ok(time < 1000, "Interval is not throttled with an audio context present (" + time + " ms)");
-    });
-  });
-});
--- a/dom/plugins/test/reftest/reftest.list
+++ b/dom/plugins/test/reftest/reftest.list
@@ -1,26 +1,27 @@
 # basic sanity checking
 random-if(!haveTestPlugin) != plugin-sanity.html about:blank
 fails-if(!haveTestPlugin) == plugin-sanity.html div-sanity.html
-fails-if(!haveTestPlugin) fuzzy-if(skiaContent&&haveTestPlugin,1,160000) == plugin-alpha-zindex.html div-alpha-zindex.html
-fails-if(!haveTestPlugin) fuzzy-if(skiaContent&&haveTestPlugin,1,164000) == plugin-alpha-opacity.html div-alpha-opacity.html
+fails-if(!haveTestPlugin) fuzzy-if(skiaContent&&haveTestPlugin,1,160000) fails-if(webrender) == plugin-alpha-zindex.html div-alpha-zindex.html
+fails-if(!haveTestPlugin) fuzzy-if(skiaContent&&haveTestPlugin,1,164000) fails-if(webrender) == plugin-alpha-opacity.html div-alpha-opacity.html
 random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) == windowless-clipping-1.html windowless-clipping-1-ref.html # bug 631832
 # fuzzy because of anti-aliasing in dashed border
-fuzzy(16,256) random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) == border-padding-1.html border-padding-1-ref.html # bug 629430
+fuzzy(16,256) random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) fails-if(webrender) == border-padding-1.html border-padding-1-ref.html # bug 629430
 fuzzy(16,256) random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) == border-padding-2.html border-padding-2-ref.html # bug 629430
-fuzzy(16,256) random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) skip-if(!haveTestPlugin||Android) == border-padding-3.html border-padding-3-ref.html # bug 629430 # bug 773482
+skip-if(!webrender) pref(layers.advanced.border-layers,true) fuzzy(16,256) random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) == border-padding-2.html border-padding-2-ref.html
+fuzzy(16,256) random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) skip-if(!haveTestPlugin||Android) fails-if(webrender) == border-padding-3.html border-padding-3-ref.html # bug 629430 # bug 773482
 # The following two "pluginproblemui-direction" tests are unreliable on all platforms. They should be re-written or replaced.
 #random-if(cocoaWidget||d2d||/^Windows\x20NT\x205\.1/.test(http.oscpu)) fails-if(!haveTestPlugin&&!Android) == pluginproblemui-direction-1.html pluginproblemui-direction-1-ref.html # bug 567367
 #random-if(cocoaWidget) fails-if(!haveTestPlugin&&!Android) == pluginproblemui-direction-2.html pluginproblemui-direction-2-ref.html
-fails-if(!haveTestPlugin) fuzzy-if(skiaContent&&haveTestPlugin,1,160000) == plugin-canvas-alpha-zindex.html div-alpha-zindex.html
-fails-if(!haveTestPlugin) fuzzy-if(skiaContent&&haveTestPlugin,1,160000) == plugin-transform-alpha-zindex.html div-alpha-zindex.html
-random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) fuzzy-if(skiaContent&&haveTestPlugin,1,160000) == plugin-busy-alpha-zindex.html div-alpha-zindex.html
-random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) fuzzy-if(skiaContent&&haveTestPlugin,1,32400) == plugin-background.html plugin-background-ref.html
-random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) fuzzy-if(skiaContent&&haveTestPlugin,1,32400) == plugin-background-1-step.html plugin-background-ref.html
-random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) fuzzy-if(skiaContent&&haveTestPlugin,1,32400) == plugin-background-2-step.html plugin-background-ref.html
-random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) fuzzy-if(skiaContent&&haveTestPlugin,1,32400) == plugin-background-5-step.html plugin-background-ref.html
-random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) fuzzy-if(skiaContent&&haveTestPlugin,1,32400) == plugin-background-10-step.html plugin-background-ref.html
+fails-if(!haveTestPlugin) fuzzy-if(skiaContent&&haveTestPlugin,1,160000) fails-if(webrender) == plugin-canvas-alpha-zindex.html div-alpha-zindex.html
+fails-if(!haveTestPlugin) fuzzy-if(skiaContent&&haveTestPlugin,1,160000) fails-if(webrender) == plugin-transform-alpha-zindex.html div-alpha-zindex.html
+random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) fuzzy-if(skiaContent&&haveTestPlugin,1,160000) fails-if(webrender) == plugin-busy-alpha-zindex.html div-alpha-zindex.html
+random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) fuzzy-if(skiaContent&&haveTestPlugin,1,32400) fails-if(webrender) == plugin-background.html plugin-background-ref.html
+random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) fuzzy-if(skiaContent&&haveTestPlugin,1,32400) fails-if(webrender) == plugin-background-1-step.html plugin-background-ref.html
+random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) fuzzy-if(skiaContent&&haveTestPlugin,1,32400) fails-if(webrender) == plugin-background-2-step.html plugin-background-ref.html
+random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) fuzzy-if(skiaContent&&haveTestPlugin,1,32400) fails-if(webrender) == plugin-background-5-step.html plugin-background-ref.html
+random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) fails-if(!haveTestPlugin) fuzzy-if(skiaContent&&haveTestPlugin,1,32400) fails-if(webrender) == plugin-background-10-step.html plugin-background-ref.html
 random-if(!haveTestPlugin) == plugin-transform-1.html plugin-transform-1-ref.html
-fails-if(!haveTestPlugin) == plugin-transform-2.html plugin-transform-2-ref.html
+fails-if(!haveTestPlugin) fails-if(webrender) == plugin-transform-2.html plugin-transform-2-ref.html
 skip-if(!haveTestPlugin) == shrink-1.html shrink-1-ref.html
 skip-if(!haveTestPlugin) == update-1.html update-1-ref.html
 skip-if(!haveTestPlugin) == windowless-layers.html windowless-layers-ref.html
--- a/dom/security/test/hsts/head.js
+++ b/dom/security/test/hsts/head.js
@@ -421,21 +421,21 @@ function build_test_uri(base_uri, host, 
           "?host=" + escape(host) +
           "&id=" + escape(test_id) +
           "&type=" + escape(type) +
           "&timeout=" + escape(timeout)
     ;
 }
 
 // open a new tab, load the test, and wait for it to finish
-function execute_test(test, mimetype) {
+async function execute_test(test, mimetype) {
   var src = build_test_uri(TOP_URI, test_servers[test].host,
       test, test_settings[which_test].type,
       test_settings[which_test].timeout);
 
   let tab = openTab(src);
   test_servers[test]['tab'] = tab;
 
   let browser = gBrowser.getBrowserForTab(tab);
-  yield BrowserTestUtils.browserLoaded(browser);
+  await BrowserTestUtils.browserLoaded(browser);
 
-  yield BrowserTestUtils.removeTab(tab);
+  await BrowserTestUtils.removeTab(tab);
 }
--- a/dom/workers/ScriptLoader.cpp
+++ b/dom/workers/ScriptLoader.cpp
@@ -1242,17 +1242,18 @@ private:
       nsILoadGroup* loadGroup = mWorkerPrivate->GetLoadGroup();
       MOZ_ASSERT(loadGroup);
 
       mozilla::DebugOnly<bool> equal = false;
       MOZ_ASSERT(responsePrincipal && NS_SUCCEEDED(responsePrincipal->Equals(principal, &equal)));
       MOZ_ASSERT(equal);
 
       mWorkerPrivate->InitChannelInfo(aChannelInfo);
-      mWorkerPrivate->SetPrincipalOnMainThread(responsePrincipal, loadGroup);
+      rv = mWorkerPrivate->SetPrincipalOnMainThread(responsePrincipal, loadGroup);
+      MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
     }
 
     if (NS_SUCCEEDED(rv)) {
       DataReceived();
     }
 
     LoadingFinished(aIndex, rv);
   }
--- a/dom/workers/ServiceWorkerPrivate.cpp
+++ b/dom/workers/ServiceWorkerPrivate.cpp
@@ -1452,27 +1452,20 @@ public:
     mRequestCredentials = InternalRequest::MapChannelToRequestCredentials(channel);
 
     rv = httpChannel->VisitNonDefaultRequestHeaders(this);
     NS_ENSURE_SUCCESS(rv, rv);
 
     nsCOMPtr<nsIUploadChannel2> uploadChannel = do_QueryInterface(httpChannel);
     if (uploadChannel) {
       MOZ_ASSERT(!mUploadStream);
-      bool bodyHasHeaders = false;
-      rv = uploadChannel->GetUploadStreamHasHeaders(&bodyHasHeaders);
-      NS_ENSURE_SUCCESS(rv, rv);
       nsCOMPtr<nsIInputStream> uploadStream;
       rv = uploadChannel->CloneUploadStream(getter_AddRefs(uploadStream));
       NS_ENSURE_SUCCESS(rv, rv);
-      if (bodyHasHeaders) {
-        HandleBodyWithHeaders(uploadStream);
-      } else {
-        mUploadStream = uploadStream;
-      }
+      mUploadStream = uploadStream;
     }
 
     return NS_OK;
   }
 
   bool
   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
   {
@@ -1595,62 +1588,16 @@ private:
         runnable = new ResumeRequest(mInterceptedChannel);
       }
 
       MOZ_ALWAYS_SUCCEEDS(mWorkerPrivate->DispatchToMainThread(runnable.forget()));
     }
 
     return true;
   }
-
-  nsresult
-  HandleBodyWithHeaders(nsIInputStream* aUploadStream)
-  {
-    // We are dealing with an nsMIMEInputStream which uses string input streams
-    // under the hood, so all of the data is available synchronously.
-    bool nonBlocking = false;
-    nsresult rv = aUploadStream->IsNonBlocking(&nonBlocking);
-    NS_ENSURE_SUCCESS(rv, rv);
-    if (NS_WARN_IF(!nonBlocking)) {
-      return NS_ERROR_NOT_AVAILABLE;
-    }
-    nsAutoCString body;
-    rv = NS_ConsumeStream(aUploadStream, UINT32_MAX, body);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    // Extract the headers in the beginning of the buffer
-    nsAutoCString::const_iterator begin, end;
-    body.BeginReading(begin);
-    body.EndReading(end);
-    const nsAutoCString::const_iterator body_end = end;
-    nsAutoCString headerName, headerValue;
-    bool emptyHeader = false;
-    while (FetchUtil::ExtractHeader(begin, end, headerName,
-                                    headerValue, &emptyHeader) &&
-           !emptyHeader) {
-      mHeaderNames.AppendElement(headerName);
-      mHeaderValues.AppendElement(headerValue);
-      headerName.Truncate();
-      headerValue.Truncate();
-    }
-
-    // Replace the upload stream with one only containing the body text.
-    nsCOMPtr<nsIStringInputStream> strStream =
-      do_CreateInstance(NS_STRINGINPUTSTREAM_CONTRACTID, &rv);
-    NS_ENSURE_SUCCESS(rv, rv);
-    // Skip past the "\r\n" that separates the headers and the body.
-    ++begin;
-    ++begin;
-    body.Assign(Substring(begin, body_end));
-    rv = strStream->SetData(body.BeginReading(), body.Length());
-    NS_ENSURE_SUCCESS(rv, rv);
-    mUploadStream = strStream;
-
-    return NS_OK;
-  }
 };
 
 NS_IMPL_ISUPPORTS_INHERITED(FetchEventRunnable, WorkerRunnable, nsIHttpHeaderVisitor)
 
 } // anonymous namespace
 
 nsresult
 ServiceWorkerPrivate::SendFetchEvent(nsIInterceptedChannel* aChannel,
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -1830,53 +1830,56 @@ WorkerLoadInfo::StealFrom(WorkerLoadInfo
   mReportCSPViolations = aOther.mReportCSPViolations;
   mXHRParamsAllowed = aOther.mXHRParamsAllowed;
   mPrincipalIsSystem = aOther.mPrincipalIsSystem;
   mStorageAllowed = aOther.mStorageAllowed;
   mServiceWorkersTestingInWindow = aOther.mServiceWorkersTestingInWindow;
   mOriginAttributes = aOther.mOriginAttributes;
 }
 
-void
+nsresult
 WorkerLoadInfo::SetPrincipalOnMainThread(nsIPrincipal* aPrincipal,
                                          nsILoadGroup* aLoadGroup)
 {
   AssertIsOnMainThread();
   MOZ_ASSERT(NS_LoadGroupMatchesPrincipal(aLoadGroup, aPrincipal));
   MOZ_ASSERT(!mPrincipalInfo);
 
   mPrincipal = aPrincipal;
   mPrincipalIsSystem = nsContentUtils::IsSystemPrincipal(aPrincipal);
 
-  aPrincipal->GetCsp(getter_AddRefs(mCSP));
+  nsresult rv = aPrincipal->GetCsp(getter_AddRefs(mCSP));
+  NS_ENSURE_SUCCESS(rv, rv);
 
   if (mCSP) {
     mCSP->GetAllowsEval(&mReportCSPViolations, &mEvalAllowed);
     // Set ReferrerPolicy
     bool hasReferrerPolicy = false;
     uint32_t rp = mozilla::net::RP_Unset;
 
-    nsresult rv = mCSP->GetReferrerPolicy(&rp, &hasReferrerPolicy);
-    NS_ENSURE_SUCCESS_VOID(rv);
+    rv = mCSP->GetReferrerPolicy(&rp, &hasReferrerPolicy);
+    NS_ENSURE_SUCCESS(rv, rv);
 
     if (hasReferrerPolicy) {
       mReferrerPolicy = static_cast<net::ReferrerPolicy>(rp);
     }
   } else {
     mEvalAllowed = true;
     mReportCSPViolations = false;
   }
 
   mLoadGroup = aLoadGroup;
 
   mPrincipalInfo = new PrincipalInfo();
   mOriginAttributes = nsContentUtils::GetOriginAttributes(aLoadGroup);
 
-  MOZ_ALWAYS_SUCCEEDS(
-    PrincipalToPrincipalInfo(aPrincipal, mPrincipalInfo));
+  rv = PrincipalToPrincipalInfo(aPrincipal, mPrincipalInfo);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
 }
 
 nsresult
 WorkerLoadInfo::GetPrincipalAndLoadGroupFromChannel(nsIChannel* aChannel,
                                                     nsIPrincipal** aPrincipalOut,
                                                     nsILoadGroup** aLoadGroupOut)
 {
   AssertIsOnMainThread();
@@ -1948,18 +1951,17 @@ WorkerLoadInfo::SetPrincipalFromChannel(
 
   nsCOMPtr<nsIPrincipal> principal;
   nsCOMPtr<nsILoadGroup> loadGroup;
   nsresult rv = GetPrincipalAndLoadGroupFromChannel(aChannel,
                                                     getter_AddRefs(principal),
                                                     getter_AddRefs(loadGroup));
   NS_ENSURE_SUCCESS(rv, rv);
 
-  SetPrincipalOnMainThread(principal, loadGroup);
-  return NS_OK;
+  return SetPrincipalOnMainThread(principal, loadGroup);
 }
 
 #if defined(DEBUG) || !defined(RELEASE_OR_BETA)
 bool
 WorkerLoadInfo::FinalChannelPrincipalIsValid(nsIChannel* aChannel)
 {
   AssertIsOnMainThread();
 
@@ -3798,21 +3800,21 @@ WorkerPrivateParent<Derived>::SetBaseURI
   else {
     mLocationInfo.mHost.Assign(mLocationInfo.mHostname);
   }
 
   nsContentUtils::GetUTFOrigin(aBaseURI, mLocationInfo.mOrigin);
 }
 
 template <class Derived>
-void
+nsresult
 WorkerPrivateParent<Derived>::SetPrincipalOnMainThread(nsIPrincipal* aPrincipal,
                                                        nsILoadGroup* aLoadGroup)
 {
-  mLoadInfo.SetPrincipalOnMainThread(aPrincipal, aLoadGroup);
+  return mLoadInfo.SetPrincipalOnMainThread(aPrincipal, aLoadGroup);
 }
 
 template <class Derived>
 nsresult
 WorkerPrivateParent<Derived>::SetPrincipalFromChannel(nsIChannel* aChannel)
 {
   return mLoadInfo.SetPrincipalFromChannel(aChannel);
 }
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -602,17 +602,17 @@ public:
   // Principals are main-thread objects so the caller must ensure that all
   // access occurs on the main thread.
   nsIPrincipal*
   GetPrincipalDontAssertMainThread() const
   {
       return mLoadInfo.mPrincipal;
   }
 
-  void
+  nsresult
   SetPrincipalOnMainThread(nsIPrincipal* aPrincipal, nsILoadGroup* aLoadGroup);
 
   nsresult
   SetPrincipalFromChannel(nsIChannel* aChannel);
 
 #if defined(DEBUG) || !defined(RELEASE_OR_BETA)
   bool
   FinalChannelPrincipalIsValid(nsIChannel* aChannel);
--- a/dom/workers/Workers.h
+++ b/dom/workers/Workers.h
@@ -273,17 +273,17 @@ struct WorkerLoadInfo
   bool mServiceWorkersTestingInWindow;
   OriginAttributes mOriginAttributes;
 
   WorkerLoadInfo();
   ~WorkerLoadInfo();
 
   void StealFrom(WorkerLoadInfo& aOther);
 
-  void
+  nsresult
   SetPrincipalOnMainThread(nsIPrincipal* aPrincipal, nsILoadGroup* aLoadGroup);
 
   nsresult
   GetPrincipalAndLoadGroupFromChannel(nsIChannel* aChannel,
                                       nsIPrincipal** aPrincipalOut,
                                       nsILoadGroup** aLoadGroupOut);
 
   nsresult
--- a/dom/xhr/XMLHttpRequestMainThread.cpp
+++ b/dom/xhr/XMLHttpRequestMainThread.cpp
@@ -2293,16 +2293,17 @@ XMLHttpRequestMainThread::OnStopRequest(
   NS_ENSURE_TRUE(channel, NS_ERROR_UNEXPECTED);
 
   channel->SetNotificationCallbacks(nullptr);
   mNotificationCallbacks = nullptr;
   mChannelEventSink = nullptr;
   mProgressEventSink = nullptr;
 
   mFlagSyncLooping = false;
+  mRequestSentTime = 0;
 
   // update our charset and decoder to match mResponseXML,
   // before it is possibly nulled out
   MatchCharsetAndDecoderToResponseDocument();
 
   if (NS_FAILED(status)) {
     // This can happen if the server is unreachable. Other possible
     // reasons are that the user leaves the page or hits the ESC key.
--- a/extensions/spellcheck/hunspell/tests/unit/test_hunspell.js
+++ b/extensions/spellcheck/hunspell/tests/unit/test_hunspell.js
@@ -119,17 +119,17 @@ const tests = [
     ["opentaal-cpdpat", "iso-8859-1"],
     ["opentaal-cpdpat2", "iso-8859-1"],
     ["2999225", "iso-8859-1"],
     ["onlyincompound2", "iso-8859-1"],
     ["forceucase", "iso-8859-1"],
     ["warn", "iso-8859-1"]
 ];
 
-function do_get_file_by_line(file, charset) {
+function* do_get_file_by_line(file, charset) {
   dump("getting file by line for file " + file.path + "\n");
   dump("using charset " + charset +"\n");
   let fis = Cc["@mozilla.org/network/file-input-stream;1"].
               createInstance(Ci.nsIFileInputStream);
   fis.init(file, 0x1 /* READONLY */,
            0o444, Ci.nsIFileInputStream.CLOSE_ON_EOF);
 
   let lis = Cc["@mozilla.org/intl/converter-input-stream;1"].
@@ -158,17 +158,17 @@ function do_run_test(checker, name, char
   dump("Need some expected output\n")
   do_check_true(good.exists() || bad.exists() || sug.exists());
 
   dump("Setting dictionary to " + name + "\n");
   checker.dictionary = name;
 
   if (good.exists()) {
     var good_counter = 0;
-    for (val in do_get_file_by_line(good, charset)) {
+    for (val of do_get_file_by_line(good, charset)) {
       let todo = false;
       good_counter++;
       if (todo_good && todo_good[good_counter]) {
         todo = true;
         dump("TODO\n");
       }
 
       dump("Expect word " + val + " is spelled correctly\n");
@@ -177,17 +177,17 @@ function do_run_test(checker, name, char
       } else {
         do_check_true(checker.check(val));
       }
     }
   }
 
   if (bad.exists()) {
     var bad_counter = 0;
-    for (val in do_get_file_by_line(bad, charset)) {
+    for (val of do_get_file_by_line(bad, charset)) {
       let todo = false;
       bad_counter++;
       if (todo_bad && todo_bad[bad_counter]) {
         todo = true;
         dump("TODO\n");
       }
 
       dump("Expect word " + val + " is spelled wrong\n");
--- a/gfx/2d/2D.h
+++ b/gfx/2d/2D.h
@@ -746,16 +746,18 @@ public:
   virtual void GetGlyphDesignMetrics(const uint16_t* aGlyphIndices, uint32_t aNumGlyphs, GlyphMetrics* aGlyphMetrics) = 0;
 
   virtual bool GetFontFileData(FontFileDataOutput, void *) { return false; }
 
   virtual bool GetFontInstanceData(FontInstanceDataOutput, void *) { return false; }
 
   virtual bool GetFontDescriptor(FontDescriptorOutput, void *) { return false; }
 
+  virtual bool CanSerialize() { return false; }
+
   void AddUserData(UserDataKey *key, void *userData, void (*destroy)(void*)) {
     mUserData.Add(key, userData, destroy);
   }
   void *GetUserData(UserDataKey *key) {
     return mUserData.Get(key);
   }
 
 protected:
--- a/gfx/2d/FilterNodeSoftware.cpp
+++ b/gfx/2d/FilterNodeSoftware.cpp
@@ -3325,17 +3325,17 @@ FilterNodeLightingSoftware<LightType, Li
 {
   if (mLight.SetAttribute(aIndex, aValue) ||
       mLighting.SetAttribute(aIndex, aValue)) {
     Invalidate();
     return;
   }
   switch (aIndex) {
     case ATT_LIGHTING_SURFACE_SCALE:
-      mSurfaceScale = aValue;
+      mSurfaceScale = std::fpclassify(aValue) == FP_SUBNORMAL ? 0.0 : aValue;
       break;
     default:
       MOZ_CRASH("GFX: FilterNodeLightingSoftware::SetAttribute float");
   }
   Invalidate();
 }
 
 template<typename LightType, typename LightingType>
--- a/gfx/2d/ScaledFontDWrite.cpp
+++ b/gfx/2d/ScaledFontDWrite.cpp
@@ -223,16 +223,20 @@ ScaledFontDWrite::GetFontFileData(FontFi
   UINT32 fileCount = 0;
   mFontFace->GetFiles(&fileCount, nullptr);
 
   if (fileCount > 1) {
     MOZ_ASSERT(false);
     return false;
   }
 
+  if (!aDataCallback) {
+    return true;
+  }
+
   RefPtr<IDWriteFontFile> file;
   mFontFace->GetFiles(&fileCount, getter_AddRefs(file));
 
   const void *referenceKey;
   UINT32 refKeySize;
   // XXX - This can currently crash for webfonts, as when we get the reference
   // key out of the file, that can be an invalid reference key for the loader
   // we use it with. The fix to this is not obvious but it will probably 
--- a/gfx/2d/ScaledFontDWrite.h
+++ b/gfx/2d/ScaledFontDWrite.h
@@ -35,16 +35,18 @@ public:
   virtual void CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, const Matrix *aTransformHint);
 
   void CopyGlyphsToSink(const GlyphBuffer &aBuffer, ID2D1GeometrySink *aSink);
 
   virtual void GetGlyphDesignMetrics(const uint16_t* aGlyphIndices, uint32_t aNumGlyphs, GlyphMetrics* aGlyphMetrics);
 
   virtual bool GetFontFileData(FontFileDataOutput aDataCallback, void *aBaton);
 
+  virtual bool CanSerialize() override { return true; }
+
   virtual AntialiasMode GetDefaultAAMode() override;
 
   bool UseEmbeddedBitmaps() { return mUseEmbeddedBitmap; }
   bool ForceGDIMode() { return mForceGDIMode; }
 
 #ifdef USE_SKIA
   virtual SkTypeface* GetSkTypeface();
   SkFontStyle mStyle;
--- a/gfx/config/gfxFeature.h
+++ b/gfx/config/gfxFeature.h
@@ -20,16 +20,17 @@ namespace gfx {
   _(HW_COMPOSITING,               Feature,      "Compositing")                    \
   _(D3D11_COMPOSITING,            Feature,      "Direct3D11 Compositing")         \
   _(D3D9_COMPOSITING,             Feature,      "Direct3D9 Compositing")          \
   _(OPENGL_COMPOSITING,           Feature,      "OpenGL Compositing")             \
   _(DIRECT2D,                     Feature,      "Direct2D")                       \
   _(D3D11_HW_ANGLE,               Feature,      "Direct3D11 hardware ANGLE")      \
   _(DIRECT_DRAW,                  Feature,      "DirectDraw")                     \
   _(GPU_PROCESS,                  Feature,      "GPU Process")                    \
+  _(WEBRENDER,                    Feature,      "WebRender")                      \
   /* Add new entries above this comment */
 
 enum class Feature : uint32_t {
 #define MAKE_ENUM(name, type, desc) name,
   GFX_FEATURE_MAP(MAKE_ENUM)
 #undef MAKE_ENUM
   NumValues
 };
--- a/gfx/config/gfxVars.h
+++ b/gfx/config/gfxVars.h
@@ -28,16 +28,17 @@ class gfxVarReceiver;
   _(TileSize,                   IntSize,          IntSize(-1, -1))      \
   _(UseXRender,                 bool,             false)                \
   _(OffscreenFormat,            gfxImageFormat,   mozilla::gfx::SurfaceFormat::X8R8G8B8_UINT32) \
   _(RequiresAcceleratedGLContextForCompositorOGL, bool, false)          \
   _(CanUseHardwareVideoDecoding, bool,            false)                \
   _(PDMWMFDisableD3D11Dlls,     nsCString,        nsCString())          \
   _(PDMWMFDisableD3D9Dlls,      nsCString,        nsCString())          \
   _(DXInterop2Blocked,          bool,             false)                \
+  _(UseWebRender,               bool,             false)                \
 
   /* Add new entries above this line. */
 
 // Some graphics settings are computed on the UI process and must be
 // communicated to content and GPU processes. gfxVars helps facilitate
 // this. Its function is similar to gfxPrefs, except rather than hold
 // user preferences, it holds dynamically computed values.
 //
new file mode 100644
--- /dev/null
+++ b/gfx/doc/README.displayitem
@@ -0,0 +1,27 @@
+How to add a new WebRender display item from a Gecko Display item, the general flow is to:
+
+(1) Force layout to create a new active layer for the gecko display item.
+(2) Plumb the data needed for the display item from content through WebRenderBridgeParent on the parent side.
+(3) From WebRenderBridgeParent, call out into bindings.rs and implement the appropriate WR calls.
+
+More detailed steps are:
+
+1) Force layout to create an active layer for the gecko display item.
+See http://searchfox.org/mozilla-central/rev/0f254a30d684796bcc8b6e2a102a0095d25842bb/layout/generic/nsTextFrame.cpp#4983
+as an example for text layers. Ping Matt Woodrow or Markus Stange for help.
+
+The Active layer part comes from nsDisplayText::GetLayerState
+
+2) Create the new display item layer:
+
+See text layer:
+http://searchfox.org/mozilla-central/rev/0f254a30d684796bcc8b6e2a102a0095d25842bb/gfx/layers/Layers.h#2403
+
+The layer should have all the information to display the item.
+
+3) Create the WebRender equivalent layer. In YourLayerType::RenderLayer, serialize the data needed for the layer type.
+4) If you need to add a custom IPC serialization mechanism, do it in WebRenderMessageUtils.h
+5) Create a WebRender command to process the new layer type in WebRenderMessages.ipdlh. These are the struct OpDPPushYourLayerTypeHere
+6) Add a new function in WebRender.h that will call out into webrender to render your display item.
+7) In WebRenderBridgeParent::ProcessWebRenderCommands, call out to the new function in (6).
+8) Fill out the function in (6) in bindings.rs to make webrender do the right thing. Generally, it's just push a display item.
new file mode 100644
--- /dev/null
+++ b/gfx/doc/README.webrender
@@ -0,0 +1,82 @@
+Step 1: Install Rust if you don't have it already
+    If you are doing gecko builds already, you should already have Rust as it is a build requirement.
+    If not, you can install it using |mach bootstrap| (recommended) or from https://www.rust-lang.org/
+        Note: If installing manually, use the stable 64-bit release - on Windows make sure to use the MSVC ABI installer.
+    Ensure that rustc and cargo are in your $PATH (adding $HOME/.cargo/bin/ should be sufficient)
+
+Step 2: Set up mozconfig
+    Add the following line to your mozconfig:
+        ac_add_options --enable-webrender
+    The first time you do a build with this changes, you should also run |mach clobber|
+
+Step 3:
+    Build using |mach build|
+
+
+When making changes:
+    - Make the changes you want.
+    - Run |mach build| or |mach build binaries| as desired.
+
+
+For a debug webrender build:
+    Use a debug mozconfig (ac_add_options --enable-debug)
+    You can also use an opt build but make webrender less optimized by putting opt-level=0 in the [profile.release] section of your toolkit/library/rust/Cargo.toml file
+    See also https://groups.google.com/forum/#!topic/mozilla.dev.servo/MbeMcqqO1fs
+
+
+What if you have to pull in an update to webrender itself?
+
+1) Update your graphics branch checkout to the latest code on the
+   graphics branch
+2) Check out and update the webrender repo to the version you want
+3) Copy over the webrender and webrender_traits folders into gfx/. The best way
+   to do this is to simply delete the gfx/webrender and gfx/webrender_traits
+   folders and use |cp -R| to copy them in again from the webrender repo. Update
+   the "latest commit" information at the bottom of this file with the version.
+4) If you need to modify webrender_bindings/Cargo.toml to include or remove
+   features, do so now.
+4) Commit your changes to the graphics branch locally
+5) Run |mach vendor rust| to update the rust dependencies in third_party/rust
+6) Commit the vendored changes locally
+7) Build and test. You may need to make changes in bindings.rs or on
+   the C++ side depending on what changed in webrender. This can
+   potentially be quite tricky if you don't fully understand the API
+   changes on the webrender side. In this step, try to not use your new
+   features yet, just get the build working with the minimal changes.
+8) Commit the changes locally from step 7, and push everything to the
+   graphics branch.
+9) Now you have an update webrender with the new features you wanted,
+   so you can write gecko code against them.
+
+Yes, this is somewhat painful. It used to be worse. :)
+
+Note that when webrender is built as part of gecko, it may end up using slightly
+different versions of its dependencies than when it is built standalone from the
+webrender repo. The reason is that the Cargo.lock files in m-c and in the WR
+repo may reference different versions of the dependencies. Both builds will be
+compatible in terms of semantic versioning, but may produce different results -
+for example the standalone webrender might use euclid 0.10.4 while the
+one in gecko uses euclid 0.10.3. Although both choices are "valid" per
+the semantic versioning rules in webrender's Cargo.toml, the 0.2.3 may provide
+a bugfix that is needed for correct behaviour in webrender. If this is the case,
+the technically "correct" fix is to change the upstream webrender Cargo.toml
+file to require the correct version. Alternnatively, you can update the
+Cargo.lock files in m-c to pull in the new version. The way to do this is as
+follows:
+- Go to toolkit/library/rust and run |cargo update -p <package> --precise <version>|.
+  Repeat this for as many libraries as you need to update. Run the same commands
+  in toolkit/library/gtest/rust and js/src (ignore any errors about unmatched
+  packages). Commit all the changes locally.
+- Run |mach vendor rust|, which will update the corresponding libraries in
+  third_party/rust to the versions you specified.
+The reason we don't do this by default is to work around bug 1336528. Specifically,
+there is another crate in m-c called mozjs_sys which is built separately but uses
+the same folder to store its rust dependencies. If one of the libraries that is
+required by both mozjs_sys and webrender is updated without updating the other
+project's Cargo.lock file, that results in build bustage.
+This means that any time you do this sort of manual update of packages, you need
+to make sure that mozjs_sys also has its Cargo.lock file updated if needed, hence
+the need to run the cargo update command in js/src as well. Hopefully this will
+be resolved soon.
+
+Latest Commit: bcf3c371086894f5e1d098ee60f0592abf01f6b3
--- a/gfx/gl/GLContextGLX.h
+++ b/gfx/gl/GLContextGLX.h
@@ -29,17 +29,18 @@ public:
                     bool deleteDrawable,
                     gfxXlibSurface* pixmap = nullptr,
                     ContextProfile profile = ContextProfile::OpenGLCompatibility);
 
     // Finds a GLXFBConfig compatible with the provided window.
     static bool
     FindFBConfigForWindow(Display* display, int screen, Window window,
                           ScopedXFree<GLXFBConfig>* const out_scopedConfigArr,
-                          GLXFBConfig* const out_config, int* const out_visid);
+                          GLXFBConfig* const out_config, int* const out_visid,
+                          bool aWebRender);
 
     ~GLContextGLX();
 
     virtual GLContextType GetContextType() const override { return GLContextType::GLX; }
 
     static GLContextGLX* Cast(GLContext* gl) {
         MOZ_ASSERT(gl->GetContextType() == GLContextType::GLX);
         return static_cast<GLContextGLX*>(gl);
--- a/gfx/gl/GLContextProviderCGL.mm
+++ b/gfx/gl/GLContextProviderCGL.mm
@@ -8,16 +8,17 @@
 #include "nsDebug.h"
 #include "nsIWidget.h"
 #include <OpenGL/gl.h>
 #include "gfxFailure.h"
 #include "gfxPrefs.h"
 #include "prenv.h"
 #include "GeckoProfiler.h"
 #include "mozilla/gfx/MacIOSurface.h"
+#include "mozilla/layers/CompositorOptions.h"
 #include "mozilla/widget/CompositorWidget.h"
 
 #include <OpenGL/OpenGL.h>
 
 // When running inside a VM, creating an accelerated OpenGL context usually
 // fails. Uncomment this line to emulate that behavior.
 // #define EMULATE_VM
 
@@ -197,16 +198,26 @@ static const NSOpenGLPixelFormatAttribut
 
 static const NSOpenGLPixelFormatAttribute kAttribs_doubleBuffered_accel[] = {
     NSOpenGLPFAAccelerated,
     NSOpenGLPFAAllowOfflineRenderers,
     NSOpenGLPFADoubleBuffer,
     0
 };
 
+static const NSOpenGLPixelFormatAttribute kAttribs_doubleBuffered_accel_webrender[] = {
+    NSOpenGLPFAAccelerated,
+    NSOpenGLPFAAllowOfflineRenderers,
+    NSOpenGLPFADoubleBuffer,
+    NSOpenGLPFAOpenGLProfile,
+    NSOpenGLProfileVersion3_2Core,
+    NSOpenGLPFADepthSize, 24,
+    0
+};
+
 static const NSOpenGLPixelFormatAttribute kAttribs_offscreen[] = {
     0
 };
 
 static const NSOpenGLPixelFormatAttribute kAttribs_offscreen_allow_offline[] = {
     NSOpenGLPFAAllowOfflineRenderers,
     0
 };
@@ -238,35 +249,43 @@ CreateWithFormat(const NSOpenGLPixelForm
     [format release];
 
     return context;
 }
 
 already_AddRefed<GLContext>
 GLContextProviderCGL::CreateForCompositorWidget(CompositorWidget* aCompositorWidget, bool aForceAccelerated)
 {
-    return CreateForWindow(aCompositorWidget->RealWidget(), aForceAccelerated);
+    return CreateForWindow(aCompositorWidget->RealWidget(),
+                           aCompositorWidget->GetCompositorOptions().UseWebRender(),
+                           aForceAccelerated);
 }
 
 already_AddRefed<GLContext>
-GLContextProviderCGL::CreateForWindow(nsIWidget* aWidget, bool aForceAccelerated)
+GLContextProviderCGL::CreateForWindow(nsIWidget* aWidget,
+                                      bool aWebRender,
+                                      bool aForceAccelerated)
 {
     if (!sCGLLibrary.EnsureInitialized()) {
         return nullptr;
     }
 
 #ifdef EMULATE_VM
     if (aForceAccelerated) {
         return nullptr;
     }
 #endif
 
     const NSOpenGLPixelFormatAttribute* attribs;
     if (sCGLLibrary.UseDoubleBufferedWindows()) {
-        attribs = aForceAccelerated ? kAttribs_doubleBuffered_accel : kAttribs_doubleBuffered;
+        if (aWebRender) {
+            attribs = aForceAccelerated ? kAttribs_doubleBuffered_accel_webrender : kAttribs_doubleBuffered;
+        } else {
+            attribs = aForceAccelerated ? kAttribs_doubleBuffered_accel : kAttribs_doubleBuffered;
+        }
     } else {
         attribs = aForceAccelerated ? kAttribs_singleBuffered_accel : kAttribs_singleBuffered;
     }
     NSOpenGLContext* context = CreateWithFormat(attribs);
     if (!context) {
         return nullptr;
     }
 
--- a/gfx/gl/GLContextProviderEAGL.mm
+++ b/gfx/gl/GLContextProviderEAGL.mm
@@ -6,16 +6,17 @@
 #include "GLContextProvider.h"
 #include "GLContextEAGL.h"
 #include "nsDebug.h"
 #include "nsIWidget.h"
 #include "gfxPrefs.h"
 #include "gfxFailure.h"
 #include "prenv.h"
 #include "mozilla/Preferences.h"
+#include "mozilla/layers/CompositorOptions.h"
 #include "mozilla/widget/CompositorWidget.h"
 #include "GeckoProfiler.h"
 
 #import <UIKit/UIKit.h>
 
 namespace mozilla {
 namespace gl {
 
@@ -210,21 +211,25 @@ CreateEAGLContext(CreateContextFlags fla
     }
 
     return glContext.forget();
 }
 
 already_AddRefed<GLContext>
 GLContextProviderEAGL::CreateForCompositorWidget(CompositorWidget* aCompositorWidget, bool aForceAccelerated)
 {
-    return CreateForWindow(aCompositorWidget->RealWidget(), aForceAccelerated);
+    return CreateForWindow(aCompositorWidget->RealWidget(),
+                           aCompositorWidget->GetCompositorOptions().UseWebRender(),
+                           aForceAccelerated);
 }
 
 already_AddRefed<GLContext>
-GLContextProviderEAGL::CreateForWindow(nsIWidget* aWidget, bool aForceAccelerated)
+GLContextProviderEAGL::CreateForWindow(nsIWidget* aWidget,
+                                       bool aWebRender,
+                                       bool aForceAccelerated)
 {
     RefPtr<GLContext> glContext = CreateEAGLContext(CreateContextFlags::NONE, false,
                                                     GetGlobalContextEAGL());
     if (!glContext) {
         return nullptr;
     }
 
     if (!GLContextEAGL::Cast(glContext)->AttachToWindow(aWidget)) {
--- a/gfx/gl/GLContextProviderEGL.cpp
+++ b/gfx/gl/GLContextProviderEGL.cpp
@@ -46,16 +46,17 @@
 #include "gfxPlatform.h"
 #include "gfxUtils.h"
 #include "GLBlitHelper.h"
 #include "GLContextEGL.h"
 #include "GLContextProvider.h"
 #include "GLLibraryEGL.h"
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Preferences.h"
+#include "mozilla/layers/CompositorOptions.h"
 #include "mozilla/widget/CompositorWidget.h"
 #include "nsDebug.h"
 #include "nsIWidget.h"
 #include "nsThreadUtils.h"
 #include "ScopedGLHelpers.h"
 #include "TextureImageEGL.h"
 
 using namespace mozilla::gfx;
@@ -144,24 +145,26 @@ CreateSurfaceFromNativeWindow(EGLNativeW
 
 /* GLContextEGLFactory class was added as a friend of GLContextEGL
  * so that it could access  GLContextEGL::CreateGLContext. This was
  * done so that a new function would not need to be added to the shared
  * GLContextProvider interface.
  */
 class GLContextEGLFactory {
 public:
-    static already_AddRefed<GLContext> Create(EGLNativeWindowType aWindow);
+    static already_AddRefed<GLContext> Create(EGLNativeWindowType aWindow,
+                                              bool aWebRender);
 private:
     GLContextEGLFactory(){}
     ~GLContextEGLFactory(){}
 };
 
 already_AddRefed<GLContext>
-GLContextEGLFactory::Create(EGLNativeWindowType aWindow)
+GLContextEGLFactory::Create(EGLNativeWindowType aWindow,
+                            bool aWebRender)
 {
     MOZ_ASSERT(aWindow);
     nsCString discardFailureId;
     if (!sEGLLibrary.EnsureInitialized(false, &discardFailureId)) {
         MOZ_CRASH("GFX: Failed to load EGL library 3!\n");
         return nullptr;
     }
 
@@ -175,18 +178,22 @@ GLContextEGLFactory::Create(EGLNativeWin
 
     EGLSurface surface = mozilla::gl::CreateSurfaceFromNativeWindow(aWindow, config);
 
     if (!surface) {
         MOZ_CRASH("GFX: Failed to create EGLSurface!\n");
         return nullptr;
     }
 
+    CreateContextFlags flags = CreateContextFlags::NONE;
+    if (aWebRender) {
+        flags |= CreateContextFlags::PREFER_ES3;
+    }
     SurfaceCaps caps = SurfaceCaps::Any();
-    RefPtr<GLContextEGL> gl = GLContextEGL::CreateGLContext(CreateContextFlags::NONE,
+    RefPtr<GLContextEGL> gl = GLContextEGL::CreateGLContext(flags,
                                                             caps, nullptr, false, config,
                                                             surface, &discardFailureId);
     if (!gl) {
         MOZ_CRASH("GFX: Failed to create EGLContext!\n");
         mozilla::gl::DestroySurface(surface);
         return nullptr;
     }
 
@@ -706,24 +713,28 @@ GLContextProviderEGL::CreateWrappingExis
 
     return gl.forget();
 }
 
 already_AddRefed<GLContext>
 GLContextProviderEGL::CreateForCompositorWidget(CompositorWidget* aCompositorWidget, bool aForceAccelerated)
 {
     MOZ_ASSERT(aCompositorWidget);
-    return GLContextEGLFactory::Create(GET_NATIVE_WINDOW_FROM_COMPOSITOR_WIDGET(aCompositorWidget));
+    return GLContextEGLFactory::Create(GET_NATIVE_WINDOW_FROM_COMPOSITOR_WIDGET(aCompositorWidget),
+                                       aCompositorWidget->GetCompositorOptions().UseWebRender());
 }
 
 already_AddRefed<GLContext>
-GLContextProviderEGL::CreateForWindow(nsIWidget* aWidget, bool aForceAccelerated)
+GLContextProviderEGL::CreateForWindow(nsIWidget* aWidget,
+                                      bool aWebRender,
+                                      bool aForceAccelerated)
 {
     MOZ_ASSERT(aWidget);
-    return GLContextEGLFactory::Create(GET_NATIVE_WINDOW_FROM_REAL_WIDGET(aWidget));
+    return GLContextEGLFactory::Create(GET_NATIVE_WINDOW_FROM_REAL_WIDGET(aWidget),
+                                       aWebRender);
 }
 
 #if defined(ANDROID)
 EGLSurface
 GLContextProviderEGL::CreateEGLSurface(void* aWindow)
 {
     // NOTE: aWindow is an ANativeWindow
     nsCString discardFailureId;
--- a/gfx/gl/GLContextProviderGLX.cpp
+++ b/gfx/gl/GLContextProviderGLX.cpp
@@ -10,16 +10,17 @@
 #endif
 
 #include <X11/Xlib.h>
 #include <X11/Xutil.h>
 #include "X11UndefineNone.h"
 
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/StaticPtr.h"
+#include "mozilla/layers/CompositorOptions.h"
 #include "mozilla/widget/CompositorWidget.h"
 #include "mozilla/widget/X11CompositorWidget.h"
 #include "mozilla/Unused.h"
 
 #include "prenv.h"
 #include "GLContextProvider.h"
 #include "GLLibraryLoader.h"
 #include "nsDebug.h"
@@ -1115,17 +1116,19 @@ GLContextProviderGLX::CreateWrappingExis
 
         return glContext.forget();
     }
 
     return nullptr;
 }
 
 already_AddRefed<GLContext>
-CreateForWidget(Display* aXDisplay, Window aXWindow, bool aForceAccelerated)
+CreateForWidget(Display* aXDisplay, Window aXWindow,
+                bool aWebRender,
+                bool aForceAccelerated)
 {
     if (!sGLXLibrary.EnsureInitialized()) {
         return nullptr;
     }
 
     // Currently, we take whatever Visual the window already has, and
     // try to create an fbconfig for that visual.  This isn't
     // necessarily what we want in the long run; an fbconfig may not
@@ -1139,49 +1142,63 @@ CreateForWidget(Display* aXDisplay, Wind
     }
 
     int xscreen = DefaultScreen(aXDisplay);
 
     ScopedXFree<GLXFBConfig> cfgs;
     GLXFBConfig config;
     int visid;
     if (!GLContextGLX::FindFBConfigForWindow(aXDisplay, xscreen, aXWindow, &cfgs,
-                                             &config, &visid))
+                                             &config, &visid, aWebRender))
     {
         return nullptr;
     }
 
     SurfaceCaps caps = SurfaceCaps::Any();
     GLContextGLX* shareContext = GetGlobalContextGLX();
-    RefPtr<GLContextGLX> gl = GLContextGLX::CreateGLContext(CreateContextFlags::NONE,
-                                                            caps, shareContext, false,
-                                                            aXDisplay, aXWindow, config,
-                                                            false);
+    RefPtr<GLContextGLX> gl;
+    if (aWebRender) {
+      gl = GLContextGLX::CreateGLContext(CreateContextFlags::NONE,
+                                         caps, shareContext, false,
+                                         aXDisplay, aXWindow, config,
+                                         //TODO: we might want to pass an additional bool to select GL core/compat
+                                         false, nullptr, ContextProfile::OpenGLCore); //WR: required GL 3.2+
+    } else {
+      gl = GLContextGLX::CreateGLContext(CreateContextFlags::NONE,
+                                         caps, shareContext, false,
+                                         aXDisplay, aXWindow, config,
+                                         false);
+    }
+
     return gl.forget();
 }
 
 already_AddRefed<GLContext>
 GLContextProviderGLX::CreateForCompositorWidget(CompositorWidget* aCompositorWidget, bool aForceAccelerated)
 {
     X11CompositorWidget* compWidget = aCompositorWidget->AsX11();
     MOZ_ASSERT(compWidget);
 
     return CreateForWidget(compWidget->XDisplay(),
                            compWidget->XWindow(),
+                           compWidget->GetCompositorOptions().UseWebRender(),
                            aForceAccelerated);
 }
 
 already_AddRefed<GLContext>
-GLContextProviderGLX::CreateForWindow(nsIWidget* aWidget, bool aForceAccelerated)
+GLContextProviderGLX::CreateForWindow(nsIWidget* aWidget,
+                                      bool aWebRender,
+                                      bool aForceAccelerated)
 {
     Display* display = (Display*)aWidget->GetNativeData(NS_NATIVE_COMPOSITOR_DISPLAY);
     Window window = GET_NATIVE_WINDOW(aWidget);
 
     return CreateForWidget(display,
                            window,
+                           aWebRender,
                            aForceAccelerated);
 }
 
 static bool
 ChooseConfig(GLXLibrary* glx, Display* display, int screen, const SurfaceCaps& minCaps,
              ScopedXFree<GLXFBConfig>* const out_scopedConfigArr,
              GLXFBConfig* const out_config, int* const out_visid)
 {
@@ -1235,34 +1252,61 @@ ChooseConfig(GLXLibrary* glx, Display* d
     }
 
     return false;
 }
 
 bool
 GLContextGLX::FindFBConfigForWindow(Display* display, int screen, Window window,
                                     ScopedXFree<GLXFBConfig>* const out_scopedConfigArr,
-                                    GLXFBConfig* const out_config, int* const out_visid)
+                                    GLXFBConfig* const out_config, int* const out_visid,
+                                    bool aWebRender)
 {
     ScopedXFree<GLXFBConfig>& cfgs = *out_scopedConfigArr;
     int numConfigs;
     if (sGLXLibrary.IsATI() ||
         !sGLXLibrary.GLXVersionCheck(1, 3)) {
         const int attribs[] = {
             LOCAL_GLX_DOUBLEBUFFER, False,
             0
         };
-        cfgs = sGLXLibrary.xChooseFBConfig(display,
+        const int webrenderAttribs[] = {
+            LOCAL_GLX_DOUBLEBUFFER, False,
+            LOCAL_GLX_DEPTH_SIZE, 24,
+            0
+        };
+
+        if (aWebRender) {
+          cfgs = sGLXLibrary.xChooseFBConfig(display,
+                                             screen,
+                                             webrenderAttribs,
+                                             &numConfigs);
+        } else {
+          cfgs = sGLXLibrary.xChooseFBConfig(display,
+                                             screen,
+                                             attribs,
+                                             &numConfigs);
+        }
+    } else {
+        const int webrenderAttribs[] = {
+            LOCAL_GLX_DEPTH_SIZE, 24,
+            LOCAL_GLX_DOUBLEBUFFER, True,
+            0
+        };
+
+        if (aWebRender) {
+          cfgs = sGLXLibrary.xChooseFBConfig(display,
+                                             screen,
+                                             webrenderAttribs,
+                                             &numConfigs);
+        } else {
+          cfgs = sGLXLibrary.xGetFBConfigs(display,
                                            screen,
-                                           attribs,
                                            &numConfigs);
-    } else {
-        cfgs = sGLXLibrary.xGetFBConfigs(display,
-                                         screen,
-                                         &numConfigs);
+        }
     }
 
     if (!cfgs) {
         NS_WARNING("[GLX] glXGetFBConfigs() failed");
         return false;
     }
     NS_ASSERTION(numConfigs > 0, "No FBConfigs found!");
 
@@ -1274,37 +1318,57 @@ GLContextGLX::FindFBConfigForWindow(Disp
         NS_WARNING("[GLX] XGetWindowAttributes() failed");
         return false;
     }
     const VisualID windowVisualID = XVisualIDFromVisual(windowAttrs.visual);
 #ifdef DEBUG
     printf("[GLX] window %lx has VisualID 0x%lx\n", window, windowVisualID);
 #endif
 
-    for (int i = 0; i < numConfigs; i++) {
-        int visid = X11None;
-        sGLXLibrary.xGetFBConfigAttrib(display, cfgs[i], LOCAL_GLX_VISUAL_ID, &visid);
-        if (!visid) {
-            continue;
-        }
-        if (sGLXLibrary.IsATI()) {
+    if (aWebRender) {
+        for (int i = 0; i < numConfigs; i++) {
+            int visid = X11None;
+            sGLXLibrary.xGetFBConfigAttrib(display, cfgs[i], LOCAL_GLX_VISUAL_ID, &visid);
+            if (!visid) {
+                continue;
+            }
+
             int depth;
             Visual* visual;
             FindVisualAndDepth(display, visid, &visual, &depth);
             if (depth == windowAttrs.depth &&
                 AreCompatibleVisuals(windowAttrs.visual, visual)) {
                 *out_config = cfgs[i];
                 *out_visid = visid;
                 return true;
             }
-        } else {
-            if (windowVisualID == static_cast<VisualID>(visid)) {
-                *out_config = cfgs[i];
-                *out_visid = visid;
-                return true;
+        }
+    } else {
+        for (int i = 0; i < numConfigs; i++) {
+            int visid = X11None;
+            sGLXLibrary.xGetFBConfigAttrib(display, cfgs[i], LOCAL_GLX_VISUAL_ID, &visid);
+            if (!visid) {
+                continue;
+            }
+            if (sGLXLibrary.IsATI()) {
+                int depth;
+                Visual* visual;
+                FindVisualAndDepth(display, visid, &visual, &depth);
+                if (depth == windowAttrs.depth &&
+                    AreCompatibleVisuals(windowAttrs.visual, visual)) {
+                    *out_config = cfgs[i];
+                    *out_visid = visid;
+                    return true;
+                }
+            } else {
+                if (windowVisualID == static_cast<VisualID>(visid)) {
+                    *out_config = cfgs[i];
+                    *out_visid = visid;
+                    return true;
+                }
             }
         }
     }
 
     NS_WARNING("[GLX] Couldn't find a FBConfig matching window visual");
     return false;
 }
 
--- a/gfx/gl/GLContextProviderImpl.h
+++ b/gfx/gl/GLContextProviderImpl.h
@@ -56,22 +56,23 @@ public:
      * The GetSharedContext() method will return non-null if sharing
      * was successful.
      *
      * Note: a context created for a widget /must not/ hold a strong
      * reference to the widget; otherwise a cycle can be created through
      * a GL layer manager.
      *
      * @param aWidget Widget whose surface to create a context for
+     * @param aWebRender If the compositor is a WebRender compositor
      * @param aForceAccelerated true if only accelerated contexts are allowed
      *
      * @return Context to use for the window
      */
     static already_AddRefed<GLContext>
-    CreateForWindow(nsIWidget* aWidget, bool aForceAccelerated);
+    CreateForWindow(nsIWidget* aWidget, bool aWebRender, bool aForceAccelerated);
 
     /**
      * Create a context for offscreen rendering.  The target of this
      * context should be treated as opaque -- it might be a FBO, or a
      * pbuffer, or some other construct.  Users of this GLContext
      * should bind framebuffer 0 directly to use this offscreen buffer.
      *
      * The offscreen context returned by this method will always have
--- a/gfx/gl/GLContextProviderNull.cpp
+++ b/gfx/gl/GLContextProviderNull.cpp
@@ -12,17 +12,19 @@ using namespace mozilla::widget;
 
 already_AddRefed<GLContext>
 GLContextProviderNull::CreateForCompositorWidget(CompositorWidget* aCompositorWidget, bool aForceAccelerated)
 {
     return nullptr;
 }
 
 already_AddRefed<GLContext>
-GLContextProviderNull::CreateForWindow(nsIWidget* aWidget, bool aForceAccelerated)
+GLContextProviderNull::CreateForWindow(nsIWidget* aWidget,
+                                       bool aWebRender,
+                                       bool aForceAccelerated)
 {
     return nullptr;
 }
 
 already_AddRefed<GLContext>
 GLContextProviderNull::CreateWrappingExisting(void*, void*)
 {
     return nullptr;
--- a/gfx/gl/GLContextProviderWGL.cpp
+++ b/gfx/gl/GLContextProviderWGL.cpp
@@ -13,17 +13,19 @@
 
 #include "gfxCrashReporterUtils.h"
 
 #include "prenv.h"
 
 #include "mozilla/gfx/gfxVars.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/StaticPtr.h"
+#include "mozilla/layers/CompositorOptions.h"
 #include "mozilla/widget/CompositorWidget.h"
+#include "mozilla/widget/WinCompositorWidget.h"
 
 namespace mozilla {
 namespace gl {
 
 using namespace mozilla::gfx;
 using namespace mozilla::widget;
 
 WGLLibrary sWGLLib;
@@ -61,17 +63,17 @@ WGLLibrary::CreateDummyWindow(HDC* aWind
         pfd.nVersion = 1;
         pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
         pfd.iPixelType = PFD_TYPE_RGBA;
         pfd.cColorBits = 24;
         pfd.cRedBits = 8;
         pfd.cGreenBits = 8;
         pfd.cBlueBits = 8;
         pfd.cAlphaBits = 8;
-        pfd.cDepthBits = 0;
+        pfd.cDepthBits = gfxVars::UseWebRender() ? 24 : 0;
         pfd.iLayerType = PFD_MAIN_PLANE;
 
         mWindowPixelFormat = ChoosePixelFormat(dc, &pfd);
     }
 
     if (!mWindowPixelFormat ||
         !SetPixelFormat(dc, mWindowPixelFormat, nullptr))
     {
@@ -436,35 +438,31 @@ GetGlobalContextWGL()
 
 already_AddRefed<GLContext>
 GLContextProviderWGL::CreateWrappingExisting(void*, void*)
 {
     return nullptr;
 }
 
 already_AddRefed<GLContext>
-GLContextProviderWGL::CreateForCompositorWidget(CompositorWidget* aCompositorWidget, bool aForceAccelerated)
-{
-    return CreateForWindow(aCompositorWidget->RealWidget(), aForceAccelerated);
-}
-
-already_AddRefed<GLContext>
-GLContextProviderWGL::CreateForWindow(nsIWidget* aWidget, bool aForceAccelerated)
+CreateForWidget(HWND aHwnd,
+                bool aWebRender,
+                bool aForceAccelerated)
 {
     if (!sWGLLib.EnsureInitialized()) {
         return nullptr;
     }
 
     /**
        * We need to make sure we call SetPixelFormat -after- calling
        * EnsureInitialized, otherwise it can load/unload the dll and
        * wglCreateContext will fail.
        */
 
-    HDC dc = (HDC)aWidget->GetNativeData(NS_NATIVE_GRAPHIC);
+    HDC dc = ::GetDC(aHwnd);
 
     SetPixelFormat(dc, sWGLLib.GetWindowPixelFormat(), nullptr);
     HGLRC context;
 
     if (sWGLLib.HasRobustness()) {
         int attribs[] = {
             LOCAL_WGL_CONTEXT_FLAGS_ARB, LOCAL_WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB,
             LOCAL_WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, LOCAL_WGL_LOSE_CONTEXT_ON_RESET_ARB,
@@ -487,16 +485,30 @@ GLContextProviderWGL::CreateForWindow(ns
         return nullptr;
     }
 
     glContext->SetIsDoubleBuffered(true);
 
     return glContext.forget();
 }
 
+already_AddRefed<GLContext>
+GLContextProviderWGL::CreateForCompositorWidget(CompositorWidget* aCompositorWidget, bool aForceAccelerated)
+{
+    return CreateForWidget(aCompositorWidget->AsWindows()->GetHwnd(),
+                           aCompositorWidget->GetCompositorOptions().UseWebRender(),
+                           aForceAccelerated);
+}
+
+already_AddRefed<GLContext>
+GLContextProviderWGL::CreateForWindow(nsIWidget* aWidget, bool aWebRender, bool aForceAccelerated)
+{
+    return CreateForWidget((HWND)aWidget->GetNativeData(NS_NATIVE_WINDOW), aWebRender, aForceAccelerated);
+}
+
 static already_AddRefed<GLContextWGL>
 CreatePBufferOffscreenContext(CreateContextFlags flags, const IntSize& aSize)
 {
     WGLLibrary& wgl = sWGLLib;
 
     const int pfAttribs[] = {
         LOCAL_WGL_SUPPORT_OPENGL_ARB, LOCAL_GL_TRUE,
         LOCAL_WGL_ACCELERATION_ARB, LOCAL_WGL_FULL_ACCELERATION_ARB,
--- a/gfx/gl/GLLibraryEGL.cpp
+++ b/gfx/gl/GLLibraryEGL.cpp
@@ -3,16 +3,17 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "GLLibraryEGL.h"
 
 #include "angle/Platform.h"
 #include "gfxConfig.h"
 #include "gfxCrashReporterUtils.h"
 #include "gfxUtils.h"
+#include "mozilla/layers/CompositorThread.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/Tokenizer.h"
 #include "mozilla/ScopeExit.h"
 #include "mozilla/Unused.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsDirectoryServiceUtils.h"
@@ -143,16 +144,24 @@ GetAndInitWARPDisplay(GLLibraryEGL& egl,
 
     return display;
 }
 
 static bool
 IsAccelAngleSupported(const nsCOMPtr<nsIGfxInfo>& gfxInfo,
                       nsACString* const out_failureId)
 {
+    if (CompositorThreadHolder::IsInCompositorThread()) {
+        // We can only enter here with WebRender, so assert that this is a
+        // WebRender-enabled build.
+#ifndef MOZ_ENABLE_WEBRENDER
+        MOZ_ASSERT(false);
+#endif
+        return true;
+    }
     int32_t angleSupport;
     nsCString failureId;
     gfxUtils::ThreadSafeGetFeatureStatus(gfxInfo,
                                          nsIGfxInfo::FEATURE_WEBGL_ANGLE,
                                          failureId,
                                          &angleSupport);
     if (failureId.IsEmpty() && angleSupport != nsIGfxInfo::FEATURE_STATUS_OK) {
         // This shouldn't happen, if we see this it's because we've missed
--- a/gfx/gl/GLLibraryLoader.cpp
+++ b/gfx/gl/GLLibraryLoader.cpp
@@ -36,16 +36,22 @@ GLLibraryLoader::LoadSymbols(const SymLo
     return LoadSymbols(mLibrary,
                        firstStruct,
                        tryplatform ? mLookupFunc : nullptr,
                        prefix,
                        warnOnFailure);
 }
 
 PRFuncPtr
+GLLibraryLoader::LookupSymbol(const char* sym)
+{
+    return LookupSymbol(mLibrary, sym, mLookupFunc);
+}
+
+PRFuncPtr
 GLLibraryLoader::LookupSymbol(PRLibrary* lib,
                               const char* sym,
                               PlatformLookupFunction lookupFunction)
 {
     PRFuncPtr res = 0;
 
     // try finding it in the library directly, if we have one
     if (lib) {
--- a/gfx/gl/GLLibraryLoader.h
+++ b/gfx/gl/GLLibraryLoader.h
@@ -31,16 +31,18 @@ public:
         const char* symNames[MAX_SYMBOL_NAMES];
     } SymLoadStruct;
 
     bool LoadSymbols(const SymLoadStruct* firstStruct,
                      bool tryplatform = false,
                      const char* prefix = nullptr,
                      bool warnOnFailure = true);
 
+    PRFuncPtr LookupSymbol(const char* symname);
+
     /*
      * Static version of the functions in this class
      */
     static PRFuncPtr LookupSymbol(PRLibrary* lib,
                                   const char* symname,
                                   PlatformLookupFunction lookupFunction = nullptr);
     static bool LoadSymbols(PRLibrary* lib,
                             const SymLoadStruct* firstStruct,
--- a/gfx/ipc/CompositorOptions.h
+++ b/gfx/ipc/CompositorOptions.h
@@ -26,30 +26,35 @@ namespace layers {
  * and are accessible to content processes over PCompositorBridge as well.
  */
 class CompositorOptions
 {
 public:
   // This constructor needed for IPDL purposes, don't use it anywhere else.
   CompositorOptions()
     : mUseAPZ(false)
+    , mUseWebRender(false)
   {
   }
 
-  explicit CompositorOptions(bool aUseAPZ)
+  explicit CompositorOptions(bool aUseAPZ,
+                             bool aUseWebRender)
     : mUseAPZ(aUseAPZ)
+    , mUseWebRender(aUseWebRender)
   {
   }
 
   bool UseAPZ() const { return mUseAPZ; }
+  bool UseWebRender() const { return mUseWebRender; }
 
   friend struct IPC::ParamTraits<CompositorOptions>;
 
 private:
   bool mUseAPZ;
+  bool mUseWebRender;
 
   // Make sure to add new fields to the ParamTraits implementation
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif // _include_mozilla_gfx_ipc_CompositorOptions_h_
--- a/gfx/ipc/GPUParent.cpp
+++ b/gfx/ipc/GPUParent.cpp
@@ -23,16 +23,17 @@
 #include "mozilla/ipc/ProcessChild.h"
 #include "mozilla/layers/APZThreadUtils.h"
 #include "mozilla/layers/APZCTreeManager.h"
 #include "mozilla/layers/CompositorBridgeParent.h"
 #include "mozilla/layers/CompositorThread.h"
 #include "mozilla/layers/ImageBridgeParent.h"
 #include "mozilla/layers/LayerTreeOwnerTracker.h"
 #include "mozilla/layers/UiCompositorControllerParent.h"
+#include "mozilla/webrender/RenderThread.h"
 #include "nsDebugImpl.h"
 #include "nsExceptionHandler.h"
 #include "nsThreadManager.h"
 #include "prenv.h"
 #include "ProcessUtils.h"
 #include "VRManager.h"
 #include "VRManagerParent.h"
 #include "VsyncBridgeParent.h"
@@ -186,16 +187,21 @@ GPUParent::RecvInit(nsTArray<GfxPrefSett
     };
     char** argvp = argv;
     gtk_init(&argc, &argvp);
   } else {
     gtk_init(nullptr, nullptr);
   }
 #endif
 
+  // Make sure to do this *after* we update gfxVars above.
+  if (gfxVars::UseWebRender()) {
+    wr::RenderThread::Start();
+  }
+
   VRManager::ManagerInit();
   // Send a message to the UI process that we're done.
   GPUDeviceData data;
   RecvGetDeviceStatus(&data);
   Unused << SendInitComplete(data);
 
   Telemetry::AccumulateTimeDelta(Telemetry::GPU_PROCESS_INITIALIZATION_TIME_MS, mLaunchTime);
   return IPC_OK();
@@ -406,16 +412,19 @@ GPUParent::ActorDestroy(ActorDestroyReas
 #endif
 
   if (mVsyncBridge) {
     mVsyncBridge->Shutdown();
     mVsyncBridge = nullptr;
   }
   dom::VideoDecoderManagerParent::ShutdownVideoBridge();
   CompositorThreadHolder::Shutdown();
+  if (gfxVars::UseWebRender()) {
+    wr::RenderThread::ShutDown();
+  }
   Factory::ShutDown();
 #if defined(XP_WIN)
   DeviceManagerDx::Shutdown();
   DeviceManagerD3D9::Shutdown();
 #endif
   LayerTreeOwnerTracker::Shutdown();
   gfxVars::Shutdown();
   gfxConfig::Shutdown();
--- a/gfx/ipc/GPUProcessManager.cpp
+++ b/gfx/ipc/GPUProcessManager.cpp
@@ -149,16 +149,18 @@ GPUProcessManager::DisableGPUProcess(con
 {
   if (!gfxConfig::IsEnabled(Feature::GPU_PROCESS)) {
     return;
   }
 
   gfxConfig::SetFailed(Feature::GPU_PROCESS, FeatureStatus::Failed, aMessage);
   gfxCriticalNote << aMessage;
 
+  gfxPlatform::NotifyGPUProcessDisabled();
+
   DestroyProcess();
   ShutdownVsyncIOThread();
 }
 
 void
 GPUProcessManager::EnsureGPUReady()
 {
   if (mProcess && !mProcess->IsConnected()) {
--- a/gfx/ipc/GfxMessageUtils.h
+++ b/gfx/ipc/GfxMessageUtils.h
@@ -1335,20 +1335,22 @@ struct ParamTraits<mozilla::Array<T, Len
 
 template <>
 struct ParamTraits<mozilla::layers::CompositorOptions>
 {
   typedef mozilla::layers::CompositorOptions paramType;
 
   static void Write(Message* aMsg, const paramType& aParam) {
     WriteParam(aMsg, aParam.mUseAPZ);
+    WriteParam(aMsg, aParam.mUseWebRender);
   }
 
   static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult) {
-    return ReadParam(aMsg, aIter, &aResult->mUseAPZ);
+    return ReadParam(aMsg, aIter, &aResult->mUseAPZ)
+        && ReadParam(aMsg, aIter, &aResult->mUseWebRender);
   }
 };
 
 template <>
 struct ParamTraits<mozilla::layers::SimpleLayerAttributes>
 {
   typedef mozilla::layers::SimpleLayerAttributes paramType;
 
--- a/gfx/layers/Compositor.h
+++ b/gfx/layers/Compositor.h
@@ -130,16 +130,17 @@ class CompositingRenderTarget;
 class CompositorBridgeParent;
 class LayerManagerComposite;
 class CompositorOGL;
 class CompositorD3D9;
 class CompositorD3D11;
 class BasicCompositor;
 class TextureHost;
 class TextureReadLock;
+class WebRenderCompositorOGL;
 
 enum SurfaceInitMode
 {
   INIT_MODE_NONE,
   INIT_MODE_CLEAR
 };
 
 /**
@@ -468,16 +469,17 @@ public:
 #endif // MOZ_DUMP_PAINTING
 
   virtual LayersBackend GetBackendType() const = 0;
 
   virtual CompositorOGL* AsCompositorOGL() { return nullptr; }
   virtual CompositorD3D9* AsCompositorD3D9() { return nullptr; }
   virtual CompositorD3D11* AsCompositorD3D11() { return nullptr; }
   virtual BasicCompositor* AsBasicCompositor() { return nullptr; }
+  virtual WebRenderCompositorOGL* AsWebRenderCompositorOGL() { return nullptr; }
 
   /**
    * Each Compositor has a unique ID.
    * This ID is used to keep references to each Compositor in a map accessed
    * from the compositor thread only, so that async compositables can find
    * the right compositor parent and schedule compositing even if the compositor
    * changed.
    */
@@ -548,30 +550,30 @@ public:
   void SetCompositionTime(TimeStamp aTimeStamp) {
     mCompositionTime = aTimeStamp;
     if (!mCompositionTime.IsNull() && !mCompositeUntilTime.IsNull() &&
         mCompositionTime >= mCompositeUntilTime) {
       mCompositeUntilTime = TimeStamp();
     }
   }
 
-  void CompositeUntil(TimeStamp aTimeStamp) {
+  virtual void CompositeUntil(TimeStamp aTimeStamp) {
     if (mCompositeUntilTime.IsNull() ||
         mCompositeUntilTime < aTimeStamp) {
       mCompositeUntilTime = aTimeStamp;
     }
   }
   TimeStamp GetCompositeUntilTime() const {
     return mCompositeUntilTime;
   }
 
   // A stale Compositor has no CompositorBridgeParent; it will not process
   // frames and should not be used.
   void SetInvalid();
-  bool IsValid() const;
+  virtual bool IsValid() const;
   CompositorBridgeParent* GetCompositorBridgeParent() const {
     return mParent;
   }
 
   /// Most compositor backends operate asynchronously under the hood. This
   /// means that when a layer stops using a texture it is often desirable to
   /// wait for the end of the next composition before releasing the texture's
   /// ReadLock.
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -53,16 +53,17 @@
 #include "ImageContainer.h"
 
 class gfxContext;
 
 extern uint8_t gLayerManagerLayerBuilder;
 
 namespace mozilla {
 
+class ComputedTimingFunction;
 class FrameLayerBuilder;
 class StyleAnimationValue;
 
 namespace gl {
 class GLContext;
 } // namespace gl
 
 namespace gfx {
@@ -78,44 +79,53 @@ class AsyncPanZoomController;
 class BasicLayerManager;
 class ClientLayerManager;
 class Layer;
 class LayerMetricsWrapper;
 class PaintedLayer;
 class ContainerLayer;
 class ImageLayer;
 class ColorLayer;
+class CompositorBridgeChild;
 class TextLayer;
 class CanvasLayer;
 class BorderLayer;
 class ReadbackLayer;
 class ReadbackProcessor;
 class RefLayer;
 class HostLayer;
+class KnowsCompositor;
 class ShadowableLayer;
 class ShadowLayerForwarder;
 class LayerManagerComposite;
 class SpecificLayerAttributes;
+class TransactionIdAllocator;
 class Compositor;
 class FrameUniformityData;
 class PersistentBufferProvider;
 class GlyphArray;
+class WebRenderLayerManager;
 struct AnimData;
 
 namespace layerscope {
 class LayersPacket;
 } // namespace layerscope
 
 #define MOZ_LAYER_DECL_NAME(n, e)                              \
   virtual const char* Name() const override { return n; }  \
   virtual LayerType GetType() const override { return e; }
 
 // Defined in LayerUserData.h; please include that file instead.
 class LayerUserData;
 
+class DidCompositeObserver {
+  public:
+    virtual void DidComposite() = 0;
+};
+
 /*
  * Motivation: For truly smooth animation and video playback, we need to
  * be able to compose frames and render them on a dedicated thread (i.e.
  * off the main thread where DOM manipulation, script execution and layout
  * induce difficult-to-bound latency). This requires Gecko to construct
  * some kind of persistent scene structure (graph or tree) that can be
  * safely transmitted across threads. We have other scenarios (e.g. mobile
  * browsing) where retaining some rendered data between paints is desired
@@ -188,25 +198,31 @@ public:
     mUserData.Destroy();
     mRoot = nullptr;
   }
   bool IsDestroyed() { return mDestroyed; }
 
   virtual ShadowLayerForwarder* AsShadowForwarder()
   { return nullptr; }
 
+  virtual KnowsCompositor* AsKnowsCompositor()
+  { return nullptr; }
+
   virtual LayerManagerComposite* AsLayerManagerComposite()
   { return nullptr; }
 
   virtual ClientLayerManager* AsClientLayerManager()
   { return nullptr; }
 
   virtual BasicLayerManager* AsBasicLayerManager()
   { return nullptr; }
 
+  virtual WebRenderLayerManager* AsWebRenderLayerManager()
+  { return nullptr; }
+
   /**
    * Returns true if this LayerManager is owned by an nsIWidget,
    * and is used for drawing into the widget.
    */
   virtual bool IsWidgetLayerManager() { return true; }
   virtual bool IsInactiveLayerManager() { return false; }
 
   /**
@@ -661,16 +677,37 @@ public:
   }
 
   uint32_t GetAndClearPaintedPixelCount() {
     uint32_t count = mPaintedPixelCount;
     mPaintedPixelCount = 0;
     return count;
   }
 
+  virtual void SetLayerObserverEpoch(uint64_t aLayerObserverEpoch) {}
+
+  virtual void DidComposite(uint64_t aTransactionId,
+                            const mozilla::TimeStamp& aCompositeStart,
+                            const mozilla::TimeStamp& aCompositeEnd) {}
+
+  virtual void AddDidCompositeObserver(DidCompositeObserver* aObserver) { MOZ_CRASH("GFX: LayerManager"); }
+  virtual void RemoveDidCompositeObserver(DidCompositeObserver* aObserver) { MOZ_CRASH("GFX: LayerManager"); }
+
+  virtual void UpdateTextureFactoryIdentifier(const TextureFactoryIdentifier& aNewIdentifier,
+											  uint64_t aDeviceResetSeqNo) {}
+
+  virtual TextureFactoryIdentifier GetTextureFactoryIdentifier()
+  {
+    return TextureFactoryIdentifier();
+  }
+
+  virtual void SetTransactionIdAllocator(TransactionIdAllocator* aAllocator) {}
+
+  virtual CompositorBridgeChild* GetCompositorBridgeChild() { return nullptr; }
+
 protected:
   RefPtr<Layer> mRoot;
   gfx::UserData mUserData;
   bool mDestroyed;
   bool mSnapEffectiveTransforms;
 
   nsIntRegion mRegionToClear;
 
@@ -1737,16 +1774,18 @@ public:
   // and can be used anytime.
   // A layer has an APZC at index aIndex only-if GetFrameMetrics(aIndex).IsScrollable();
   // attempting to get an APZC for a non-scrollable metrics will return null.
   // The aIndex for these functions must be less than GetScrollMetadataCount().
   void SetAsyncPanZoomController(uint32_t aIndex, AsyncPanZoomController *controller);
   AsyncPanZoomController* GetAsyncPanZoomController(uint32_t aIndex) const;
   // The ScrollMetadataChanged function is used internally to ensure the APZC array length
   // matches the frame metrics array length.
+
+  virtual void ClearCachedResources() {}
 private:
   void ScrollMetadataChanged();
 public:
 
   void ApplyPendingUpdatesForThisTransaction();
 
 #ifdef DEBUG
   void SetDebugColorIndex(uint32_t aIndex) { mDebugColorIndex = aIndex; }
--- a/gfx/layers/LayersTypes.h
+++ b/gfx/layers/LayersTypes.h
@@ -44,16 +44,17 @@ class TextureHost;
 
 enum class LayersBackend : int8_t {
   LAYERS_NONE = 0,
   LAYERS_BASIC,
   LAYERS_OPENGL,
   LAYERS_D3D9,
   LAYERS_D3D11,
   LAYERS_CLIENT,
+  LAYERS_WR,
   LAYERS_LAST
 };
 
 enum class BufferMode : int8_t {
   BUFFER_NONE,
   BUFFERED
 };
 
--- a/gfx/layers/RotatedBuffer.cpp
+++ b/gfx/layers/RotatedBuffer.cpp
@@ -422,18 +422,20 @@ RotatedContentBuffer::FlushBuffers()
 
 RotatedContentBuffer::PaintState
 RotatedContentBuffer::BeginPaint(PaintedLayer* aLayer,
                                  uint32_t aFlags)
 {
   PaintState result;
   // We need to disable rotation if we're going to be resampled when
   // drawing, because we might sample across the rotation boundary.
+  // Also disable buffer rotation when using webrender.
   bool canHaveRotation = gfxPlatform::BufferRotationEnabled() &&
-                         !(aFlags & (PAINT_WILL_RESAMPLE | PAINT_NO_ROTATION));
+                         !(aFlags & (PAINT_WILL_RESAMPLE | PAINT_NO_ROTATION)) &&
+                         !(aLayer->Manager()->AsWebRenderLayerManager());
 
   nsIntRegion validRegion = aLayer->GetValidRegion();
 
   bool canUseOpaqueSurface = aLayer->CanUseOpaqueSurface();
   ContentType layerContentType =
     canUseOpaqueSurface ? gfxContentType::COLOR :
                           gfxContentType::COLOR_ALPHA;
 
--- a/gfx/layers/apz/src/GestureEventListener.cpp
+++ b/gfx/layers/apz/src/GestureEventListener.cpp
@@ -6,16 +6,17 @@
 
 #include "GestureEventListener.h"
 #include <math.h>                       // for fabsf
 #include <stddef.h>                     // for size_t
 #include "AsyncPanZoomController.h"     // for AsyncPanZoomController
 #include "InputBlockState.h"            // for TouchBlockState
 #include "base/task.h"                  // for CancelableTask, etc
 #include "gfxPrefs.h"                   // for gfxPrefs
+#include "InputBlockState.h"            // for TouchBlockState
 #include "mozilla/SizePrintfMacros.h"   // for PRIuSIZE
 #include "nsDebug.h"                    // for NS_WARNING
 #include "nsMathUtils.h"                // for NS_hypot
 
 #define GEL_LOG(...)
 // #define GEL_LOG(...) printf_stderr("GEL: " __VA_ARGS__)
 
 namespace mozilla {
--- a/gfx/layers/client/ClientLayerManager.cpp
+++ b/gfx/layers/client/ClientLayerManager.cpp
@@ -774,17 +774,17 @@ ClientLayerManager::HandleMemoryPressure
   if (GetCompositorBridgeChild()) {
     GetCompositorBridgeChild()->HandleMemoryPressure();
   }
 }
 
 void
 ClientLayerManager::ClearLayer(Layer* aLayer)
 {
-  ClientLayer::ToClientLayer(aLayer)->ClearCachedResources();
+  aLayer->ClearCachedResources();
   for (Layer* child = aLayer->GetFirstChild(); child;
        child = child->GetNextSibling()) {
     ClearLayer(child);
   }
 }
 
 void
 ClientLayerManager::HandleMemoryPressureLayer(Layer* aLayer)
--- a/gfx/layers/client/ClientLayerManager.h
+++ b/gfx/layers/client/ClientLayerManager.h
@@ -47,16 +47,21 @@ protected:
   virtual ~ClientLayerManager();
 
 public:
   virtual ShadowLayerForwarder* AsShadowForwarder() override
   {
     return mForwarder;
   }
 
+  virtual KnowsCompositor* AsKnowsCompositor() override
+  {
+    return mForwarder;
+  }
+
   virtual ClientLayerManager* AsClientLayerManager() override
   {
     return this;
   }
 
   virtual int32_t GetMaxTextureSize() const override;
 
   virtual void SetDefaultTargetConfiguration(BufferMode aDoubleBuffering, ScreenRotation aRotation);
@@ -86,19 +91,19 @@ public:
   virtual already_AddRefed<ImageLayer> CreateImageLayer() override;
   virtual already_AddRefed<CanvasLayer> CreateCanvasLayer() override;
   virtual already_AddRefed<ReadbackLayer> CreateReadbackLayer() override;
   virtual already_AddRefed<ColorLayer> CreateColorLayer() override;
   virtual already_AddRefed<TextLayer> CreateTextLayer() override;
   virtual already_AddRefed<BorderLayer> CreateBorderLayer() override;
   virtual already_AddRefed<RefLayer> CreateRefLayer() override;
 
-  void UpdateTextureFactoryIdentifier(const TextureFactoryIdentifier& aNewIdentifier,
-                                      uint64_t aDeviceResetSeqNo);
-  TextureFactoryIdentifier GetTextureFactoryIdentifier()
+  virtual void UpdateTextureFactoryIdentifier(const TextureFactoryIdentifier& aNewIdentifier,
+											  uint64_t aDeviceResetSeqNo) override;
+  virtual TextureFactoryIdentifier GetTextureFactoryIdentifier() override
   {
     return AsShadowForwarder()->GetTextureFactoryIdentifier();
   }
 
   virtual void FlushRendering() override;
   void SendInvalidRegion(const nsIntRegion& aRegion);
 
   virtual uint32_t StartFrameTimeRecording(int32_t aBufferSize) override;
@@ -148,17 +153,17 @@ public:
   DrawPaintedLayerCallback GetPaintedLayerCallback() const
   { return mPaintedLayerCallback; }
 
   void* GetPaintedLayerCallbackData() const
   { return mPaintedLayerCallbackData; }
 
   CompositorBridgeChild* GetRemoteRenderer();
 
-  CompositorBridgeChild* GetCompositorBridgeChild();
+  virtual CompositorBridgeChild* GetCompositorBridgeChild() override;
 
   // Disable component alpha layers with the software compositor.
   virtual bool ShouldAvoidComponentAlphaLayers() override { return !IsCompositingCheap(); }
 
   bool InConstruction() { return mPhase == PHASE_CONSTRUCTION; }
 #ifdef DEBUG
   bool InDrawing() { return mPhase == PHASE_DRAWING; }
   bool InForward() { return mPhase == PHASE_FORWARD; }
@@ -169,19 +174,19 @@ public:
   {
     mNeedsComposite = aNeedsComposite;
   }
   bool NeedsComposite() const { return mNeedsComposite; }
 
   virtual void Composite() override;
   virtual void GetFrameUniformity(FrameUniformityData* aFrameUniformityData) override;
 
-  void DidComposite(uint64_t aTransactionId,
-                    const mozilla::TimeStamp& aCompositeStart,
-                    const mozilla::TimeStamp& aCompositeEnd);
+  virtual void DidComposite(uint64_t aTransactionId,
+                            const mozilla::TimeStamp& aCompositeStart,
+                            const mozilla::TimeStamp& aCompositeEnd) override;
 
   virtual bool AreComponentAlphaLayersEnabled() override;
 
   // Log APZ test data for the current paint. We supply the paint sequence
   // number ourselves, and take care of calling APZTestData::StartNewPaint()
   // when a new paint is started.
   void LogTestDataForCurrentPaint(FrameMetrics::ViewID aScrollId,
                                   const std::string& aKey,
@@ -210,33 +215,31 @@ public:
   // LogTestData...() functions.
   const APZTestData& GetAPZTestData() const {
     return mApzTestData;
   }
 
   // Get a copy of the compositor-side APZ test data for our layers ID.
   void GetCompositorSideAPZTestData(APZTestData* aData) const;
 
-  void SetTransactionIdAllocator(TransactionIdAllocator* aAllocator) { mTransactionIdAllocator = aAllocator; }
+  virtual void SetTransactionIdAllocator(TransactionIdAllocator* aAllocator) override
+  {
+     mTransactionIdAllocator = aAllocator;
+  }
 
   float RequestProperty(const nsAString& aProperty) override;
 
   bool AsyncPanZoomEnabled() const override;
 
   void SetNextPaintSyncId(int32_t aSyncId);
 
-  void SetLayerObserverEpoch(uint64_t aLayerObserverEpoch);
+  virtual void SetLayerObserverEpoch(uint64_t aLayerObserverEpoch) override;
 
-  class DidCompositeObserver {
-  public:
-    virtual void DidComposite() = 0;
-  };
-
-  void AddDidCompositeObserver(DidCompositeObserver* aObserver);
-  void RemoveDidCompositeObserver(DidCompositeObserver* aObserver);
+  virtual void AddDidCompositeObserver(DidCompositeObserver* aObserver) override;
+  virtual void RemoveDidCompositeObserver(DidCompositeObserver* aObserver) override;
 
   virtual already_AddRefed<PersistentBufferProvider>
   CreatePersistentBufferProvider(const gfx::IntSize& aSize, gfx::SurfaceFormat aFormat) override;
 
 protected:
   enum TransactionPhase {
     PHASE_NONE, PHASE_CONSTRUCTION, PHASE_DRAWING, PHASE_FORWARD
   };
@@ -345,18 +348,16 @@ class ClientLayer : public ShadowableLay
 public:
   ClientLayer()
   {
     MOZ_COUNT_CTOR(ClientLayer);
   }
 
   ~ClientLayer();
 
-  virtual void ClearCachedResources() { }
-
   // Shrink memory usage.
   // Called when "memory-pressure" is observed.
   virtual void HandleMemoryPressure() { }
 
   virtual void RenderLayer() = 0;
   virtual void RenderLayerWithReadback(ReadbackProcessor *aReadback) { RenderLayer(); }
 
   virtual ClientPaintedLayer* AsThebes() { return nullptr; }
--- a/gfx/layers/client/ContentClient.cpp
+++ b/gfx/layers/client/ContentClient.cpp
@@ -58,16 +58,17 @@ static TextureFlags TextureFlagsForRotat
 
 /* static */ already_AddRefed<ContentClient>
 ContentClient::CreateContentClient(CompositableForwarder* aForwarder)
 {
   LayersBackend backend = aForwarder->GetCompositorBackendType();
   if (backend != LayersBackend::LAYERS_OPENGL &&
       backend != LayersBackend::LAYERS_D3D9 &&
       backend != LayersBackend::LAYERS_D3D11 &&
+      backend != LayersBackend::LAYERS_WR &&
       backend != LayersBackend::LAYERS_BASIC) {
     return nullptr;
   }
 
   bool useDoubleBuffering = false;
 
 #ifdef XP_WIN
   if (backend == LayersBackend::LAYERS_D3D11) {
--- a/gfx/layers/client/TextureClient.cpp
+++ b/gfx/layers/client/TextureClient.cpp
@@ -1047,17 +1047,18 @@ TextureClient::CreateForDrawing(TextureF
       aAllocator->IsSameProcess() &&
       aSize.width <= aMaxTextureSize &&
       aSize.height <= aMaxTextureSize &&
       NS_IsMainThread() &&
       DeviceManagerD3D9::GetDevice()) {
     data = D3D9TextureData::Create(aSize, aFormat, aAllocFlags);
   }
 
-  if (!data && aFormat == SurfaceFormat::B8G8R8X8 &&
+  if (aLayersBackend != LayersBackend::LAYERS_WR &&
+      !data && aFormat == SurfaceFormat::B8G8R8X8 &&
       moz2DBackend == gfx::BackendType::CAIRO &&
       NS_IsMainThread()) {
     data = DIBTextureData::Create(aSize, aFormat, aAllocator);
   }
 #endif
 
 #ifdef MOZ_X11
   gfxSurfaceType type =
--- a/gfx/layers/composite/CompositableHost.cpp
+++ b/gfx/layers/composite/CompositableHost.cpp
@@ -6,18 +6,20 @@
 #include "CompositableHost.h"
 #include <map>                          // for _Rb_tree_iterator, map, etc
 #include <utility>                      // for pair
 #include "ContentHost.h"                // for ContentHostDoubleBuffered, etc
 #include "Effects.h"                    // for EffectMask, Effect, etc
 #include "gfxUtils.h"
 #include "ImageHost.h"                  // for ImageHostBuffered, etc
 #include "TiledContentHost.h"           // for TiledContentHost
+#include "mozilla/gfx/gfxVars.h"
 #include "mozilla/layers/LayersSurfaces.h"  // for SurfaceDescriptor
 #include "mozilla/layers/TextureHost.h"  // for TextureHost, etc
+#include "mozilla/layers/WebRenderImageHost.h"
 #include "mozilla/RefPtr.h"                   // for nsRefPtr
 #include "nsDebug.h"                    // for NS_WARNING
 #include "nsISupportsImpl.h"            // for MOZ_COUNT_CTOR, etc
 #include "gfxPlatform.h"                // for gfxPlatform
 #include "IPDLActor.h"
 
 namespace mozilla {
 
@@ -123,17 +125,21 @@ CompositableHost::Create(const TextureIn
   switch (aTextureInfo.mCompositableType) {
   case CompositableType::IMAGE_BRIDGE:
     NS_ERROR("Cannot create an image bridge compositable this way");
     break;
   case CompositableType::CONTENT_TILED:
     result = new TiledContentHost(aTextureInfo);
     break;
   case CompositableType::IMAGE:
-    result = new ImageHost(aTextureInfo);
+    if (gfxVars::UseWebRender()) {
+      result = new WebRenderImageHost(aTextureInfo);
+    } else {
+      result = new ImageHost(aTextureInfo);
+    }
     break;
   case CompositableType::CONTENT_SINGLE:
     result = new ContentHostSingleBuffered(aTextureInfo);
     break;
   case CompositableType::CONTENT_DOUBLE:
     result = new ContentHostDoubleBuffered(aTextureInfo);
     break;
   default:
--- a/gfx/layers/composite/CompositableHost.h
+++ b/gfx/layers/composite/CompositableHost.h
@@ -40,16 +40,21 @@ namespace layers {
 class Layer;
 class LayerComposite;
 class Compositor;
 class ThebesBufferData;
 class TiledContentHost;
 class CompositableParentManager;
 struct EffectChain;
 
+struct ImageCompositeNotificationInfo {
+  base::ProcessId mImageBridgeProcessId;
+  ImageCompositeNotification mNotification;
+};
+
 struct AsyncCompositableRef
 {
   AsyncCompositableRef()
    : mProcessId(mozilla::ipc::kInvalidProcessId)
   {}
   AsyncCompositableRef(base::ProcessId aProcessId, const CompositableHandle& aHandle)
    : mProcessId(aProcessId), mHandle(aHandle)
   {}
@@ -228,16 +233,18 @@ public:
     return nullptr;
   }
 
   /// Called when shutting down the layer tree.
   /// This is a good place to clear all potential gpu resources before the widget
   /// is is destroyed.
   virtual void CleanupResources() {}
 
+  virtual void BindTextureSource() {}
+
 protected:
   TextureInfo mTextureInfo;
   AsyncCompositableRef mAsyncRef;
   uint64_t mCompositorID;
   RefPtr<Compositor> mCompositor;
   Layer* mLayer;
   uint32_t mFlashCounter; // used when the pref "layers.flash-borders" is true.
   bool mAttached;
copy from gfx/layers/composite/ImageHost.cpp
copy to gfx/layers/composite/ImageComposite.cpp
--- a/gfx/layers/composite/ImageHost.cpp
+++ b/gfx/layers/composite/ImageComposite.cpp
@@ -1,222 +1,95 @@
 /* -*- 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 "ImageHost.h"
+#include "ImageComposite.h"
 
-#include "LayersLogging.h"              // for AppendToString
-#include "composite/CompositableHost.h"  // for CompositableHost, etc
-#include "ipc/IPCMessageUtils.h"        // for null_t
-#include "mozilla/layers/Compositor.h"  // for Compositor
-#include "mozilla/layers/Effects.h"     // for TexturedEffect, Effect, etc
-#include "mozilla/layers/LayerManagerComposite.h"     // for TexturedEffect, Effect, etc
-#include "nsAString.h"
-#include "nsDebug.h"                    // for NS_WARNING, NS_ASSERTION
-#include "nsPrintfCString.h"            // for nsPrintfCString
-#include "nsString.h"                   // for nsAutoCString
-
+// this is also defined in ImageHost.cpp
 #define BIAS_TIME_MS 1.0
 
 namespace mozilla {
 
 using namespace gfx;
 
 namespace layers {
 
-class ISurfaceAllocator;
-
-ImageHost::ImageHost(const TextureInfo& aTextureInfo)
-  : CompositableHost(aTextureInfo)
-  , mLastFrameID(-1)
+ImageComposite::ImageComposite()
+  : mLastFrameID(-1)
   , mLastProducerID(-1)
   , mBias(BIAS_NONE)
-  , mLocked(false)
 {}
 
-ImageHost::~ImageHost()
+ImageComposite::~ImageComposite()
 {
 }
 
-void
-ImageHost::UseTextureHost(const nsTArray<TimedTexture>& aTextures)
-{
-  MOZ_ASSERT(!mLocked);
-
-  CompositableHost::UseTextureHost(aTextures);
-  MOZ_ASSERT(aTextures.Length() >= 1);
-
-  nsTArray<TimedImage> newImages;
-
-  for (uint32_t i = 0; i < aTextures.Length(); ++i) {
-    const TimedTexture& t = aTextures[i];
-    MOZ_ASSERT(t.mTexture);
-    if (i + 1 < aTextures.Length() &&
-        t.mProducerID == mLastProducerID && t.mFrameID < mLastFrameID) {
-      // Ignore frames before a frame that we already composited. We don't
-      // ever want to display these frames. This could be important if
-      // the frame producer adjusts timestamps (e.g. to track the audio clock)
-      // and the new frame times are earlier.
-      continue;
-    }
-    TimedImage& img = *newImages.AppendElement();
-    img.mTextureHost = t.mTexture;
-    img.mTimeStamp = t.mTimeStamp;
-    img.mPictureRect = t.mPictureRect;
-    img.mFrameID = t.mFrameID;
-    img.mProducerID = t.mProducerID;
-    img.mTextureHost->SetCropRect(img.mPictureRect);
-    img.mTextureHost->Updated();
-  }
-
-  mImages.SwapElements(newImages);
-  newImages.Clear();
-
-  // If we only have one image we can upload it right away, otherwise we'll upload
-  // on-demand during composition after we have picked the proper timestamp.
-  if (mImages.Length() == 1) {
-    SetCurrentTextureHost(mImages[0].mTextureHost);
-  }
-
-  // Video producers generally send replacement images with the same frameID but
-  // slightly different timestamps in order to sync with the audio clock. This
-  // means that any CompositeUntil() call we made in Composite() may no longer
-  // guarantee that we'll composite until the next frame is ready. Fix that here.
-  if (GetCompositor() && mLastFrameID >= 0) {
-    for (size_t i = 0; i < mImages.Length(); ++i) {
-      bool frameComesAfter = mImages[i].mFrameID > mLastFrameID ||
-                             mImages[i].mProducerID != mLastProducerID;
-      if (frameComesAfter && !mImages[i].mTimeStamp.IsNull()) {
-        GetCompositor()->CompositeUntil(mImages[i].mTimeStamp +
-                                        TimeDuration::FromMilliseconds(BIAS_TIME_MS));
-        break;
-      }
-    }
-  }
-}
-
-void
-ImageHost::SetCurrentTextureHost(TextureHost* aTexture)
-{
-  if (aTexture == mCurrentTextureHost.get()) {
-    return;
-  }
-
-  bool swapTextureSources = !!mCurrentTextureHost && !!mCurrentTextureSource
-                            && mCurrentTextureHost->HasIntermediateBuffer();
-
-  if (swapTextureSources) {
-    auto dataSource = mCurrentTextureSource->AsDataTextureSource();
-    if (dataSource) {
-      // The current textureHost has an internal buffer in the form of the
-      // DataTextureSource. Removing the ownership of the texture source
-      // will enable the next texture host we bind to the texture source to
-      // acquire it instead of creating a new one. This is desirable in
-      // ImageHost because the current texture won't be used again with the
-      // same content. It wouldn't be desirable with ContentHost for instance,
-      // because the latter reuses the texture's valid regions.
-      dataSource->SetOwner(nullptr);
-    }
-
-    RefPtr<TextureSource> tmp = mExtraTextureSource;
-    mExtraTextureSource = mCurrentTextureSource.get();
-    mCurrentTextureSource = tmp;
-  } else {
-    mExtraTextureSource = nullptr;
-  }
-
-  mCurrentTextureHost = aTexture;
-  mCurrentTextureHost->PrepareTextureSource(mCurrentTextureSource);
-}
-
-void
-ImageHost::CleanupResources()
-{
-  mExtraTextureSource = nullptr;
-  mCurrentTextureSource = nullptr;
-  mCurrentTextureHost = nullptr;
-}
-
-void
-ImageHost::RemoveTextureHost(TextureHost* aTexture)
-{
-  MOZ_ASSERT(!mLocked);
-
-  CompositableHost::RemoveTextureHost(aTexture);
-
-  for (int32_t i = mImages.Length() - 1; i >= 0; --i) {
-    if (mImages[i].mTextureHost == aTexture) {
-      aTexture->UnbindTextureSource();
-      mImages.RemoveElementAt(i);
-    }
-  }
-}
-
 static TimeStamp
-GetBiasedTime(const TimeStamp& aInput, ImageHost::Bias aBias)
+GetBiasedTime(const TimeStamp& aInput, ImageComposite::Bias aBias)
 {
   switch (aBias) {
-  case ImageHost::BIAS_NEGATIVE:
+  case ImageComposite::BIAS_NEGATIVE:
     return aInput - TimeDuration::FromMilliseconds(BIAS_TIME_MS);
-  case ImageHost::BIAS_POSITIVE:
+  case ImageComposite::BIAS_POSITIVE:
     return aInput + TimeDuration::FromMilliseconds(BIAS_TIME_MS);
   default:
     return aInput;
   }
 }
 
-static ImageHost::Bias
-UpdateBias(const TimeStamp& aCompositionTime,
-           const TimeStamp& aCompositedImageTime,
-           const TimeStamp& aNextImageTime, // may be null
-           ImageHost::Bias aBias)
+/* static */ ImageComposite::Bias
+ImageComposite::UpdateBias(const TimeStamp& aCompositionTime,
+                           const TimeStamp& aCompositedImageTime,
+                           const TimeStamp& aNextImageTime, // may be null
+                           ImageComposite::Bias aBias)
 {
   if (aCompositedImageTime.IsNull()) {
-    return ImageHost::BIAS_NONE;
+    return ImageComposite::BIAS_NONE;
   }
   TimeDuration threshold = TimeDuration::FromMilliseconds(1.0);
   if (aCompositionTime - aCompositedImageTime < threshold &&
       aCompositionTime - aCompositedImageTime > -threshold) {
     // The chosen frame's time is very close to the composition time (probably
     // just before the current composition time, but due to previously set
     // negative bias, it could be just after the current composition time too).
     // If the inter-frame time is almost exactly equal to (a multiple of)
     // the inter-composition time, then we're in a dangerous situation because
     // jitter might cause frames to fall one side or the other of the
     // composition times, causing many frames to be skipped or duplicated.
     // Try to prevent that by adding a negative bias to the frame times during
     // the next composite; that should ensure the next frame's time is treated
     // as falling just before a composite time.
-    return ImageHost::BIAS_NEGATIVE;
+    return ImageComposite::BIAS_NEGATIVE;
   }
   if (!aNextImageTime.IsNull() &&
       aNextImageTime - aCompositionTime < threshold &&
       aNextImageTime - aCompositionTime > -threshold) {
     // The next frame's time is very close to our composition time (probably
     // just after the current composition time, but due to previously set
     // positive bias, it could be just before the current composition time too).
     // We're in a dangerous situation because jitter might cause frames to
     // fall one side or the other of the composition times, causing many frames
     // to be skipped or duplicated.
     // Try to prevent that by adding a negative bias to the frame times during
     // the next composite; that should ensure the next frame's time is treated
     // as falling just before a composite time.
-    return ImageHost::BIAS_POSITIVE;
+    return ImageComposite::BIAS_POSITIVE;
   }
-  return ImageHost::BIAS_NONE;
+  return ImageComposite::BIAS_NONE;
 }
 
-int ImageHost::ChooseImageIndex() const
+int
+ImageComposite::ChooseImageIndex() const
 {
-  if (!GetCompositor() || mImages.IsEmpty()) {
+  if (mImages.IsEmpty()) {
     return -1;
   }
-  TimeStamp now = GetCompositor()->GetCompositionTime();
+  TimeStamp now = GetCompositionTime();
 
   if (now.IsNull()) {
     // Not in a composition, so just return the last image we composited
     // (if it's one of the current images).
     for (uint32_t i = 0; i < mImages.Length(); ++i) {
       if (mImages[i].mFrameID == mLastFrameID &&
           mImages[i].mProducerID == mLastProducerID) {
         return i;
@@ -228,349 +101,24 @@ int ImageHost::ChooseImageIndex() const
   uint32_t result = 0;
   while (result + 1 < mImages.Length() &&
       GetBiasedTime(mImages[result + 1].mTimeStamp, mBias) <= now) {
     ++result;
   }
   return result;
 }
 
-const ImageHost::TimedImage* ImageHost::ChooseImage() const
-{
-  int index = ChooseImageIndex();
-  return index >= 0 ? &mImages[index] : nullptr;
-}
-
-ImageHost::TimedImage* ImageHost::ChooseImage()
+const ImageComposite::TimedImage* ImageComposite::ChooseImage() const
 {
   int index = ChooseImageIndex();
   return index >= 0 ? &mImages[index] : nullptr;
 }
 
-TextureHost*
-ImageHost::GetAsTextureHost(IntRect* aPictureRect)
-{
-  TimedImage* img = ChooseImage();
-  if (img) {
-    SetCurrentTextureHost(img->mTextureHost);
-  }
-  if (aPictureRect && img) {
-    *aPictureRect = img->mPictureRect;
-  }
-  return img ? img->mTextureHost.get() : nullptr;
-}
-
-void ImageHost::Attach(Layer* aLayer,
-                       Compositor* aCompositor,
-                       AttachFlags aFlags)
-{
-  CompositableHost::Attach(aLayer, aCompositor, aFlags);
-  for (auto& img : mImages) {
-    if (GetCompositor()) {
-      img.mTextureHost->SetCompositor(GetCompositor());
-    }
-    img.mTextureHost->Updated();
-  }
-}
-
-void
-ImageHost::Composite(LayerComposite* aLayer,
-                     EffectChain& aEffectChain,
-                     float aOpacity,
-                     const gfx::Matrix4x4& aTransform,
-                     const gfx::SamplingFilter aSamplingFilter,
-                     const gfx::IntRect& aClipRect,
-                     const nsIntRegion* aVisibleRegion,
-                     const Maybe<gfx::Polygon>& aGeometry)
+ImageComposite::TimedImage* ImageComposite::ChooseImage()
 {
-  if (!GetCompositor()) {
-    // should only happen when a tab is dragged to another window and
-    // async-video is still sending frames but we haven't attached the
-    // set the new compositor yet.
-    return;
-  }
-
-  int imageIndex = ChooseImageIndex();
-  if (imageIndex < 0) {
-    return;
-  }
-
-  if (uint32_t(imageIndex) + 1 < mImages.Length()) {
-    GetCompositor()->CompositeUntil(mImages[imageIndex + 1].mTimeStamp + TimeDuration::FromMilliseconds(BIAS_TIME_MS));
-  }
-
-  TimedImage* img = &mImages[imageIndex];
-  img->mTextureHost->SetCompositor(GetCompositor());
-  SetCurrentTextureHost(img->mTextureHost);
-
-  {
-    AutoLockCompositableHost autoLock(this);
-    if (autoLock.Failed()) {
-      NS_WARNING("failed to lock front buffer");
-      return;
-    }
-
-    if (!mCurrentTextureHost->BindTextureSource(mCurrentTextureSource)) {
-      return;
-    }
-
-    if (!mCurrentTextureSource) {
-      // BindTextureSource above should have returned false!
-      MOZ_ASSERT(false);
-      return;
-    }
-
-    bool isAlphaPremultiplied =
-        !(mCurrentTextureHost->GetFlags() & TextureFlags::NON_PREMULTIPLIED);
-    RefPtr<TexturedEffect> effect =
-        CreateTexturedEffect(mCurrentTextureHost,
-            mCurrentTextureSource.get(), aSamplingFilter, isAlphaPremultiplied,
-            GetRenderState());
-    if (!effect) {
-      return;
-    }
-
-    if (!GetCompositor()->SupportsEffect(effect->mType)) {
-      return;
-    }
-
-    DiagnosticFlags diagnosticFlags = DiagnosticFlags::IMAGE;
-    if (effect->mType == EffectTypes::NV12) {
-      diagnosticFlags |= DiagnosticFlags::NV12;
-    } else if (effect->mType == EffectTypes::YCBCR) {
-      diagnosticFlags |= DiagnosticFlags::YCBCR;
-    }
-
-    if (mLastFrameID != img->mFrameID || mLastProducerID != img->mProducerID) {
-      if (mAsyncRef) {
-        ImageCompositeNotificationInfo info;
-        info.mImageBridgeProcessId = mAsyncRef.mProcessId;
-        info.mNotification = ImageCompositeNotification(
-          mAsyncRef.mHandle,
-          img->mTimeStamp, GetCompositor()->GetCompositionTime(),
-          img->mFrameID, img->mProducerID);
-        static_cast<LayerManagerComposite*>(aLayer->GetLayerManager())->
-            AppendImageCompositeNotification(info);
-      }
-      mLastFrameID = img->mFrameID;
-      mLastProducerID = img->mProducerID;
-    }
-    aEffectChain.mPrimaryEffect = effect;
-    gfx::Rect pictureRect(0, 0, img->mPictureRect.width, img->mPictureRect.height);
-    BigImageIterator* it = mCurrentTextureSource->AsBigImageIterator();
-    if (it) {
-
-      // This iteration does not work if we have multiple texture sources here
-      // (e.g. 3 YCbCr textures). There's nothing preventing the different
-      // planes from having different resolutions or tile sizes. For example, a
-      // YCbCr frame could have Cb and Cr planes that are half the resolution of
-      // the Y plane, in such a way that the Y plane overflows the maximum
-      // texture size and the Cb and Cr planes do not. Then the Y plane would be
-      // split into multiple tiles and the Cb and Cr planes would just be one
-      // tile each.
-      // To handle the general case correctly, we'd have to create a grid of
-      // intersected tiles over all planes, and then draw each grid tile using
-      // the corresponding source tiles from all planes, with appropriate
-      // per-plane per-tile texture coords.
-      // DrawQuad currently assumes that all planes use the same texture coords.
-      MOZ_ASSERT(it->GetTileCount() == 1 || !mCurrentTextureSource->GetNextSibling(),
-                 "Can't handle multi-plane BigImages");
-
-      it->BeginBigImageIteration();
-      do {
-        IntRect tileRect = it->GetTileRect();
-        gfx::Rect rect(tileRect.x, tileRect.y, tileRect.width, tileRect.height);
-        rect = rect.Intersect(pictureRect);
-        effect->mTextureCoords = Rect(Float(rect.x - tileRect.x) / tileRect.width,
-                                      Float(rect.y - tileRect.y) / tileRect.height,
-                                      Float(rect.width) / tileRect.width,
-                                      Float(rect.height) / tileRect.height);
-        if (img->mTextureHost->GetFlags() & TextureFlags::ORIGIN_BOTTOM_LEFT) {
-          effect->mTextureCoords.y = effect->mTextureCoords.YMost();
-          effect->mTextureCoords.height = -effect->mTextureCoords.height;
-        }
-        GetCompositor()->DrawGeometry(rect, aClipRect, aEffectChain,
-                                      aOpacity, aTransform, aGeometry);
-        GetCompositor()->DrawDiagnostics(diagnosticFlags | DiagnosticFlags::BIGIMAGE,
-                                         rect, aClipRect, aTransform, mFlashCounter);
-      } while (it->NextTile());
-      it->EndBigImageIteration();
-      // layer border
-      GetCompositor()->DrawDiagnostics(diagnosticFlags, pictureRect,
-                                       aClipRect, aTransform, mFlashCounter);
-    } else {
-      IntSize textureSize = mCurrentTextureSource->GetSize();
-      effect->mTextureCoords = Rect(Float(img->mPictureRect.x) / textureSize.width,
-                                    Float(img->mPictureRect.y) / textureSize.height,
-                                    Float(img->mPictureRect.width) / textureSize.width,
-                                    Float(img->mPictureRect.height) / textureSize.height);
-
-      if (img->mTextureHost->GetFlags() & TextureFlags::ORIGIN_BOTTOM_LEFT) {
-        effect->mTextureCoords.y = effect->mTextureCoords.YMost();
-        effect->mTextureCoords.height = -effect->mTextureCoords.height;
-      }
-
-      GetCompositor()->DrawGeometry(pictureRect, aClipRect, aEffectChain,
-                                    aOpacity, aTransform, aGeometry);
-      GetCompositor()->DrawDiagnostics(diagnosticFlags,
-                                       pictureRect, aClipRect,
-                                       aTransform, mFlashCounter);
-    }
-  }
-
-  // Update mBias last. This can change which frame ChooseImage(Index) would
-  // return, and we don't want to do that until we've finished compositing
-  // since callers of ChooseImage(Index) assume the same image will be chosen
-  // during a given composition. This must happen after autoLock's
-  // destructor!
-  mBias = UpdateBias(
-      GetCompositor()->GetCompositionTime(), mImages[imageIndex].mTimeStamp,
-      uint32_t(imageIndex + 1) < mImages.Length() ?
-          mImages[imageIndex + 1].mTimeStamp : TimeStamp(),
-      mBias);
-}
-
-void
-ImageHost::SetCompositor(Compositor* aCompositor)
-{
-  if (mCompositor != aCompositor) {
-    for (auto& img : mImages) {
-      img.mTextureHost->SetCompositor(aCompositor);
-    }
-  }
-  CompositableHost::SetCompositor(aCompositor);
-}
-
-void
-ImageHost::PrintInfo(std::stringstream& aStream, const char* aPrefix)
-{
-  aStream << aPrefix;
-  aStream << nsPrintfCString("ImageHost (0x%p)", this).get();
-
-  nsAutoCString pfx(aPrefix);
-  pfx += "  ";
-  for (auto& img : mImages) {
-    aStream << "\n";
-    img.mTextureHost->PrintInfo(aStream, pfx.get());
-    AppendToString(aStream, img.mPictureRect, " [picture-rect=", "]");
-  }
-}
-
-void
-ImageHost::Dump(std::stringstream& aStream,
-                const char* aPrefix,
-                bool aDumpHtml)
-{
-  for (auto& img : mImages) {
-    aStream << aPrefix;
-    aStream << (aDumpHtml ? "<ul><li>TextureHost: "
-                             : "TextureHost: ");
-    DumpTextureHost(aStream, img.mTextureHost);
-    aStream << (aDumpHtml ? " </li></ul> " : " ");
-  }
-}
-
-LayerRenderState
-ImageHost::GetRenderState()
-{
-  TimedImage* img = ChooseImage();
-  if (img) {
-    SetCurrentTextureHost(img->mTextureHost);
-    return img->mTextureHost->GetRenderState();
-  }
-  return LayerRenderState();
-}
-
-already_AddRefed<gfx::DataSourceSurface>
-ImageHost::GetAsSurface()
-{
-  TimedImage* img = ChooseImage();
-  if (img) {
-    return img->mTextureHost->GetAsSurface();
-  }
-  return nullptr;
-}
-
-bool
-ImageHost::Lock()
-{
-  MOZ_ASSERT(!mLocked);
-  TimedImage* img = ChooseImage();
-  if (!img) {
-    return false;
-  }
-
-  SetCurrentTextureHost(img->mTextureHost);
-
-  if (!mCurrentTextureHost->Lock()) {
-    return false;
-  }
-  mLocked = true;
-  return true;
-}
-
-void
-ImageHost::Unlock()
-{
-  MOZ_ASSERT(mLocked);
-
-  if (mCurrentTextureHost) {
-    mCurrentTextureHost->Unlock();
-  }
-  mLocked = false;
-}
-
-IntSize
-ImageHost::GetImageSize() const
-{
-  const TimedImage* img = ChooseImage();
-  if (img) {
-    return IntSize(img->mPictureRect.width, img->mPictureRect.height);
-  }
-  return IntSize();
-}
-
-bool
-ImageHost::IsOpaque()
-{
-  const TimedImage* img = ChooseImage();
-  if (!img) {
-    return false;
-  }
-
-  if (img->mPictureRect.width == 0 ||
-      img->mPictureRect.height == 0 ||
-      !img->mTextureHost) {
-    return false;
-  }
-
-  gfx::SurfaceFormat format = img->mTextureHost->GetFormat();
-  if (gfx::IsOpaque(format)) {
-    return true;
-  }
-  return false;
-}
-
-already_AddRefed<TexturedEffect>
-ImageHost::GenEffect(const gfx::SamplingFilter aSamplingFilter)
-{
-  TimedImage* img = ChooseImage();
-  if (!img) {
-    return nullptr;
-  }
-  SetCurrentTextureHost(img->mTextureHost);
-  if (!mCurrentTextureHost->BindTextureSource(mCurrentTextureSource)) {
-    return nullptr;
-  }
-  bool isAlphaPremultiplied = true;
-  if (mCurrentTextureHost->GetFlags() & TextureFlags::NON_PREMULTIPLIED) {
-    isAlphaPremultiplied = false;
-  }
-
-  return CreateTexturedEffect(mCurrentTextureHost,
-                              mCurrentTextureSource,
-                              aSamplingFilter,
-                              isAlphaPremultiplied,
-                              GetRenderState());
+  int index = ChooseImageIndex();
+  return index >= 0 ? &mImages[index] : nullptr;
 }
 
 } // namespace layers
 } // namespace mozilla
+
+#undef BIAS_TIME_MS
copy from gfx/layers/composite/ImageHost.h
copy to gfx/layers/composite/ImageComposite.h
--- a/gfx/layers/composite/ImageHost.h
+++ b/gfx/layers/composite/ImageComposite.h
@@ -1,95 +1,32 @@
 /* -*- 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/. */
 
-#ifndef MOZILLA_GFX_IMAGEHOST_H
-#define MOZILLA_GFX_IMAGEHOST_H
+#ifndef MOZILLA_GFX_IMAGECOMPOSITE_H
+#define MOZILLA_GFX_IMAGECOMPOSITE_H
 
-#include <stdio.h>                      // for FILE
-#include "CompositableHost.h"           // for CompositableHost
-#include "mozilla/Attributes.h"         // for override
-#include "mozilla/RefPtr.h"             // for RefPtr
-#include "mozilla/gfx/MatrixFwd.h"      // for Matrix4x4
-#include "mozilla/gfx/Point.h"          // for Point
-#include "mozilla/gfx/Polygon.h"        // for Polygon
-#include "mozilla/gfx/Rect.h"           // for Rect
-#include "mozilla/gfx/Types.h"          // for SamplingFilter
-#include "mozilla/layers/CompositorTypes.h"  // for TextureInfo, etc
-#include "mozilla/layers/LayersSurfaces.h"  // for SurfaceDescriptor
-#include "mozilla/layers/LayersTypes.h"  // for LayerRenderState, etc
-#include "mozilla/layers/TextureHost.h"  // for TextureHost, etc
-#include "mozilla/mozalloc.h"           // for operator delete
-#include "nsCOMPtr.h"                   // for already_AddRefed
-#include "nsRect.h"                     // for mozilla::gfx::IntRect
-#include "nsRegionFwd.h"                // for nsIntRegion
-#include "nscore.h"                     // for nsACString
+#include "CompositableHost.h"           // for CompositableTextureHostRef
+#include "mozilla/gfx/2D.h"
+#include "mozilla/TimeStamp.h"          // for TimeStamp
+#include "nsTArray.h"
 
 namespace mozilla {
 namespace layers {
 
-class Compositor;
-struct EffectChain;
-class ImageContainerParent;
-
 /**
- * ImageHost. Works with ImageClientSingle and ImageClientBuffered
+ * Implements Image selection logic.
  */
-class ImageHost : public CompositableHost
+class ImageComposite
 {
 public:
-  explicit ImageHost(const TextureInfo& aTextureInfo);
-  ~ImageHost();
-
-  virtual CompositableType GetType() override { return mTextureInfo.mCompositableType; }
-
-  virtual void Composite(LayerComposite* aLayer,
-                         EffectChain& aEffectChain,
-                         float aOpacity,
-                         const gfx::Matrix4x4& aTransform,
-                         const gfx::SamplingFilter aSamplingFilter,
-                         const gfx::IntRect& aClipRect,
-                         const nsIntRegion* aVisibleRegion = nullptr,
-                         const Maybe<gfx::Polygon>& aGeometry = Nothing()) override;
-
-  virtual void UseTextureHost(const nsTArray<TimedTexture>& aTextures) override;
-
-  virtual void RemoveTextureHost(TextureHost* aTexture) override;
-
-  virtual TextureHost* GetAsTextureHost(gfx::IntRect* aPictureRect = nullptr) override;
-
-  virtual void Attach(Layer* aLayer,
-                      Compositor* aCompositor,
-                      AttachFlags aFlags = NO_FLAGS) override;
-
-  virtual void SetCompositor(Compositor* aCompositor) override;
-
-  gfx::IntSize GetImageSize() const override;
-
-  virtual LayerRenderState GetRenderState() override;
-
-  virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) override;
-
-  virtual void Dump(std::stringstream& aStream,
-                    const char* aPrefix = "",
-                    bool aDumpHtml = false) override;
-
-  virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override;
-
-  virtual bool Lock() override;
-
-  virtual void Unlock() override;
-
-  virtual already_AddRefed<TexturedEffect> GenEffect(const gfx::SamplingFilter aSamplingFilter) override;
-
-  void SetCurrentTextureHost(TextureHost* aTexture);
-
-  virtual void CleanupResources() override;
+  explicit ImageComposite();
+  ~ImageComposite();
 
   int32_t GetFrameID()
   {
     const TimedImage* img = ChooseImage();
     return img ? img->mFrameID : -1;
   }
 
   int32_t GetProducerID()
@@ -105,37 +42,32 @@ public:
     // Don't apply bias to frame times
     BIAS_NONE,
     // Apply a negative bias to frame times to keep them before the vsync time
     BIAS_NEGATIVE,
     // Apply a positive bias to frame times to keep them after the vsync time
     BIAS_POSITIVE,
   };
 
-  bool IsOpaque();
+protected:
+  static Bias UpdateBias(const TimeStamp& aCompositionTime,
+                         const TimeStamp& aCompositedImageTime,
+                         const TimeStamp& aNextImageTime, // may be null
+                         ImageComposite::Bias aBias);
 
-protected:
+  virtual TimeStamp GetCompositionTime() const = 0;
+
   struct TimedImage {
     CompositableTextureHostRef mTextureHost;
     TimeStamp mTimeStamp;
     gfx::IntRect mPictureRect;
     int32_t mFrameID;
     int32_t mProducerID;
   };
 
-  // Use a simple RefPtr because the same texture is already held by a
-  // a CompositableTextureHostRef in the array of TimedImage.
-  // See the comment in CompositableTextureRef for more details.
-  RefPtr<TextureHost> mCurrentTextureHost;
-  CompositableTextureSourceRef mCurrentTextureSource;
-  // When doing texture uploads it's best to alternate between two (or three)
-  // texture sources so that the texture we upload to isn't being used by
-  // the GPU to composite the previous frame.
-  RefPtr<TextureSource> mExtraTextureSource;
-
   /**
    * ChooseImage is guaranteed to return the same TimedImage every time it's
    * called during the same composition, up to the end of Composite() ---
    * it depends only on mImages, mCompositor->GetCompositionTime(), and mBias.
    * mBias is updated at the end of Composite().
    */
   const TimedImage* ChooseImage() const;
   TimedImage* ChooseImage();
@@ -143,16 +75,14 @@ protected:
 
   nsTArray<TimedImage> mImages;
   int32_t mLastFrameID;
   int32_t mLastProducerID;
   /**
    * Bias to apply to the next frame.
    */
   Bias mBias;
-
-  bool mLocked;
 };
 
 } // namespace layers
 } // namespace mozilla
 
-#endif
+#endif // MOZILLA_GFX_IMAGECOMPOSITE_H
--- a/gfx/layers/composite/ImageHost.cpp
+++ b/gfx/layers/composite/ImageHost.cpp
@@ -11,31 +11,30 @@
 #include "mozilla/layers/Compositor.h"  // for Compositor
 #include "mozilla/layers/Effects.h"     // for TexturedEffect, Effect, etc
 #include "mozilla/layers/LayerManagerComposite.h"     // for TexturedEffect, Effect, etc
 #include "nsAString.h"
 #include "nsDebug.h"                    // for NS_WARNING, NS_ASSERTION
 #include "nsPrintfCString.h"            // for nsPrintfCString
 #include "nsString.h"                   // for nsAutoCString
 
+// this is also defined in ImageComposite.cpp
 #define BIAS_TIME_MS 1.0
 
 namespace mozilla {
 
 using namespace gfx;
 
 namespace layers {
 
 class ISurfaceAllocator;
 
 ImageHost::ImageHost(const TextureInfo& aTextureInfo)
   : CompositableHost(aTextureInfo)
-  , mLastFrameID(-1)
-  , mLastProducerID(-1)
-  , mBias(BIAS_NONE)
+  , ImageComposite()
   , mLocked(false)
 {}
 
 ImageHost::~ImageHost()
 {
 }
 
 void
@@ -147,107 +146,24 @@ ImageHost::RemoveTextureHost(TextureHost
   for (int32_t i = mImages.Length() - 1; i >= 0; --i) {
     if (mImages[i].mTextureHost == aTexture) {
       aTexture->UnbindTextureSource();
       mImages.RemoveElementAt(i);
     }
   }
 }
 
-static TimeStamp
-GetBiasedTime(const TimeStamp& aInput, ImageHost::Bias aBias)
+TimeStamp
+ImageHost::GetCompositionTime() const
 {
-  switch (aBias) {
-  case ImageHost::BIAS_NEGATIVE:
-    return aInput - TimeDuration::FromMilliseconds(BIAS_TIME_MS);
-  case ImageHost::BIAS_POSITIVE:
-    return aInput + TimeDuration::FromMilliseconds(BIAS_TIME_MS);
-  default:
-    return aInput;
-  }
-}
-
-static ImageHost::Bias
-UpdateBias(const TimeStamp& aCompositionTime,
-           const TimeStamp& aCompositedImageTime,
-           const TimeStamp& aNextImageTime, // may be null
-           ImageHost::Bias aBias)
-{
-  if (aCompositedImageTime.IsNull()) {
-    return ImageHost::BIAS_NONE;
-  }
-  TimeDuration threshold = TimeDuration::FromMilliseconds(1.0);
-  if (aCompositionTime - aCompositedImageTime < threshold &&
-      aCompositionTime - aCompositedImageTime > -threshold) {
-    // The chosen frame's time is very close to the composition time (probably
-    // just before the current composition time, but due to previously set
-    // negative bias, it could be just after the current composition time too).
-    // If the inter-frame time is almost exactly equal to (a multiple of)
-    // the inter-composition time, then we're in a dangerous situation because
-    // jitter might cause frames to fall one side or the other of the
-    // composition times, causing many frames to be skipped or duplicated.
-    // Try to prevent that by adding a negative bias to the frame times during
-    // the next composite; that should ensure the next frame's time is treated
-    // as falling just before a composite time.
-    return ImageHost::BIAS_NEGATIVE;
+  TimeStamp time;
+  if (GetCompositor()) {
+    time = GetCompositor()->GetCompositionTime();
   }
-  if (!aNextImageTime.IsNull() &&
-      aNextImageTime - aCompositionTime < threshold &&
-      aNextImageTime - aCompositionTime > -threshold) {
-    // The next frame's time is very close to our composition time (probably
-    // just after the current composition time, but due to previously set
-    // positive bias, it could be just before the current composition time too).
-    // We're in a dangerous situation because jitter might cause frames to
-    // fall one side or the other of the composition times, causing many frames
-    // to be skipped or duplicated.
-    // Try to prevent that by adding a negative bias to the frame times during
-    // the next composite; that should ensure the next frame's time is treated
-    // as falling just before a composite time.
-    return ImageHost::BIAS_POSITIVE;
-  }
-  return ImageHost::BIAS_NONE;
-}
-
-int ImageHost::ChooseImageIndex() const
-{
-  if (!GetCompositor() || mImages.IsEmpty()) {
-    return -1;
-  }
-  TimeStamp now = GetCompositor()->GetCompositionTime();
-
-  if (now.IsNull()) {
-    // Not in a composition, so just return the last image we composited
-    // (if it's one of the current images).
-    for (uint32_t i = 0; i < mImages.Length(); ++i) {
-      if (mImages[i].mFrameID == mLastFrameID &&
-          mImages[i].mProducerID == mLastProducerID) {
-        return i;
-      }
-    }
-    return -1;
-  }
-
-  uint32_t result = 0;
-  while (result + 1 < mImages.Length() &&
-      GetBiasedTime(mImages[result + 1].mTimeStamp, mBias) <= now) {
-    ++result;
-  }
-  return result;
-}
-
-const ImageHost::TimedImage* ImageHost::ChooseImage() const
-{
-  int index = ChooseImageIndex();
-  return index >= 0 ? &mImages[index] : nullptr;
-}
-
-ImageHost::TimedImage* ImageHost::ChooseImage()
-{
-  int index = ChooseImageIndex();
-  return index >= 0 ? &mImages[index] : nullptr;
+  return time;
 }
 
 TextureHost*
 ImageHost::GetAsTextureHost(IntRect* aPictureRect)
 {
   TimedImage* img = ChooseImage();
   if (img) {
     SetCurrentTextureHost(img->mTextureHost);
@@ -569,8 +485,10 @@ ImageHost::GenEffect(const gfx::Sampling
                               mCurrentTextureSource,
                               aSamplingFilter,
                               isAlphaPremultiplied,
                               GetRenderState());
 }
 
 } // namespace layers
 } // namespace mozilla
+
+#undef BIAS_TIME_MS
--- a/gfx/layers/composite/ImageHost.h
+++ b/gfx/layers/composite/ImageHost.h
@@ -11,36 +11,37 @@
 #include "mozilla/Attributes.h"         // for override
 #include "mozilla/RefPtr.h"             // for RefPtr
 #include "mozilla/gfx/MatrixFwd.h"      // for Matrix4x4
 #include "mozilla/gfx/Point.h"          // for Point
 #include "mozilla/gfx/Polygon.h"        // for Polygon
 #include "mozilla/gfx/Rect.h"           // for Rect
 #include "mozilla/gfx/Types.h"          // for SamplingFilter
 #include "mozilla/layers/CompositorTypes.h"  // for TextureInfo, etc
+#include "mozilla/layers/ImageComposite.h"  // for ImageComposite
 #include "mozilla/layers/LayersSurfaces.h"  // for SurfaceDescriptor
 #include "mozilla/layers/LayersTypes.h"  // for LayerRenderState, etc
 #include "mozilla/layers/TextureHost.h"  // for TextureHost, etc
 #include "mozilla/mozalloc.h"           // for operator delete
 #include "nsCOMPtr.h"                   // for already_AddRefed
 #include "nsRect.h"                     // for mozilla::gfx::IntRect
 #include "nsRegionFwd.h"                // for nsIntRegion
 #include "nscore.h"                     // for nsACString
 
 namespace mozilla {
 namespace layers {
 
 class Compositor;
 struct EffectChain;
-class ImageContainerParent;
 
 /**
  * ImageHost. Works with ImageClientSingle and ImageClientBuffered
  */
-class ImageHost : public CompositableHost
+class ImageHost : public CompositableHost,
+                  public ImageComposite
 {
 public:
   explicit ImageHost(const TextureInfo& aTextureInfo);
   ~ImageHost();
 
   virtual CompositableType GetType() override { return mTextureInfo.mCompositableType; }
 
   virtual void Composite(LayerComposite* aLayer,
@@ -81,78 +82,31 @@ public:
   virtual void Unlock() override;
 
   virtual already_AddRefed<TexturedEffect> GenEffect(const gfx::SamplingFilter aSamplingFilter) override;
 
   void SetCurrentTextureHost(TextureHost* aTexture);
 
   virtual void CleanupResources() override;
 
-  int32_t GetFrameID()
-  {
-    const TimedImage* img = ChooseImage();
-    return img ? img->mFrameID : -1;
-  }
-
-  int32_t GetProducerID()
-  {
-    const TimedImage* img = ChooseImage();
-    return img ? img->mProducerID : -1;
-  }
-
-  int32_t GetLastFrameID() const { return mLastFrameID; }
-  int32_t GetLastProducerID() const { return mLastProducerID; }
-
-  enum Bias {
-    // Don't apply bias to frame times
-    BIAS_NONE,
-    // Apply a negative bias to frame times to keep them before the vsync time
-    BIAS_NEGATIVE,
-    // Apply a positive bias to frame times to keep them after the vsync time
-    BIAS_POSITIVE,
-  };
-
   bool IsOpaque();
 
 protected:
-  struct TimedImage {
-    CompositableTextureHostRef mTextureHost;
-    TimeStamp mTimeStamp;
-    gfx::IntRect mPictureRect;
-    int32_t mFrameID;
-    int32_t mProducerID;
-  };
+  // ImageComposite
+  virtual TimeStamp GetCompositionTime() const override;
 
   // Use a simple RefPtr because the same texture is already held by a
   // a CompositableTextureHostRef in the array of TimedImage.
   // See the comment in CompositableTextureRef for more details.
   RefPtr<TextureHost> mCurrentTextureHost;
   CompositableTextureSourceRef mCurrentTextureSource;
   // When doing texture uploads it's best to alternate between two (or three)
   // texture sources so that the texture we upload to isn't being used by
   // the GPU to composite the previous frame.
   RefPtr<TextureSource> mExtraTextureSource;
 
-  /**
-   * ChooseImage is guaranteed to return the same TimedImage every time it's
-   * called during the same composition, up to the end of Composite() ---
-   * it depends only on mImages, mCompositor->GetCompositionTime(), and mBias.
-   * mBias is updated at the end of Composite().
-   */
-  const TimedImage* ChooseImage() const;
-  TimedImage* ChooseImage();
-  int ChooseImageIndex() const;
-
-  nsTArray<TimedImage> mImages;
-  int32_t mLastFrameID;
-  int32_t mLastProducerID;
-  /**
-   * Bias to apply to the next frame.
-   */
-  Bias mBias;
-
   bool mLocked;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif
--- a/gfx/layers/composite/LayerManagerComposite.cpp
+++ b/gfx/layers/composite/LayerManagerComposite.cpp
@@ -55,16 +55,17 @@
 #ifdef MOZ_WIDGET_ANDROID
 #include <android/log.h>
 #include <android/native_window.h>
 #endif
 #if defined(MOZ_WIDGET_ANDROID)
 #include "opengl/CompositorOGL.h"
 #include "GLContextEGL.h"
 #include "GLContextProvider.h"
+#include "mozilla/widget/AndroidCompositorWidget.h"
 #include "ScopedGLHelpers.h"
 #endif
 #include "GeckoProfiler.h"
 #include "TextRenderer.h"               // for TextRenderer
 #include "mozilla/layers/CompositorBridgeParent.h"
 #include "TreeTraversal.h"              // for ForEachNode
 
 #ifdef USE_SKIA
--- a/gfx/layers/composite/LayerManagerComposite.h
+++ b/gfx/layers/composite/LayerManagerComposite.h
@@ -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/. */
 
 #ifndef GFX_LayerManagerComposite_H
 #define GFX_LayerManagerComposite_H
 
 #include <stdint.h>                     // for int32_t, uint32_t
+#include "CompositableHost.h"           // for CompositableHost, ImageCompositeNotificationInfo
 #include "GLDefs.h"                     // for GLenum
 #include "Layers.h"
 #include "Units.h"                      // for ParentLayerIntRect
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT, etc
 #include "mozilla/Attributes.h"         // for override
 #include "mozilla/RefPtr.h"             // for RefPtr, already_AddRefed
 #include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/Point.h"          // for IntSize
@@ -44,37 +45,31 @@ namespace mozilla {
 namespace gfx {
 class DrawTarget;
 } // namespace gfx
 
 namespace layers {
 
 class CanvasLayerComposite;
 class ColorLayerComposite;
-class CompositableHost;
 class Compositor;
 class ContainerLayerComposite;
 struct EffectChain;
 class ImageLayer;
 class ImageLayerComposite;
 class LayerComposite;
 class RefLayerComposite;
 class PaintedLayerComposite;
 class TextRenderer;
 class CompositingRenderTarget;
 struct FPSState;
 class PaintCounter;
 
 static const int kVisualWarningDuration = 150; // ms
 
-struct ImageCompositeNotificationInfo {
-  base::ProcessId mImageBridgeProcessId;
-  ImageCompositeNotification mNotification;
-};
-
 // An implementation of LayerManager that acts as a pair with ClientLayerManager
 // and is mirrored across IPDL. This gets managed/updated by LayerTransactionParent.
 class HostLayerManager : public LayerManager
 {
 public:
   HostLayerManager();
   ~HostLayerManager();
 
@@ -104,18 +99,16 @@ public:
   virtual LayersBackend GetBackendType() override
   {
     MOZ_CRASH("GFX: Shouldn't be called for composited layer manager");
   }
   virtual void GetBackendName(nsAString& name) override
   {
     MOZ_CRASH("GFX: Shouldn't be called for composited layer manager");
   }
-  virtual TextureFactoryIdentifier GetTextureFactoryIdentifier() = 0;
-
 
   virtual void ForcePresent() = 0;
   virtual void AddInvalidRegion(const nsIntRegion& aRegion) = 0;
   virtual void ClearApproximatelyVisibleRegions(uint64_t aLayersId,
                                                 const Maybe<uint32_t>& aPresShellId) = 0;
   virtual void UpdateApproximatelyVisibleRegion(const ScrollableLayerGuid& aGuid,
                                                 const CSSIntRegion& aRegion) = 0;
 
--- a/gfx/layers/composite/TextureHost.cpp
+++ b/gfx/layers/composite/TextureHost.cpp
@@ -197,17 +197,18 @@ TextureHost::Create(const SurfaceDescrip
       return CreateBackendIndependentTextureHost(aDesc, aDeallocator, aFlags);
 
     case SurfaceDescriptor::TEGLImageDescriptor:
     case SurfaceDescriptor::TSurfaceTextureDescriptor:
     case SurfaceDescriptor::TSurfaceDescriptorSharedGLTexture:
       return CreateTextureHostOGL(aDesc, aDeallocator, aFlags);
 
     case SurfaceDescriptor::TSurfaceDescriptorMacIOSurface:
-      if (aBackend == LayersBackend::LAYERS_OPENGL) {
+      if (aBackend == LayersBackend::LAYERS_OPENGL ||
+          aBackend == LayersBackend::LAYERS_WR) {
         return CreateTextureHostOGL(aDesc, aDeallocator, aFlags);
       } else {
         return CreateTextureHostBasic(aDesc, aDeallocator, aFlags);
       }
 
 #ifdef MOZ_X11
     case SurfaceDescriptor::TSurfaceDescriptorX11: {
       const SurfaceDescriptorX11& desc = aDesc.get_SurfaceDescriptorX11();
--- a/gfx/layers/ipc/CompositorBridgeChild.cpp
+++ b/gfx/layers/ipc/CompositorBridgeChild.cpp
@@ -15,16 +15,17 @@
 #include "mozilla/layers/ImageBridgeChild.h"
 #include "mozilla/layers/APZChild.h"
 #include "mozilla/layers/IAPZCTreeManager.h"
 #include "mozilla/layers/APZCTreeManagerChild.h"
 #include "mozilla/layers/LayerTransactionChild.h"
 #include "mozilla/layers/PLayerTransactionChild.h"
 #include "mozilla/layers/TextureClient.h"// for TextureClient
 #include "mozilla/layers/TextureClientPool.h"// for TextureClientPool
+#include "mozilla/layers/WebRenderBridgeChild.h"
 #include "mozilla/gfx/gfxVars.h"
 #include "mozilla/gfx/GPUProcessManager.h"
 #include "mozilla/gfx/Logging.h"
 #include "mozilla/mozalloc.h"           // for operator new, etc
 #include "nsAutoPtr.h"
 #include "nsDebug.h"                    // for NS_RUNTIMEABORT
 #include "nsIObserver.h"                // for nsIObserver
 #include "nsISupportsImpl.h"            // for MOZ_COUNT_CTOR, etc
@@ -140,16 +141,24 @@ CompositorBridgeChild::Destroy()
   AutoTArray<PLayerTransactionChild*, 16> transactions;
   ManagedPLayerTransactionChild(transactions);
   for (int i = transactions.Length() - 1; i >= 0; --i) {
     RefPtr<LayerTransactionChild> layers =
       static_cast<LayerTransactionChild*>(transactions[i]);
     layers->Destroy();
   }
 
+  AutoTArray<PWebRenderBridgeChild*, 16> wRBridges;
+  ManagedPWebRenderBridgeChild(wRBridges);
+  for (int i = wRBridges.Length() - 1; i >= 0; --i) {
+    RefPtr<WebRenderBridgeChild> wRBridge =
+      static_cast<WebRenderBridgeChild*>(wRBridges[i]);
+    wRBridge->Destroy();
+  }
+
   const ManagedContainer<PTextureChild>& textures = ManagedPTextureChild();
   for (auto iter = textures.ConstIter(); !iter.Done(); iter.Next()) {
     RefPtr<TextureClient> texture = TextureClient::AsTextureClient(iter.Get()->GetKey());
 
     if (texture) {
       texture->Destroy();
     }
   }
@@ -563,18 +572,20 @@ CompositorBridgeChild::RecvHideAllPlugin
 
 mozilla::ipc::IPCResult
 CompositorBridgeChild::RecvDidComposite(const uint64_t& aId, const uint64_t& aTransactionId,
                                         const TimeStamp& aCompositeStart,
                                         const TimeStamp& aCompositeEnd)
 {
   if (mLayerManager) {
     MOZ_ASSERT(aId == 0);
-    RefPtr<ClientLayerManager> m = mLayerManager->AsClientLayerManager();
-    MOZ_ASSERT(m);
+    MOZ_ASSERT(mLayerManager->GetBackendType() == LayersBackend::LAYERS_CLIENT ||
+               mLayerManager->GetBackendType() == LayersBackend::LAYERS_WR);
+    // Hold a reference to keep LayerManager alive. See Bug 1242668.
+    RefPtr<LayerManager> m = mLayerManager;
     m->DidComposite(aTransactionId, aCompositeStart, aCompositeEnd);
   } else if (aId != 0) {
     RefPtr<dom::TabChild> child = dom::TabChild::GetFrom(aId);
     if (child) {
       child->DidComposite(aTransactionId, aCompositeStart, aCompositeEnd);
     }
   }
 
@@ -1114,11 +1125,27 @@ CompositorBridgeChild::WillEndTransactio
 }
 
 void
 CompositorBridgeChild::HandleFatalError(const char* aName, const char* aMsg) const
 {
   dom::ContentChild::FatalErrorIfNotUsingGPUProcess(aName, aMsg, OtherPid());
 }
 
+PWebRenderBridgeChild*
+CompositorBridgeChild::AllocPWebRenderBridgeChild(const wr::PipelineId& aPipelineId, TextureFactoryIdentifier*)
+{
+  WebRenderBridgeChild* child = new WebRenderBridgeChild(aPipelineId);
+  child->AddIPDLReference();
+  return child;
+}
+
+bool
+CompositorBridgeChild::DeallocPWebRenderBridgeChild(PWebRenderBridgeChild* aActor)
+{
+  WebRenderBridgeChild* child = static_cast<WebRenderBridgeChild*>(aActor);
+  child->ReleaseIPDLReference();
+  return true;
+}
+
 } // namespace layers
 } // namespace mozilla
 
--- a/gfx/layers/ipc/CompositorBridgeChild.h
+++ b/gfx/layers/ipc/CompositorBridgeChild.h
@@ -99,17 +99,17 @@ public:
                    const TimeStamp& aCompositeEnd) override;
 
   virtual mozilla::ipc::IPCResult
   RecvInvalidateLayers(const uint64_t& aLayersId) override;
 
   virtual mozilla::ipc::IPCResult
   RecvCompositorUpdated(const uint64_t& aLayersId,
                         const TextureFactoryIdentifier& aNewIdentifier,
-                        const uint64_t& aSequenceNumber) override;
+                        const uint64_t& aSeqNo) override;
 
   virtual mozilla::ipc::IPCResult
   RecvUpdatePluginConfigurations(const LayoutDeviceIntPoint& aContentOffset,
                                  const LayoutDeviceIntRegion& aVisibleRegion,
                                  nsTArray<PluginWindowData>&& aPlugins) override;
 
   virtual mozilla::ipc::IPCResult
   RecvCaptureAllPlugins(const uintptr_t& aParentWidget) override;
@@ -218,16 +218,19 @@ public:
 
   PAPZChild* AllocPAPZChild(const uint64_t& aLayersId) override;
   bool DeallocPAPZChild(PAPZChild* aActor) override;
 
   void ProcessingError(Result aCode, const char* aReason) override;
 
   void WillEndTransaction();
 
+  PWebRenderBridgeChild* AllocPWebRenderBridgeChild(const wr::PipelineId& aPipelineId, TextureFactoryIdentifier*) override;
+  bool DeallocPWebRenderBridgeChild(PWebRenderBridgeChild* aActor) override;
+
   uint64_t DeviceResetSequenceNumber() const {
     return mDeviceResetSequenceNumber;
   }
 
 private:
   // Private destructor, to discourage deletion outside of Release():
   virtual ~CompositorBridgeChild();
 
--- a/gfx/layers/ipc/CompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CompositorBridgeParent.cpp
@@ -23,18 +23,20 @@
 #endif
 #include "gfxPrefs.h"                   // for gfxPrefs
 #include "mozilla/AutoRestore.h"        // for AutoRestore
 #include "mozilla/ClearOnShutdown.h"    // for ClearOnShutdown
 #include "mozilla/DebugOnly.h"          // for DebugOnly
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/TabParent.h"
 #include "mozilla/gfx/2D.h"          // for DrawTarget
+#include "mozilla/gfx/GPUChild.h"       // for GfxPrefValue
 #include "mozilla/gfx/Point.h"          // for IntSize
 #include "mozilla/gfx/Rect.h"          // for IntSize
+#include "mozilla/gfx/gfxVars.h"        // for gfxVars
 #include "VRManager.h"                  // for VRManager
 #include "mozilla/ipc/Transport.h"      // for Transport
 #include "mozilla/layers/APZCTreeManager.h"  // for APZCTreeManager
 #include "mozilla/layers/APZCTreeManagerParent.h"  // for APZCTreeManagerParent
 #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
@@ -45,17 +47,21 @@
 #include "mozilla/layers/CrossProcessCompositorBridgeParent.h"
 #include "mozilla/layers/FrameUniformityData.h"
 #include "mozilla/layers/ImageBridgeParent.h"
 #include "mozilla/layers/LayerManagerComposite.h"
 #include "mozilla/layers/LayerTreeOwnerTracker.h"
 #include "mozilla/layers/LayersTypes.h"
 #include "mozilla/layers/PLayerTransactionParent.h"
 #include "mozilla/layers/RemoteContentController.h"
+#include "mozilla/layers/WebRenderBridgeParent.h"
+#include "mozilla/layers/WebRenderCompositableHolder.h"
+#include "mozilla/layers/WebRenderCompositorOGL.h"
 #include "mozilla/layout/RenderFrameParent.h"
+#include "mozilla/webrender/WebRenderAPI.h"
 #include "mozilla/media/MediaSystemResourceService.h" // for MediaSystemResourceService
 #include "mozilla/mozalloc.h"           // for operator new, etc
 #include "mozilla/Telemetry.h"
 #ifdef MOZ_WIDGET_GTK
 #include "basic/X11BasicCompositor.h" // for X11BasicCompositor
 #endif
 #include "nsCOMPtr.h"                   // for already_AddRefed
 #include "nsDebug.h"                    // for NS_ASSERTION, etc
@@ -230,24 +236,30 @@ static StaticAutoPtr<CompositorMap> sCom
 
 void
 CompositorBridgeParent::Setup()
 {
   EnsureLayerTreeMapReady();
 
   MOZ_ASSERT(!sCompositorMap);
   sCompositorMap = new CompositorMap;
+
+  gfxPrefs::SetWebRenderProfilerEnabledChangeCallback(
+    [](const GfxPrefValue& aValue) -> void {
+      CompositorBridgeParent::SetWebRenderProfilerEnabled(aValue.get_bool());
+  });
 }
 
 void
 CompositorBridgeParent::Shutdown()
 {
   MOZ_ASSERT(sCompositorMap);
   MOZ_ASSERT(sCompositorMap->empty());
   sCompositorMap = nullptr;
+  gfxPrefs::SetWebRenderProfilerEnabledChangeCallback(nullptr);
 }
 
 void
 CompositorBridgeParent::FinishShutdown()
 {
   // TODO: this should be empty by now...
   sIndirectLayerTrees.clear();
 }
@@ -374,28 +386,30 @@ CompositorBridgeParent::Initialize()
 
   { // scope lock
     MonitorAutoLock lock(*sIndirectLayerTreesLock);
     sIndirectLayerTrees[mRootLayerTreeID].mParent = this;
   }
 
   LayerScope::SetPixelScale(mScale.scale);
 
-  mCompositorScheduler = new CompositorVsyncScheduler(this, mWidget);
+  if (!mOptions.UseWebRender()) {
+    mCompositorScheduler = new CompositorVsyncScheduler(this, mWidget);
+  }
 }
 
 mozilla::ipc::IPCResult
 CompositorBridgeParent::RecvReset(nsTArray<LayersBackend>&& aBackendHints,
                                   const uint64_t& aSeqNo,
                                   bool* aResult,
                                   TextureFactoryIdentifier* aOutIdentifier)
 {
   Maybe<TextureFactoryIdentifier> newIdentifier;
   ResetCompositorTask(aBackendHints, aSeqNo, &newIdentifier);
-  
+
   if (newIdentifier) {
     *aResult = true;
     *aOutIdentifier = newIdentifier.value();
   } else {
     *aResult = false;
   }
 
   return IPC_OK();
@@ -445,16 +459,29 @@ CompositorBridgeParent::StopAndClearReso
       lts->mLayerManager = nullptr;
       lts->mParent = nullptr;
     });
     mLayerManager->Destroy();
     mLayerManager = nullptr;
     mCompositionManager = nullptr;
   }
 
+  if (mWrBridge) {
+    MonitorAutoLock lock(*sIndirectLayerTreesLock);
+    ForEachIndirectLayerTree([this] (LayerTreeState* lts, uint64_t) -> void {
+      if (lts->mWrBridge) {
+        lts->mWrBridge->Destroy();
+        lts->mWrBridge = nullptr;
+      }
+      lts->mParent = nullptr;
+    });
+    mWrBridge->Destroy();
+    mWrBridge = nullptr;
+  }
+
   if (mCompositor) {
     mCompositor->DetachWidget();
     mCompositor->Destroy();
     mCompositor = nullptr;
   }
 
   // This must be destroyed now since it accesses the widget.
   if (mCompositorScheduler) {
@@ -975,24 +1002,28 @@ CompositorBridgeParent::CompositeToTarge
   mLayerManager->SetDebugOverlayWantsNextFrame(false);
   mLayerManager->EndTransaction(time);
 
   if (!aTarget) {
     TimeStamp end = TimeStamp::Now();
     DidComposite(start, end);
   }
 
-  // We're not really taking advantage of the stored composite-again-time here.
-  // We might be able to skip the next few composites altogether. However,
-  // that's a bit complex to implement and we'll get most of the advantage
-  // by skipping compositing when we detect there's nothing invalid. This is why
-  // we do "composite until" rather than "composite again at".
-  if (!mCompositor->GetCompositeUntilTime().IsNull() ||
-      mLayerManager->DebugOverlayWantsNextFrame()) {
-    ScheduleComposition();
+  if (mCompositor) {
+    // We're not really taking advantage of the stored composite-again-time here.
+    // We might be able to skip the next few composites altogether. However,
+    // that's a bit complex to implement and we'll get most of the advantage
+    // by skipping compositing when we detect there's nothing invalid. This is why
+    // we do "composite until" rather than "composite again at".
+    if (!mCompositor->GetCompositeUntilTime().IsNull() ||
+        mLayerManager->DebugOverlayWantsNextFrame()) {
+      ScheduleComposition();
+    }
+  } else {
+    // TODO(bug 1328602) Figure out what we should do here with the render thread.
   }
 
 #ifdef COMPOSITOR_PERFORMANCE_WARNING
   TimeDuration executionTime = TimeStamp::Now() - mCompositorScheduler->GetLastComposeTime();
   TimeDuration frameBudget = TimeDuration::FromMilliseconds(15);
   int32_t frameRate = CalculateCompositionFrameRate();
   if (frameRate > 0) {
     frameBudget = TimeDuration::FromSeconds(1.0 / frameRate);
@@ -1004,17 +1035,22 @@ CompositorBridgeParent::CompositeToTarge
 #endif
 
   // 0 -> Full-tilt composite
   if (gfxPrefs::LayersCompositionFrameRate() == 0
     || mLayerManager->GetCompositor()->GetDiagnosticTypes() & DiagnosticTypes::FLASH_BORDERS) {
     // Special full-tilt composite mode for performance testing
     ScheduleComposition();
   }
-  mCompositor->SetCompositionTime(TimeStamp());
+
+  if (mCompositor) {
+    mCompositor->SetCompositionTime(TimeStamp());
+  } else {
+    // TODO(bug 1328602) Need an equivalent that works with the rende thread.
+  }
 
   mozilla::Telemetry::AccumulateTimeDelta(mozilla::Telemetry::COMPOSITE_TIME, start);
 }
 
 mozilla::ipc::IPCResult
 CompositorBridgeParent::RecvRemotePluginsReady()
 {
 #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
@@ -1531,16 +1567,82 @@ CompositorBridgeParent::RecvAdoptChild(c
   }
 
   if (mApzcTreeManager && parent) {
     parent->ChildAdopted(mApzcTreeManager);
   }
   return IPC_OK();
 }
 
+PWebRenderBridgeParent*
+CompositorBridgeParent::AllocPWebRenderBridgeParent(const wr::PipelineId& aPipelineId,
+                                                    TextureFactoryIdentifier* aTextureFactoryIdentifier)
+{
+#ifndef MOZ_ENABLE_WEBRENDER
+  // Extra guard since this in the parent process and we don't want a malicious
+  // child process invoking this codepath before it's ready
+  MOZ_RELEASE_ASSERT(false);
+#endif
+  MOZ_ASSERT(aPipelineId.mHandle == mRootLayerTreeID);
+  MOZ_ASSERT(!mWrBridge);
+  MOZ_ASSERT(!mCompositor);
+  MOZ_ASSERT(!mCompositorScheduler);
+
+
+  MOZ_ASSERT(mWidget);
+  RefPtr<widget::CompositorWidget> widget = mWidget;
+  RefPtr<wr::WebRenderAPI> api = wr::WebRenderAPI::Create(gfxPrefs::WebRenderProfilerEnabled(), this, Move(widget));
+  RefPtr<WebRenderCompositableHolder> holder = new WebRenderCompositableHolder();
+  MOZ_ASSERT(api); // TODO have a fallback
+  api->SetRootPipeline(aPipelineId);
+  mWrBridge = new WebRenderBridgeParent(this, aPipelineId, mWidget, Move(api), Move(holder));
+
+  mCompositorScheduler = mWrBridge->CompositorScheduler();
+  MOZ_ASSERT(mCompositorScheduler);
+  mWrBridge.get()->AddRef(); // IPDL reference
+  MonitorAutoLock lock(*sIndirectLayerTreesLock);
+  auto pipelineHandle = aPipelineId.mHandle;
+  MOZ_ASSERT(sIndirectLayerTrees[pipelineHandle].mWrBridge == nullptr);
+  sIndirectLayerTrees[pipelineHandle].mWrBridge = mWrBridge;
+  *aTextureFactoryIdentifier = mWrBridge->GetTextureFactoryIdentifier();
+  return mWrBridge;
+}
+
+bool
+CompositorBridgeParent::DeallocPWebRenderBridgeParent(PWebRenderBridgeParent* aActor)
+{
+#ifndef MOZ_ENABLE_WEBRENDER
+  // Extra guard since this in the parent process and we don't want a malicious
+  // child process invoking this codepath before it's ready
+  MOZ_RELEASE_ASSERT(false);
+#endif
+  WebRenderBridgeParent* parent = static_cast<WebRenderBridgeParent*>(aActor);
+  {
+    MonitorAutoLock lock(*sIndirectLayerTreesLock);
+    auto it = sIndirectLayerTrees.find(parent->PipelineId().mHandle);
+    if (it != sIndirectLayerTrees.end()) {
+      it->second.mWrBridge = nullptr;
+    }
+  }
+  parent->Release(); // IPDL reference
+  return true;
+}
+
+void
+CompositorBridgeParent::SetWebRenderProfilerEnabled(bool aEnabled)
+{
+  MonitorAutoLock lock(*sIndirectLayerTreesLock);
+  for (auto it = sIndirectLayerTrees.begin(); it != sIndirectLayerTrees.end(); it++) {
+    LayerTreeState* state = &it->second;
+    if (state->mWrBridge) {
+      state->mWrBridge->SetWebRenderProfilerEnabled(aEnabled);
+    }
+  }
+}
+
 void
 EraseLayerState(uint64_t aId)
 {
   MonitorAutoLock lock(*sIndirectLayerTreesLock);
 
   auto iter = sIndirectLayerTrees.find(aId);
   if (iter != sIndirectLayerTrees.end()) {
     CompositorBridgeParent* parent = iter->second.mParent;
@@ -1711,27 +1813,41 @@ CompositorBridgeParent::LayerTreeState::
 {
   return mParent;
 }
 
 void
 CompositorBridgeParent::DidComposite(TimeStamp& aCompositeStart,
                                      TimeStamp& aCompositeEnd)
 {
-  Unused << SendDidComposite(0, mPendingTransaction, aCompositeStart, aCompositeEnd);
+  NotifyDidComposite(mPendingTransaction, aCompositeStart, aCompositeEnd);
   mPendingTransaction = 0;
+}
+
+void
+CompositorBridgeParent::NotifyDidComposite(uint64_t aTransactionId, TimeStamp& aCompositeStart, TimeStamp& aCompositeEnd)
+{
+  Unused << SendDidComposite(0, aTransactionId, aCompositeStart, aCompositeEnd);
 
   if (mLayerManager) {
     nsTArray<ImageCompositeNotificationInfo> notifications;
     mLayerManager->ExtractImageCompositeNotifications(&notifications);
     if (!notifications.IsEmpty()) {
       Unused << ImageBridgeParent::NotifyImageComposites(notifications);
     }
   }
 
+  if (mWrBridge) {
+    nsTArray<ImageCompositeNotificationInfo> notifications;
+    mWrBridge->ExtractImageCompositeNotifications(&notifications);
+    if (!notifications.IsEmpty()) {
+      Unused << ImageBridgeParent::NotifyImageComposites(notifications);
+    }
+  }
+
   MonitorAutoLock lock(*sIndirectLayerTreesLock);
   ForEachIndirectLayerTree([&] (LayerTreeState* lts, const uint64_t& aLayersId) -> void {
     if (lts->mCrossProcessParent) {
       CrossProcessCompositorBridgeParent* cpcp = lts->mCrossProcessParent;
       cpcp->DidComposite(aLayersId, aCompositeStart, aCompositeEnd);
     }
   });
 }
--- a/gfx/layers/ipc/CompositorBridgeParent.h
+++ b/gfx/layers/ipc/CompositorBridgeParent.h
@@ -66,16 +66,17 @@ class Compositor;
 class CompositorBridgeParent;
 class CompositorVsyncScheduler;
 class HostLayerManager;
 class LayerTransactionParent;
 class PAPZParent;
 class CrossProcessCompositorBridgeParent;
 class CompositorThreadHolder;
 class InProcessCompositorSession;
+class WebRenderBridgeParent;
 
 struct ScopedLayerTreeRegistration
 {
   ScopedLayerTreeRegistration(APZCTreeManager* aApzctm,
                               uint64_t aLayersId,
                               Layer* aRoot,
                               GeckoContentController* aController);
   ~ScopedLayerTreeRegistration();
@@ -110,16 +111,20 @@ public:
                                       const uint64_t& aInputBlockId,
                                       const nsTArray<ScrollableLayerGuid>& aTargets) = 0;
   virtual void UpdatePaintTime(LayerTransactionParent* aLayerTree, const TimeDuration& aPaintTime) {}
 
   virtual ShmemAllocator* AsShmemAllocator() override { return this; }
 
   virtual mozilla::ipc::IPCResult RecvSyncWithCompositor() override { return IPC_OK(); }
 
+  virtual void ObserveLayerUpdate(uint64_t aLayersId, uint64_t aEpoch, bool aActive) = 0;
+
+  virtual void NotifyDidComposite(uint64_t aTransactionId, TimeStamp& aCompositeStart, TimeStamp& aCompositeEnd) {}
+
   // HostIPCAllocator
   virtual base::ProcessId GetChildProcessId() override;
   virtual void NotifyNotUsed(PTextureParent* aTexture, uint64_t aTransactionId) override;
   virtual void SendAsyncMessage(const InfallibleTArray<AsyncParentMessageData>& aMessage) override;
 
   // ShmemAllocator
   virtual bool AllocShmem(size_t aSize,
                           mozilla::ipc::SharedMemory::SharedMemoryType aType,
@@ -237,16 +242,18 @@ public:
   virtual bool DeallocPTextureParent(PTextureParent* actor) override;
 
   virtual bool IsSameProcess() const override;
 
 
   PCompositorWidgetParent* AllocPCompositorWidgetParent(const CompositorWidgetInitData& aInitData) override;
   bool DeallocPCompositorWidgetParent(PCompositorWidgetParent* aActor) override;
 
+  void ObserveLayerUpdate(uint64_t aLayersId, uint64_t aEpoch, bool aActive) override { }
+
   /**
    * Request that the compositor be recreated due to a shared device reset.
    * This must be called on the main thread, and blocks until a task posted
    * to the compositor thread has completed.
    *
    * Note that this posts a task directly, rather than using synchronous
    * IPDL, and waits on a monitor notification from the compositor thread.
    * We do this as a best-effort attempt to jump any IPDL messages that
@@ -341,16 +348,17 @@ public:
   struct LayerTreeState {
     LayerTreeState();
     ~LayerTreeState();
     RefPtr<Layer> mRoot;
     RefPtr<GeckoContentController> mController;
     APZCTreeManagerParent* mApzcTreeManagerParent;
     RefPtr<CompositorBridgeParent> mParent;
     HostLayerManager* mLayerManager;
+    RefPtr<WebRenderBridgeParent> mWrBridge;
     // Pointer to the CrossProcessCompositorBridgeParent. Used by APZCs to share
     // their FrameMetrics with the corresponding child process that holds
     // the PCompositorBridgeChild
     CrossProcessCompositorBridgeParent* mCrossProcessParent;
     TargetConfig mTargetConfig;
     APZTestData mApzTestData;
     LayerTransactionParent* mLayerTree;
     nsTArray<PluginWindowData> mPluginData;
@@ -437,16 +445,21 @@ public:
                                                    CompositorOptions* aOptions) override;
 
   RefPtr<APZCTreeManager> GetAPZCTreeManager();
 
   CompositorOptions GetOptions() const {
     return mOptions;
   }
 
+  PWebRenderBridgeParent* AllocPWebRenderBridgeParent(const wr::PipelineId& aPipelineId,
+                                                      TextureFactoryIdentifier* aTextureFactoryIdentifier) override;
+  bool DeallocPWebRenderBridgeParent(PWebRenderBridgeParent* aActor) override;
+  static void SetWebRenderProfilerEnabled(bool aEnabled);
+
   static CompositorBridgeParent* GetCompositorBridgeParentFromLayersId(const uint64_t& aLayersId);
 private:
 
   void Initialize();
 
   /**
    * Called during destruction in order to release resources as early as possible.
    */
@@ -531,24 +544,27 @@ protected:
   /**
    * Return true if current state allows compositing, that is
    * finishing a layers transaction.
    */
   bool CanComposite();
 
   void DidComposite(TimeStamp& aCompositeStart, TimeStamp& aCompositeEnd);
 
+  virtual void NotifyDidComposite(uint64_t aTransactionId, TimeStamp& aCompositeStart, TimeStamp& aCompositeEnd) override;
+
   // The indirect layer tree lock must be held before calling this function.
   // Callback should take (LayerTreeState* aState, const uint64_t& aLayersId)
   template <typename Lambda>
   inline void ForEachIndirectLayerTree(const Lambda& aCallback);
 
   RefPtr<HostLayerManager> mLayerManager;
   RefPtr<Compositor> mCompositor;
   RefPtr<AsyncCompositionManager> mCompositionManager;
+  RefPtr<WebRenderBridgeParent> mWrBridge;
   widget::CompositorWidget* mWidget;
   TimeStamp mTestTime;
   CSSToLayoutDeviceScale mScale;
   TimeDuration mVsyncRate;
   bool mIsTesting;
 
   uint64_t mPendingTransaction;
 
--- a/gfx/layers/ipc/CompositorVsyncScheduler.cpp
+++ b/gfx/layers/ipc/CompositorVsyncScheduler.cpp
@@ -79,17 +79,16 @@ CompositorVsyncScheduler::CompositorVsyn
   , mNeedsComposite(0)
   , mVsyncNotificationsSkipped(0)
   , mWidget(aWidget)
   , mCurrentCompositeTaskMonitor("CurrentCompositeTaskMonitor")
   , mCurrentCompositeTask(nullptr)
   , mSetNeedsCompositeMonitor("SetNeedsCompositeMonitor")
   , mSetNeedsCompositeTask(nullptr)
 {
-  MOZ_ASSERT(NS_IsMainThread() || XRE_GetProcessType() == GeckoProcessType_GPU);
   mVsyncObserver = new Observer(this);
 
   // mAsapScheduling is set on the main thread during init,
   // but is only accessed after on the compositor thread.
   mAsapScheduling = gfxPrefs::LayersCompositionFrameRate() == 0 ||
                     gfxPlatform::IsInLayoutAsapMode();
 }
 
--- a/gfx/layers/ipc/CrossProcessCompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CrossProcessCompositorBridgeParent.cpp
@@ -16,16 +16,18 @@
 #include "mozilla/layers/APZThreadUtils.h"  // for APZCTreeManager
 #include "mozilla/layers/AsyncCompositionManager.h"
 #include "mozilla/layers/CompositorOptions.h"
 #include "mozilla/layers/CompositorThread.h"
 #include "mozilla/layers/LayerManagerComposite.h"
 #include "mozilla/layers/LayerTreeOwnerTracker.h"
 #include "mozilla/layers/PLayerTransactionParent.h"
 #include "mozilla/layers/RemoteContentController.h"
+#include "mozilla/layers/WebRenderBridgeParent.h"
+#include "mozilla/layers/WebRenderCompositableHolder.h"
 #include "mozilla/mozalloc.h"           // for operator new, etc
 #include "nsDebug.h"                    // for NS_ASSERTION, etc
 #include "nsTArray.h"                   // for nsTArray
 #include "nsXULAppAPI.h"                // for XRE_GetIOMessageLoop
 #include "mozilla/Unused.h"
 #include "mozilla/StaticPtr.h"
 
 using namespace std;
@@ -189,16 +191,64 @@ CrossProcessCompositorBridgeParent::Allo
 bool
 CrossProcessCompositorBridgeParent::DeallocPAPZParent(PAPZParent* aActor)
 {
   RemoteContentController* controller = static_cast<RemoteContentController*>(aActor);
   controller->Release();
   return true;
 }
 
+PWebRenderBridgeParent*
+CrossProcessCompositorBridgeParent::AllocPWebRenderBridgeParent(const wr::PipelineId& aPipelineId,
+                                                                TextureFactoryIdentifier* aTextureFactoryIdentifier)
+{
+#ifndef MOZ_ENABLE_WEBRENDER
+  // Extra guard since this in the parent process and we don't want a malicious
+  // child process invoking this codepath before it's ready
+  MOZ_RELEASE_ASSERT(false);
+#endif
+  // Check to see if this child process has access to this layer tree.
+  if (!LayerTreeOwnerTracker::Get()->IsMapped(aPipelineId.mHandle, OtherPid())) {
+    NS_ERROR("Unexpected layers id in AllocPAPZCTreeManagerParent; dropping message...");
+    return nullptr;
+  }
+
+  auto pipelineHandle = aPipelineId.mHandle;
+  MonitorAutoLock lock(*sIndirectLayerTreesLock);
+  MOZ_ASSERT(sIndirectLayerTrees.find(pipelineHandle) != sIndirectLayerTrees.end());
+  MOZ_ASSERT(sIndirectLayerTrees[pipelineHandle].mWrBridge == nullptr);
+  CompositorBridgeParent* cbp = sIndirectLayerTrees[pipelineHandle].mParent;
+  WebRenderBridgeParent* root = sIndirectLayerTrees[cbp->RootLayerTreeId()].mWrBridge.get();
+
+  WebRenderBridgeParent* parent = nullptr;
+  RefPtr<wr::WebRenderAPI> api = root->GetWebRenderAPI();
+  RefPtr<WebRenderCompositableHolder> holder = root->CompositableHolder();
+  parent = new WebRenderBridgeParent(this, aPipelineId, nullptr, Move(api), Move(holder));
+
+  parent->AddRef(); // IPDL reference
+  sIndirectLayerTrees[pipelineHandle].mCrossProcessParent = this;
+  sIndirectLayerTrees[pipelineHandle].mWrBridge = parent;
+  *aTextureFactoryIdentifier = parent->GetTextureFactoryIdentifier();
+  return parent;
+}
+
+bool
+CrossProcessCompositorBridgeParent::DeallocPWebRenderBridgeParent(PWebRenderBridgeParent* aActor)
+{
+#ifndef MOZ_ENABLE_WEBRENDER
+  // Extra guard since this in the parent process and we don't want a malicious
+  // child process invoking this codepath before it's ready
+  MOZ_RELEASE_ASSERT(false);
+#endif
+  WebRenderBridgeParent* parent = static_cast<WebRenderBridgeParent*>(aActor);
+  EraseLayerState(parent->PipelineId().mHandle);
+  parent->Release(); // IPDL reference
+  return true;
+}
+
 mozilla::ipc::IPCResult
 CrossProcessCompositorBridgeParent::RecvNotifyChildCreated(const uint64_t& child)
 {
   MonitorAutoLock lock(*sIndirectLayerTreesLock);
   for (LayerTreeMap::iterator it = sIndirectLayerTrees.begin();
        it != sIndirectLayerTrees.end(); it++) {
     CompositorBridgeParent::LayerTreeState* lts = &it->second;
     if (lts->mParent && lts->mCrossProcessParent == this) {
@@ -267,16 +317,19 @@ CrossProcessCompositorBridgeParent::DidC
   uint64_t aId,
   TimeStamp& aCompositeStart,
   TimeStamp& aCompositeEnd)
 {
   sIndirectLayerTreesLock->AssertCurrentThreadOwns();
   if (LayerTransactionParent *layerTree = sIndirectLayerTrees[aId].mLayerTree) {
     Unused << SendDidComposite(aId, layerTree->GetPendingTransactionId(), aCompositeStart, aCompositeEnd);
     layerTree->SetPendingTransactionId(0);
+  } else if (WebRenderBridgeParent* wrbridge = sIndirectLayerTrees[aId].mWrBridge) {
+    Unused << SendDidComposite(aId, wrbridge->GetPendingTransactionId(), aCompositeStart, aCompositeEnd);
+    wrbridge->SetPendingTransactionId(0);
   }
 }
 
 void
 CrossProcessCompositorBridgeParent::ForceComposite(LayerTransactionParent* aLayerTree)
 {
   uint64_t id = aLayerTree->GetId();
   MOZ_ASSERT(id != 0);
@@ -524,10 +577,24 @@ CrossProcessCompositorBridgeParent::Upda
     CompositorBridgeParent::GetIndirectShadowTree(id);
   if (!state || !state->mParent) {
     return;
   }
 
   state->mParent->UpdatePaintTime(aLayerTree, aPaintTime);
 }
 
+void
+CrossProcessCompositorBridgeParent::ObserveLayerUpdate(uint64_t aLayersId, uint64_t aEpoch, bool aActive)
+{
+  MOZ_ASSERT(aLayersId != 0);
+
+  CompositorBridgeParent::LayerTreeState* state =
+    CompositorBridgeParent::GetIndirectShadowTree(aLayersId);
+  if (!state || !state->mParent) {
+    return;
+  }
+
+  Unused << state->mParent->SendObserveLayerUpdate(aLayersId, aEpoch, aActive);
+}
+
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/ipc/CrossProcessCompositorBridgeParent.h
+++ b/gfx/layers/ipc/CrossProcessCompositorBridgeParent.h
@@ -146,16 +146,22 @@ public:
   virtual PAPZCTreeManagerParent* AllocPAPZCTreeManagerParent(const uint64_t& aLayersId) override;
   virtual bool DeallocPAPZCTreeManagerParent(PAPZCTreeManagerParent* aActor) override;
 
   virtual PAPZParent* AllocPAPZParent(const uint64_t& aLayersId) override;
   virtual bool DeallocPAPZParent(PAPZParent* aActor) override;
 
   virtual void UpdatePaintTime(LayerTransactionParent* aLayerTree, const TimeDuration& aPaintTime) override;
 
+  PWebRenderBridgeParent* AllocPWebRenderBridgeParent(const wr::PipelineId& aPipelineId,
+                                                      TextureFactoryIdentifier* aTextureFactoryIdentifier) override;
+  bool DeallocPWebRenderBridgeParent(PWebRenderBridgeParent* aActor) override;
+
+  void ObserveLayerUpdate(uint64_t aLayersId, uint64_t aEpoch, bool aActive) override;
+
 protected:
   void OnChannelConnected(int32_t pid) override {
     mCompositorThreadHolder = CompositorThreadHolder::GetSingleton();
   }
 private:
   // Private destructor, to discourage deletion outside of Release():
   virtual ~CrossProcessCompositorBridgeParent();
 
--- a/gfx/layers/ipc/PCompositorBridge.ipdl
+++ b/gfx/layers/ipc/PCompositorBridge.ipdl
@@ -9,16 +9,17 @@ include LayersSurfaces;
 include LayersMessages;
 include PlatformWidgetTypes;
 include protocol PAPZ;
 include protocol PAPZCTreeManager;
 include protocol PBrowser;
 include protocol PCompositorWidget;
 include protocol PLayerTransaction;
 include protocol PTexture;
+include protocol PWebRenderBridge;
 include "mozilla/GfxMessageUtils.h";
 
 using struct mozilla::null_t from "ipc/IPCMessageUtils.h";
 using struct mozilla::layers::TextureFactoryIdentifier from "mozilla/layers/CompositorTypes.h";
 using struct mozilla::layers::FrameMetrics from "FrameMetrics.h";
 using mozilla::layers::FrameMetrics::ViewID from "FrameMetrics.h";
 using mozilla::layers::MaybeZoomConstraints from "FrameMetrics.h";
 using struct mozilla::layers::ScrollableLayerGuid from "FrameMetrics.h";
@@ -28,16 +29,17 @@ using mozilla::CrossProcessMutexHandle f
 using mozilla::ipc::SharedMemoryBasic::Handle from "mozilla/ipc/SharedMemoryBasic.h";
 using mozilla::CSSIntRegion from "Units.h";
 using mozilla::LayoutDeviceIntPoint from "Units.h";
 using mozilla::LayoutDeviceIntRegion from "Units.h";
 using class mozilla::TimeStamp from "mozilla/TimeStamp.h";
 using class mozilla::layers::FrameUniformityData from "mozilla/layers/FrameUniformityData.h";
 using mozilla::layers::TextureFlags from "mozilla/layers/CompositorTypes.h";
 using mozilla::layers::CompositorOptions from "mozilla/layers/CompositorOptions.h";
+using mozilla::wr::PipelineId from "mozilla/webrender/WebRenderTypes.h";
 
 namespace mozilla {
 namespace layers {
 
 
 /**
  * The PCompositorBridge protocol is a top-level protocol for the compositor.
  * There is an instance of the protocol for each compositor, plus one for each
@@ -66,16 +68,17 @@ namespace layers {
 sync protocol PCompositorBridge
 {
   manages PAPZ;
   manages PAPZCTreeManager;
   // A Compositor manages a single Layer Manager (PLayerTransaction)
   manages PLayerTransaction;
   manages PTexture;
   manages PCompositorWidget;
+  manages PWebRenderBridge;
 
 child:
   // The child should invalidate retained layers. This is used for local
   // compositor device resets, such as in CompositorD3D9, and ensures that
   // TextureSources are recreated.
   async InvalidateLayers(uint64_t layersId);
 
   // The compositor type or device has changed, and a new texture factory
@@ -233,16 +236,20 @@ parent:
    * Sent when the child has finished CaptureAllPlugins.
    */
   async AllPluginsCaptured();
 
   async PTexture(SurfaceDescriptor aSharedData, LayersBackend aBackend, TextureFlags aTextureFlags, uint64_t id, uint64_t aSerial);
 
   sync SyncWithCompositor();
 
+  // The pipelineId is the same as the layersId
+  sync PWebRenderBridge(PipelineId pipelineId)
+    returns (TextureFactoryIdentifier textureFactoryIdentifier);
+
 child:
   // Send back Compositor Frame Metrics from APZCs so tiled layers can
   // update progressively.
   async SharedCompositorFrameMetrics(Handle metrics, CrossProcessMutexHandle mutex, uint64_t aLayersId, uint32_t aAPZCId);
   async ReleaseSharedCompositorFrameMetrics(ViewID aId, uint32_t aAPZCId);
 };
 
 } // layers
new file mode 100644
--- /dev/null
+++ b/gfx/layers/ipc/PWebRenderBridge.ipdl
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* 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 LayersSurfaces;
+include LayersMessages;
+include "mozilla/GfxMessageUtils.h";
+include "mozilla/layers/WebRenderMessageUtils.h";
+
+include WebRenderMessages;
+include protocol PCompositorBridge;
+include protocol PTexture;
+
+using struct mozilla::layers::TextureInfo from "mozilla/layers/CompositorTypes.h";
+using mozilla::layers::CompositableHandle from "mozilla/layers/LayersTypes.h";
+using mozilla::wr::ByteBuffer from "mozilla/webrender/WebRenderTypes.h";
+using mozilla::wr::ImageKey from "mozilla/webrender/WebRenderTypes.h";
+
+namespace mozilla {
+namespace layers {
+
+sync protocol PWebRenderBridge
+{
+  manager PCompositorBridge;
+
+parent:
+  async NewCompositable(CompositableHandle handle, TextureInfo info);
+  async ReleaseCompositable(CompositableHandle compositable);
+
+  sync Create(IntSize aSize);
+  sync AddImage(IntSize aSize, uint32_t aStride,
+                SurfaceFormat aFormat, ByteBuffer aBytes)
+    returns (ImageKey aOutImageKey);
+  sync UpdateImage(ImageKey aImageKey, IntSize aSize,
+                   SurfaceFormat aFormat, ByteBuffer aBytes);
+  sync DeleteImage(ImageKey aImageKey);
+  sync DPBegin(IntSize aSize)
+    returns (bool aOutSuccess);
+  async DPEnd(WebRenderCommand[] commands, OpDestroy[] toDestroy, uint64_t fwdTransactionId, uint64_t transactionId);
+  sync DPSyncEnd(WebRenderCommand[] commands, OpDestroy[] toDestroy, uint64_t fwdTransactionId, uint64_t transactionId);
+  sync DPGetSnapshot(PTexture texture);
+  async AddExternalImageId(uint64_t aImageId, CompositableHandle aHandle);
+  async AddExternalImageIdForCompositable(uint64_t aImageId, CompositableHandle aHandle);
+  async RemoveExternalImageId(uint64_t aImageId);
+  async SetLayerObserverEpoch(uint64_t layerObserverEpoch);
+  async ClearCachedResources();
+
+  async Shutdown();
+child:
+  async __delete__();
+};
+
+} // layers
+} // mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/layers/ipc/WebRenderMessages.ipdlh
@@ -0,0 +1,105 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* 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 LayersSurfaces;
+include LayersMessages;
+include protocol PTexture;
+
+using WrBorderRadius from "mozilla/webrender/webrender_ffi.h";
+using WrBorderSide from "mozilla/webrender/webrender_ffi.h";
+using WrColor from "mozilla/webrender/webrender_ffi.h";
+using WrImageKey from "mozilla/webrender/webrender_ffi.h";
+using WrTextureFilter from "mozilla/webrender/webrender_ffi.h";
+using WrLayoutSize from "mozilla/webrender/webrender_ffi.h";
+using WrRect from "mozilla/webrender/webrender_ffi.h";
+using WrGlyphArray from "mozilla/webrender/webrender_ffi.h";
+using WrMixBlendMode from "mozilla/webrender/webrender_ffi.h";
+using MaybeImageMask from "mozilla/webrender/WebRenderTypes.h";
+using mozilla::gfx::Matrix4x4 from "mozilla/gfx/Matrix.h";
+using mozilla::wr::ByteBuffer from "mozilla/webrender/WebRenderTypes.h";
+using mozilla::wr::PipelineId from "mozilla/webrender/WebRenderTypes.h";
+using mozilla::LayerIntRegion from "Units.h";
+
+namespace mozilla {
+namespace layers {
+
+struct OpDPPushStackingContext {
+  WrRect bounds;
+  WrRect overflow;
+  MaybeImageMask mask;
+  float opacity;
+  Animation[] animations;
+  Matrix4x4 matrix;
+  WrMixBlendMode mixBlendMode;
+  uint64_t scrollid;
+};
+
+struct OpDPPopStackingContext { };
+
+struct OpDPPushRect {
+  WrRect bounds;
+  WrRect clip;
+  WrColor color;
+};
+
+struct OpDPPushBorder {
+  WrRect bounds;
+  WrRect clip;
+  WrBorderSide top;
+  WrBorderSide right;
+  WrBorderSide bottom;
+  WrBorderSide left;
+  WrBorderRadius radius;
+};
+
+struct OpDPPushImage {
+  WrRect bounds;
+  WrRect clip;
+  MaybeImageMask mask;
+  WrTextureFilter filter;
+  WrImageKey key;
+};
+
+struct OpDPPushExternalImageId {
+  LayerIntRegion validBufferRegion;
+  WrRect bounds;
+  WrRect clip;
+  MaybeImageMask mask;
+  WrTextureFilter filter;
+  uint64_t externalImageId;
+};
+
+struct OpDPPushIframe {
+  WrRect bounds;
+  WrRect clip;
+  PipelineId pipelineId;
+};
+
+struct OpDPPushText {
+  WrRect bounds;
+  WrRect clip;
+  WrGlyphArray[] glyph_array;
+  uint32_t font_index;
+  float glyph_size;
+  ByteBuffer font_buffer;
+  uint32_t font_buffer_length;
+};
+
+union WebRenderCommand {
+  OpDPPushStackingContext;
+  OpDPPopStackingContext;
+  OpDPPushRect;
+  OpDPPushBorder;
+  OpDPPushImage;
+  OpDPPushExternalImageId;
+  OpDPPushIframe;
+  OpDPPushText;
+  CompositableOperation;
+};
+
+} // namespace
+} // namespace
--- a/gfx/layers/moz.build
+++ b/gfx/layers/moz.build
@@ -145,16 +145,17 @@ EXPORTS.mozilla.layers += [
     'client/TiledContentClient.h',
     'composite/AsyncCompositionManager.h',
     'composite/CanvasLayerComposite.h',
     'composite/ColorLayerComposite.h',
     'composite/ContainerLayerComposite.h',
     'composite/ContentHost.h',
     'composite/FrameUniformityData.h',
     'composite/GPUVideoTextureHost.h',
+    'composite/ImageComposite.h',
     'composite/ImageHost.h',
     'composite/ImageLayerComposite.h',
     'composite/LayerManagerComposite.h',
     'composite/PaintedLayerComposite.h',
     'composite/TextureHost.h',
     'composite/TiledContentHost.h',
     'Compositor.h',
     'CompositorTypes.h',
@@ -201,16 +202,24 @@ EXPORTS.mozilla.layers += [
     'opengl/TextureClientOGL.h',
     'opengl/TextureHostOGL.h',
     'PersistentBufferProvider.h',
     'RenderTrace.h',
     'SourceSurfaceSharedData.h',
     'SourceSurfaceVolatileData.h',
     'TextureWrapperImage.h',
     'TransactionIdAllocator.h',
+    'wr/WebRenderBridgeChild.h',
+    'wr/WebRenderBridgeParent.h',
+    'wr/WebRenderCompositableHolder.h',
+    'wr/WebRenderCompositorOGL.h',
+    'wr/WebRenderImageHost.h',
+    'wr/WebRenderLayerManager.h',
+    'wr/WebRenderLayersLogging.h',
+    'wr/WebRenderMessageUtils.h',
 ]
 
 if CONFIG['MOZ_X11']:
     EXPORTS.mozilla.layers += [
         'basic/TextureClientX11.h',
         'basic/X11TextureSourceBasic.h',
         'composite/X11TextureHost.h',
         'ipc/ShadowLayerUtilsX11.h',
@@ -317,16 +326,17 @@ UNIFIED_SOURCES += [
     'composite/CanvasLayerComposite.cpp',
     'composite/ColorLayerComposite.cpp',
     'composite/CompositableHost.cpp',
     'composite/ContainerLayerComposite.cpp',
     'composite/ContentHost.cpp',
     'composite/FPSCounter.cpp',
     'composite/FrameUniformityData.cpp',
     'composite/GPUVideoTextureHost.cpp',
+    'composite/ImageComposite.cpp',
     'composite/ImageHost.cpp',
     'composite/ImageLayerComposite.cpp',
     'composite/LayerManagerComposite.cpp',
     'composite/PaintedLayerComposite.cpp',
     'composite/TextRenderer.cpp',
     'composite/TextureHost.cpp',
     'composite/TiledContentHost.cpp',
     'Compositor.cpp',
@@ -375,16 +385,30 @@ UNIFIED_SOURCES += [
     'protobuf/LayerScopePacket.pb.cc',
     'ReadbackProcessor.cpp',
     'RenderTrace.cpp',
     'RotatedBuffer.cpp',
     'ShareableCanvasLayer.cpp',
     'SourceSurfaceSharedData.cpp',
     'SourceSurfaceVolatileData.cpp',
     'TextureWrapperImage.cpp',
+    'wr/WebRenderBorderLayer.cpp',
+    'wr/WebRenderBridgeChild.cpp',
+    'wr/WebRenderBridgeParent.cpp',
+    'wr/WebRenderCanvasLayer.cpp',
+    'wr/WebRenderColorLayer.cpp',
+    'wr/WebRenderCompositableHolder.cpp',
+    'wr/WebRenderCompositorOGL.cpp',
+    'wr/WebRenderContainerLayer.cpp',
+    'wr/WebRenderImageHost.cpp',
+    'wr/WebRenderImageLayer.cpp',
+    'wr/WebRenderLayerManager.cpp',
+    'wr/WebRenderLayersLogging.cpp',
+    'wr/WebRenderPaintedLayer.cpp',
+    'wr/WebRenderTextLayer.cpp',
 ]
 
 SOURCES += [
     'basic/BasicImageLayer.cpp',
     'ImageContainer.cpp',
     'Layers.cpp',
     'LayerTreeInvalidation.cpp',
     'PersistentBufferProvider.cpp',
@@ -414,16 +438,18 @@ IPDL_SOURCES += [
     'ipc/PAPZ.ipdl',
     'ipc/PAPZCTreeManager.ipdl',
     'ipc/PCompositorBridge.ipdl',
     'ipc/PImageBridge.ipdl',
     'ipc/PLayerTransaction.ipdl',
     'ipc/PTexture.ipdl',
     'ipc/PUiCompositorController.ipdl',
     'ipc/PVideoBridge.ipdl',
+    'ipc/PWebRenderBridge.ipdl',
+    'ipc/WebRenderMessages.ipdlh',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 LOCAL_INCLUDES += [
     '/docshell/base',  # for nsDocShell.h
     '/layout/base',    # for TouchManager.h
     '/layout/generic', # for nsTextFrame.h
new file mode 100644
--- /dev/null
+++ b/gfx/layers/wr/WebRenderBorderLayer.cpp
@@ -0,0 +1,66 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "WebRenderBorderLayer.h"
+
+#include "WebRenderLayersLogging.h"
+#include "mozilla/gfx/Rect.h"
+#include "mozilla/webrender/webrender_ffi.h"
+#include "mozilla/layers/WebRenderBridgeChild.h"
+#include "mozilla/webrender/WebRenderTypes.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+void
+WebRenderBorderLayer::RenderLayer()
+{
+  WrScrollFrameStackingContextGenerator scrollFrames(this);
+
+  Rect rect = RelativeToVisible(mRect.ToUnknownRect());
+  Rect clip;
+  if (GetClipRect().isSome()) {
+    clip = RelativeToTransformedVisible(IntRectToRect(GetClipRect().ref().ToUnknownRect()));
+  } else {
+    clip = rect;
+  }
+
+  Rect relBounds = TransformedVisibleBoundsRelativeToParent();
+  Rect overflow(0, 0, relBounds.width, relBounds.height);
+  Matrix4x4 transform;// = GetTransform();
+
+  if (gfxPrefs::LayersDump()) {
+    printf_stderr("BorderLayer %p using bounds=%s, overflow=%s, transform=%s, rect=%s, clip=%s\n",
+                  this->GetLayer(),
+                  Stringify(relBounds).c_str(),
+                  Stringify(overflow).c_str(),
+                  Stringify(transform).c_str(),
+                  Stringify(rect).c_str(),
+                  Stringify(clip).c_str());
+  }
+
+  WrBridge()->AddWebRenderCommand(
+      OpDPPushStackingContext(wr::ToWrRect(relBounds),
+                              wr::ToWrRect(overflow),
+                              Nothing(),
+                              1.0f,
+                              GetAnimations(),
+                              transform,
+                              WrMixBlendMode::Normal,
+                              FrameMetrics::NULL_SCROLL_ID));
+  WrBridge()->AddWebRenderCommand(
+    OpDPPushBorder(wr::ToWrRect(rect), wr::ToWrRect(clip),
+                   wr::ToWrBorderSide(mWidths[0], mColors[0], mBorderStyles[0]),
+                   wr::ToWrBorderSide(mWidths[1], mColors[1], mBorderStyles[1]),
+                   wr::ToWrBorderSide(mWidths[2], mColors[2], mBorderStyles[2]),
+                   wr::ToWrBorderSide(mWidths[3], mColors[3], mBorderStyles[3]),
+                   wr::ToWrBorderRadius(mCorners[0], mCorners[1], mCorners[3], mCorners[2])));
+  WrBridge()->AddWebRenderCommand(OpDPPopStackingContext());
+}
+
+} // namespace layers
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/layers/wr/WebRenderBorderLayer.h
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_WEBRENDERBORDERLAYER_H
+#define GFX_WEBRENDERBORDERLAYER_H
+
+#include "Layers.h"
+#include "WebRenderLayerManager.h"
+
+namespace mozilla {
+namespace layers {
+
+class WebRenderBorderLayer : public WebRenderLayer,
+                             public BorderLayer {
+public:
+  explicit WebRenderBorderLayer(WebRenderLayerManager* aLayerManager)
+    : BorderLayer(aLayerManager, static_cast<WebRenderLayer*>(this))
+  {
+    MOZ_COUNT_CTOR(WebRenderBorderLayer);
+  }
+
+protected:
+  virtual ~WebRenderBorderLayer()
+  {
+    MOZ_COUNT_DTOR(WebRenderBorderLayer);
+  }
+
+public:
+  Layer* GetLayer() override { return this; }
+  void RenderLayer() override;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // GFX_WEBRENDERBORDERLAYER_H
new file mode 100644
--- /dev/null
+++ b/gfx/layers/wr/WebRenderBridgeChild.cpp
@@ -0,0 +1,299 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=4 ts=8 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/WebRenderBridgeChild.h"
+
+#include "gfxPlatform.h"
+#include "mozilla/layers/CompositableClient.h"
+#include "mozilla/layers/CompositorBridgeChild.h"
+#include "mozilla/layers/ImageDataSerializer.h"
+#include "mozilla/layers/PTextureChild.h"
+
+namespace mozilla {
+namespace layers {
+
+WebRenderBridgeChild::WebRenderBridgeChild(const wr::PipelineId& aPipelineId)
+  : mIsInTransaction(false)
+  , mSyncTransaction(false)
+  , mIPCOpen(false)
+  , mDestroyed(false)
+{
+}
+
+void
+WebRenderBridgeChild::Destroy()
+{
+  if (!IPCOpen()) {
+    return;
+  }
+  // mDestroyed is used to prevent calling Send__delete__() twice.
+  // When this function is called from CompositorBridgeChild::Destroy().
+  mDestroyed = true;
+
+  SendShutdown();
+}
+
+void
+WebRenderBridgeChild::ActorDestroy(ActorDestroyReason why)
+{
+  mDestroyed = true;
+}
+
+void
+WebRenderBridgeChild::AddWebRenderCommand(const WebRenderCommand& aCmd)
+{
+  MOZ_ASSERT(mIsInTransaction);
+  mCommands.AppendElement(aCmd);
+}
+
+bool
+WebRenderBridgeChild::DPBegin(const gfx::IntSize& aSize)
+{
+  MOZ_ASSERT(!mDestroyed);
+  MOZ_ASSERT(!mIsInTransaction);
+  bool success = false;
+  UpdateFwdTransactionId();
+  this->SendDPBegin(aSize, &success);
+  if (!success) {
+    return false;
+  }
+
+  mIsInTransaction = true;
+  return true;
+}
+
+void
+WebRenderBridgeChild::DPEnd(bool aIsSync, uint64_t aTransactionId)
+{
+  MOZ_ASSERT(!mDestroyed);
+  MOZ_ASSERT(mIsInTransaction);
+  if (aIsSync) {
+    this->SendDPSyncEnd(mCommands, mDestroyedActors, GetFwdTransactionId(), aTransactionId);
+  } else {
+    this->SendDPEnd(mCommands, mDestroyedActors, GetFwdTransactionId(), aTransactionId);
+  }
+
+  mCommands.Clear();
+  mDestroyedActors.Clear();
+  mIsInTransaction = false;
+  mSyncTransaction = false;
+}
+
+uint64_t
+WebRenderBridgeChild::GetNextExternalImageId()
+{
+  static uint32_t sNextID = 1;
+  ++sNextID;
+  MOZ_RELEASE_ASSERT(sNextID != UINT32_MAX);
+
+  // XXX replace external image id allocation with webrender's id allocation.
+  // Use proc id as IdNamespace for now.
+  uint32_t procId = static_cast<uint32_t>(base::GetCurrentProcId());
+  uint64_t imageId = procId;
+  imageId = imageId << 32 | sNextID;
+  return imageId;
+}
+
+uint64_t
+WebRenderBridgeChild::AllocExternalImageId(const CompositableHandle& aHandle)
+{
+  MOZ_ASSERT(!mDestroyed);
+
+  uint64_t imageId = GetNextExternalImageId();
+  SendAddExternalImageId(imageId, aHandle);
+  return imageId;
+}
+
+uint64_t
+WebRenderBridgeChild::AllocExternalImageIdForCompositable(CompositableClient* aCompositable)
+{
+  MOZ_ASSERT(!mDestroyed);
+  MOZ_ASSERT(aCompositable->IsConnected());
+
+  uint64_t imageId = GetNextExternalImageId();
+  SendAddExternalImageIdForCompositable(imageId, aCompositable->GetIPCHandle());
+  return imageId;
+}
+
+void
+WebRenderBridgeChild::DeallocExternalImageId(uint64_t aImageId)
+{
+  MOZ_ASSERT(!mDestroyed);
+  MOZ_ASSERT(aImageId);
+  SendRemoveExternalImageId(aImageId);
+}
+
+CompositorBridgeChild*
+WebRenderBridgeChild::GetCompositorBridgeChild()
+{
+  return static_cast<CompositorBridgeChild*>(Manager());
+}
+
+TextureForwarder*
+WebRenderBridgeChild::GetTextureForwarder()
+{
+  return static_cast<TextureForwarder*>(GetCompositorBridgeChild());
+}
+
+LayersIPCActor*
+WebRenderBridgeChild::GetLayersIPCActor()
+{
+  return static_cast<LayersIPCActor*>(GetCompositorBridgeChild());
+}
+
+void
+WebRenderBridgeChild::Connect(CompositableClient* aCompositable,
+                              ImageContainer* aImageContainer)
+{
+  MOZ_ASSERT(aCompositable);
+
+  static uint64_t sNextID = 1;
+  uint64_t id = sNextID++;
+
+  mCompositables.Put(id, aCompositable);
+
+  CompositableHandle handle(id);
+  aCompositable->InitIPDL(handle);
+  SendNewCompositable(handle, aCompositable->GetTextureInfo());
+}
+
+void
+WebRenderBridgeChild::UseTiledLayerBuffer(CompositableClient* aCompositable,
+                                          const SurfaceDescriptorTiles& aTiledDescriptor)
+{
+
+}
+
+void
+WebRenderBridgeChild::UpdateTextureRegion(CompositableClient* aCompositable,
+                                          const ThebesBufferData& aThebesBufferData,
+                                          const nsIntRegion& aUpdatedRegion)
+{
+
+}
+
+bool
+WebRenderBridgeChild::AddOpDestroy(const OpDestroy& aOp, bool aSynchronously)
+{
+  if (!mIsInTransaction) {
+    return false;
+  }
+
+  mDestroyedActors.AppendElement(aOp);
+  if (aSynchronously) {
+    MarkSyncTransaction();
+  }
+  return true;
+}
+
+void
+WebRenderBridgeChild::ReleaseCompositable(const CompositableHandle& aHandle)
+{
+  if (!DestroyInTransaction(aHandle)) {
+    SendReleaseCompositable(aHandle);
+  }
+  mCompositables.Remove(aHandle.Value());
+}
+
+bool
+WebRenderBridgeChild::DestroyInTransaction(PTextureChild* aTexture, bool aSynchronously)
+{
+  return AddOpDestroy(OpDestroy(aTexture), aSynchronously);
+}
+
+bool
+WebRenderBridgeChild::DestroyInTransaction(const CompositableHandle& aHandle)
+{
+  return AddOpDestroy(OpDestroy(aHandle), false);
+}
+
+void
+WebRenderBridgeChild::RemoveTextureFromCompositable(CompositableClient* aCompositable,
+                                                    TextureClient* aTexture)
+{
+  MOZ_ASSERT(aCompositable);
+  MOZ_ASSERT(aTexture);
+  MOZ_ASSERT(aTexture->GetIPDLActor());
+  MOZ_RELEASE_ASSERT(aTexture->GetIPDLActor()->GetIPCChannel() == GetIPCChannel());
+  if (!aCompositable->IsConnected() || !aTexture->GetIPDLActor()) {
+    // We don't have an actor anymore, don't try to use it!
+    return;
+  }
+
+  AddWebRenderCommand(
+    CompositableOperation(
+      aCompositable->GetIPCHandle(),
+      OpRemoveTexture(nullptr, aTexture->GetIPDLActor())));
+  if (aTexture->GetFlags() & TextureFlags::DEALLOCATE_CLIENT) {
+    MarkSyncTransaction();
+  }
+}
+
+void
+WebRenderBridgeChild::UseTextures(CompositableClient* aCompositable,
+                                  const nsTArray<TimedTextureClient>& aTextures)
+{
+  MOZ_ASSERT(aCompositable);
+
+  if (!aCompositable->IsConnected()) {
+    return;
+  }
+
+  AutoTArray<TimedTexture,4> textures;
+
+  for (auto& t : aTextures) {
+    MOZ_ASSERT(t.mTextureClient);
+    MOZ_ASSERT(t.mTextureClient->GetIPDLActor());
+    MOZ_RELEASE_ASSERT(t.mTextureClient->GetIPDLActor()->GetIPCChannel() == GetIPCChannel());
+    ReadLockDescriptor readLock;
+    t.mTextureClient->SerializeReadLock(readLock);
+    textures.AppendElement(TimedTexture(nullptr, t.mTextureClient->GetIPDLActor(),
+                                        readLock,
+                                        t.mTimeStamp, t.mPictureRect,
+                                        t.mFrameID, t.mProducerID));
+    if ((t.mTextureClient->GetFlags() & TextureFlags::IMMEDIATE_UPLOAD)
+        && t.mTextureClient->HasIntermediateBuffer()) {
+
+      // We use IMMEDIATE_UPLOAD when we want to be sure that the upload cannot
+      // race with updates on the main thread. In this case we want the transaction
+      // to be synchronous.
+
+      MarkSyncTransaction();
+    }
+    GetCompositorBridgeChild()->HoldUntilCompositableRefReleasedIfNecessary(t.mTextureClient);
+  }
+  AddWebRenderCommand(CompositableOperation(aCompositable->GetIPCHandle(),
+                                            OpUseTexture(textures)));
+}
+
+void
+WebRenderBridgeChild::UseComponentAlphaTextures(CompositableClient* aCompositable,
+                                                TextureClient* aClientOnBlack,
+                                                TextureClient* aClientOnWhite)
+{
+
+}
+
+void
+WebRenderBridgeChild::UpdateFwdTransactionId()
+{
+  GetCompositorBridgeChild()->UpdateFwdTransactionId();
+}
+
+uint64_t
+WebRenderBridgeChild::GetFwdTransactionId()
+{
+  return GetCompositorBridgeChild()->GetFwdTransactionId();
+}
+
+bool
+WebRenderBridgeChild::InForwarderThread()
+{
+  return NS_IsMainThread();
+}
+
+} // namespace layers
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/layers/wr/WebRenderBridgeChild.h
@@ -0,0 +1,118 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=4 ts=8 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/. */
+
+#ifndef mozilla_layers_WebRenderBridgeChild_h
+#define mozilla_layers_WebRenderBridgeChild_h
+
+#include "mozilla/layers/CompositableForwarder.h"
+#include "mozilla/layers/PWebRenderBridgeChild.h"
+
+namespace mozilla {
+
+namespace widget {
+class CompositorWidget;
+}
+
+namespace layers {
+
+class CompositableClient;
+class CompositorBridgeChild;
+class TextureForwarder;
+
+class WebRenderBridgeChild final : public PWebRenderBridgeChild
+                                 , public CompositableForwarder
+{
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WebRenderBridgeChild, override)
+
+public:
+  explicit WebRenderBridgeChild(const wr::PipelineId& aPipelineId);
+
+  void AddWebRenderCommand(const WebRenderCommand& aCmd);
+
+  bool DPBegin(const  gfx::IntSize& aSize);
+  void DPEnd(bool aIsSync, uint64_t aTransactionId);
+
+  CompositorBridgeChild* GetCompositorBridgeChild();
+
+  // KnowsCompositor
+  TextureForwarder* GetTextureForwarder() override;
+  LayersIPCActor* GetLayersIPCActor() override;
+
+  uint64_t AllocExternalImageId(const CompositableHandle& aHandle);
+  uint64_t AllocExternalImageIdForCompositable(CompositableClient* aCompositable);
+  void DeallocExternalImageId(uint64_t aImageId);
+
+  /**
+   * Clean this up, finishing with SendShutDown() which will cause __delete__
+   * to be sent from the parent side.
+   */
+  void Destroy();
+  bool IPCOpen() const { return mIPCOpen && !mDestroyed; }
+  bool IsDestroyed() const { return mDestroyed; }
+
+  void MarkSyncTransaction()
+  {
+    mSyncTransaction = true;
+  }
+
+private:
+  friend class CompositorBridgeChild;
+
+  ~WebRenderBridgeChild() {}
+
+  uint64_t GetNextExternalImageId();
+
+  // CompositableForwarder
+  void Connect(CompositableClient* aCompositable,
+               ImageContainer* aImageContainer = nullptr) override;
+  void UseTiledLayerBuffer(CompositableClient* aCompositable,
+                           const SurfaceDescriptorTiles& aTiledDescriptor) override;
+  void UpdateTextureRegion(CompositableClient* aCompositable,
+                           const ThebesBufferData& aThebesBufferData,
+                           const nsIntRegion& aUpdatedRegion) override;
+  void ReleaseCompositable(const CompositableHandle& aHandle) override;
+  bool DestroyInTransaction(PTextureChild* aTexture, bool aSynchronously) override;
+  bool DestroyInTransaction(const CompositableHandle& aHandle);
+  void RemoveTextureFromCompositable(CompositableClient* aCompositable,
+                                     TextureClient* aTexture) override;
+  void UseTextures(CompositableClient* aCompositable,
+                   const nsTArray<TimedTextureClient>& aTextures) override;
+  void UseComponentAlphaTextures(CompositableClient* aCompositable,
+                                 TextureClient* aClientOnBlack,
+                                 TextureClient* aClientOnWhite) override;
+  void UpdateFwdTransactionId() override;
+  uint64_t GetFwdTransactionId() override;
+  bool InForwarderThread() override;
+
+  void ActorDestroy(ActorDestroyReason why) override;
+
+  void AddIPDLReference() {
+    MOZ_ASSERT(mIPCOpen == false);
+    mIPCOpen = true;
+    AddRef();
+  }
+  void ReleaseIPDLReference() {
+    MOZ_ASSERT(mIPCOpen == true);
+    mIPCOpen = false;
+    Release();
+  }
+
+  bool AddOpDestroy(const OpDestroy& aOp, bool aSynchronously);
+
+  nsTArray<WebRenderCommand> mCommands;
+  nsTArray<OpDestroy> mDestroyedActors;
+  nsDataHashtable<nsUint64HashKey, CompositableClient*> mCompositables;
+  bool mIsInTransaction;
+  bool mSyncTransaction;
+
+  bool mIPCOpen;
+  bool mDestroyed;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_WebRenderBridgeChild_h
new file mode 100644
--- /dev/null
+++ b/gfx/layers/wr/WebRenderBridgeParent.cpp
@@ -0,0 +1,665 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=4 ts=8 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/WebRenderBridgeParent.h"
+
+#include "CompositableHost.h"
+#include "GLContext.h"
+#include "GLContextProvider.h"
+#include "mozilla/Range.h"
+#include "mozilla/layers/Compositor.h"
+#include "mozilla/layers/CompositorBridgeParent.h"
+#include "mozilla/layers/CompositorThread.h"
+#include "mozilla/layers/CompositorVsyncScheduler.h"
+#include "mozilla/layers/ImageBridgeParent.h"
+#include "mozilla/layers/ImageDataSerializer.h"
+#include "mozilla/layers/TextureHost.h"
+#include "mozilla/layers/WebRenderCompositableHolder.h"
+#include "mozilla/layers/WebRenderCompositorOGL.h"
+#include "mozilla/webrender/RenderThread.h"
+#include "mozilla/widget/CompositorWidget.h"
+
+bool is_in_compositor_thread()
+{
+  return mozilla::layers::CompositorThreadHolder::IsInCompositorThread();
+}
+
+bool is_in_render_thread()
+{
+  return mozilla::wr::RenderThread::IsInRenderThread();
+}
+
+void* get_proc_address_from_glcontext(void* glcontext_ptr, const char* procname)
+{
+  MOZ_ASSERT(glcontext_ptr);
+
+  mozilla::gl::GLContext* glcontext = reinterpret_cast<mozilla::gl::GLContext*>(glcontext_ptr);
+  if (!glcontext) {
+    return nullptr;
+  }
+  PRFuncPtr p = glcontext->LookupSymbol(procname);
+  return reinterpret_cast<void*>(p);
+}
+
+namespace mozilla {
+namespace layers {
+
+using namespace mozilla::gfx;
+
+class MOZ_STACK_CLASS AutoWebRenderBridgeParentAsyncMessageSender
+{
+public:
+  explicit AutoWebRenderBridgeParentAsyncMessageSender(WebRenderBridgeParent* aWebRenderBridgeParent,
+                                                       InfallibleTArray<OpDestroy>* aDestroyActors = nullptr)
+    : mWebRenderBridgeParent(aWebRenderBridgeParent)
+    , mActorsToDestroy(aDestroyActors)
+  {
+    mWebRenderBridgeParent->SetAboutToSendAsyncMessages();
+  }
+
+  ~AutoWebRenderBridgeParentAsyncMessageSender()
+  {
+    mWebRenderBridgeParent->SendPendingAsyncMessages();
+    if (mActorsToDestroy) {
+      // Destroy the actors after sending the async messages because the latter may contain
+      // references to some actors.
+      for (const auto& op : *mActorsToDestroy) {
+        mWebRenderBridgeParent->DestroyActor(op);
+      }
+    }
+  }
+private:
+  WebRenderBridgeParent* mWebRenderBridgeParent;
+  InfallibleTArray<OpDestroy>* mActorsToDestroy;
+};
+
+WebRenderBridgeParent::WebRenderBridgeParent(CompositorBridgeParentBase* aCompositorBridge,
+                                             const wr::PipelineId& aPipelineId,
+                                             widget::CompositorWidget* aWidget,
+                                             RefPtr<wr::WebRenderAPI>&& aApi,
+                                             RefPtr<WebRenderCompositableHolder>&& aHolder)
+  : mCompositorBridge(aCompositorBridge)
+  , mPipelineId(aPipelineId)
+  , mWidget(aWidget)
+  , mBuilder(Nothing())
+  , mApi(aApi)
+  , mCompositableHolder(aHolder)
+  , mChildLayerObserverEpoch(0)
+  , mParentLayerObserverEpoch(0)
+  , mPendingTransactionId(0)
+  , mDestroyed(false)
+{
+  MOZ_ASSERT(mCompositableHolder);
+  if (mWidget) {
+    mCompositorScheduler = new CompositorVsyncScheduler(this, mWidget);
+  }
+}
+
+
+mozilla::ipc::IPCResult
+WebRenderBridgeParent::RecvCreate(const gfx::IntSize& aSize)
+{
+  if (mDestroyed) {
+    return IPC_OK();
+  }
+
+  if (mBuilder.isSome()) {
+    return IPC_OK();
+  }
+  MOZ_ASSERT(mApi);
+  mBuilder.emplace(LayerIntSize(aSize.width, aSize.height), mPipelineId);
+
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+WebRenderBridgeParent::RecvShutdown()
+{
+  if (mDestroyed) {
+    return IPC_OK();
+  }
+  Destroy();
+  if (!Send__delete__(this)) {
+    return IPC_FAIL_NO_REASON(this);
+  }
+  return IPC_OK();
+}
+
+void
+WebRenderBridgeParent::Destroy()
+{
+  if (mDestroyed) {
+    return;
+  }
+  MOZ_ASSERT(mBuilder.isSome());
+  mDestroyed = true;
+  ClearResources();
+}
+
+mozilla::ipc::IPCResult
+WebRenderBridgeParent::RecvAddImage(const gfx::IntSize& aSize,
+                                    const uint32_t& aStride,
+                                    const gfx::SurfaceFormat& aFormat,
+                                    const ByteBuffer& aBuffer,
+                                    wr::ImageKey* aOutImageKey)
+{
+  if (mDestroyed) {
+    return IPC_OK();
+  }
+  MOZ_ASSERT(mApi);
+  *aOutImageKey = mApi->AddImageBuffer(aSize, aStride, aFormat,
+                                       aBuffer.AsSlice());
+
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+WebRenderBridgeParent::RecvUpdateImage(const wr::ImageKey& aImageKey,
+                                       const gfx::IntSize& aSize,
+                                       const gfx::SurfaceFormat& aFormat,
+                                       const ByteBuffer& aBuffer)
+{
+  if (mDestroyed) {
+    return IPC_OK();
+  }
+  MOZ_ASSERT(mApi);
+  mApi->UpdateImageBuffer(aImageKey, aSize, aFormat, aBuffer.AsSlice());
+
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+WebRenderBridgeParent::RecvDeleteImage(const wr::ImageKey& aImageKey)
+{
+  if (mDestroyed) {
+    return IPC_OK();
+  }
+  MOZ_ASSERT(mApi);
+  mKeysToDelete.push_back(aImageKey);
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+WebRenderBridgeParent::RecvDPBegin(const gfx::IntSize& aSize,
+                                   bool* aOutSuccess)
+{
+  if (mDestroyed) {
+    return IPC_OK();
+  }
+  MOZ_ASSERT(mBuilder.isSome());
+  mBuilder.ref().Begin(LayerIntSize(aSize.width, aSize.height));
+  *aOutSuccess = true;
+
+  return IPC_OK();
+}
+
+void
+WebRenderBridgeParent::HandleDPEnd(InfallibleTArray<WebRenderCommand>&& aCommands,
+                                 InfallibleTArray<OpDestroy>&& aToDestroy,
+                                 const uint64_t& aFwdTransactionId,
+                                 const uint64_t& aTransactionId)
+{
+  UpdateFwdTransactionId(aFwdTransactionId);
+
+  if (mDestroyed) {
+    for (const auto& op : aToDestroy) {
+      DestroyActor(op);
+    }
+    return;
+  }
+  // This ensures that destroy operations are always processed. It is not safe
+  // to early-return from RecvDPEnd without doing so.
+  AutoWebRenderBridgeParentAsyncMessageSender autoAsyncMessageSender(this, &aToDestroy);
+
+  ProcessWebrenderCommands(aCommands, wr::Epoch(aTransactionId));
+
+  // The transaction ID might get reset to 1 if the page gets reloaded, see
+  // https://bugzilla.mozilla.org/show_bug.cgi?id=1145295#c41
+  // Otherwise, it should be continually increasing.
+  MOZ_ASSERT(aTransactionId == 1 || aTransactionId > mPendingTransactionId);
+  mPendingTransactionId = aTransactionId;
+}
+
+mozilla::ipc::IPCResult
+WebRenderBridgeParent::RecvDPEnd(InfallibleTArray<WebRenderCommand>&& aCommands,
+                                 InfallibleTArray<OpDestroy>&& aToDestroy,
+                                 const uint64_t& aFwdTransactionId,
+                                 const uint64_t& aTransactionId)
+{
+  HandleDPEnd(Move(aCommands), Move(aToDestroy), aFwdTransactionId, aTransactionId);
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+WebRenderBridgeParent::RecvDPSyncEnd(InfallibleTArray<WebRenderCommand>&& aCommands,
+                                     InfallibleTArray<OpDestroy>&& aToDestroy,
+                                     const uint64_t& aFwdTransactionId,
+                                     const uint64_t& aTransactionId)
+{
+  HandleDPEnd(Move(aCommands), Move(aToDestroy), aFwdTransactionId, aTransactionId);
+  return IPC_OK();
+}
+
+void
+WebRenderBridgeParent::ProcessWebrenderCommands(InfallibleTArray<WebRenderCommand>& aCommands, const wr::Epoch& aEpoch)
+{
+  MOZ_ASSERT(mBuilder.isSome());
+  wr::DisplayListBuilder& builder = mBuilder.ref();
+  // XXX remove it when external image key is used.
+  std::vector<wr::ImageKey> keysToDelete;
+
+  for (InfallibleTArray<WebRenderCommand>::index_type i = 0; i < aCommands.Length(); ++i) {
+    const WebRenderCommand& cmd = aCommands[i];
+
+    switch (cmd.type()) {
+      case WebRenderCommand::TOpDPPushStackingContext: {
+        const OpDPPushStackingContext& op = cmd.get_OpDPPushStackingContext();
+        builder.PushStackingContext(op.bounds(), op.overflow(), op.mask().ptrOr(nullptr), op.opacity(), op.matrix(), op.mixBlendMode());
+        break;
+      }
+      case WebRenderCommand::TOpDPPopStackingContext: {
+        builder.PopStackingContext();
+        break;
+      }
+      case WebRenderCommand::TOpDPPushRect: {
+        const OpDPPushRect& op = cmd.get_OpDPPushRect();
+        builder.PushRect(op.bounds(), op.clip(), op.color());
+        break;
+      }
+      case WebRenderCommand::TOpDPPushBorder: {
+        const OpDPPushBorder& op = cmd.get_OpDPPushBorder();
+        builder.PushBorder(op.bounds(), op.clip(),
+                           op.top(), op.right(), op.bottom(), op.left(),
+                           op.radius());
+        break;
+      }
+      case WebRenderCommand::TOpDPPushImage: {
+        const OpDPPushImage& op = cmd.get_OpDPPushImage();
+        builder.PushImage(op.bounds(), op.clip(),
+                          op.mask().ptrOr(nullptr), op.filter(), wr::ImageKey(op.key()));
+        break;
+      }
+      case WebRenderCommand::TOpDPPushExternalImageId: {
+        const OpDPPushExternalImageId& op = cmd.get_OpDPPushExternalImageId();
+        MOZ_ASSERT(mExternalImageIds.Get(op.externalImageId()).get());
+
+        RefPtr<CompositableHost> host = mExternalImageIds.Get(op.externalImageId());
+        if (!host) {
+          break;
+        }
+        RefPtr<DataSourceSurface> dSurf = host->GetAsSurface();
+        if (!dSurf) {
+          break;
+        }
+
+        nsIntRegion validBufferRegion = op.validBufferRegion().ToUnknownRegion();
+        IntRect validRect = IntRect(IntPoint(0,0), dSurf->GetSize());
+        if (!validBufferRegion.IsEmpty()) {
+          IntPoint offset = validBufferRegion.GetBounds().TopLeft();
+          validBufferRegion.MoveBy(-offset);
+          validBufferRegion.AndWith(IntRect(IntPoint(0,0), dSurf->GetSize()));
+          validRect = validBufferRegion.GetBounds().ToUnknownRect();
+
+          // XXX Remove it when we can put subimage in WebRender.
+          RefPtr<DrawTarget> target =
+           gfx::Factory::CreateDrawTarget(gfx::BackendType::SKIA, validRect.Size(), SurfaceFormat::B8G8R8A8);
+          for (auto iter = validBufferRegion.RectIter(); !iter.Done(); iter.Next()) {
+            IntRect regionRect = iter.Get();
+            Rect srcRect(regionRect.x + offset.x, regionRect.y + offset.y, regionRect.width, regionRect.height);
+            Rect dstRect(regionRect.x, regionRect.y, regionRect.width, regionRect.height);
+            target->DrawSurface(dSurf, dstRect, srcRect);
+          }
+          RefPtr<SourceSurface> surf = target->Snapshot();
+          dSurf = surf->GetDataSurface();
+        }
+
+        DataSourceSurface::MappedSurface map;
+        if (!dSurf->Map(gfx::DataSourceSurface::MapType::READ, &map)) {
+          break;
+        }
+
+        wr::ImageKey key;
+        auto slice = Range<uint8_t>(map.mData, validRect.height * map.mStride);
+        key = mApi->AddImageBuffer(validRect.Size(), map.mStride, SurfaceFormat::B8G8R8A8, slice);
+
+        builder.PushImage(op.bounds(), op.clip(), op.mask().ptrOr(nullptr), op.filter(), key);
+        keysToDelete.push_back(key);
+        dSurf->Unmap();
+        break;
+      }
+      case WebRenderCommand::TOpDPPushIframe: {
+        const OpDPPushIframe& op = cmd.get_OpDPPushIframe();
+        builder.PushIFrame(op.bounds(), op.clip(), op.pipelineId());
+        break;
+      }
+      case WebRenderCommand::TCompositableOperation: {
+        EditReplyVector replyv;
+        if (!ReceiveCompositableUpdate(cmd.get_CompositableOperation(),
+                                       replyv)) {
+          NS_ERROR("ReceiveCompositableUpdate failed");
+        }
+        break;
+      }
+      case WebRenderCommand::TOpDPPushText: {
+        const OpDPPushText& op = cmd.get_OpDPPushText();
+        const nsTArray<WrGlyphArray>& glyph_array = op.glyph_array();
+
+        for (size_t i = 0; i < glyph_array.Length(); i++) {
+          const nsTArray<WrGlyphInstance>& glyphs = glyph_array[i].glyphs;
+
+          // TODO: We are leaking the key
+          wr::FontKey fontKey;
+          auto slice = Range<uint8_t>(op.font_buffer().mData, op.font_buffer_length());
+          fontKey = mApi->AddRawFont(slice);
+          builder.PushText(op.bounds(),
+                           op.clip(),
+                           glyph_array[i].color,
+                           fontKey,
+                           Range<const WrGlyphInstance>(glyphs.Elements(), glyphs.Length()),
+                           op.glyph_size());
+        }
+
+        break;
+      }
+      default:
+        NS_RUNTIMEABORT("not reached");
+    }
+  }
+  builder.End(*mApi, aEpoch);
+
+  ScheduleComposition();
+  DeleteOldImages();
+
+  // XXX remove it when external image key is used.
+  if (!keysToDelete.empty()) {
+    mKeysToDelete.swap(keysToDelete);
+  }
+
+  if (ShouldParentObserveEpoch()) {
+    mCompositorBridge->ObserveLayerUpdate(mPipelineId.mHandle, GetChildLayerObserverEpoch(), true);
+  }
+}
+
+mozilla::ipc::IPCResult
+WebRenderBridgeParent::RecvDPGetSnapshot(PTextureParent* aTexture)
+{
+  if (mDestroyed) {
+    return IPC_OK();
+  }
+
+  RefPtr<TextureHost> texture = TextureHost::AsTextureHost(aTexture);
+  if (!texture) {
+    // We kill the content process rather than have it continue with an invalid
+    // snapshot, that may be too harsh and we could decide to return some sort
+    // of error to the child process and let it deal with it...
+    return IPC_FAIL_NO_REASON(this);
+  }
+
+  // XXX Add other TextureHost supports.
+  // Only BufferTextureHost is supported now.
+  BufferTextureHost* bufferTexture = texture->AsBufferTextureHost();
+  if (!bufferTexture) {
+    // We kill the content process rather than have it continue with an invalid
+    // snapshot, that may be too harsh and we could decide to return some sort
+    // of error to the child process and let it deal with it...
+    return IPC_FAIL_NO_REASON(this);
+  }
+
+  MOZ_ASSERT(bufferTexture->GetBufferDescriptor().type() == BufferDescriptor::TRGBDescriptor);
+  DebugOnly<uint32_t> stride = ImageDataSerializer::GetRGBStride(bufferTexture->GetBufferDescriptor().get_RGBDescriptor());
+  uint8_t* buffer = bufferTexture->GetBuffer();
+  IntSize size = bufferTexture->GetSize();
+
+  // We only support B8G8R8A8 for now.
+  MOZ_ASSERT(buffer);
+  MOZ_ASSERT(bufferTexture->GetFormat() == SurfaceFormat::B8G8R8A8);
+  uint32_t buffer_size = size.width * size.height * 4;
+
+  // Assert the stride of the buffer is what webrender expects
+  MOZ_ASSERT((uint32_t)(size.width * 4) == stride);
+
+  MOZ_ASSERT(mBuilder.isSome());
+  mApi->Readback(size, buffer, buffer_size);
+
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+WebRenderBridgeParent::RecvAddExternalImageId(const uint64_t& aImageId,
+                                              const CompositableHandle& aHandle)
+{
+  if (mDestroyed) {
+    return IPC_OK();
+  }
+
+  MOZ_ASSERT(!mExternalImageIds.Get(aImageId).get());
+
+  ImageBridgeParent* imageBridge = ImageBridgeParent::GetInstance(OtherPid());
+  if (!imageBridge) {
+     return IPC_FAIL_NO_REASON(this);
+  }
+  RefPtr<CompositableHost> host = imageBridge->FindCompositable(aHandle);
+  if (!host) {
+    NS_ERROR("CompositableHost not found in the map!");
+    return IPC_FAIL_NO_REASON(this);
+  }
+  if (host->GetType() != CompositableType::IMAGE &&
+      host->GetType() != CompositableType::CONTENT_SINGLE &&
+      host->GetType() != CompositableType::CONTENT_DOUBLE) {
+    NS_ERROR("Incompatible CompositableHost");
+    return IPC_OK();
+  }
+
+  mCompositableHolder->AddExternalImageId(aImageId, host);
+  mExternalImageIds.Put(aImageId, host);
+
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+WebRenderBridgeParent::RecvAddExternalImageIdForCompositable(const uint64_t& aImageId,
+                                                             const CompositableHandle& aHandle)
+{
+  if (mDestroyed) {
+    return IPC_OK();
+  }
+  MOZ_ASSERT(!mExternalImageIds.Get(aImageId).get());
+
+  RefPtr<CompositableHost> host = FindCompositable(aHandle);
+  if (host->GetType() != CompositableType::IMAGE &&
+      host->GetType() != CompositableType::CONTENT_SINGLE &&
+      host->GetType() != CompositableType::CONTENT_DOUBLE) {
+    NS_ERROR("Incompatible CompositableHost");
+    return IPC_OK();
+  }
+
+  mCompositableHolder->AddExternalImageId(aImageId, host);
+  mExternalImageIds.Put(aImageId, host);
+
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+WebRenderBridgeParent::RecvRemoveExternalImageId(const uint64_t& aImageId)
+{
+  if (mDestroyed) {
+    return IPC_OK();
+  }
+  MOZ_ASSERT(mExternalImageIds.Get(aImageId).get());
+  mExternalImageIds.Remove(aImageId);
+  mCompositableHolder->RemoveExternalImageId(aImageId);
+
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+WebRenderBridgeParent::RecvSetLayerObserverEpoch(const uint64_t& aLayerObserverEpoch)
+{
+  mChildLayerObserverEpoch = aLayerObserverEpoch;
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+WebRenderBridgeParent::RecvClearCachedResources()
+{
+  mCompositorBridge->ObserveLayerUpdate(mPipelineId.mHandle, GetChildLayerObserverEpoch(), false);
+  return IPC_OK();
+}
+
+void
+WebRenderBridgeParent::ActorDestroy(ActorDestroyReason aWhy)
+{
+  Destroy();
+}
+
+void
+WebRenderBridgeParent::CompositeToTarget(gfx::DrawTarget* aTarget, const gfx::IntRect* aRect)
+{
+  // TODO(bug 1328602) With the RenderThread, calling SetRootStackingContext
+  // should trigger the composition on the render thread.
+  MOZ_ASSERT_UNREACHABLE("unexpected to be called");
+}
+
+void
+WebRenderBridgeParent::DidComposite(uint64_t aTransactionId, TimeStamp aStart, TimeStamp aEnd)
+{
+  mCompositorBridge->NotifyDidComposite(aTransactionId, aStart, aEnd);
+}
+
+WebRenderBridgeParent::~WebRenderBridgeParent()
+{
+}
+
+void
+WebRenderBridgeParent::DeleteOldImages()
+{
+  for (wr::ImageKey key : mKeysToDelete) {
+    mApi->DeleteImage(key);
+  }
+  mKeysToDelete.clear();
+}
+
+void
+WebRenderBridgeParent::ScheduleComposition()
+{
+  MOZ_ASSERT(mApi);
+  // TODO(bug 1328602) should probably send a message to the render
+  // thread and force rendering, although in most cases where this is
+  // called, rendering should be triggered automatically already (maybe
+  // not in the ImageBridge case).
+}
+
+void
+WebRenderBridgeParent::ClearResources()
+{
+  DeleteOldImages();
+  if (mCompositableHolder) {
+    for (auto iter = mExternalImageIds.Iter(); !iter.Done(); iter.Next()) {
+      uint64_t externalImageId = iter.Key();
+      mCompositableHolder->RemoveExternalImageId(externalImageId);
+    }
+  }
+  mExternalImageIds.Clear();
+
+  if (mBuilder.isSome()) {
+    mBuilder.reset();
+  }
+  if (mCompositorScheduler) {
+    mCompositorScheduler->Destroy();
+    mCompositorScheduler = nullptr;
+  }
+
+  mApi = nullptr;
+  mCompositorBridge = nullptr;
+}
+
+bool
+WebRenderBridgeParent::ShouldParentObserveEpoch()
+{
+  if (mParentLayerObserverEpoch == mChildLayerObserverEpoch) {
+    return false;
+  }
+
+  mParentLayerObserverEpoch = mChildLayerObserverEpoch;
+  return true;
+}
+
+void
+WebRenderBridgeParent::SendAsyncMessage(const InfallibleTArray<AsyncParentMessageData>& aMessage)
+{
+  MOZ_ASSERT_UNREACHABLE("unexpected to be called");
+}
+
+void
+WebRenderBridgeParent::SendPendingAsyncMessages()
+{
+  MOZ_ASSERT(mCompositorBridge);
+  mCompositorBridge->SendPendingAsyncMessages();
+}
+
+void
+WebRenderBridgeParent::SetAboutToSendAsyncMessages()
+{
+  MOZ_ASSERT(mCompositorBridge);
+  mCompositorBridge->SetAboutToSendAsyncMessages();
+}
+
+void
+WebRenderBridgeParent::NotifyNotUsed(PTextureParent* aTexture, uint64_t aTransactionId)
+{
+  MOZ_ASSERT_UNREACHABLE("unexpected to be called");
+}
+
+base::ProcessId
+WebRenderBridgeParent::GetChildProcessId()
+{
+  return OtherPid();
+}
+
+bool
+WebRenderBridgeParent::IsSameProcess() const
+{
+  return OtherPid() == base::GetCurrentProcId();
+}
+
+mozilla::ipc::IPCResult
+WebRenderBridgeParent::RecvNewCompositable(const CompositableHandle& aHandle,
+                                           const TextureInfo& aInfo)
+{
+  if (!AddCompositable(aHandle, aInfo)) {
+    return IPC_FAIL_NO_REASON(this);
+  }
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+WebRenderBridgeParent::RecvReleaseCompositable(const CompositableHandle& aHandle)
+{
+  ReleaseCompositable(aHandle);
+  return IPC_OK();
+}
+
+void
+WebRenderBridgeParent::SetWebRenderProfilerEnabled(bool aEnabled)
+{
+  if (mWidget) {
+    // Only set the flag to "root" WebRenderBridgeParent.
+    mApi->SetProfilerEnabled(aEnabled);
+  }
+}
+
+TextureFactoryIdentifier
+WebRenderBridgeParent::GetTextureFactoryIdentifier()
+{
+  MOZ_ASSERT(mApi);
+
+  return TextureFactoryIdentifier(LayersBackend::LAYERS_WR,
+                                  XRE_GetProcessType(),
+                                  mApi->GetMaxTextureSize());
+}
+
+} // namespace layers
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/layers/wr/WebRenderBridgeParent.h
@@ -0,0 +1,172 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=4 ts=8 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/. */
+
+#ifndef mozilla_layers_WebRenderBridgeParent_h
+#define mozilla_layers_WebRenderBridgeParent_h
+
+#include "CompositableHost.h"           // for CompositableHost, ImageCompositeNotificationInfo
+#include "GLContextProvider.h"
+#include "mozilla/layers/CompositableTransactionParent.h"
+#include "mozilla/layers/CompositorVsyncSchedulerOwner.h"
+#include "mozilla/layers/PWebRenderBridgeParent.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/webrender/WebRenderTypes.h"
+#include "mozilla/webrender/WebRenderAPI.h"
+#include "nsTArrayForwardDeclare.h"
+
+namespace mozilla {
+
+namespace gl {
+class GLContext;
+}
+
+namespace widget {
+class CompositorWidget;
+}
+
+namespace wr {
+class WebRenderAPI;
+}
+
+namespace layers {
+
+class Compositor;
+class CompositorBridgeParentBase;
+class CompositorVsyncScheduler;
+class WebRenderCompositableHolder;
+
+class WebRenderBridgeParent final : public PWebRenderBridgeParent
+                                  , public CompositorVsyncSchedulerOwner
+                                  , public CompositableParentManager
+{
+public:
+  WebRenderBridgeParent(CompositorBridgeParentBase* aCompositorBridge,
+                        const wr::PipelineId& aPipelineId,
+                        widget::CompositorWidget* aWidget,
+                        RefPtr<wr::WebRenderAPI>&& aApi,
+                        RefPtr<WebRenderCompositableHolder>&& aHolder);
+
+  wr::PipelineId PipelineId() { return mPipelineId; }
+  wr::WebRenderAPI* GetWebRenderAPI() { return mApi; }
+  WebRenderCompositableHolder* CompositableHolder() { return mCompositableHolder; }
+  CompositorVsyncScheduler* CompositorScheduler() { return mCompositorScheduler.get(); }
+
+  mozilla::ipc::IPCResult RecvNewCompositable(const CompositableHandle& aHandle,
+                                              const TextureInfo& aInfo) override;
+  mozilla::ipc::IPCResult RecvReleaseCompositable(const CompositableHandle& aHandle) override;
+
+  mozilla::ipc::IPCResult RecvCreate(const gfx::IntSize& aSize) override;
+  mozilla::ipc::IPCResult RecvShutdown() override;
+  mozilla::ipc::IPCResult RecvAddImage(const gfx::IntSize& aSize,
+                                       const uint32_t& aStride,
+                                       const gfx::SurfaceFormat& aFormat,
+                                       const ByteBuffer& aBuffer,
+                                       wr::ImageKey* aOutImageKey) override;
+  mozilla::ipc::IPCResult RecvUpdateImage(const wr::ImageKey& aImageKey,
+                                          const gfx::IntSize& aSize,
+                                          const gfx::SurfaceFormat& aFormat,
+                                          const ByteBuffer& aBuffer) override;
+  mozilla::ipc::IPCResult RecvDeleteImage(const wr::ImageKey& a1) override;
+  mozilla::ipc::IPCResult RecvDPBegin(const gfx::IntSize& aSize,
+                                      bool* aOutSuccess) override;
+  mozilla::ipc::IPCResult RecvDPEnd(InfallibleTArray<WebRenderCommand>&& aCommands,
+                                    InfallibleTArray<OpDestroy>&& aToDestroy,
+                                    const uint64_t& aFwdTransactionId,
+                                    const uint64_t& aTransactionId) override;
+  mozilla::ipc::IPCResult RecvDPSyncEnd(InfallibleTArray<WebRenderCommand>&& aCommands,
+                                        InfallibleTArray<OpDestroy>&& aToDestroy,
+                                        const uint64_t& aFwdTransactionId,
+                                        const uint64_t& aTransactionId) override;
+  mozilla::ipc::IPCResult RecvDPGetSnapshot(PTextureParent* aTexture) override;
+
+  mozilla::ipc::IPCResult RecvAddExternalImageId(const uint64_t& aImageId,
+                                                 const CompositableHandle& aHandle) override;
+  mozilla::ipc::IPCResult RecvAddExternalImageIdForCompositable(const uint64_t& aImageId,
+                                                                const CompositableHandle& aHandle) override;
+  mozilla::ipc::IPCResult RecvRemoveExternalImageId(const uint64_t& aImageId) override;
+  mozilla::ipc::IPCResult RecvSetLayerObserverEpoch(const uint64_t& aLayerObserverEpoch) override;
+
+  mozilla::ipc::IPCResult RecvClearCachedResources() override;
+
+  void ActorDestroy(ActorDestroyReason aWhy) override;
+  void SetWebRenderProfilerEnabled(bool aEnabled);
+
+  void Destroy();
+
+  const uint64_t& GetPendingTransactionId() { return mPendingTransactionId; }
+  void SetPendingTransactionId(uint64_t aId) { mPendingTransactionId = aId; }
+
+  // CompositorVsyncSchedulerOwner
+  bool IsPendingComposite() override { return false; }
+  void FinishPendingComposite() override { }
+  void CompositeToTarget(gfx::DrawTarget* aTarget, const gfx::IntRect* aRect = nullptr) override;
+
+  // CompositableParentManager
+  bool IsSameProcess() const override;
+  base::ProcessId GetChildProcessId() override;
+  void NotifyNotUsed(PTextureParent* aTexture, uint64_t aTransactionId) override;
+  void SendAsyncMessage(const InfallibleTArray<AsyncParentMessageData>& aMessage) override;
+  void SendPendingAsyncMessages() override;
+  void SetAboutToSendAsyncMessages() override;
+
+  void DidComposite(uint64_t aTransactionId, TimeStamp aStart, TimeStamp aEnd);
+
+  TextureFactoryIdentifier GetTextureFactoryIdentifier();
+
+  void AppendImageCompositeNotification(const ImageCompositeNotificationInfo& aNotification)
+  {
+    MOZ_ASSERT(mWidget);
+    mImageCompositeNotifications.AppendElement(aNotification);
+  }
+
+  void ExtractImageCompositeNotifications(nsTArray<ImageCompositeNotificationInfo>* aNotifications)
+  {
+    MOZ_ASSERT(mWidget);
+    aNotifications->AppendElements(Move(mImageCompositeNotifications));
+  }
+
+private:
+  virtual ~WebRenderBridgeParent();
+
+  void DeleteOldImages();
+  void ProcessWebrenderCommands(InfallibleTArray<WebRenderCommand>& commands, const wr::Epoch& aEpoch);
+  void ScheduleComposition();
+  void ClearResources();
+  uint64_t GetChildLayerObserverEpoch() const { return mChildLayerObserverEpoch; }
+  bool ShouldParentObserveEpoch();
+  void HandleDPEnd(InfallibleTArray<WebRenderCommand>&& aCommands,
+                   InfallibleTArray<OpDestroy>&& aToDestroy,
+                   const uint64_t& aFwdTransactionId,
+                   const uint64_t& aTransactionId);
+
+private:
+  CompositorBridgeParentBase* MOZ_NON_OWNING_REF mCompositorBridge;
+  wr::PipelineId mPipelineId;
+  RefPtr<widget::CompositorWidget> mWidget;
+  Maybe<wr::DisplayListBuilder> mBuilder;
+  RefPtr<wr::WebRenderAPI> mApi;
+  RefPtr<WebRenderCompositableHolder> mCompositableHolder;
+  RefPtr<CompositorVsyncScheduler> mCompositorScheduler;
+  std::vector<wr::ImageKey> mKeysToDelete;
+  nsDataHashtable<nsUint64HashKey, RefPtr<CompositableHost>> mExternalImageIds;
+  nsTArray<ImageCompositeNotificationInfo> mImageCompositeNotifications;
+
+  // These fields keep track of the latest layer observer epoch values in the child and the
+  // parent. mChildLayerObserverEpoch is the latest epoch value received from the child.
+  // mParentLayerObserverEpoch is the latest epoch value that we have told TabParent about
+  // (via ObserveLayerUpdate).
+  uint64_t mChildLayerObserverEpoch;
+  uint64_t mParentLayerObserverEpoch;
+
+  uint64_t mPendingTransactionId;
+
+  bool mDestroyed;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // mozilla_layers_WebRenderBridgeParent_h
new file mode 100644
--- /dev/null
+++ b/gfx/layers/wr/WebRenderCanvasLayer.cpp
@@ -0,0 +1,111 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "WebRenderCanvasLayer.h"
+
+#include "AsyncCanvasRenderer.h"
+#include "gfxUtils.h"
+#include "GLContext.h"
+#include "GLScreenBuffer.h"
+#include "WebRenderLayersLogging.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/layers/TextureClientSharedSurface.h"
+#include "mozilla/layers/WebRenderBridgeChild.h"
+#include "PersistentBufferProvider.h"
+#include "SharedSurface.h"
+#include "SharedSurfaceGL.h"
+#include "mozilla/webrender/WebRenderTypes.h"
+
+namespace mozilla {
+namespace layers {
+
+WebRenderCanvasLayer::~WebRenderCanvasLayer()
+{
+  MOZ_COUNT_DTOR(WebRenderCanvasLayer);
+
+  if (mExternalImageId) {
+    WrBridge()->DeallocExternalImageId(mExternalImageId);
+  }
+}
+
+void
+WebRenderCanvasLayer::Initialize(const Data& aData)
+{
+  ShareableCanvasLayer::Initialize(aData);
+
+  // XXX: Use basic surface factory until we support shared surface.
+  if (!mGLContext || mGLFrontbuffer)
+    return;
+
+  gl::GLScreenBuffer* screen = mGLContext->Screen();
+  auto factory = MakeUnique<gl::SurfaceFactory_Basic>(mGLContext, screen->mCaps, mFlags);
+  screen->Morph(Move(factory));
+}
+
+void
+WebRenderCanvasLayer::RenderLayer()
+{
+  UpdateCompositableClient();
+
+  if (!mExternalImageId) {
+    mExternalImageId = WrBridge()->AllocExternalImageIdForCompositable(mCanvasClient);
+  }
+
+  MOZ_ASSERT(mExternalImageId);
+
+  gfx::Matrix4x4 transform;// = GetTransform();
+  const bool needsYFlip = (mOriginPos == gl::OriginPos::BottomLeft);
+  if (needsYFlip) {
+    transform.PreTranslate(0, mBounds.height, 0).PreScale(1, -1, 1);
+  }
+  gfx::Rect rect(0, 0, mBounds.width, mBounds.height);
+  rect = RelativeToTransformedVisible(GetTransform().TransformBounds(rect));
+
+  gfx::Rect clip;
+  if (GetClipRect().isSome()) {
+      clip = RelativeToTransformedVisible(IntRectToRect(GetClipRect().ref().ToUnknownRect()));
+  } else {
+      clip = rect;
+  }
+
+  gfx::Rect relBounds = TransformedVisibleBoundsRelativeToParent();
+  gfx::Rect overflow(0, 0, relBounds.width, relBounds.height);
+  Maybe<WrImageMask> mask = buildMaskLayer();
+  WrTextureFilter filter = (mSamplingFilter == gfx::SamplingFilter::POINT) ? WrTextureFilter::Point : WrTextureFilter::Linear;
+  WrMixBlendMode mixBlendMode = wr::ToWrMixBlendMode(GetMixBlendMode());
+
+  if (gfxPrefs::LayersDump()) {
+    printf_stderr("CanvasLayer %p using bounds=%s, overflow=%s, transform=%s, rect=%s, clip=%s, texture-filter=%s, mix-blend-mode=%s\n",
+                  this->GetLayer(),
+                  Stringify(relBounds).c_str(),
+                  Stringify(overflow).c_str(),
+                  Stringify(transform).c_str(),
+                  Stringify(rect).c_str(),
+                  Stringify(clip).c_str(),
+                  Stringify(filter).c_str(),
+                  Stringify(mixBlendMode).c_str());
+  }
+
+  WrBridge()->AddWebRenderCommand(
+      OpDPPushStackingContext(wr::ToWrRect(relBounds),
+                              wr::ToWrRect(overflow),
+                              mask,
+                              1.0f,
+                              GetAnimations(),
+                              transform,
+                              mixBlendMode,
+                              FrameMetrics::NULL_SCROLL_ID));
+  WrBridge()->AddWebRenderCommand(OpDPPushExternalImageId(LayerIntRegion(), wr::ToWrRect(rect), wr::ToWrRect(clip), Nothing(), filter, mExternalImageId));
+  WrBridge()->AddWebRenderCommand(OpDPPopStackingContext());
+}
+
+void
+WebRenderCanvasLayer::AttachCompositable()
+{
+  mCanvasClient->Connect();
+}
+
+} // namespace layers
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/layers/wr/WebRenderCanvasLayer.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_WEBRENDERCANVASLAYER_H
+#define GFX_WEBRENDERCANVASLAYER_H
+
+#include "ShareableCanvasLayer.h"
+#include "WebRenderLayerManager.h"
+
+namespace mozilla {
+namespace gfx {
+class SourceSurface;
+}; // namespace gfx
+
+namespace layers {
+
+class WebRenderCanvasLayer : public WebRenderLayer,
+                             public ShareableCanvasLayer
+{
+public:
+  explicit WebRenderCanvasLayer(WebRenderLayerManager* aLayerManager)
+    : ShareableCanvasLayer(aLayerManager, static_cast<WebRenderLayer*>(this))
+    , mExternalImageId(0)
+  {
+    MOZ_COUNT_CTOR(WebRenderCanvasLayer);
+  }
+
+  virtual void Initialize(const Data& aData) override;
+
+  virtual CompositableForwarder* GetForwarder() override
+  {
+    return Manager()->WrBridge();
+  }
+
+  virtual void AttachCompositable() override;
+
+protected:
+  virtual ~WebRenderCanvasLayer();
+  WebRenderLayerManager* Manager()
+  {
+    return static_cast<WebRenderLayerManager*>(mManager);
+  }
+
+public:
+  Layer* GetLayer() override { return this; }
+  void RenderLayer() override;
+
+protected:
+  uint64_t mExternalImageId;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // GFX_WEBRENDERCANVASLAYER_H
new file mode 100644
--- /dev/null
+++ b/gfx/layers/wr/WebRenderColorLayer.cpp
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "WebRenderColorLayer.h"
+
+#include "WebRenderLayersLogging.h"
+#include "mozilla/webrender/webrender_ffi.h"
+#include "mozilla/webrender/WebRenderTypes.h"
+#include "mozilla/layers/WebRenderBridgeChild.h"
+
+namespace mozilla {
+namespace layers {
+
+void
+WebRenderColorLayer::RenderLayer()
+{
+  WrScrollFrameStackingContextGenerator scrollFrames(this);
+
+  LayerIntRegion visibleRegion = GetVisibleRegion();
+  LayerIntRect bounds = visibleRegion.GetBounds();
+  Rect rect = RelativeToVisible(IntRectToRect(bounds.ToUnknownRect()));
+  Rect clip;
+  if (GetClipRect().isSome()) {
+      clip = RelativeToTransformedVisible(IntRectToRect(GetClipRect().ref().ToUnknownRect()));
+  } else {
+      clip = rect;
+  }
+
+  gfx::Matrix4x4 transform;// = GetTransform();
+  gfx::Rect relBounds = TransformedVisibleBoundsRelativeToParent();
+  gfx::Rect overflow(0, 0, relBounds.width, relBounds.height);
+  WrMixBlendMode mixBlendMode = wr::ToWrMixBlendMode(GetMixBlendMode());
+
+  Maybe<WrImageMask> mask = buildMaskLayer();
+
+  if (gfxPrefs::LayersDump()) {
+    printf_stderr("ColorLayer %p using bounds=%s, overflow=%s, transform=%s, rect=%s, clip=%s, mix-blend-mode=%s\n",
+                  this->GetLayer(),
+                  Stringify(relBounds).c_str(),
+                  Stringify(overflow).c_str(),
+                  Stringify(transform).c_str(),
+                  Stringify(rect).c_str(),
+                  Stringify(clip).c_str(),
+                  Stringify(mixBlendMode).c_str());
+  }
+
+  WrBridge()->AddWebRenderCommand(
+      OpDPPushStackingContext(wr::ToWrRect(relBounds),
+                              wr::ToWrRect(overflow),
+                              mask,
+                              1.0f,
+                              GetAnimations(),
+                              transform,
+                              mixBlendMode,
+                              FrameMetrics::NULL_SCROLL_ID));
+  WrBridge()->AddWebRenderCommand(
+    OpDPPushRect(wr::ToWrRect(rect), wr::ToWrRect(clip), wr::ToWrColor(mColor)));
+  WrBridge()->AddWebRenderCommand(OpDPPopStackingContext());
+}
+
+} // namespace layers
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/layers/wr/WebRenderColorLayer.h
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_WEBRENDERCOLORLAYER_H
+#define GFX_WEBRENDERCOLORLAYER_H
+
+#include "Layers.h"
+#include "WebRenderLayerManager.h"
+
+namespace mozilla {
+namespace layers {
+
+class WebRenderColorLayer : public WebRenderLayer,
+                            public ColorLayer {
+public:
+  explicit WebRenderColorLayer(WebRenderLayerManager* aLayerManager)
+    : ColorLayer(aLayerManager, static_cast<WebRenderLayer*>(this))
+  {
+    MOZ_COUNT_CTOR(WebRenderColorLayer);
+  }
+
+protected:
+  virtual ~WebRenderColorLayer()
+  {
+    MOZ_COUNT_DTOR(WebRenderColorLayer);
+  }
+
+public:
+  Layer* GetLayer() override { return this; }
+  void RenderLayer() override;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // GFX_WEBRENDERCOLORLAYER_H
new file mode 100644
--- /dev/null
+++ b/gfx/layers/wr/WebRenderCompositableHolder.cpp
@@ -0,0 +1,59 @@
+/* -*- 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 "WebRenderCompositableHolder.h"
+
+#include "CompositableHost.h"
+//#include "mozilla/layers/CompositorBridgeParent.h"
+
+namespace mozilla {
+
+using namespace gfx;
+
+namespace layers {
+
+WebRenderCompositableHolder::WebRenderCompositableHolder()
+{
+  MOZ_COUNT_CTOR(WebRenderCompositableHolder);
+}
+
+WebRenderCompositableHolder::~WebRenderCompositableHolder()
+{
+  MOZ_COUNT_DTOR(WebRenderCompositableHolder);
+  Destroy();
+}
+
+void
+WebRenderCompositableHolder::Destroy()
+{
+  mCompositableHosts.Clear();
+}
+
+void
+WebRenderCompositableHolder::AddExternalImageId(uint64_t aExternalImageId, CompositableHost* aHost)
+{
+  MOZ_ASSERT(!mCompositableHosts.Get(aExternalImageId));
+  mCompositableHosts.Put(aExternalImageId, aHost);
+}
+
+void
+WebRenderCompositableHolder::RemoveExternalImageId(uint64_t aExternalImageId)
+{
+  MOZ_ASSERT(mCompositableHosts.Get(aExternalImageId));
+  mCompositableHosts.Remove(aExternalImageId);
+}
+
+void
+WebRenderCompositableHolder::UpdateExternalImages()
+{
+  for (auto iter = mCompositableHosts.Iter(); !iter.Done(); iter.Next()) {
+    RefPtr<CompositableHost>& host = iter.Data();
+    // XXX Change to correct TextrueSource handling here.
+    host->BindTextureSource();
+  }
+}
+
+} // namespace layers
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/layers/wr/WebRenderCompositableHolder.h
@@ -0,0 +1,43 @@
+/* -*- 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/. */
+
+#ifndef MOZILLA_GFX_WEBRENDERCOMPOSITABLE_HOLDER_H
+#define MOZILLA_GFX_WEBRENDERCOMPOSITABLE_HOLDER_H
+
+#include "nsDataHashtable.h"
+
+namespace mozilla {
+namespace layers {
+
+class CompositableHost;
+
+class WebRenderCompositableHolder final
+{
+public:
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WebRenderCompositableHolder)
+
+  explicit WebRenderCompositableHolder();
+
+protected:
+  virtual ~WebRenderCompositableHolder();
+
+public:
+
+  virtual void Destroy();
+
+  void AddExternalImageId(uint64_t aExternalImageId, CompositableHost* aHost);
+  void RemoveExternalImageId(uint64_t aExternalImageId);
+  void UpdateExternalImages();
+
+private:
+
+  // Holds CompositableHosts that are bound to external image ids.
+  nsDataHashtable<nsUint64HashKey, RefPtr<CompositableHost> > mCompositableHosts;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_WEBRENDERCOMPOSITABLE_HOLDER_H */
new file mode 100644
--- /dev/null
+++ b/gfx/layers/wr/WebRenderCompositorOGL.cpp
@@ -0,0 +1,152 @@
+/* -*- 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 "WebRenderCompositorOGL.h"
+
+#include "CompositableHost.h"
+#include "GLContext.h"                  // for GLContext
+#include "GLUploadHelpers.h"
+#include "mozilla/layers/CompositorBridgeParent.h"
+#include "mozilla/layers/TextureHost.h"  // for TextureSource, etc
+#include "mozilla/layers/TextureHostOGL.h"  // for TextureSourceOGL, etc
+
+namespace mozilla {
+
+using namespace gfx;
+using namespace gl;
+
+namespace layers {
+
+WebRenderCompositorOGL::WebRenderCompositorOGL(CompositorBridgeParent* aCompositorBridge,
+                                               GLContext* aGLContext)
+  : Compositor(nullptr, nullptr)
+  , mCompositorBridge(aCompositorBridge)
+  , mGLContext(aGLContext)
+  , mDestroyed(false)
+{
+  MOZ_COUNT_CTOR(WebRenderCompositorOGL);
+}
+
+WebRenderCompositorOGL::~WebRenderCompositorOGL()
+{
+  MOZ_COUNT_DTOR(WebRenderCompositorOGL);
+  Destroy();
+}
+
+void
+WebRenderCompositorOGL::Destroy()
+{
+  Compositor::Destroy();
+
+  mCompositableHosts.Clear();
+  mCompositorBridge = nullptr;
+
+  if (!mDestroyed) {
+    mDestroyed = true;
+    CleanupResources();
+  }
+}
+
+void
+WebRenderCompositorOGL::CleanupResources()
+{
+  if (!mGLContext) {
+    return;
+  }
+
+  // On the main thread the Widget will be destroyed soon and calling MakeCurrent
+  // after that could cause a crash (at least with GLX, see bug 1059793), unless
+  // context is marked as destroyed.
+  // There may be some textures still alive that will try to call MakeCurrent on
+  // the context so let's make sure it is marked destroyed now.
+  mGLContext->MarkDestroyed();
+
+  mGLContext = nullptr;
+}
+
+bool
+WebRenderCompositorOGL::Initialize(nsCString* const out_failureReason)
+{
+  MOZ_ASSERT(mGLContext);
+  return true;
+}
+
+already_AddRefed<DataTextureSource>
+WebRenderCompositorOGL::CreateDataTextureSource(TextureFlags aFlags)
+{
+  return nullptr;
+}
+
+bool
+WebRenderCompositorOGL::SupportsPartialTextureUpdate()
+{
+  return CanUploadSubTextures(mGLContext);
+}
+
+int32_t
+WebRenderCompositorOGL::GetMaxTextureSize() const
+{
+  MOZ_ASSERT(mGLContext);
+  GLint texSize = 0;
+  mGLContext->fGetIntegerv(LOCAL_GL_MAX_TEXTURE_SIZE,
+                            &texSize);
+  MOZ_ASSERT(texSize != 0);
+  return texSize;
+}
+
+void
+WebRenderCompositorOGL::MakeCurrent(MakeCurrentFlags aFlags) {
+  if (mDestroyed) {
+    NS_WARNING("Call on destroyed layer manager");
+    return;
+  }
+  mGLContext->MakeCurrent(aFlags & ForceMakeCurrent);
+}
+
+void
+WebRenderCompositorOGL::CompositeUntil(TimeStamp aTimeStamp)
+{
+  Compositor::CompositeUntil(aTimeStamp);
+  // We're not really taking advantage of the stored composite-again-time here.
+  // We might be able to skip the next few composites altogether. However,
+  // that's a bit complex to implement and we'll get most of the advantage
+  // by skipping compositing when we detect there's nothing invalid. This is why
+  // we do "composite until" rather than "composite again at".
+  ScheduleComposition();
+}
+
+void
+WebRenderCompositorOGL::AddExternalImageId(uint64_t aExternalImageId, CompositableHost* aHost)
+{
+  MOZ_ASSERT(!mCompositableHosts.Get(aExternalImageId));
+  mCompositableHosts.Put(aExternalImageId, aHost);
+}
+
+void
+WebRenderCompositorOGL::RemoveExternalImageId(uint64_t aExternalImageId)
+{
+  MOZ_ASSERT(mCompositableHosts.Get(aExternalImageId));
+  mCompositableHosts.Remove(aExternalImageId);
+}
+
+void
+WebRenderCompositorOGL::UpdateExternalImages()
+{
+  for (auto iter = mCompositableHosts.Iter(); !iter.Done(); iter.Next()) {
+    RefPtr<CompositableHost>& host = iter.Data();
+    // XXX Change to correct TextrueSource handling here.
+    host->BindTextureSource();
+  }
+}
+
+void
+WebRenderCompositorOGL::ScheduleComposition()
+{
+  MOZ_ASSERT(mCompositorBridge);
+  mCompositorBridge->ScheduleComposition();
+}
+
+} // namespace layers
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/layers/wr/WebRenderCompositorOGL.h
@@ -0,0 +1,138 @@
+/* -*- 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/. */
+
+#ifndef MOZILLA_GFX_WEBRENDERCOMPOSITOROGL_H
+#define MOZILLA_GFX_WEBRENDERCOMPOSITOROGL_H
+
+#include "GLContextTypes.h"             // for GLContext, etc
+#include "GLDefs.h"                     // for GLuint, LOCAL_GL_TEXTURE_2D, etc
+#include "mozilla/layers/Compositor.h"  // for SurfaceInitMode, Compositor, etc
+#include "nsDataHashtable.h"
+
+namespace mozilla {
+namespace layers {
+
+class CompositableHost;
+class CompositorBridgeParent;
+
+class WebRenderCompositorOGL final : public Compositor
+{
+  typedef mozilla::gl::GLContext GLContext;
+
+public:
+  explicit WebRenderCompositorOGL(CompositorBridgeParent* aCompositorBridge, GLContext* aGLContext);
+
+protected:
+  virtual ~WebRenderCompositorOGL();
+
+public:
+  virtual WebRenderCompositorOGL* AsWebRenderCompositorOGL() override { return this; }
+
+  virtual already_AddRefed<DataTextureSource>
+  CreateDataTextureSource(TextureFlags aFlags = TextureFlags::NO_FLAGS) override;
+
+  virtual bool Initialize(nsCString* const out_failureReason) override;
+
+  virtual void Destroy() override;
+
+  virtual TextureFactoryIdentifier GetTextureFactoryIdentifier() override
+  {
+    TextureFactoryIdentifier result =
+      TextureFactoryIdentifier(LayersBackend::LAYERS_WR,
+                               XRE_GetProcessType(),
+                               GetMaxTextureSize(),
+                               true,
+                               SupportsPartialTextureUpdate());
+    return result;
+  }
+
+  virtual already_AddRefed<CompositingRenderTarget>
+  CreateRenderTarget(const gfx::IntRect &aRect, SurfaceInitMode aInit) override { return nullptr; }
+
+  virtual already_AddRefed<CompositingRenderTarget>
+  CreateRenderTargetFromSource(const gfx::IntRect &aRect,
+                               const CompositingRenderTarget *aSource,
+                               const gfx::IntPoint &aSourcePoint) override { return nullptr; }
+
+  virtual void SetRenderTarget(CompositingRenderTarget *aSurface) override { }
+
+  virtual CompositingRenderTarget* GetCurrentRenderTarget() const override { return nullptr; }
+
+  virtual void DrawQuad(const gfx::Rect& aRect,
+                        const gfx::IntRect& aClipRect,
+                        const EffectChain &aEffectChain,
+                        gfx::Float aOpacity,
+                        const gfx::Matrix4x4& aTransform,
+                        const gfx::Rect& aVisibleRect) override { }
+
+  virtual void DrawTriangle(const gfx::TexturedTriangle& aTriangle,
+                            const gfx::IntRect& aClipRect,
+                            const EffectChain& aEffectChain,
+                            gfx::Float aOpacity,
+                            const gfx::Matrix4x4& aTransform,
+                            const gfx::Rect& aVisibleRect) override { }
+
+  virtual void ClearRect(const gfx::Rect& aRect) override { }
+
+  virtual void BeginFrame(const nsIntRegion& aInvalidRegion,
+                          const gfx::IntRect *aClipRectIn,
+                          const gfx::IntRect& aRenderBounds,
+                          const nsIntRegion& aOpaqueRegion,
+                          gfx::IntRect *aClipRectOut = nullptr,
+                          gfx::IntRect *aRenderBoundsOut = nullptr) override { }
+
+  virtual void EndFrame() override { }
+
+  virtual bool SupportsPartialTextureUpdate() override;
+
+  virtual bool CanUseCanvasLayerForSize(const gfx::IntSize &aSize) override
+  {
+    if (!mGLContext)
+      return false;
+    int32_t maxSize = GetMaxTextureSize();
+    return aSize <= gfx::IntSize(maxSize, maxSize);
+  }
+
+  virtual int32_t GetMaxTextureSize() const override;
+
+  virtual void SetDestinationSurfaceSize(const gfx::IntSize& aSize) override { }
+  virtual void SetScreenRenderOffset(const ScreenPoint& aOffset) override { }
+
+  virtual void MakeCurrent(MakeCurrentFlags aFlags = 0) override;
+
+#ifdef MOZ_DUMP_PAINTING
+  virtual const char* Name() const override { return "WROGL"; }
+#endif // MOZ_DUMP_PAINTING
+
+  virtual LayersBackend GetBackendType() const override {
+    return LayersBackend::LAYERS_WR;
+  }
+
+  virtual bool IsValid() const override { return true; }
+
+  virtual void CompositeUntil(TimeStamp aTimeStamp) override;
+
+  GLContext* gl() const { return mGLContext; }
+
+  void AddExternalImageId(uint64_t aExternalImageId, CompositableHost* aHost);
+  void RemoveExternalImageId(uint64_t aExternalImageId);
+  void UpdateExternalImages();
+
+  void ScheduleComposition();
+private:
+  void CleanupResources();
+
+  CompositorBridgeParent* MOZ_NON_OWNING_REF mCompositorBridge;
+  RefPtr<GLContext> mGLContext;
+  // Holds CompositableHosts that are bound to external image ids.
+  nsDataHashtable<nsUint64HashKey, RefPtr<CompositableHost> > mCompositableHosts;
+
+  bool mDestroyed;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* MOZILLA_GFX_WEBRENDERCOMPOSITOROGL_H */
new file mode 100644
--- /dev/null
+++ b/gfx/layers/wr/WebRenderContainerLayer.cpp
@@ -0,0 +1,77 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "WebRenderContainerLayer.h"
+
+#include <inttypes.h>
+#include "mozilla/layers/WebRenderBridgeChild.h"
+#include "mozilla/webrender/WebRenderTypes.h"
+#include "WebRenderLayersLogging.h"
+
+namespace mozilla {
+namespace layers {
+
+void
+WebRenderContainerLayer::RenderLayer()
+{
+  WrScrollFrameStackingContextGenerator scrollFrames(this);
+
+  nsTArray<LayerPolygon> children = SortChildrenBy3DZOrder(SortMode::WITHOUT_GEOMETRY);
+
+  gfx::Rect relBounds = TransformedVisibleBoundsRelativeToParent();
+  gfx::Rect overflow(0, 0, relBounds.width, relBounds.height);
+  gfx::Matrix4x4 transform;// = GetTransform();
+  WrMixBlendMode mixBlendMode = wr::ToWrMixBlendMode(GetMixBlendMode());
+  Maybe<WrImageMask> mask = buildMaskLayer();
+
+  if (gfxPrefs::LayersDump()) {
+    printf_stderr("ContainerLayer %p using bounds=%s, overflow=%s, transform=%s, mix-blend-mode=%s\n",
+                  this->GetLayer(),
+                  Stringify(relBounds).c_str(),
+                  Stringify(overflow).c_str(),
+                  Stringify(transform).c_str(),
+                  Stringify(mixBlendMode).c_str());
+  }
+
+  WrBridge()->AddWebRenderCommand(
+    OpDPPushStackingContext(wr::ToWrRect(relBounds),
+                            wr::ToWrRect(overflow),
+                            mask,
+                            GetLocalOpacity(),
+                            GetLayer()->GetAnimations(),
+                            transform,
+                            mixBlendMode,
+                            FrameMetrics::NULL_SCROLL_ID));
+  for (LayerPolygon& child : children) {
+    if (child.layer->IsBackfaceHidden()) {
+      continue;
+    }
+    ToWebRenderLayer(child.layer)->RenderLayer();
+  }
+  WrBridge()->AddWebRenderCommand(
+    OpDPPopStackingContext());
+}
+
+void
+WebRenderRefLayer::RenderLayer()
+{
+  WrScrollFrameStackingContextGenerator scrollFrames(this);
+
+  gfx::Rect relBounds = TransformedVisibleBoundsRelativeToParent();
+  gfx::Matrix4x4 transform;// = GetTransform();
+
+  if (gfxPrefs::LayersDump()) {
+    printf_stderr("RefLayer %p (%" PRIu64 ") using bounds/overflow=%s, transform=%s\n",
+                  this->GetLayer(),
+                  mId,
+                  Stringify(relBounds).c_str(),
+                  Stringify(transform).c_str());
+  }
+
+  WrBridge()->AddWebRenderCommand(OpDPPushIframe(wr::ToWrRect(relBounds), wr::ToWrRect(relBounds), wr::PipelineId(mId)));
+}
+
+} // namespace layers
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/layers/wr/WebRenderContainerLayer.h
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_WEBRENDERCONTAINERLAYER_H
+#define GFX_WEBRENDERCONTAINERLAYER_H
+
+#include "Layers.h"
+#include "WebRenderLayerManager.h"
+
+namespace mozilla {
+namespace layers {
+
+class WebRenderContainerLayer : public WebRenderLayer,
+                                public ContainerLayer
+{
+public:
+  explicit WebRenderContainerLayer(WebRenderLayerManager* aManager)
+    : ContainerLayer(aManager, static_cast<WebRenderLayer*>(this))
+  {
+    MOZ_COUNT_CTOR(WebRenderContainerLayer);
+  }
+
+protected:
+  virtual ~WebRenderContainerLayer()
+  {
+    ContainerLayer::RemoveAllChildren();
+    MOZ_COUNT_DTOR(WebRenderContainerLayer);
+  }
+
+public:
+  Layer* GetLayer() override { return this; }
+  void RenderLayer() override;
+
+  virtual void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface) override
+  {
+    DefaultComputeEffectiveTransforms(aTransformToSurface);
+  }
+};
+
+class WebRenderRefLayer : public WebRenderLayer,
+                          public RefLayer {
+public:
+  explicit WebRenderRefLayer(WebRenderLayerManager* aManager) :
+    RefLayer(aManager, static_cast<WebRenderLayer*>(this))
+  {
+    MOZ_COUNT_CTOR(WebRenderRefLayer);
+  }
+
+protected:
+  virtual ~WebRenderRefLayer()
+  {
+    MOZ_COUNT_DTOR(WebRenderRefLayer);
+  }
+
+public:
+  Layer* GetLayer() override { return this; }
+  void RenderLayer() override;
+
+  virtual void ComputeEffectiveTransforms(const gfx::Matrix4x4& aTransformToSurface) override
+  {
+    DefaultComputeEffectiveTransforms(aTransformToSurface);
+  }
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // GFX_WEBRENDERCONTAINERLAYER_H
new file mode 100644
--- /dev/null
+++ b/gfx/layers/wr/WebRenderImageHost.cpp
@@ -0,0 +1,200 @@
+/* -*- 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 "WebRenderImageHost.h"
+
+#include "LayersLogging.h"              // for AppendToString
+
+#include "mozilla/layers/Compositor.h"  // for Compositor
+#include "mozilla/layers/Effects.h"     // for TexturedEffect, Effect, etc
+#include "mozilla/layers/LayerManagerComposite.h"     // for TexturedEffect, Effect, etc
+#include "nsAString.h"
+#include "nsDebug.h"                    // for NS_WARNING, NS_ASSERTION
+#include "nsPrintfCString.h"            // for nsPrintfCString
+#include "nsString.h"                   // for nsAutoCString
+
+namespace mozilla {
+
+using namespace gfx;
+
+namespace layers {
+
+class ISurfaceAllocator;
+
+WebRenderImageHost::WebRenderImageHost(const TextureInfo& aTextureInfo)
+  : CompositableHost(aTextureInfo)
+  , ImageComposite()
+{}
+
+WebRenderImageHost::~WebRenderImageHost()
+{
+}
+
+void
+WebRenderImageHost::UseTextureHost(const nsTArray<TimedTexture>& aTextures)
+{
+  CompositableHost::UseTextureHost(aTextures);
+  MOZ_ASSERT(aTextures.Length() >= 1);
+
+  nsTArray<TimedImage> newImages;
+
+  for (uint32_t i = 0; i < aTextures.Length(); ++i) {
+    const TimedTexture& t = aTextures[i];
+    MOZ_ASSERT(t.mTexture);
+    if (i + 1 < aTextures.Length() &&
+        t.mProducerID == mLastProducerID && t.mFrameID < mLastFrameID) {
+      // Ignore frames before a frame that we already composited. We don't
+      // ever want to display these frames. This could be important if
+      // the frame producer adjusts timestamps (e.g. to track the audio clock)
+      // and the new frame times are earlier.
+      continue;
+    }
+    TimedImage& img = *newImages.AppendElement();
+    img.mTextureHost = t.mTexture;
+    img.mTimeStamp = t.mTimeStamp;
+    img.mPictureRect = t.mPictureRect;
+    img.mFrameID = t.mFrameID;
+    img.mProducerID = t.mProducerID;
+    img.mTextureHost->SetCropRect(img.mPictureRect);
+    img.mTextureHost->Updated();
+  }
+
+  mImages.SwapElements(newImages);
+  newImages.Clear();
+}
+
+void
+WebRenderImageHost::CleanupResources()
+{
+}
+
+void
+WebRenderImageHost::RemoveTextureHost(TextureHost* aTexture)
+{
+  CompositableHost::RemoveTextureHost(aTexture);
+
+  for (int32_t i = mImages.Length() - 1; i >= 0; --i) {
+    if (mImages[i].mTextureHost == aTexture) {
+      aTexture->UnbindTextureSource();
+      mImages.RemoveElementAt(i);
+    }
+  }
+}
+
+TimeStamp
+WebRenderImageHost::GetCompositionTime() const
+{
+  // XXX temporary workaround
+  return TimeStamp::Now();
+}
+
+TextureHost*
+WebRenderImageHost::GetAsTextureHost(IntRect* aPictureRect)
+{
+  MOZ_ASSERT_UNREACHABLE("unexpected to be called");
+  return nullptr;
+}
+
+void WebRenderImageHost::Attach(Layer* aLayer,
+                       Compositor* aCompositor,
+                       AttachFlags aFlags)
+{
+  MOZ_ASSERT_UNREACHABLE("unexpected to be called");
+}
+
+void
+WebRenderImageHost::Composite(LayerComposite* aLayer,
+                     EffectChain& aEffectChain,
+                     float aOpacity,
+                     const gfx::Matrix4x4& aTransform,
+                     const gfx::SamplingFilter aSamplingFilter,
+                     const gfx::IntRect& aClipRect,
+                     const nsIntRegion* aVisibleRegion,
+                     const Maybe<gfx::Polygon>& aGeometry)
+{
+  MOZ_ASSERT_UNREACHABLE("unexpected to be called");
+}
+
+void
+WebRenderImageHost::SetCompositor(Compositor* aCompositor)
+{
+  if (mCompositor != aCompositor) {
+    for (auto& img : mImages) {
+      img.mTextureHost->SetCompositor(aCompositor);
+    }
+  }
+  CompositableHost::SetCompositor(aCompositor);
+}
+
+void
+WebRenderImageHost::PrintInfo(std::stringstream& aStream, const char* aPrefix)
+{
+  aStream << aPrefix;
+  aStream << nsPrintfCString("WebRenderImageHost (0x%p)", this).get();
+
+  nsAutoCString pfx(aPrefix);
+  pfx += "  ";
+  for (auto& img : mImages) {
+    aStream << "\n";
+    img.mTextureHost->PrintInfo(aStream, pfx.get());
+    AppendToString(aStream, img.mPictureRect, " [picture-rect=", "]");
+  }
+}
+
+void
+WebRenderImageHost::Dump(std::stringstream& aStream,
+                const char* aPrefix,
+                bool aDumpHtml)
+{
+  for (auto& img : mImages) {
+    aStream << aPrefix;
+    aStream << (aDumpHtml ? "<ul><li>TextureHost: "
+                             : "TextureHost: ");
+    DumpTextureHost(aStream, img.mTextureHost);
+    aStream << (aDumpHtml ? " </li></ul> " : " ");
+  }
+}
+
+LayerRenderState
+WebRenderImageHost::GetRenderState()
+{
+  return LayerRenderState();
+}
+
+already_AddRefed<gfx::DataSourceSurface>
+WebRenderImageHost::GetAsSurface()
+{
+  TimedImage* img = ChooseImage();
+  if (img) {
+    return img->mTextureHost->GetAsSurface();
+  }
+  return nullptr;
+}
+
+bool
+WebRenderImageHost::Lock()
+{
+  MOZ_ASSERT_UNREACHABLE("unexpected to be called");
+  return false;
+}
+
+void
+WebRenderImageHost::Unlock()
+{
+  MOZ_ASSERT_UNREACHABLE("unexpected to be called");
+}
+
+IntSize
+WebRenderImageHost::GetImageSize() const
+{
+  const TimedImage* img = ChooseImage();
+  if (img) {
+    return IntSize(img->mPictureRect.width, img->mPictureRect.height);
+  }
+  return IntSize();
+}
+
+} // namespace layers
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/layers/wr/WebRenderImageHost.h
@@ -0,0 +1,74 @@
+/* -*- 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/. */
+
+#ifndef MOZILLA_GFX_WEBRENDERIMAGEHOST_H
+#define MOZILLA_GFX_WEBRENDERIMAGEHOST_H
+
+#include "CompositableHost.h"           // for CompositableHost
+#include "mozilla/layers/ImageComposite.h"  // for ImageComposite
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * ImageHost. Works with ImageClientSingle and ImageClientBuffered
+ */
+class WebRenderImageHost : public CompositableHost,
+                           public ImageComposite
+{
+public:
+  explicit WebRenderImageHost(const TextureInfo& aTextureInfo);
+  ~WebRenderImageHost();
+
+  virtual CompositableType GetType() override { return mTextureInfo.mCompositableType; }
+
+  virtual void Composite(LayerComposite* aLayer,
+                         EffectChain& aEffectChain,
+                         float aOpacity,
+                         const gfx::Matrix4x4& aTransform,
+                         const gfx::SamplingFilter aSamplingFilter,
+                         const gfx::IntRect& aClipRect,
+                         const nsIntRegion* aVisibleRegion = nullptr,
+                         const Maybe<gfx::Polygon>& aGeometry = Nothing()) override;
+
+  virtual void UseTextureHost(const nsTArray<TimedTexture>& aTextures) override;
+
+  virtual void RemoveTextureHost(TextureHost* aTexture) override;
+
+  virtual TextureHost* GetAsTextureHost(gfx::IntRect* aPictureRect = nullptr) override;
+
+  virtual void Attach(Layer* aLayer,
+                      Compositor* aCompositor,
+                      AttachFlags aFlags = NO_FLAGS) override;
+
+  virtual void SetCompositor(Compositor* aCompositor) override;
+
+  gfx::IntSize GetImageSize() const override;
+
+  virtual LayerRenderState GetRenderState() override;
+
+  virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) override;
+
+  virtual void Dump(std::stringstream& aStream,
+                    const char* aPrefix = "",
+                    bool aDumpHtml = false) override;
+
+  virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override;
+
+  virtual bool Lock() override;
+
+  virtual void Unlock() override;
+
+  virtual void CleanupResources() override;
+
+protected:
+  // ImageComposite
+  virtual TimeStamp GetCompositionTime() const override;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // MOZILLA_GFX_WEBRENDERIMAGEHOST_H
new file mode 100644
--- /dev/null
+++ b/gfx/layers/wr/WebRenderImageLayer.cpp
@@ -0,0 +1,175 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "WebRenderImageLayer.h"
+
+#include "WebRenderLayersLogging.h"
+#include "mozilla/layers/ImageClient.h"
+#include "mozilla/layers/TextureClientRecycleAllocator.h"
+#include "mozilla/layers/TextureWrapperImage.h"
+#include "mozilla/layers/WebRenderBridgeChild.h"
+#include "mozilla/webrender/WebRenderTypes.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace gfx;
+
+WebRenderImageLayer::WebRenderImageLayer(WebRenderLayerManager* aLayerManager)
+  : ImageLayer(aLayerManager, static_cast<WebRenderLayer*>(this))
+  , mExternalImageId(0)
+  , mImageClientTypeContainer(CompositableType::UNKNOWN)
+{
+  MOZ_COUNT_CTOR(WebRenderImageLayer);
+}
+
+WebRenderImageLayer::~WebRenderImageLayer()
+{
+  MOZ_COUNT_DTOR(WebRenderImageLayer);
+  if (mExternalImageId) {
+    WrBridge()->DeallocExternalImageId(mExternalImageId);
+  }
+}
+
+CompositableType
+WebRenderImageLayer::GetImageClientType()
+{
+  if (mImageClientTypeContainer != CompositableType::UNKNOWN) {
+    return mImageClientTypeContainer;
+  }
+
+  if (mContainer->IsAsync()) {
+    mImageClientTypeContainer = CompositableType::IMAGE_BRIDGE;
+    return mImageClientTypeContainer;
+  }
+
+  AutoLockImage autoLock(mContainer);
+
+  mImageClientTypeContainer = autoLock.HasImage()
+    ? CompositableType::IMAGE : CompositableType::UNKNOWN;
+  return mImageClientTypeContainer;
+}
+
+already_AddRefed<gfx::SourceSurface>
+WebRenderImageLayer::GetAsSourceSurface()
+{
+  if (!mContainer) {
+    return nullptr;
+  }
+  AutoLockImage autoLock(mContainer);
+  Image *image = autoLock.GetImage();
+  if (!image) {
+    return nullptr;
+  }
+  RefPtr<gfx::SourceSurface> surface = image->GetAsSourceSurface();
+  if (!surface || !surface->IsValid()) {
+    return nullptr;
+  }
+  return surface.forget();
+}
+
+void
+WebRenderImageLayer::ClearCachedResources()
+{
+  if (mImageClient) {
+    mImageClient->ClearCachedResources();
+  }
+}
+
+void
+WebRenderImageLayer::RenderLayer()
+{
+  if (!mContainer) {
+     return;
+  }
+
+  CompositableType type = GetImageClientType();
+  if (type == CompositableType::UNKNOWN) {
+    return;
+  }
+
+  MOZ_ASSERT(GetImageClientType() != CompositableType::UNKNOWN);
+
+  if (GetImageClientType() == CompositableType::IMAGE && !mImageClient) {
+    mImageClient = ImageClient::CreateImageClient(CompositableType::IMAGE,
+                                                  WrBridge(),
+                                                  TextureFlags::DEFAULT);
+    if (!mImageClient) {
+      return;
+    }
+    mImageClient->Connect();
+  }
+
+  if (!mExternalImageId) {
+    if (GetImageClientType() == CompositableType::IMAGE_BRIDGE) {
+      MOZ_ASSERT(!mImageClient);
+      mExternalImageId = WrBridge()->AllocExternalImageId(mContainer->GetAsyncContainerHandle());
+    } else {
+      // Handle CompositableType::IMAGE case
+      MOZ_ASSERT(mImageClient);
+      mExternalImageId = WrBridge()->AllocExternalImageIdForCompositable(mImageClient);
+    }
+  }
+  MOZ_ASSERT(mExternalImageId);
+
+  // XXX Not good for async ImageContainer case.
+  AutoLockImage autoLock(mContainer);
+  Image* image = autoLock.GetImage();
+  if (!image) {
+    return;
+  }
+  gfx::IntSize size = image->GetSize();
+
+  if (mImageClient && !mImageClient->UpdateImage(mContainer, /* unused */0)) {
+    return;
+  }
+
+  WrScrollFrameStackingContextGenerator scrollFrames(this);
+
+  Rect rect(0, 0, size.width, size.height);
+
+  Rect clip;
+  if (GetClipRect().isSome()) {
+      clip = RelativeToTransformedVisible(IntRectToRect(GetClipRect().ref().ToUnknownRect()));
+  } else {
+      clip = rect;
+  }
+
+  Rect relBounds = TransformedVisibleBoundsRelativeToParent();
+  Rect overflow(0, 0, relBounds.width, relBounds.height);
+  Matrix4x4 transform;// = GetTransform();
+  Maybe<WrImageMask> mask = buildMaskLayer();
+  WrTextureFilter filter = (mSamplingFilter == gfx::SamplingFilter::POINT) ? WrTextureFilter::Point : WrTextureFilter::Linear;
+  WrMixBlendMode mixBlendMode = wr::ToWrMixBlendMode(GetMixBlendMode());
+
+  if (gfxPrefs::LayersDump()) {
+    printf_stderr("ImageLayer %p using bounds=%s, overflow=%s, transform=%s, rect=%s, clip=%s, texture-filter=%s, mix-blend-mode=%s\n",
+                  this->GetLayer(),
+                  Stringify(relBounds).c_str(),
+                  Stringify(overflow).c_str(),
+                  Stringify(transform).c_str(),
+                  Stringify(rect).c_str(),
+                  Stringify(clip).c_str(),
+                  Stringify(filter).c_str(),
+                  Stringify(mixBlendMode).c_str());
+  }
+
+  WrBridge()->AddWebRenderCommand(
+    OpDPPushStackingContext(wr::ToWrRect(relBounds),
+                            wr::ToWrRect(overflow),
+                            mask,
+                            1.0f,
+                            GetAnimations(),
+                            transform,
+                            mixBlendMode,
+                            FrameMetrics::NULL_SCROLL_ID));
+  WrBridge()->AddWebRenderCommand(OpDPPushExternalImageId(LayerIntRegion(), wr::ToWrRect(rect), wr::ToWrRect(clip), Nothing(), filter, mExternalImageId));
+  WrBridge()->AddWebRenderCommand(OpDPPopStackingContext());
+
+  //mContainer->SetImageFactory(originalIF);
+}
+
+} // namespace layers
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/layers/wr/WebRenderImageLayer.h
@@ -0,0 +1,48 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_WEBRENDERIMAGELAYER_H
+#define GFX_WEBRENDERIMAGELAYER_H
+
+#include "ImageLayers.h"
+#include "WebRenderLayerManager.h"
+
+namespace mozilla {
+namespace layers {
+
+class ImageClient;
+
+class WebRenderImageLayer : public WebRenderLayer,
+                            public ImageLayer {
+public:
+  explicit WebRenderImageLayer(WebRenderLayerManager* aLayerManager);
+
+  virtual already_AddRefed<gfx::SourceSurface> GetAsSourceSurface() override;
+
+  virtual void ClearCachedResources() override;
+protected:
+  virtual ~WebRenderImageLayer();
+
+  WebRenderLayerManager* Manager()
+  {
+    return static_cast<WebRenderLayerManager*>(mManager);
+  }
+
+public:
+  Layer* GetLayer() override { return this; }
+  void RenderLayer() override;
+
+protected:
+  CompositableType GetImageClientType();
+
+  uint64_t mExternalImageId;
+  RefPtr<ImageClient> mImageClient;
+  CompositableType mImageClientTypeContainer;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // GFX_WEBRENDERIMAGELAYER_H
new file mode 100644
--- /dev/null
+++ b/gfx/layers/wr/WebRenderLayerManager.cpp
@@ -0,0 +1,542 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "WebRenderLayerManager.h"
+
+#include "apz/src/AsyncPanZoomController.h"
+#include "WebRenderLayersLogging.h"
+#include "mozilla/dom/TabChild.h"
+#include "mozilla/layers/APZCTreeManager.h"
+#include "mozilla/layers/AsyncCompositionManager.h"
+#include "mozilla/layers/CompositorBridgeChild.h"
+#include "mozilla/layers/TextureClient.h"
+#include "mozilla/layers/WebRenderBridgeChild.h"
+#include "mozilla/widget/PlatformWidgetTypes.h"
+#include "nsThreadUtils.h"
+#include "TreeTraversal.h"
+#include "WebRenderBorderLayer.h"
+#include "WebRenderCanvasLayer.h"
+#include "WebRenderColorLayer.h"
+#include "WebRenderContainerLayer.h"
+#include "WebRenderImageLayer.h"
+#include "WebRenderPaintedLayer.h"
+#include "WebRenderTextLayer.h"
+
+namespace mozilla {
+
+using namespace gfx;
+
+namespace layers {
+
+WebRenderLayerManager*
+WebRenderLayer::WrManager()
+{
+  return static_cast<WebRenderLayerManager*>(GetLayer()->Manager());
+}
+
+WebRenderBridgeChild*
+WebRenderLayer::WrBridge()
+{
+  return WrManager()->WrBridge();
+}
+
+Rect
+WebRenderLayer::RelativeToVisible(Rect aRect)
+{
+  IntRect bounds = GetLayer()->GetVisibleRegion().GetBounds().ToUnknownRect();
+  aRect.MoveBy(-bounds.x, -bounds.y);
+  return aRect;
+}
+
+Rect
+WebRenderLayer::RelativeToTransformedVisible(Rect aRect)
+{
+  IntRect bounds = GetLayer()->GetVisibleRegion().GetBounds().ToUnknownRect();
+  Rect transformed = GetLayer()->GetTransform().TransformBounds(IntRectToRect(bounds));
+  aRect.MoveBy(-transformed.x, -transformed.y);
+  return aRect;
+}
+
+Rect
+WebRenderLayer::ParentStackingContextBounds(size_t aScrollMetadataIndex)
+{
+  // Walk up to find the parent stacking context. This will be created either
+  // by the nearest scrollable metrics, or by the parent layer which must be a
+  // ContainerLayer.
+  Layer* layer = GetLayer();
+  for (size_t i = aScrollMetadataIndex + 1; i < layer->GetScrollMetadataCount(); i++) {
+    if (layer->GetFrameMetrics(i).IsScrollable()) {
+      return layer->GetFrameMetrics(i).GetCompositionBounds().ToUnknownRect();
+    }
+  }
+  if (layer->GetParent()) {
+    return IntRectToRect(layer->GetParent()->GetVisibleRegion().GetBounds().ToUnknownRect());
+  }
+  return Rect();
+}
+
+Rect
+WebRenderLayer::RelativeToParent(Rect aRect)
+{
+  Rect parentBounds = ParentStackingContextBounds(-1);
+  aRect.MoveBy(-parentBounds.x, -parentBounds.y);
+  return aRect;
+}
+
+Rect
+WebRenderLayer::TransformedVisibleBoundsRelativeToParent()
+{
+  IntRect bounds = GetLayer()->GetVisibleRegion().GetBounds().ToUnknownRect();
+  Rect transformed = GetLayer()->GetTransform().TransformBounds(IntRectToRect(bounds));
+  return RelativeToParent(transformed);
+}
+
+Maybe<WrImageMask>
+WebRenderLayer::buildMaskLayer() {
+  Maybe<WrImageMask> mask = Nothing();
+  WrImageMask imageMask;
+  Layer* maskLayer = GetLayer()->GetMaskLayer();
+
+  if (maskLayer) {
+    RefPtr<SourceSurface> surface = WebRenderLayer::ToWebRenderLayer(maskLayer)->GetAsSourceSurface();
+    if (surface) {
+      Matrix transform;
+      Matrix4x4 effectiveTransform = maskLayer->GetEffectiveTransform();
+      DebugOnly<bool> maskIs2D = effectiveTransform.CanDraw2D(&transform);
+      NS_ASSERTION(maskIs2D, "How did we end up with a 3D transform here?!");
+      //XXX: let's assert that the mask transform is the same as the layer transform
+      //transform.PostTranslate(-aDeviceOffset.x, -aDeviceOffset.y);
+      {
+          RefPtr<DataSourceSurface> dataSurface = surface->GetDataSurface();
+          DataSourceSurface::ScopedMap map(dataSurface, DataSourceSurface::MapType::READ);
+          gfx::IntSize size = surface->GetSize();
+          MOZ_RELEASE_ASSERT(surface->GetFormat() == SurfaceFormat::A8, "bad format");
+          wr::ByteBuffer buf(size.height * map.GetStride(), map.GetData());
+          wr::ImageKey maskKey;
+          WrBridge()->SendAddImage(size, map.GetStride(), SurfaceFormat::A8, buf, &maskKey);
+
+          imageMask.image = maskKey.mHandle;
+          imageMask.rect = wr::ToWrRect(Rect(0, 0, size.width, size.height));
+          imageMask.repeat = false;
+          WrManager()->AddImageKeyForDiscard(maskKey);
+          mask = Some(imageMask);
+      }
+    }
+  }
+  return mask;
+}
+
+
+
+
+WrScrollFrameStackingContextGenerator::WrScrollFrameStackingContextGenerator(
+        WebRenderLayer* aLayer)
+  : mLayer(aLayer)
+{
+  Matrix4x4 identity;
+  Layer* layer = mLayer->GetLayer();
+  for (size_t i = layer->GetScrollMetadataCount(); i > 0; i--) {
+    const FrameMetrics& fm = layer->GetFrameMetrics(i - 1);
+    if (!fm.IsScrollable()) {
+      continue;
+    }
+    Rect bounds = fm.GetCompositionBounds().ToUnknownRect();
+    Rect overflow = (fm.GetExpandedScrollableRect() * fm.LayersPixelsPerCSSPixel()).ToUnknownRect();
+    Point scrollPos = (fm.GetScrollOffset() * fm.LayersPixelsPerCSSPixel()).ToUnknownPoint();
+    Rect parentBounds = mLayer->ParentStackingContextBounds(i);
+    bounds.MoveBy(-parentBounds.x, -parentBounds.y);
+    // Subtract the MT scroll position from the overflow here so that the WR
+    // scroll offset (which is the APZ async scroll component) always fits in
+    // the available overflow. If we didn't do this and WR did bounds checking
+    // on the scroll offset, we'd fail those checks.
+    overflow.MoveBy(bounds.x - scrollPos.x, bounds.y - scrollPos.y);
+    if (gfxPrefs::LayersDump()) {
+      printf_stderr("Pushing stacking context id %" PRIu64 " with bounds=%s, overflow=%s\n",
+        fm.GetScrollId(), Stringify(bounds).c_str(), Stringify(overflow).c_str());
+    }
+
+    mLayer->WrBridge()->AddWebRenderCommand(
+      OpDPPushStackingContext(wr::ToWrRect(bounds),
+                              wr::ToWrRect(overflow),
+                              Nothing(),
+                              1.0f,
+                              layer->GetAnimations(),
+                              identity,
+                              WrMixBlendMode::Normal,
+                              fm.GetScrollId()));
+  }
+}
+
+WrScrollFrameStackingContextGenerator::~WrScrollFrameStackingContextGenerator()
+{
+  Layer* layer = mLayer->GetLayer();
+  for (size_t i = 0; i < layer->GetScrollMetadataCount(); i++) {
+    const FrameMetrics& fm = layer->GetFrameMetrics(i);
+    if (!fm.IsScrollable()) {
+      continue;
+    }
+    if (gfxPrefs::LayersDump()) printf_stderr("Popping stacking context id %" PRIu64"\n", fm.GetScrollId());
+    mLayer->WrBridge()->AddWebRenderCommand(OpDPPopStackingContext());
+  }
+}
+
+
+WebRenderLayerManager::WebRenderLayerManager(nsIWidget* aWidget)
+  : mWidget(aWidget)
+  , mLatestTransactionId(0)
+  , mTarget(nullptr)
+{
+  MOZ_COUNT_CTOR(WebRenderLayerManager);
+}
+
+KnowsCompositor*
+WebRenderLayerManager::AsKnowsCompositor()
+{
+  return mWrChild;
+}
+
+void
+WebRenderLayerManager::Initialize(PCompositorBridgeChild* aCBChild,
+                                  uint64_t aLayersId,
+                                  TextureFactoryIdentifier* aTextureFactoryIdentifier)
+{
+  MOZ_ASSERT(mWrChild == nullptr);
+  MOZ_ASSERT(aTextureFactoryIdentifier);
+
+  TextureFactoryIdentifier textureFactoryIdentifier;
+  PWebRenderBridgeChild* bridge = aCBChild->SendPWebRenderBridgeConstructor(wr::PipelineId(aLayersId),
+                                                                            &textureFactoryIdentifier);
+  MOZ_ASSERT(bridge);
+  mWrChild = static_cast<WebRenderBridgeChild*>(bridge);
+  LayoutDeviceIntSize size = mWidget->GetClientSize();
+  WrBridge()->SendCreate(size.ToUnknownSize());
+  WrBridge()->IdentifyTextureHost(textureFactoryIdentifier);
+  *aTextureFactoryIdentifier = textureFactoryIdentifier;
+}
+
+void
+WebRenderLayerManager::Destroy()
+{
+  if (IsDestroyed()) {
+    return;
+  }
+
+  LayerManager::Destroy();
+  DiscardImages();
+  WrBridge()->Destroy();
+
+  if (mTransactionIdAllocator) {
+    // Make sure to notify the refresh driver just in case it's waiting on a
+    // pending transaction. Do this at the top of the event loop so we don't
+    // cause a paint to occur during compositor shutdown.
+    RefPtr<TransactionIdAllocator> allocator = mTransactionIdAllocator;
+    uint64_t id = mLatestTransactionId;
+
+    RefPtr<Runnable> task = NS_NewRunnableFunction([allocator, id] () -> void {
+      allocator->NotifyTransactionCompleted(id);
+    });
+    NS_DispatchToMainThread(task.forget());
+  }
+}
+
+WebRenderLayerManager::~WebRenderLayerManager()
+{
+  Destroy();
+  MOZ_COUNT_DTOR(WebRenderLayerManager);
+}
+
+CompositorBridgeChild*
+WebRenderLayerManager::GetCompositorBridgeChild()
+{
+  return mWidget ? mWidget->GetRemoteRenderer() : nullptr;
+}
+
+int32_t
+WebRenderLayerManager::GetMaxTextureSize() const
+{
+  return WrBridge()->GetMaxTextureSize();
+}
+
+bool
+WebRenderLayerManager::BeginTransactionWithTarget(gfxContext* aTarget)
+{
+  mTarget = aTarget;
+  return BeginTransaction();
+}
+
+bool
+WebRenderLayerManager::BeginTransaction()
+{
+  return true;
+}
+
+bool
+WebRenderLayerManager::EndEmptyTransaction(EndTransactionFlags aFlags)
+{
+  return false;
+}
+
+void
+WebRenderLayerManager::EndTransaction(DrawPaintedLayerCallback aCallback,
+                                      void* aCallbackData,
+                                      EndTransactionFlags aFlags)
+{
+  DiscardImages();
+
+  mPaintedLayerCallback = aCallback;
+  mPaintedLayerCallbackData = aCallbackData;
+
+  if (gfxPrefs::LayersDump()) {
+    this->Dump();
+  }
+
+  // Since we don't do repeat transactions right now, just set the time
+  mAnimationReadyTime = TimeStamp::Now();
+
+  LayoutDeviceIntSize size = mWidget->GetClientSize();
+  if (!WrBridge()->DPBegin(size.ToUnknownSize())) {
+    return;
+  }
+
+  WebRenderLayer::ToWebRenderLayer(mRoot)->RenderLayer();
+
+  bool sync = mTarget != nullptr;
+  mLatestTransactionId = mTransactionIdAllocator->GetTransactionId();
+
+  WrBridge()->DPEnd(sync, mLatestTransactionId);
+
+  MakeSnapshotIfRequired(size);
+
+  // this may result in Layers being deleted, which results in
+  // PLayer::Send__delete__() and DeallocShmem()
+  mKeepAlive.Clear();
+}
+
+void
+WebRenderLayerManager::MakeSnapshotIfRequired(LayoutDeviceIntSize aSize)
+{
+  if (!mTarget || aSize.IsEmpty()) {
+    return;
+  }
+
+  // XXX Add other TextureData supports.
+  // Only BufferTexture is supported now.
+
+  // TODO: fixup for proper surface format.
+  RefPtr<TextureClient> texture =
+    TextureClient::CreateForRawBufferAccess(WrBridge(),
+                                            SurfaceFormat::B8G8R8A8,
+                                            aSize.ToUnknownSize(),
+                                            BackendType::SKIA,
+                                            TextureFlags::DEFAULT);
+  if (!texture) {
+    return;
+  }
+
+  texture->InitIPDLActor(WrBridge());
+  if (!texture->GetIPDLActor()) {
+    return;
+  }
+
+  IntRect bounds = ToOutsideIntRect(mTarget->GetClipExtents());
+  if (!WrBridge()->SendDPGetSnapshot(texture->GetIPDLActor())) {
+    return;
+  }
+
+  TextureClientAutoLock autoLock(texture, OpenMode::OPEN_READ_ONLY);
+  if (!autoLock.Succeeded()) {
+    return;
+  }
+  RefPtr<DrawTarget> drawTarget = texture->BorrowDrawTarget();
+  if (!drawTarget || !drawTarget->IsValid()) {
+    return;
+  }
+  RefPtr<SourceSurface> snapshot = drawTarget->Snapshot();
+/*
+  static int count = 0;
+  char filename[100];
+  snprintf(filename, 100, "output%d.png", count++);
+  printf_stderr("Writing to :%s\n", filename);
+  gfxUtils::WriteAsPNG(snapshot, filename);
+  */
+
+  Rect dst(bounds.x, bounds.y, bounds.width, bounds.height);
+  Rect src(0, 0, bounds.width, bounds.height);
+
+  // The data we get from webrender is upside down. So flip and translate up so the image is rightside up.
+  // Webrender always does a full screen readback.
+  SurfacePattern pattern(snapshot, ExtendMode::CLAMP,
+                         Matrix::Scaling(1.0, -1.0).PostTranslate(0.0, aSize.height));
+  DrawTarget* dt = mTarget->GetDrawTarget();
+  MOZ_RELEASE_ASSERT(dt);
+  dt->FillRect(dst, pattern);
+
+  mTarget = nullptr;
+}
+
+void
+WebRenderLayerManager::AddImageKeyForDiscard(wr::ImageKey key)
+{
+  mImageKeys.push_back(key);
+}
+
+void
+WebRenderLayerManager::DiscardImages()
+{
+  for (auto key : mImageKeys) {
+      WrBridge()->SendDeleteImage(key);
+  }
+  mImageKeys.clear();
+}
+
+void
+WebRenderLayerManager::Hold(Layer* aLayer)
+{
+  mKeepAlive.AppendElement(aLayer);
+}
+
+void
+WebRenderLayerManager::SetLayerObserverEpoch(uint64_t aLayerObserverEpoch)
+{
+  WrBridge()->SendSetLayerObserverEpoch(aLayerObserverEpoch);
+}
+
+void
+WebRenderLayerManager::DidComposite(uint64_t aTransactionId,
+                                    const mozilla::TimeStamp& aCompositeStart,
+                                    const mozilla::TimeStamp& aCompositeEnd)
+{
+  MOZ_ASSERT(mWidget);
+
+  // |aTransactionId| will be > 0 if the compositor is acknowledging a shadow
+  // layers transaction.
+  if (aTransactionId) {
+    nsIWidgetListener *listener = mWidget->GetWidgetListener();
+    if (listener) {
+      listener->DidCompositeWindow(aTransactionId, aCompositeStart, aCompositeEnd);
+    }
+    listener = mWidget->GetAttachedWidgetListener();
+    if (listener) {
+      listener->DidCompositeWindow(aTransactionId, aCompositeStart, aCompositeEnd);
+    }
+    mTransactionIdAllocator->NotifyTransactionCompleted(aTransactionId);
+  }
+
+  // These observers fire whether or not we were in a transaction.
+  for (size_t i = 0; i < mDidCompositeObservers.Length(); i++) {
+    mDidCompositeObservers[i]->DidComposite();
+  }
+}
+
+void
+WebRenderLayerManager::ClearLayer(Layer* aLayer)
+{
+  aLayer->ClearCachedResources();
+  for (Layer* child = aLayer->GetFirstChild(); child;
+       child = child->GetNextSibling()) {
+    ClearLayer(child);
+  }
+}
+
+void
+WebRenderLayerManager::ClearCachedResources(Layer* aSubtree)
+{
+  WrBridge()->SendClearCachedResources();
+  if (aSubtree) {
+    ClearLayer(aSubtree);
+  } else if (mRoot) {
+    ClearLayer(mRoot);
+  }
+}
+
+void
+WebRenderLayerManager::UpdateTextureFactoryIdentifier(const TextureFactoryIdentifier& aNewIdentifier,
+                                                      uint64_t aDeviceResetSeqNo)
+{
+  WrBridge()->IdentifyTextureHost(aNewIdentifier);
+}
+
+TextureFactoryIdentifier
+WebRenderLayerManager::GetTextureFactoryIdentifier()
+{
+  return WrBridge()->GetTextureFactoryIdentifier();
+}
+
+void
+WebRenderLayerManager::AddDidCompositeObserver(DidCompositeObserver* aObserver)
+{
+  if (!mDidCompositeObservers.Contains(aObserver)) {
+    mDidCompositeObservers.AppendElement(aObserver);
+  }
+}
+
+void
+WebRenderLayerManager::RemoveDidCompositeObserver(DidCompositeObserver* aObserver)
+{
+  mDidCompositeObservers.RemoveElement(aObserver);
+}
+
+void
+WebRenderLayerManager::SetRoot(Layer* aLayer)
+{
+  mRoot = aLayer;
+}
+
+already_AddRefed<PaintedLayer>
+WebRenderLayerManager::CreatePaintedLayer()
+{
+  return MakeAndAddRef<WebRenderPaintedLayer>(this);
+}
+
+already_AddRefed<ContainerLayer>
+WebRenderLayerManager::CreateContainerLayer()
+{
+  return MakeAndAddRef<WebRenderContainerLayer>(this);
+}
+
+already_AddRefed<ImageLayer>
+WebRenderLayerManager::CreateImageLayer()
+{
+  return MakeAndAddRef<WebRenderImageLayer>(this);
+}
+
+already_AddRefed<CanvasLayer>
+WebRenderLayerManager::CreateCanvasLayer()
+{
+  return MakeAndAddRef<WebRenderCanvasLayer>(this);
+}
+
+already_AddRefed<ReadbackLayer>
+WebRenderLayerManager::CreateReadbackLayer()
+{
+  return nullptr;
+}
+
+already_AddRefed<ColorLayer>
+WebRenderLayerManager::CreateColorLayer()
+{
+  return MakeAndAddRef<WebRenderColorLayer>(this);
+}
+
+already_AddRefed<RefLayer>
+WebRenderLayerManager::CreateRefLayer()
+{
+  return MakeAndAddRef<WebRenderRefLayer>(this);
+}
+
+already_AddRefed<TextLayer>
+WebRenderLayerManager::CreateTextLayer()
+{
+  return MakeAndAddRef<WebRenderTextLayer>(this);
+}
+
+already_AddRefed<BorderLayer>
+WebRenderLayerManager::CreateBorderLayer()
+{
+  return MakeAndAddRef<WebRenderBorderLayer>(this);
+}
+
+} // namespace layers
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/layers/wr/WebRenderLayerManager.h
@@ -0,0 +1,179 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_WEBRENDERLAYERMANAGER_H
+#define GFX_WEBRENDERLAYERMANAGER_H
+
+#include "Layers.h"
+#include "mozilla/layers/CompositorController.h"
+#include "mozilla/layers/TransactionIdAllocator.h"
+#include "mozilla/webrender/webrender_ffi.h"
+#include "mozilla/webrender/WebRenderTypes.h"
+
+class nsIWidget;
+
+namespace mozilla {
+namespace layers {
+
+class CompositorBridgeChild;
+class KnowsCompositor;
+class PCompositorBridgeChild;
+class WebRenderBridgeChild;
+class WebRenderLayerManager;
+class APZCTreeManager;
+
+class WebRenderLayer
+{
+public:
+  virtual Layer* GetLayer() = 0;
+  virtual void RenderLayer() = 0;
+
+  virtual already_AddRefed<gfx::SourceSurface> GetAsSourceSurface() { return nullptr; }
+  static inline WebRenderLayer*
+  ToWebRenderLayer(Layer* aLayer)
+  {
+    return static_cast<WebRenderLayer*>(aLayer->ImplData());
+  }
+
+  WebRenderLayerManager* WrManager();
+  WebRenderBridgeChild* WrBridge();
+
+  gfx::Rect RelativeToVisible(gfx::Rect aRect);
+  gfx::Rect RelativeToTransformedVisible(gfx::Rect aRect);
+  gfx::Rect ParentStackingContextBounds(size_t aScrollMetadataIndex);
+  gfx::Rect RelativeToParent(gfx::Rect aRect);
+  gfx::Rect TransformedVisibleBoundsRelativeToParent();
+protected:
+  Maybe<WrImageMask> buildMaskLayer();
+
+};
+
+class MOZ_RAII WrScrollFrameStackingContextGenerator
+{
+public:
+  explicit WrScrollFrameStackingContextGenerator(WebRenderLayer* aLayer);
+  ~WrScrollFrameStackingContextGenerator();
+private:
+  WebRenderLayer* mLayer;
+};
+
+class WebRenderLayerManager final : public LayerManager
+{
+  typedef nsTArray<RefPtr<Layer> > LayerRefArray;
+
+public:
+  explicit WebRenderLayerManager(nsIWidget* aWidget);
+  void Initialize(PCompositorBridgeChild* aCBChild, uint64_t aLayersId, TextureFactoryIdentifier* aTextureFactoryIdentifier);
+
+  virtual void Destroy() override;
+
+protected:
+  virtual ~WebRenderLayerManager();
+
+public:
+  virtual KnowsCompositor* AsKnowsCompositor() override;
+  WebRenderLayerManager* AsWebRenderLayerManager() override { return this; }
+  virtual CompositorBridgeChild* GetCompositorBridgeChild() override;
+
+  virtual int32_t GetMaxTextureSize() const override;
+
+  virtual bool BeginTransactionWithTarget(gfxContext* aTarget) override;
+  virtual bool BeginTransaction() override;
+  virtual bool EndEmptyTransaction(EndTransactionFlags aFlags = END_DEFAULT) override;
+  virtual void EndTransaction(DrawPaintedLayerCallback aCallback,
+                              void* aCallbackData,
+                              EndTransactionFlags aFlags = END_DEFAULT) override;
+
+  virtual LayersBackend GetBackendType() override { return LayersBackend::LAYERS_WR; }
+  virtual void GetBackendName(nsAString& name) override { name.AssignLiteral("WebRender"); }
+  virtual const char* Name() const override { return "WebRender"; }
+
+  virtual void SetRoot(Layer* aLayer) override;
+
+  virtual already_AddRefed<PaintedLayer> CreatePaintedLayer() override;
+  virtual already_AddRefed<ContainerLayer> CreateContainerLayer() override;
+  virtual already_AddRefed<ImageLayer> CreateImageLayer() override;
+  virtual already_AddRefed<CanvasLayer> CreateCanvasLayer() override;
+  virtual already_AddRefed<ReadbackLayer> CreateReadbackLayer() override;
+  virtual already_AddRefed<ColorLayer> CreateColorLayer() override;
+  virtual already_AddRefed<RefLayer> CreateRefLayer() override;
+  virtual already_AddRefed<TextLayer> CreateTextLayer() override;
+  virtual already_AddRefed<BorderLayer> CreateBorderLayer() override;
+
+  virtual bool NeedsWidgetInvalidation() override { return true; }
+
+  virtual void SetLayerObserverEpoch(uint64_t aLayerObserverEpoch) override;
+
+  virtual void DidComposite(uint64_t aTransactionId,
+                            const mozilla::TimeStamp& aCompositeStart,
+                            const mozilla::TimeStamp& aCompositeEnd) override;
+
+  virtual void ClearCachedResources(Layer* aSubtree = nullptr) override;
+  virtual void UpdateTextureFactoryIdentifier(const TextureFactoryIdentifier& aNewIdentifier,
+                                              uint64_t aDeviceResetSeqNo) override;
+  virtual TextureFactoryIdentifier GetTextureFactoryIdentifier() override;
+
+  virtual void SetTransactionIdAllocator(TransactionIdAllocator* aAllocator) override
+  { mTransactionIdAllocator = aAllocator; }
+
+  virtual void AddDidCompositeObserver(DidCompositeObserver* aObserver) override;
+  virtual void RemoveDidCompositeObserver(DidCompositeObserver* aObserver) override;
+
+  DrawPaintedLayerCallback GetPaintedLayerCallback() const
+  { return mPaintedLayerCallback; }
+
+  void* GetPaintedLayerCallbackData() const
+  { return mPaintedLayerCallbackData; }
+
+  // adds an imagekey to a list of keys that will be discarded on the next
+  // transaction or destruction
+  void AddImageKeyForDiscard(wr::ImageKey);
+  void DiscardImages();
+
+  WebRenderBridgeChild* WrBridge() const { return mWrChild; }
+
+  void Hold(Layer* aLayer);
+
+private:
+  /**
+   * Take a snapshot of the parent context, and copy
+   * it into mTarget.
+   */
+  void MakeSnapshotIfRequired(LayoutDeviceIntSize aSize);
+
+  void ClearLayer(Layer* aLayer);
+
+private:
+  nsIWidget* MOZ_NON_OWNING_REF mWidget;
+  std::vector<wr::ImageKey> mImageKeys;
+
+  /* PaintedLayer callbacks; valid at the end of a transaciton,
+   * while rendering */
+  DrawPaintedLayerCallback mPaintedLayerCallback;
+  void *mPaintedLayerCallbackData;
+
+  RefPtr<WebRenderBridgeChild> mWrChild;
+
+  RefPtr<TransactionIdAllocator> mTransactionIdAllocator;
+  uint64_t mLatestTransactionId;
+
+  nsTArray<DidCompositeObserver*> mDidCompositeObservers;
+
+  LayerRefArray mKeepAlive;
+
+ // When we're doing a transaction in order to draw to a non-default
+ // target, the layers transaction is only performed in order to send
+ // a PLayers:Update.  We save the original non-default target to
+ // mTarget, and then perform the transaction. After the transaction ends,
+ // we send a message to our remote side to capture the actual pixels
+ // being drawn to the default target, and then copy those pixels
+ // back to mTarget.
+ RefPtr<gfxContext> mTarget;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif /* GFX_WEBRENDERLAYERMANAGER_H */
new file mode 100644
--- /dev/null
+++ b/gfx/layers/wr/WebRenderLayersLogging.cpp
@@ -0,0 +1,78 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* 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 "WebRenderLayersLogging.h"
+
+using namespace mozilla::gfx;
+
+namespace mozilla {
+namespace layers {
+
+void
+AppendToString(std::stringstream& aStream, WrMixBlendMode aMixBlendMode,
+               const char* pfx, const char* sfx)
+{
+  aStream << pfx;
+  switch (aMixBlendMode) {
+  case WrMixBlendMode::Normal:
+    aStream << "WrMixBlendMode::Normal"; break;
+  case WrMixBlendMode::Multiply:
+    aStream << "WrMixBlendMode::Multiply"; break;
+  case WrMixBlendMode::Screen:
+    aStream << "WrMixBlendMode::Screen"; break;
+  case WrMixBlendMode::Overlay:
+    aStream << "WrMixBlendMode::Overlay"; break;
+  case WrMixBlendMode::Darken:
+    aStream << "WrMixBlendMode::Darken"; break;
+  case WrMixBlendMode::Lighten:
+    aStream << "WrMixBlendMode::Lighten"; break;
+  case WrMixBlendMode::ColorDodge:
+    aStream << "WrMixBlendMode::ColorDodge"; break;
+  case WrMixBlendMode::ColorBurn:
+    aStream << "WrMixBlendMode::ColorBurn"; break;
+  case WrMixBlendMode::HardLight:
+    aStream << "WrMixBlendMode::HardLight"; break;
+  case WrMixBlendMode::SoftLight:
+    aStream << "WrMixBlendMode::SoftLight"; break;
+  case WrMixBlendMode::Difference:
+    aStream << "WrMixBlendMode::Difference"; break;
+  case WrMixBlendMode::Exclusion:
+    aStream << "WrMixBlendMode::Exclusion"; break;
+  case WrMixBlendMode::Hue:
+    aStream << "WrMixBlendMode::Hue"; break;
+  case WrMixBlendMode::Saturation:
+    aStream << "WrMixBlendMode::Saturation"; break;
+  case WrMixBlendMode::Color:
+    aStream << "WrMixBlendMode::Color"; break;
+  case WrMixBlendMode::Luminosity:
+    aStream << "WrMixBlendMode::Luminosity"; break;
+  case WrMixBlendMode::Sentinel:
+    NS_ERROR("unknown mix blend mode");
+    aStream << "???";
+  }
+  aStream << sfx;
+}
+
+void
+AppendToString(std::stringstream& aStream, WrTextureFilter aTextureFilter,
+               const char* pfx, const char* sfx)
+{
+  aStream << pfx;
+  switch (aTextureFilter) {
+  case WrTextureFilter::Linear:
+    aStream << "WrTextureFilter::Linear"; break;
+  case WrTextureFilter::Point:
+    aStream << "WrTextureFilter::Point"; break;
+  case WrTextureFilter::Sentinel:
+    NS_ERROR("unknown texture filter");
+    aStream << "???";
+  }
+  aStream << sfx;
+}
+
+} // namespace layers
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/layers/wr/WebRenderLayersLogging.h
@@ -0,0 +1,29 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+#ifndef GFX_WEBRENDERLAYERSLOGGING_H
+#define GFX_WEBRENDERLAYERSLOGGING_H
+
+#include "mozilla/webrender/WebRenderTypes.h"
+
+namespace mozilla {
+namespace layers {
+
+void
+AppendToString(std::stringstream& aStream, WrMixBlendMode aMixBlendMode,
+               const char* pfx="", const char* sfx="");
+
+void
+AppendToString(std::stringstream& aStream, WrTextureFilter aTextureFilter,
+               const char* pfx=&quo